]> rtime.felk.cvut.cz Git - jailhouse.git/commitdiff
inmates: x86: Add basic SMP support
authorJan Kiszka <jan.kiszka@siemens.com>
Thu, 14 May 2015 14:26:52 +0000 (16:26 +0200)
committerJan Kiszka <jan.kiszka@siemens.com>
Fri, 15 May 2015 07:14:00 +0000 (09:14 +0200)
Under Jailhouse, all the cell CPUs are started in parallel. To enable
SMP inmates, the entry code records their number and their APIC IDs (up
to the current limit of 255). Only the first CPU arriving at the entry
check will call inmate_main, the others are parked in halt state.

Inmates can use the recorded parameters to pick up all CPUs by sending
them regular INIT/SIPI signals. We use the entry path for this case as
well: ap_entry is introduced as an alternative entry function pointer.
If it is non-NULL, the CPU will bypass the SMP startup procedure and
call that function.

The library is extended to provide a boot-up barrier and a single-CPU
wakeup service. It also adds a simple IPI service.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
inmates/lib/x86/Makefile
inmates/lib/x86/header-32.S
inmates/lib/x86/header.S
inmates/lib/x86/inmate.h
inmates/lib/x86/int.c
inmates/lib/x86/smp.c [new file with mode: 0644]

index dda825e91da23eac5a0d8a9d4023c1bc0c806c94..0fc753280f169ec6d46cde32d730c628b28a4b3c 100644 (file)
@@ -12,7 +12,7 @@
 
 always := lib.a lib32.a
 
-TARGETS := header.o hypercall.o ioapic.o printk.o
+TARGETS := header.o hypercall.o ioapic.o printk.o smp.o
 TARGETS_64_ONLY := int.o mem.o pci.o timing.o
 
 lib-y := $(TARGETS) $(TARGETS_64_ONLY)
index 6f704f9d32aaf0769249515b007a051a504d3b38..17fa5b6d0a4c9345f015bf2e9d2790c07d62b45a 100644 (file)
@@ -58,26 +58,67 @@ start32:
        or $MTRR_ENABLE,%eax
        wrmsr
 
-       mov $stack_top,%esp
-
        mov $INMATE_DS32,%eax
        mov %eax,%ds
        mov %eax,%es
        mov %eax,%ss
 
+       xor %ebx,%ebx
+       xchg ap_entry,%ebx
+       or %ebx,%ebx
+       jnz call_entry
+
+       mov $1,%edi
+       lock xadd %edi,cpu_number + FSEGMENT_BASE
+
+       cmp $SMP_MAX_CPUS,%edi
+       jae stop
+
+       mov $0x01,%eax
+       cpuid
+       shr $24,%ebx
+       mov %bl,smp_cpu_ids(%edi)
+
+       lock incl smp_num_cpus
+
+       cmp $0,%edi
+       jne stop
+
        xor %eax,%eax
        mov $bss_start,%edi
        mov $bss_dwords,%ecx
        rep stosl
 
-       mov $inmate_main,%eax
-       call *%eax
+       mov $inmate_main,%ebx
+
+call_entry:
+       mov $stack_top,%esp
+       call *%ebx
 
 stop:  cli
        hlt
        jmp stop
 
 
+       .pushsection ".data"
+
+       .globl ap_entry
+ap_entry:
+       .long   0
+
+       .globl smp_num_cpus
+smp_num_cpus:
+       .long   0
+
+       .globl smp_cpu_ids
+smp_cpu_ids:
+       .fill   SMP_MAX_CPUS, 1, 0
+
+       .popsection
+
+cpu_number:
+       .long   0
+
        .align(16)
        .global loader_gdt
 loader_gdt:
index b178dc1a07fb7e64276f42f497b77a6a9bb6467d..af8e918504171e97af42fa71d048b425d38c06ba 100644 (file)
@@ -70,21 +70,62 @@ start32:
 
        .code64
 start64:
-       mov $stack_top,%rsp
+       xor %rbx,%rbx
+       xchg ap_entry,%rbx
+       or %rbx,%rbx
+       jnz call_entry
+
+       mov $1,%edi
+       lock xadd %edi,cpu_number + FSEGMENT_BASE
+
+       cmp $SMP_MAX_CPUS,%edi
+       jae stop
+
+       mov $0x01,%eax
+       cpuid
+       shr $24,%ebx
+       mov %bl,smp_cpu_ids(%edi)
+
+       lock incl smp_num_cpus
+
+       cmp $0,%edi
+       jne stop
 
        xor %rax,%rax
        mov $bss_start,%rdi
        mov $bss_qwords,%rcx
        rep stosq
 
