diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 6e8ae6b02e..5e5204bbf5 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -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; diff --git a/hw/arm_boot.c b/hw/arm_boot.c index bf509a8eb9..35ca22ff47 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -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; }