arm: add secondary cpu boot callbacks to arm_boot.c

Create two functions, write_secondary_boot() and secondary_cpu_reset_hook(),
to allow platforms more control of how secondary CPUs are brought up. The
new functions default to NULL and aren't called unless they are populated
so there are no changes to existing platform models.

Signed-off-by: Mark Langsdorf <mark.langsdorf@calxeda.com>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Mark Langsdorf 2012-01-26 11:43:48 +00:00 committed by Peter Maydell
parent d9fa31a3e2
commit 9d5ba9bfbc
2 changed files with 60 additions and 11 deletions

View File

@ -30,12 +30,29 @@ struct arm_boot_info {
const char *kernel_cmdline;
const char *initrd_filename;
target_phys_addr_t loader_start;
/* multicore boards that use the default secondary core boot functions
* need to put the address of the secondary boot code, the boot reg,
* and the GIC address in the next 3 values, respectively. boards that
* have their own boot functions can use these values as they want.
*/
target_phys_addr_t smp_loader_start;
target_phys_addr_t smp_bootreg_addr;
target_phys_addr_t smp_priv_base;
int nb_cpus;
int board_id;
int (*atag_board)(const struct arm_boot_info *info, void *p);
/* multicore boards that use the default secondary core boot functions
* can ignore these two function calls. If the default functions won't
* work, then write_secondary_boot() should write a suitable blob of
* code mimicing the secondary CPU startup process used by the board's
* boot loader/boot ROM code, and secondary_cpu_reset_hook() should
* perform any necessary CPU reset handling and set the PC for thei
* secondary CPUs to point at this boot blob.
*/
void (*write_secondary_boot)(CPUState *env,
const struct arm_boot_info *info);
void (*secondary_cpu_reset_hook)(CPUState *env,
const struct arm_boot_info *info);
/* Used internally by arm_boot.c */
int is_linux;
target_phys_addr_t initrd_size;

View File

@ -28,8 +28,20 @@ static uint32_t bootloader[] = {
0 /* Kernel entry point. Set by integratorcp_init. */
};
/* Entry point for secondary CPUs. Enable interrupt controller and
Issue WFI until start address is written to system controller. */
/* Handling for secondary CPU boot in a multicore system.
* Unlike the uniprocessor/primary CPU boot, this is platform
* dependent. The default code here is based on the secondary
* CPU boot protocol used on realview/vexpress boards, with
* some parameterisation to increase its flexibility.
* QEMU platform models for which this code is not appropriate
* should override write_secondary_boot and secondary_cpu_reset_hook
* instead.
*
* This code enables the interrupt controllers for the secondary
* CPUs and then puts all the secondary CPUs into a loop waiting
* for an interprocessor interrupt and polling a configurable
* location for the kernel secondary CPU entry point.
*/
static uint32_t smpboot[] = {
0xe59f201c, /* ldr r2, privbase */
0xe59f001c, /* ldr r0, startaddr */
@ -44,6 +56,26 @@ static uint32_t smpboot[] = {
0 /* bootreg: Boot register address is held here */
};
static void default_write_secondary(CPUState *env,
const struct arm_boot_info *info)
{
int n;
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
}
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
info->smp_loader_start);
}
static void default_reset_secondary(CPUState *env,
const struct arm_boot_info *info)
{
stl_phys_notdirty(info->smp_bootreg_addr, 0);
env->regs[15] = info->smp_loader_start;
}
#define WRITE_WORD(p, value) do { \
stl_phys_notdirty(p, value); \
p += 4; \
@ -197,8 +229,7 @@ static void do_cpu_reset(void *opaque)
info->loader_start);
}
} else {
stl_phys_notdirty(info->smp_bootreg_addr, 0);
env->regs[15] = info->smp_loader_start;
info->secondary_cpu_reset_hook(env, info);
}
}
}
@ -220,6 +251,13 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
exit(1);
}
if (!info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook = default_reset_secondary;
}
if (!info->write_secondary_boot) {
info->write_secondary_boot = default_write_secondary;
}
if (info->nb_cpus == 0)
info->nb_cpus = 1;
@ -273,13 +311,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
info->loader_start);
if (info->nb_cpus > 1) {
smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
smpboot[n] = tswap32(smpboot[n]);
}
rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
info->smp_loader_start);
info->write_secondary_boot(env, info);
}
info->initrd_size = initrd_size;
}