From ef3b370c6433fc98de11b844a3d578b1e58b6c5b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Thu, 14 May 2015 16:26:52 +0200 Subject: [PATCH] inmates: x86: Add basic SMP support 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 --- inmates/lib/x86/Makefile | 2 +- inmates/lib/x86/header-32.S | 49 ++++++++++++++++++++++++++++++++++--- inmates/lib/x86/header.S | 47 ++++++++++++++++++++++++++++++++--- inmates/lib/x86/inmate.h | 11 +++++++++ inmates/lib/x86/int.c | 5 ++++ inmates/lib/x86/smp.c | 40 ++++++++++++++++++++++++++++++ 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 inmates/lib/x86/smp.c diff --git a/inmates/lib/x86/Makefile b/inmates/lib/x86/Makefile index dda825e..0fc7532 100644 --- a/inmates/lib/x86/Makefile +++ b/inmates/lib/x86/Makefile @@ -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) diff --git a/inmates/lib/x86/header-32.S b/inmates/lib/x86/header-32.S index 6f704f9..17fa5b6 100644 --- a/inmates/lib/x86/header-32.S +++ b/inmates/lib/x86/header-32.S @@ -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: diff --git a/inmates/lib/x86/header.S b/inmates/lib/x86/header.S index b178dc1..af8e918 100644 --- a/inmates/lib/x86/header.S +++ b/inmates/lib/x86/header.S @@ -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 diff --git a/inmates/lib/x86/inmate.h b/inmates/lib/x86/inmate.h index 0ddc545..1ab9889 100644 --- a/inmates/lib/x86/inmate.h +++ b/inmates/lib/x86/inmate.h @@ -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 diff --git a/inmates/lib/x86/int.c b/inmates/lib/x86/int.c index d0c9cc4..6c4335d 100644 --- a/inmates/lib/x86/int.c +++ b/inmates/lib/x86/int.c @@ -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 index 0000000..accbb20 --- /dev/null +++ b/inmates/lib/x86/smp.c @@ -0,0 +1,40 @@ +/* + * Jailhouse, a Linux-based partitioning hypervisor + * + * Copyright (c) Siemens AG, 2015 + * + * Authors: + * Jan Kiszka + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + */ + +#include + +#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(); +} -- 2.39.2