summary refs log tree commit diff stats
path: root/hw/arm_boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm_boot.c')
-rw-r--r--hw/arm_boot.c54
1 files changed, 43 insertions, 11 deletions
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;
     }