From: Krystian Hebel Date: Wed, 16 Nov 2022 14:06:18 +0000 (+0100) Subject: x86/smpboot.c: TXT AP bringup X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=4fe3f7f32e795e9731b57ae2d03c450b14358137;p=people%2Fandrewcoop%2Fxen.git x86/smpboot.c: TXT AP bringup On Intel TXT, APs are started in one of two ways, depending on ACM which reports it in its information table. In both cases, all APs are started simultaneously after BSP requests them to do so. Two possible ways are: - GETSEC[WAKEUP] instruction, - MONITOR address. GETSEC[WAKEUP] requires versions >= 7 of SINIT to MLE Data, but there is no clear mapping of that version with regard to processor family and it's not known which CPUs actually use it. It could have been designed for TXT support on CPUs that lack MONITOR/MWAIT, because GETSEC[WAKEUP] seems to be more complicated, in software and hardware alike. This patch implements only MONITOR approach, GETSEC[WAKEUP] support will be added later once more details and means of testing are available and if there is a practical need for it. With this patch, every AP goes through assembly part, and only when in start_secondary() in C they re-enter MONITOR/MWAIT iff they are not the AP that was asked to boot. The same address is reused for simplicity, and on next wakeup call APs don't have to go through assembly part again (GDT, paging, stack setting). Signed-off-by: Krystian Hebel --- diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S index 2df4c61f94..4b5808b9cb 100644 --- a/xen/arch/x86/boot/trampoline.S +++ b/xen/arch/x86/boot/trampoline.S @@ -59,6 +59,16 @@ GLOBAL(trampoline_realmode_entry) ljmpl $BOOT_CS32,$bootsym_rel(trampoline_protmode_entry,6) .code32 +GLOBAL(txt_ap_entry) + /* + * APs enter here in protected mode without paging. GDT is set in JOIN + * structure, it points to trampoline_gdt. Interrupts are disabled by + * TXT (including NMI and SMI), so IDT doesn't matter at this point. + * The only missing point is telling that we are AP by saving non-zero + * value in EBX. + */ + mov $1, %ebx + trampoline_protmode_entry: /* Set up a few descriptors: on entry only CS is guaranteed good. */ mov $BOOT_DS,%eax @@ -143,7 +153,7 @@ start64: .word 0 idt_48: .word 0, 0, 0 # base = limit = 0 -trampoline_gdt: +GLOBAL(trampoline_gdt) .word 0 /* 0x0000: unused (reused for GDTR) */ gdt_48: .word .Ltrampoline_gdt_end - trampoline_gdt - 1 @@ -154,6 +164,13 @@ gdt_48: .quad 0x00cf93000000ffff /* 0x0018: ring 0 data */ .quad 0x00009b000000ffff /* 0x0020: real-mode code @ BOOT_TRAMPOLINE */ .quad 0x000093000000ffff /* 0x0028: real-mode data @ BOOT_TRAMPOLINE */ + /* + * Intel TXT requires these two in exact order. This isn't compatible + * with order required by syscall, so we have duplicated entries... + * If order ever changes, update selector numbers in asm/intel_txt.h. + */ + .quad 0x00cf9b000000ffff /* 0x0030: ring 0 code, 32-bit mode */ + .quad 0x00cf93000000ffff /* 0x0038: ring 0 data */ .Ltrampoline_gdt_end: /* Relocations for trampoline Real Mode segments. */ diff --git a/xen/arch/x86/include/asm/intel_txt.h b/xen/arch/x86/include/asm/intel_txt.h index 50b2f84def..61d950b3ba 100644 --- a/xen/arch/x86/include/asm/intel_txt.h +++ b/xen/arch/x86/include/asm/intel_txt.h @@ -73,10 +73,16 @@ #define SLAUNCH_BOOTLOADER_MAGIC 0x4c534254 +#define TXT_AP_BOOT_CS 0x0030 +#define TXT_AP_BOOT_DS 0x0038 + #ifndef __ASSEMBLY__ extern bool slaunch_active; +extern char txt_ap_entry[]; +extern uint32_t trampoline_gdt[]; + /* We need to differentiate between pre- and post paging enabled. */ #ifdef __BOOT_DEFS_H__ #define _txt(x) _p(x) diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c index 7aa899dac3..0510527caa 100644 --- a/xen/arch/x86/smpboot.c +++ b/xen/arch/x86/smpboot.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -331,6 +332,29 @@ void start_secondary(void *unused) */ unsigned int cpu = booting_cpu; + if ( slaunch_active ) { + uint64_t misc_enable; + uint32_t my_apicid; + struct txt_sinit_mle_data *sinit_mle = + txt_sinit_mle_data_start(__va(read_txt_reg(TXTCR_HEAP_BASE))); + + /* TXT released us with MONITOR disabled in IA32_MISC_ENABLE. */ + rdmsrl(MSR_IA32_MISC_ENABLE, misc_enable); + wrmsrl(MSR_IA32_MISC_ENABLE, + misc_enable | MSR_IA32_MISC_ENABLE_MONITOR_ENABLE); + + /* get_apic_id() reads from x2APIC if it thinks it is enabled. */ + x2apic_ap_setup(); + my_apicid = get_apic_id(); + + while ( my_apicid != x86_cpu_to_apicid[cpu] ) { + asm volatile ("monitor; xor %0,%0; mwait" + :: "a"(__va(sinit_mle->rlp_wakeup_addr)), "c"(0), + "d"(0) : "memory"); + cpu = booting_cpu; + } + } + /* Critical region without IDT or TSS. Any fault is deadly! */ set_current(idle_vcpu[cpu]); @@ -424,6 +448,28 @@ void start_secondary(void *unused) startup_cpu_idle_loop(); } +static int slaunch_wake_aps(unsigned long trampoline_rm) +{ + struct txt_sinit_mle_data *sinit_mle = + txt_sinit_mle_data_start(__va(read_txt_reg(TXTCR_HEAP_BASE))); + uint32_t *wakeup_addr = __va(sinit_mle->rlp_wakeup_addr); +#define trampoline_relative(x) (trampoline_rm + ((char *)(x) - trampoline_realmode_entry)) + uint32_t join[4] = { + trampoline_gdt[1], /* GDT limit */ + trampoline_relative(trampoline_gdt), /* GDT base */ + TXT_AP_BOOT_CS, /* CS selector, DS = CS+8 */ + trampoline_relative(txt_ap_entry) /* EIP */ + }; + + write_txt_reg(TXTCR_MLE_JOIN, __pa(join)); + + smp_mb(); + + *wakeup_addr = 1; + + return 0; +} + static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) { unsigned long send_status = 0, accept_status = 0; @@ -446,6 +492,9 @@ static int wakeup_secondary_cpu(int phys_apicid, unsigned long start_eip) if ( tboot_in_measured_env() && !tboot_wake_ap(phys_apicid, start_eip) ) return 0; + if ( slaunch_active ) + return slaunch_wake_aps(start_eip); + /* * Be paranoid about clearing APIC errors. */