]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
arm: add platform-dependent SMP operations
authorJean-Philippe Brucker <jean-philippe.brucker@arm.com>
Thu, 7 Aug 2014 13:42:30 +0000 (14:42 +0100)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 19 Dec 2014 10:04:07 +0000 (11:04 +0100)
Hotplugging CPUs on ARM is quite difficult, as each platform uses its
own system. The use of PSCI emulation will greatly simplify this, but
on many platforms, we still have to define a series of specific SMP
operations wrapped around the kernel hotplug implementation.

This patch adds support for the vexpress hotplug system:
- When the root cell attempts to unplug a CPU, to give it to a new
  cell, it is put in a WFI loop, which is left when Jailhouse sends
  a synchronising IPI to all CPUs that need to be parked.
- When re-assigning a CPU to the root cell, the simplest return path
  is through the kernel's secondary entry, whose address is stored in
  the system flags register.

Because the kernel only writes the flag register once, plugging CPUs in
the host cannot be accomplished by waiting for a trapped MMIO. Moreover,
such a trap would be missed on hypervisor shutdown, since CPU0 may
return to bare EL1 before secondary CPUs. On some platforms, it may be
necessary to park secondary CPUs outside of the hypervisor on shutdown,
by copying a minimal spin code in a reserved location...

This patch also attempts to combine both classical and PSCI boot methods
in SMP guests: secondary CPUs are held in the psci_emulate_spin handler,
and can be woken up by both a PSCI call and a trapped access to the
vexpress mbox.
The same applies for hotplugging secondary CPUs in the guests, but the
mailbox method only waits for an IPI.

PSCI in the host is not currently supported: it would require a call to
the actual CPU_OFF handler when shutting down the whole hypervisor.

Signed-off-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
[Jan: switch to mmio accessor]
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
hypervisor/arch/arm/Makefile
hypervisor/arch/arm/control.c
hypervisor/arch/arm/include/asm/cell.h
hypervisor/arch/arm/include/asm/control.h
hypervisor/arch/arm/include/asm/platform.h
hypervisor/arch/arm/include/asm/smp.h [new file with mode: 0644]
hypervisor/arch/arm/mmio.c
hypervisor/arch/arm/setup.c
hypervisor/arch/arm/smp-vexpress.c [new file with mode: 0644]
hypervisor/arch/arm/smp.c [new file with mode: 0644]
hypervisor/arch/arm/spin.c [deleted file]

index 91b1659b5263fa0a2c5bdfefbfa9f982b2ec17e9..7ae9c49326764b20bd07804b8be022817ad4086b 100644 (file)
@@ -19,7 +19,8 @@ always := built-in.o
 obj-y := entry.o dbg-write.o exception.o setup.o control.o lib.o
 obj-y += traps.o mmio.o
 obj-y += paging.o mmu_hyp.o mmu_cell.o caches.o
-obj-y += psci.o psci_low.o spin.o
+obj-y += psci.o psci_low.o smp.o
 obj-y += irqchip.o gic-common.o
 obj-$(CONFIG_ARM_GIC_V3) += gic-v3.o
 obj-$(CONFIG_SERIAL_AMBA_PL011) += dbg-write-pl011.o
+obj-$(CONFIG_ARCH_VEXPRESS) += smp-vexpress.o
index f621ecb098f4e95413268531d284b2cf042f5609..47acf4ac036400376705da01764bf3ffd94190a8 100644 (file)
@@ -117,11 +117,11 @@ void arch_reset_self(struct per_cpu *cpu_data)
        if (err)
                printk("IRQ setup failed\n");
 
-       if (cpu_data->cell == &root_cell)
-               /* Wait for the driver to call cpu_up */
-               reset_address = arch_cpu_spin();
+       /* Wait for the driver to call cpu_up */
+       if (cell == &root_cell)
+               reset_address = arch_smp_spin(cpu_data, root_cell.arch.smp);
        else
-               reset_address = 0;
+               reset_address = arch_smp_spin(cpu_data, cell->arch.smp);
 
        /* Set the new MPIDR */
        arm_write_sysreg(VMPIDR_EL2, cpu_data->virt_id | MPIDR_MP_BIT);