-       mov $inmate_main,%rax
-       callq *%rax
+       mov $inmate_main,%rbx
+
+call_entry:
+       mov $stack_top,%rsp
+       callq *%rbx
 
 stop:  cli
        hlt
        jmp stop
 
 
+       .pushsection ".data"
+
+       .globl ap_entry
+ap_entry:
+       .quad   0
+
+       .globl smp_num_cpus
+smp_num_cpus:
+       .long   0
+
+       .globl smp_cpu_ids
+smp_cpu_ids:
+       .fill   SMP_MAX_CPUS, 1, 0
+
+       .popsection
+
+cpu_number:
+       .long   0
+
        .align(16)
 gdt:
        .quad   0
index 0ddc5456955ae53a601b93e24dad4c80f0b42b63..1ab988970569d9552ce415af2526dc070a73aa9e 100644 (file)
@@ -36,6 +36,9 @@
 #define HUGE_PAGE_MASK         (~(HUGE_PAGE_SIZE - 1))
 
 #define X2APIC_ID              0x802
+#define X2APIC_ICR             0x830
+
+#define APIC_LVL_ASSERT                (1 << 14)
 
 #define PCI_CFG_VENDOR_ID      0x000
 #define PCI_CFG_DEVICE_ID      0x002
@@ -59,6 +62,8 @@
 #define MSIX_CTRL_ENABLE       0x8000
 #define MSIX_CTRL_FMASK                0x4000
 
+#define SMP_MAX_CPUS           255
+
 #ifndef __ASSEMBLY__
 typedef signed char s8;
 typedef unsigned char u8;
@@ -207,6 +212,7 @@ typedef void(*int_handler_t)(void);
 
 void int_init(void);
 void int_set_handler(unsigned int vector, int_handler_t handler);
+void int_send_ipi(unsigned int cpu_id, unsigned int vector);
 
 enum ioapic_trigger_mode {
        TRIGGER_EDGE = 0,
@@ -240,4 +246,9 @@ int pci_find_device(u16 vendor, u16 device, u16 start_bdf);
 int pci_find_cap(u16 bdf, u16 cap);
 void pci_msi_set_vector(u16 bdf, unsigned int vector);
 void pci_msix_set_vector(u16 bdf, unsigned int vector, u32 index);
+
+extern volatile u32 smp_num_cpus;
+extern u8 smp_cpu_ids[SMP_MAX_CPUS];
+void smp_wait_for_all_cpus(void);
+void smp_start_cpu(unsigned int cpu_id, void (*entry)(void));
 #endif
index d0c9cc4744ffeb6dac41c2645949ba3312763afa..6c4335d6510bc6e66d41a50049f77b2d1eadff7b 100644 (file)
@@ -107,3 +107,8 @@ asm(
 #else
 #error implement me!
 #endif
+
+void int_send_ipi(unsigned int cpu_id, unsigned int vector)
+{
+       write_msr(X2APIC_ICR, ((u64)cpu_id << 32) | APIC_LVL_ASSERT | vector);
+}
diff --git a/inmates/lib/x86/smp.c b/inmates/lib/x86/smp.c
new file mode 100644 (file)
index 0000000..accbb20
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2015
+ *
+ * Authors:
+ *  Jan Kiszka <jan.kiszka@siemens.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ */
+
+#include <inmate.h>
+
+#define APIC_DM_INIT   (5 << 8)
+#define APIC_DM_SIPI   (6 << 8)
+
+extern void (* volatile ap_entry)(void);
+
+void smp_wait_for_all_cpus(void)
+{
+       while (smp_num_cpus < comm_region->num_cpus)
+               cpu_relax();
+}
+
+void smp_start_cpu(unsigned int cpu_id, void (*entry)(void))
+{
+       u64 base_val = ((u64)cpu_id << 32) | APIC_LVL_ASSERT;
+
+       ap_entry = entry;
+
+       write_msr(X2APIC_ICR, base_val | APIC_DM_INIT);
+       delay_us(10000);
+       write_msr(X2APIC_ICR, base_val | APIC_DM_SIPI | 0xf0);
+       delay_us(200);
+       write_msr(X2APIC_ICR, base_val | APIC_DM_SIPI | 0xf0);
+
+       while (ap_entry != NULL)
+               cpu_relax();
+}