]> xenbits.xensource.com Git - people/andrewcoop/xen.git/commitdiff
x86/smpboot.c: TXT AP bringup
authorKrystian Hebel <krystian.hebel@3mdeb.com>
Wed, 16 Nov 2022 14:06:18 +0000 (15:06 +0100)
committerKrystian Hebel <krystian.hebel@3mdeb.com>
Fri, 26 Apr 2024 16:10:58 +0000 (18:10 +0200)
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 <krystian.hebel@3mdeb.com>
xen/arch/x86/boot/trampoline.S
xen/arch/x86/include/asm/intel_txt.h
xen/arch/x86/smpboot.c

index 2df4c61f9421a2d01f20313ea05cac26202022ff..4b5808b9cbd2df9dc4939c043d5ec4a2263d7aa9 100644 (file)
@@ -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. */
index 50b2f84def3dbe001b7baee5170de0dc581ab29a..61d950b3ba8f12425dfe3c5cdd2b955d3e43ac95 100644 (file)
 
 #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)
index 7aa899dac3367b067e433b80fdc155eee8487004..0510527caa43f3b03f4cdcbd85dcd93c37dd62ea 100644 (file)
@@ -39,6 +39,7 @@
 #include <asm/div64.h>
 #include <asm/flushtlb.h>
 #include <asm/guest.h>
+#include <asm/intel_txt.h>
 #include <asm/microcode.h>
 #include <asm/msr.h>
 #include <asm/mtrr.h>
@@ -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.
      */