@@ -302,6 +302,8 @@ int arch_cell_create(struct cell *cell)
        irqchip_cell_init(cell);
        irqchip_root_cell_shrink(cell);
 
+       register_smp_ops(cell);
+
        return 0;
 }
 
index 42b58cf78e04cdfcafa3f76001844e215e41ce01..206ec9d2c2ddd06376ca45e651839354da77550e 100644 (file)
@@ -14,6 +14,7 @@
 #define _JAILHOUSE_ASM_CELL_H
 
 #include <jailhouse/types.h>
+#include <asm/smp.h>
 #include <asm/spinlock.h>
 
 #ifndef __ASSEMBLY__
@@ -24,6 +25,7 @@
 
 struct arch_cell {
        struct paging_structures mm;
+       struct smp_ops *smp;
 
        spinlock_t caches_lock;
        bool needs_flush;
index 10a46c2a07a3c0d6f5d6657b3abf812f0a2657d9..f1842ffb65631459c796a3541f9be736b6776c8e 100644 (file)
@@ -33,8 +33,6 @@ void arch_mmu_cell_destroy(struct cell *cell);
 int arch_mmu_cpu_cell_init(struct per_cpu *cpu_data);
 void arch_handle_sgi(struct per_cpu *cpu_data, u32 irqn);
 void arch_handle_trap(struct per_cpu *cpu_data, struct registers *guest_regs);
-int arch_spin_init(void);
-unsigned long arch_cpu_spin(void);
 struct registers* arch_handle_exit(struct per_cpu *cpu_data,
                                   struct registers *regs);
 void arch_reset_self(struct per_cpu *cpu_data);
index 4b1467cb1635e33982e8dbf11ccfa4e419266105..e858be109da6bb44e35101174dcb4c4af60b0080 100644 (file)
@@ -35,7 +35,7 @@
 # endif /* GIC */
 
 # define MAINTENANCE_IRQ 25
-# define HOTPLUG_MBOX  ((void *)0x1c010030)
+# define SYSREGS_BASE  0x1c010000
 
 #endif /* CONFIG_ARCH_VEXPRESS */
 
diff --git a/hypervisor/arch/arm/include/asm/smp.h b/hypervisor/arch/arm/include/asm/smp.h
new file mode 100644 (file)
index 0000000..858b875
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#ifndef JAILHOUSE_ASM_SMP_H_
+#define JAILHOUSE_ASM_SMP_H_
+
+#ifndef __ASSEMBLY__
+
+enum smp_type {
+       SMP_PSCI,
+       SMP_SPIN
+};
+
+struct mmio_access;
+struct per_cpu;
+struct cell;
+
+struct smp_ops {
+       enum smp_type type;
+       int (*init)(struct cell *cell);
+
+       /*
+        * Uses the MMIO trap interface:
+        * returns TRAP_HANDLED when the mailbox is targeted, or else
+        * TRAP_UNHANDLED.
+        */
+       int (*mmio_handler)(struct per_cpu *cpu_data,
+                           struct mmio_access *access);
+       /* Returns an address */
+       unsigned long (*cpu_spin)(struct per_cpu *cpu_data);
+};
+
+int arch_generic_smp_init(unsigned long mbox);
+int arch_generic_smp_mmio(struct per_cpu *cpu_data, struct mmio_access *access,
+                         unsigned long mbox);
+unsigned long arch_generic_smp_spin(unsigned long mbox);
+
+int arch_smp_mmio_access(struct per_cpu *cpu_data, struct mmio_access *access);
+unsigned long arch_smp_spin(struct per_cpu *cpu_data, struct smp_ops *ops);
+void register_smp_ops(struct cell *cell);
+
+#endif /* !__ASSEMBLY__ */
+#endif /* !JAILHOUSE_ASM_SMP_H_ */
index da18212511c466b474f2976fc84b3c8f38316db6..a0f3b134ff2995c10fd6b73c869cf1026ba071e6 100644 (file)
@@ -13,6 +13,7 @@
 #include <jailhouse/mmio.h>
 #include <asm/irqchip.h>
 #include <asm/processor.h>
+#include <asm/smp.h>
 #include <asm/traps.h>
 
 /* Taken from the ARM ARM pseudocode for taking a data abort */
@@ -139,6 +140,8 @@ int arch_handle_dabt(struct per_cpu *cpu_data, struct trap_context *ctx)
        access.size = size;
 
        ret = irqchip_mmio_access(cpu_data, &access);
+       if (ret == TRAP_UNHANDLED)
+               ret = arch_smp_mmio_access(cpu_data, &access);
 
        if (ret == TRAP_HANDLED) {
                /* Put the read value into the dest register */
index 4b8e3594d7292d4e31ee20e0c9d43bdf635a2511..c11b8aa927b65789c0d4a3958a2bf912a76b6bc9 100644 (file)
@@ -86,10 +86,6 @@ int arch_cpu_init(struct per_cpu *cpu_data)
        /* Setup guest traps */
        arm_write_sysreg(HCR, hcr);
 
-       err = arch_spin_init();
-       if (err)
-               return err;
-
        err = arch_mmu_cpu_cell_init(cpu_data);
        if (err)
                return err;
@@ -105,9 +101,18 @@ int arch_cpu_init(struct per_cpu *cpu_data)
 
 int arch_init_late(void)
 {
+       int err;
+
        /* Setup the SPI bitmap */
        irqchip_cell_init(&root_cell);
 
+       /* Platform-specific SMP operations */
+       register_smp_ops(&root_cell);
+
+       err = root_cell.arch.smp->init(&root_cell);
+       if (err)
+               return err;
+
        return map_root_memory_regions();
 }
 
diff --git a/hypervisor/arch/arm/smp-vexpress.c b/hypervisor/arch/arm/smp-vexpress.c
new file mode 100644 (file)
index 0000000..c604f2a
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/processor.h>
+#include <asm/control.h>
+#include <asm/irqchip.h>
+#include <asm/paging.h>
+#include <asm/platform.h>
+#include <asm/smp.h>
+
+static unsigned long hotplug_mbox;
+
+static int smp_init(struct cell *cell)
+{
+       /* vexpress SYSFLAGS */
+       hotplug_mbox = SYSREGS_BASE + 0x30;
+
+       /* Map the mailbox page */
+       arch_generic_smp_init(hotplug_mbox);
+
+       return 0;
+}
+
+static unsigned long smp_spin(struct per_cpu *cpu_data)
+{
+       return arch_generic_smp_spin(hotplug_mbox);
+}
+
+static int smp_mmio(struct per_cpu *cpu_data, struct mmio_access *access)
+{
+       return arch_generic_smp_mmio(cpu_data, access, hotplug_mbox);
+}
+
+static struct smp_ops vexpress_smp_ops = {
+       .type = SMP_SPIN,
+       .init = smp_init,
+       .mmio_handler = smp_mmio,
+       .cpu_spin = smp_spin,
+};
+
+/*
+ * Store the guest's secondaries into our PSCI, and wake them up when we catch
+ * an access to the mbox from the primary.
+ */
+static struct smp_ops vexpress_guest_smp_ops = {
+       .type = SMP_SPIN,
+       .init = psci_cell_init,
+       .mmio_handler = smp_mmio,
+       .cpu_spin = psci_emulate_spin,
+};
+
+void register_smp_ops(struct cell *cell)
+{
+       /*
+        * mach-vexpress only writes the SYS_FLAGS once at boot, so the root
+        * cell cannot rely on this write to guess where the secondary CPUs
+        * should return.
+        */
+       if (cell == &root_cell)
+               cell->arch.smp = &vexpress_smp_ops;
+       else
+               cell->arch.smp = &vexpress_guest_smp_ops;
+}
diff --git a/hypervisor/arch/arm/smp.c b/hypervisor/arch/arm/smp.c
new file mode 100644 (file)
index 0000000..1efc612
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) ARM Limited, 2014
+ *
+ * Authors:
+ *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <jailhouse/control.h>
+#include <jailhouse/mmio.h>
+#include <jailhouse/printk.h>
+#include <asm/control.h>
+#include <asm/psci.h>
+#include <asm/setup.h>
+#include <asm/smp.h>
+#include <asm/traps.h>
+
+int arch_generic_smp_init(unsigned long mbox)
+{
+       void *mbox_page = (void *)(mbox & PAGE_MASK);
+       int err = arch_map_device(mbox_page, mbox_page, PAGE_SIZE);
+
+       if (err)
+               printk("Unable to map spin mbox page\n");
+
+       return err;
+}
+
+unsigned long arch_generic_smp_spin(unsigned long mbox)
+{
+       /*
+        * This is super-dodgy: we assume nothing wrote to the flag register
+        * since the kernel called smp_prepare_cpus, at initialisation.
+        */
+       return mmio_read32((void *)mbox);
+}
+
+int arch_generic_smp_mmio(struct per_cpu *cpu_data, struct mmio_access *access,
+                         unsigned long mbox)
+{
+       unsigned int cpu;
+       unsigned long mbox_page = mbox & PAGE_MASK;
+
+       if (access->addr < mbox_page || access->addr >= mbox_page + PAGE_SIZE)
+               return TRAP_UNHANDLED;
+
+       if (access->addr != mbox || !access->is_write)
+               /* Ignore all other accesses */
+               return TRAP_HANDLED;
+
+       for_each_cpu_except(cpu, cpu_data->cell->cpu_set, cpu_data->cpu_id) {
+               per_cpu(cpu)->guest_mbox.entry = access->val;
+               psci_try_resume(cpu);
+       }
+
+       return TRAP_HANDLED;
+}
+
+unsigned long arch_smp_spin(struct per_cpu *cpu_data, struct smp_ops *ops)
+{
+       /*
+        * Hotplugging CPU0 is not currently supported. It is always assumed to
+        * be the primary CPU. This is consistent with the linux behavior on
+        * most platforms.
+        * The guest image always starts at virtual address 0.
+        */
+       if (cpu_data->virt_id == 0)
+               return 0;
+
+       return ops->cpu_spin(cpu_data);
+}
+
+int arch_smp_mmio_access(struct per_cpu *cpu_data, struct mmio_access *access)
+{
+       struct smp_ops *smp_ops = cpu_data->cell->arch.smp;
+
+       if (smp_ops->mmio_handler)
+               return smp_ops->mmio_handler(cpu_data, access);
+
+       return TRAP_UNHANDLED;
+}
diff --git a/hypervisor/arch/arm/spin.c b/hypervisor/arch/arm/spin.c
deleted file mode 100644 (file)
index 9eca66b..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Jailhouse, a Linux-based partitioning hypervisor
- *
- * Copyright (c) ARM Limited, 2014
- *
- * Authors:
- *  Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- */
-
-#include <jailhouse/mmio.h>
-#include <jailhouse/printk.h>
-#include <asm/platform.h>
-#include <asm/setup.h>
-#include <asm/control.h>
-
-#if HOTPLUG_SPIN == 1
-int arch_spin_init(void)
-{
-       unsigned long mbox = (unsigned long)HOTPLUG_MBOX;
-       void *mbox_page = (void *)(mbox & PAGE_MASK);
-       int err = arch_map_device(mbox_page, mbox_page, PAGE_SIZE);
-
-       if (err)
-               printk("Unable to map spin mbox page\n");
-
-       return err;
-}
-
-
-unsigned long arch_cpu_spin(void)
-{
-       u32 address;
-
-       /*
-        * This is super-dodgy: we assume nothing wrote to the flag register
-        * since the kernel called smp_prepare_cpus, at initialisation.
-        */
-       do {
-               wfe();
-               address = mmio_read32((void *)HOTPLUG_MBOX);
-               cpu_relax();
-       } while (address == 0);
-
-       return address;
-}
-
-#elif HOTPLUG_PSCI == 1
-int arch_spin_init(void)
-{
-}
-
-unsigned long arch_cpu_spin(void)
-{
-       /* FIXME: wait for a PSCI hvc */
-}
-#endif