PPC: E500: Add PV spinning code
CPUs that are not the boot CPU need to run in spinning code to check if they
should run off to execute and if so where to jump to. This usually happens
by leaving secondary CPUs looping and checking if some variable in memory
changed.
In an environment like Qemu however we can be more clever. We can just export
the spin table the primary CPU modifies as MMIO region that would event based
wake up the respective secondary CPUs. That saves us quite some cycles while
the secondary CPUs are not up yet.
So this patch adds a PV device that simply exports the spinning table into the
guest and thus allows the primary CPU to wake up secondary ones.
Signed-off-by: Alexander Graf <agraf@suse.de>
---
v1 -> v2:
- change into MMIO scheme
- map the secondary NIP instead of 0 1:1
- only map 64MB for TLB, same as u-boot
- prepare code for 64-bit spinnings
v2 -> v3:
- remove r6
- set MAS2_M
- map EA 0
- use second TLB1 entry
v3 -> v4:
- change to memoryops
v4 -> v5:
- fix endianness bugs
v5 -> v6:
- add header
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 9379624..3b8b449 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -49,6 +49,7 @@
#define MPC8544_PCI_IO 0xE1000000
#define MPC8544_PCI_IOLEN 0x10000
#define MPC8544_UTIL_BASE (MPC8544_CCSRBAR_BASE + 0xe0000)
+#define MPC8544_SPIN_BASE 0xEF000000
struct boot_info
{
@@ -164,6 +165,18 @@
tlb->mas7_3 |= MAS3_UR | MAS3_UW | MAS3_UX | MAS3_SR | MAS3_SW | MAS3_SX;
}
+static void mpc8544ds_cpu_reset_sec(void *opaque)
+{
+ CPUState *env = opaque;
+
+ cpu_reset(env);
+
+ /* Secondary CPU starts in halted state for now. Needs to change when
+ implementing non-kernel boot. */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+}
+
static void mpc8544ds_cpu_reset(void *opaque)
{
CPUState *env = opaque;
@@ -172,6 +185,7 @@
cpu_reset(env);
/* Set initial guest state. */
+ env->halted = 0;
env->gpr[1] = (16<<20) - 8;
env->gpr[3] = bi->dt_base;
env->nip = bi->entry;
@@ -199,7 +213,6 @@
unsigned int pci_irq_nrs[4] = {1, 2, 3, 4};
qemu_irq **irqs, *mpic;
DeviceState *dev;
- struct boot_info *boot_info;
CPUState *firstenv = NULL;
/* Setup CPUs */
@@ -234,9 +247,16 @@
env->spr[SPR_40x_TCR] = 1 << 26;
/* Register reset handler */
- boot_info = g_malloc0(sizeof(struct boot_info));
- qemu_register_reset(mpc8544ds_cpu_reset, env);
- env->load_info = boot_info;
+ if (!i) {
+ /* Primary CPU */
+ struct boot_info *boot_info;
+ boot_info = g_malloc0(sizeof(struct boot_info));
+ qemu_register_reset(mpc8544ds_cpu_reset, env);
+ env->load_info = boot_info;
+ } else {
+ /* Secondary CPUs */
+ qemu_register_reset(mpc8544ds_cpu_reset_sec, env);
+ }
}
env = firstenv;
@@ -289,6 +309,9 @@
}
}
+ /* Register spinning region */
+ sysbus_create_simple("e500-spin", MPC8544_SPIN_BASE, NULL);
+
/* Load kernel. */
if (kernel_filename) {
kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
@@ -321,6 +344,8 @@
/* If we're loading a kernel directly, we must load the device tree too. */
if (kernel_filename) {
+ struct boot_info *boot_info;
+
#ifndef CONFIG_FDT
cpu_abort(env, "Compiled without FDT support - can't load kernel\n");
#endif