[PATCH v2 20/22] x86/slaunch: support EFI boot

Sergii Dmytruk posted 22 patches 7 months, 1 week ago
There is a newer version of this series
[PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Sergii Dmytruk 7 months, 1 week ago
When running on an EFI-enabled system, Xen needs to have access to Boot
Services in order to initialize itself properly and reach a state in
which a dom0 kernel can operate without issues.

This means that DRTM must be started in the middle of Xen's
initialization process.  This effect is achieved via a callback into
bootloader (GRUB) which is responsible for initiating DRTM and
continuing Xen's initialization process.  The latter is done by
branching in Slaunch entry point on a flag to switch back into long mode
before calling the same function which Xen would execute as the next
step without DRTM.

Signed-off-by: Krystian Hebel <krystian.hebel@3mdeb.com>
Signed-off-by: Sergii Dmytruk <sergii.dmytruk@3mdeb.com>
---
 .gitignore                                    |   1 +
 .../eclair_analysis/ECLAIR/out_of_scope.ecl   |   1 +
 docs/hypervisor-guide/x86/how-xen-boots.rst   |  10 +-
 xen/arch/x86/Makefile                         |   9 +-
 xen/arch/x86/boot/head.S                      | 124 +++++++++++++++++
 xen/arch/x86/boot/x86_64.S                    |  14 +-
 xen/arch/x86/efi/efi-boot.h                   |  88 +++++++++++-
 xen/arch/x86/efi/fixmlehdr.c                  | 127 ++++++++++++++++++
 xen/arch/x86/slaunch.c                        |  74 +++++++++-
 xen/common/efi/boot.c                         |   4 +
 xen/common/efi/runtime.c                      |   1 +
 xen/include/xen/efi.h                         |   1 +
 12 files changed, 441 insertions(+), 13 deletions(-)
 create mode 100644 xen/arch/x86/efi/fixmlehdr.c

diff --git a/.gitignore b/.gitignore
index 53f5df0003..dab829d7e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -201,6 +201,7 @@ xen/.xen.elf32
 xen/System.map
 xen/arch/x86/efi.lds
 xen/arch/x86/efi/check.efi
+xen/arch/x86/efi/fixmlehdr
 xen/arch/x86/efi/mkreloc
 xen/arch/x86/include/asm/asm-macros.h
 xen/arch/*/xen.lds
diff --git a/automation/eclair_analysis/ECLAIR/out_of_scope.ecl b/automation/eclair_analysis/ECLAIR/out_of_scope.ecl
index 9bcec4c69d..a09cf5442c 100644
--- a/automation/eclair_analysis/ECLAIR/out_of_scope.ecl
+++ b/automation/eclair_analysis/ECLAIR/out_of_scope.ecl
@@ -19,6 +19,7 @@
 
 -doc_begin="Build tools are out of scope."
 -file_tag+={out_of_scope_tools,"^xen/tools/.*$"}
+-file_tag+={out_of_scope_tools,"^xen/arch/x86/efi/fixmlehdr\\.c$"}
 -file_tag+={out_of_scope_tools,"^xen/arch/x86/efi/mkreloc\\.c$"}
 -file_tag+={out_of_scope_tools,"^xen/arch/x86/boot/mkelf32\\.c$"}
 -doc_end
diff --git a/docs/hypervisor-guide/x86/how-xen-boots.rst b/docs/hypervisor-guide/x86/how-xen-boots.rst
index 050fe9c61f..63f81a8198 100644
--- a/docs/hypervisor-guide/x86/how-xen-boots.rst
+++ b/docs/hypervisor-guide/x86/how-xen-boots.rst
@@ -55,10 +55,12 @@ If ``CONFIG_PVH_GUEST`` was selected at build time, an Elf note is included
 which indicates the ability to use the PVH boot protocol, and registers
 ``__pvh_start`` as the entrypoint, entered in 32bit mode.
 
-A combination of Multiboot 2 and MLE headers is used to implement DRTM for
-legacy (BIOS) boot. The separate entry point is used mainly to differentiate
-from other kinds of boots. It moves a magic number to EAX before jumping into
-common startup code.
+A combination of Multiboot 2 and MLE headers is used to implement DRTM. The
+separate entry point is used mainly to differentiate from other kinds of boots.
+For a legacy (BIOS) boot, it moves a magic number to EAX before jumping into
+common startup code.  For a EFI boot, it resumes execution of Xen.efi which was
+paused by handing control to a part of a bootloader responsible for initiating
+DRTM sequence.
 
 
 xen.gz
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 2527a1909c..7c2c0b5e4c 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -89,6 +89,7 @@ extra-y += xen.lds
 
 hostprogs-y += boot/mkelf32
 hostprogs-y += efi/mkreloc
+hostprogs-y += efi/fixmlehdr
 
 $(obj)/efi/mkreloc: HOSTCFLAGS += -I$(srctree)/include
 
@@ -140,6 +141,10 @@ $(TARGET): $(TARGET)-syms $(efi-y) $(obj)/boot/mkelf32
 
 CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
 
+ifeq ($(XEN_BUILD_EFI),y)
+XEN_AFLAGS += -DXEN_BUILD_EFI
+endif
+
 $(TARGET)-syms: $(objtree)/prelink.o $(obj)/xen.lds
 	$(LD) $(XEN_LDFLAGS) -T $(obj)/xen.lds $< $(build_id_linker) \
 	    $(objtree)/common/symbols-dummy.o -o $(dot-target).0
@@ -209,7 +214,7 @@ note_file_option ?= $(note_file)
 
 extra-$(XEN_BUILD_PE) += efi.lds
 ifeq ($(XEN_BUILD_PE),y)
-$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds $(obj)/efi/relocs-dummy.o $(obj)/efi/mkreloc
+$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds $(obj)/efi/relocs-dummy.o $(obj)/efi/mkreloc $(obj)/efi/fixmlehdr
 ifeq ($(CONFIG_DEBUG_INFO),y)
 	$(if $(filter --strip-debug,$(EFI_LDFLAGS)),echo,:) "Will strip debug info from $(@F)"
 endif
@@ -236,6 +241,8 @@ endif
 	$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) -T $(obj)/efi.lds $< \
 	      $(dot-target).1r.o $(dot-target).1s.o $(orphan-handling-y) \
 	      $(note_file_option) -o $@
+	# take image offset into account
+	$(obj)/efi/fixmlehdr $@ $(XEN_IMG_OFFSET)
 	$(NM) -pa --format=sysv $@ \
 		| $(objtree)/tools/symbols --all-symbols --xensyms --sysv --sort \
 		> $@.map
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index 66e1a21033..5ec2a272a9 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -397,6 +397,12 @@ slaunch_stub_entry:
         mov     %ebx, %esi
         sub     $sym_offs(slaunch_stub_entry), %esi
 
+#ifdef XEN_BUILD_EFI
+        /* If the flag is already set, then Xen should continue execution. */
+        cmpb    $0, sym_esi(slaunch_active)
+        jne     slaunch_efi_jumpback
+#endif
+
         /* On AMD, %ebp holds the base address of SLB, save it for later. */
         mov     %ebp, %ebx
 
@@ -836,6 +842,124 @@ trampoline_setup:
         /* Jump into the relocated trampoline. */
         lret
 
+#ifdef XEN_BUILD_EFI
+
+        /*
+         * The state matches that of slaunch_stub_entry above, but with %esi
+         * already initialized.
+         */
+slaunch_efi_jumpback:
+        lea     STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp
+
+        /* Prepare gdt and segments. */
+        add     %esi, sym_esi(gdt_boot_base)
+        lgdt    sym_esi(gdt_boot_descr)
+
+        mov     $BOOT_DS, %ecx
+        mov     %ecx, %ds
+        mov     %ecx, %es
+        mov     %ecx, %ss
+
+        push    $BOOT_CS32
+        lea     sym_esi(.Lgdt_is_set),%edx
+        push    %edx
+        lret
+.Lgdt_is_set:
+
+        /*
+         * Stash TSC as above because it was zeroed on jumping into bootloader
+         * to not interfere with measurements.
+         */
+        rdtsc
+        mov     %eax,     sym_esi(boot_tsc_stamp)
+        mov     %edx, 4 + sym_esi(boot_tsc_stamp)
+
+        /*
+         * Clear the pagetables before the use. We are loaded below 4GiB and
+         * this avoids the need for writing to higher dword of each entry.
+         * Additionally, this ensures those dwords are actually zero and the
+         * mappings aren't manipulated from outside.
+         */
+        lea     sym_esi(bootmap_start), %edi
+        lea     sym_esi(bootmap_end), %ecx
+        sub     %edi, %ecx
+        xor     %eax, %eax
+        shr     $2, %ecx
+        rep stosl
+
+        /* 1x L1 page, 512 entries mapping total of 2M. */
+        lea     sym_esi(l1_bootmap), %edi
+        mov     $512, %ecx
+        mov     $(__PAGE_HYPERVISOR + 512 * PAGE_SIZE), %edx
+.Lfill_l1_identmap:
+        sub     $PAGE_SIZE, %edx
+        /* Loop runs for ecx=[512..1] for entries [511..0], hence -8. */
+        mov     %edx, -8(%edi,%ecx,8)
+        loop    .Lfill_l1_identmap
+
+        /* 4x L2 pages, each page mapping 1G of RAM. */
+        lea     sym_esi(l2_bootmap), %edi
+        /* 1st entry points to L1. */
+        lea     (sym_offs(l1_bootmap) + __PAGE_HYPERVISOR)(%esi), %edx
+        mov     %edx, (%edi)
+        /* Other entries are 2MB pages. */
+        mov     $(4 * 512 - 1), %ecx
+        /*
+         * Value below should be 4GB + flags, which wouldn't fit in 32b
+         * register. To avoid warning from the assembler, 4GB is skipped here.
+         * Substitution in first iteration makes the value roll over and point
+         * to 4GB - 2MB + flags.
+         */
+        mov     $(_PAGE_PSE + __PAGE_HYPERVISOR), %edx
+.Lfill_l2_identmap:
+        sub     $(1 << L2_PAGETABLE_SHIFT), %edx
+        /* Loop runs for ecx=[2047..1] for entries [2047..1]. */
+        mov     %edx, (%edi,%ecx,8)
+        loop    .Lfill_l2_identmap
+
+        /* 1x L3 page, mapping the 4x L2 pages. */
+        lea     sym_esi(l3_bootmap), %edi
+        mov     $4, %ecx
+        lea     (sym_offs(l2_bootmap) + 4 * PAGE_SIZE + __PAGE_HYPERVISOR)(%esi), %edx
+.Lfill_l3_identmap:
+        sub     $PAGE_SIZE, %edx
+        /* Loop runs for ecx=[4..1] for entries [3..0], hence -8. */
+        mov     %edx, -8(%edi,%ecx,8)
+        loop    .Lfill_l3_identmap
+
+        /* 1x L4 page, mapping the L3 page. */
+        lea     (sym_offs(l3_bootmap) + __PAGE_HYPERVISOR)(%esi), %edx
+        mov     %edx, sym_esi(l4_bootmap)
+
+        /* Restore CR4, PAE must be enabled before IA-32e mode */
+        mov     %cr4, %ecx
+        or      $X86_CR4_PAE, %ecx
+        mov     %ecx, %cr4
+
+        /* Load PML4 table location into PT base register */
+        lea     sym_esi(l4_bootmap), %eax
+        mov     %eax, %cr3
+
+        /* Enable IA-32e mode and paging */
+        mov     $MSR_EFER, %ecx
+        rdmsr
+        or      $EFER_LME >> 8, %ah
+        wrmsr
+
+        mov     %cr0, %eax
+        or      $X86_CR0_PG | X86_CR0_NE | X86_CR0_TS | X86_CR0_MP, %eax
+        mov     %eax, %cr0
+
+        /* Now in IA-32e compatibility mode, use lret to jump to 64b mode */
+        lea     sym_esi(start_xen_from_efi), %ecx
+        push    $BOOT_CS64
+        push    %ecx
+        lret
+
+.global start_xen_from_efi
+
+#endif /* XEN_BUILD_EFI */
+
 ENTRY(trampoline_start)
 #include "trampoline.S"
 ENTRY(trampoline_end)
diff --git a/xen/arch/x86/boot/x86_64.S b/xen/arch/x86/boot/x86_64.S
index ac33576d8f..67896f5fe5 100644
--- a/xen/arch/x86/boot/x86_64.S
+++ b/xen/arch/x86/boot/x86_64.S
@@ -221,14 +221,22 @@ GLOBAL(__page_tables_end)
 /* Init pagetables. Enough page directories to map into 4GB. */
         .section .init.data, "aw", @progbits
 
-DATA_LOCAL(l1_bootmap, PAGE_SIZE)
+bootmap_start:
+
+DATA_LOCAL(l1_bootmap, PAGE_SIZE) /* 1x L1 page, mapping 2M of RAM. */
         .fill L1_PAGETABLE_ENTRIES, 8, 0
 END(l1_bootmap)
 
-DATA(l2_bootmap, PAGE_SIZE)
+DATA(l2_bootmap, PAGE_SIZE) /* 4x L2 pages, each mapping 1G of RAM. */
         .fill 4 * L2_PAGETABLE_ENTRIES, 8, 0
 END(l2_bootmap)
 
-DATA(l3_bootmap, PAGE_SIZE)
+DATA(l3_bootmap, PAGE_SIZE) /* 1x L3 page, mapping the 4x L2 pages. */
         .fill L3_PAGETABLE_ENTRIES, 8, 0
 END(l3_bootmap)
+
+DATA_LOCAL(l4_bootmap, PAGE_SIZE) /* 1x L4 page, mapping the L3 page. */
+        .fill L4_PAGETABLE_ENTRIES, 8, 0
+END(l4_bootmap)
+
+bootmap_end:
diff --git a/xen/arch/x86/efi/efi-boot.h b/xen/arch/x86/efi/efi-boot.h
index 1d8902a9a7..6aaba4a966 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -5,6 +5,12 @@
  */
 #include <xen/vga.h>
 
+/*
+ * Tell <asm/intel-txt.h> to access TXT registers without address translation
+ * which has not yet been set up.
+ */
+#define __EARLY_SLAUNCH__
+
 #include <asm/boot-helpers.h>
 #include <asm/e820.h>
 #include <asm/edd.h>
@@ -13,8 +19,11 @@
 #include <asm/setup.h>
 #include <asm/trampoline.h>
 #include <asm/efi.h>
+#include <asm/intel-txt.h>
+#include <asm/slaunch.h>
 
 static struct file __initdata ucode;
+static uint64_t __initdata xen_image_size;
 static multiboot_info_t __initdata mbi = {
     .flags = MBI_MODULES | MBI_LOADERNAME
 };
@@ -230,10 +239,29 @@ static void __init efi_arch_pre_exit_boot(void)
     }
 }
 
-static void __init noreturn efi_arch_post_exit_boot(void)
+void __init asmlinkage noreturn start_xen_from_efi(void)
 {
     u64 cr4 = XEN_MINIMAL_CR4 & ~X86_CR4_PGE, efer;
 
+    if ( slaunch_active )
+    {
+        struct slr_table *slrt = (struct slr_table *)efi.slr;
+        struct slr_entry_intel_info *intel_info;
+
+        intel_info = (struct slr_entry_intel_info *)
+            slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO);
+        if ( intel_info != NULL )
+        {
+            void *txt_heap = txt_init();
+            struct txt_os_mle_data *os_mle = txt_os_mle_data_start(txt_heap);
+            struct txt_os_sinit_data *os_sinit =
+                txt_os_sinit_data_start(txt_heap);
+
+            txt_verify_pmr_ranges(os_mle, os_sinit, intel_info, xen_phys_start,
+                                  xen_phys_start, xen_image_size);
+        }
+    }
+
     efi_arch_relocate_image(__XEN_VIRT_START - xen_phys_start);
     memcpy(_p(trampoline_phys), trampoline_start, cfg.size);
 
@@ -279,6 +307,63 @@ static void __init noreturn efi_arch_post_exit_boot(void)
     unreachable();
 }
 
+static void __init attempt_secure_launch(void)
+{
+    struct slr_table *slrt;
+    struct slr_entry_dl_info *dlinfo;
+    dl_handler_func handler_callback;
+
+    /* The presence of this table indicates a Secure Launch boot. */
+    slrt = (struct slr_table *)efi.slr;
+    if ( efi.slr == EFI_INVALID_TABLE_ADDR || slrt->magic != SLR_TABLE_MAGIC ||
+         slrt->revision != SLR_TABLE_REVISION )
+        return;
+
+    /* Avoid calls into firmware after DRTM. */
+    __clear_bit(EFI_RS, &efi_flags);
+
+    /*
+     * Make measurements less sensitive to hardware-specific details.
+     *
+     * Intentionally leaving efi_ct and efi_num_ct intact.
+     */
+    efi_ih = NULL;
+    efi_bs = NULL;
+    efi_bs_revision = 0;
+    efi_rs = NULL;
+    efi_version = 0;
+    efi_fw_vendor = NULL;
+    efi_fw_revision = 0;
+    StdOut = NULL;
+    StdErr = NULL;
+    boot_tsc_stamp = 0;
+
+    slaunch_active = true;
+    slaunch_slrt = efi.slr;
+
+    /* Jump through DL stub to initiate Secure Launch. */
+    dlinfo = (struct slr_entry_dl_info *)
+        slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DL_INFO);
+
+    handler_callback = (dl_handler_func)dlinfo->dl_handler;
+    handler_callback(&dlinfo->bl_context);
+
+    unreachable();
+}
+
+static void __init noreturn efi_arch_post_exit_boot(void)
+{
+    /*
+     * If Secure Launch happens, attempt_secure_launch() doesn't return and
+     * start_xen_from_efi() is invoked after DRTM has been initiated.
+     * Otherwise, attempt_secure_launch() returns and execution continues as
+     * usual.
+     */
+    attempt_secure_launch();
+
+    start_xen_from_efi();
+}
+
 static void __init efi_arch_cfg_file_early(const EFI_LOADED_IMAGE *image,
                                            EFI_FILE_HANDLE dir_handle,
                                            const char *section)
@@ -775,6 +860,7 @@ static void __init efi_arch_halt(void)
 static void __init efi_arch_load_addr_check(const EFI_LOADED_IMAGE *loaded_image)
 {
     xen_phys_start = (UINTN)loaded_image->ImageBase;
+    xen_image_size = loaded_image->ImageSize;
     if ( (xen_phys_start + loaded_image->ImageSize - 1) >> 32 )
         blexit(L"Xen must be loaded below 4Gb.");
     if ( xen_phys_start & ((1 << L2_PAGETABLE_SHIFT) - 1) )
diff --git a/xen/arch/x86/efi/fixmlehdr.c b/xen/arch/x86/efi/fixmlehdr.c
new file mode 100644
index 0000000000..60a91c6b73
--- /dev/null
+++ b/xen/arch/x86/efi/fixmlehdr.c
@@ -0,0 +1,127 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Depending on the toolchain and its configuration the header can end up quite
+ * far from the start of the file.
+ */
+#define PREFIX_SIZE (8*1024)
+
+struct mle_header
+{
+    uint8_t uuid[16];
+    uint32_t header_len;
+    uint32_t version;
+    uint32_t entry_point;
+    uint32_t first_valid_page;
+    uint32_t mle_start;
+    uint32_t mle_end;
+    uint32_t capabilities;
+    uint32_t cmdline_start;
+    uint32_t cmdline_end;
+} __attribute__ ((packed));
+
+static const uint8_t MLE_HEADER_UUID[] = {
+    0x5a, 0xac, 0x82, 0x90, 0x6f, 0x47, 0xa7, 0x74,
+    0x0f, 0x5c, 0x55, 0xa2, 0xcb, 0x51, 0xb6, 0x42
+};
+
+int main(int argc, char *argv[])
+{
+    FILE *fp;
+    struct mle_header header;
+    int i;
+    char *end_ptr;
+    long long correction;
+    const char *file_path;
+
+    if ( argc != 3 )
+    {
+        fprintf(stderr, "Usage: %s <xen.efi> <entry-correction>\n", argv[0]);
+        return 1;
+    }
+
+    correction = strtoll(argv[2], &end_ptr, 0);
+    if ( *end_ptr != '\0' )
+    {
+        fprintf(stderr, "Failed to parse '%s' as a number\n", argv[2]);
+        return 1;
+    }
+    if ( correction < INT32_MIN  )
+    {
+        fprintf(stderr, "Correction '%s' is too small\n", argv[2]);
+        return 1;
+    }
+    if ( correction > INT32_MAX  )
+    {
+        fprintf(stderr, "Correction '%s' is too large\n", argv[2]);
+        return 1;
+    }
+
+    file_path = argv[1];
+
+    fp = fopen(file_path, "r+");
+    if ( fp == NULL )
+    {
+        fprintf(stderr, "Failed to open %s\n", file_path);
+        return 1;
+    }
+
+    for ( i = 0; i < PREFIX_SIZE; i += 16 )
+    {
+        uint8_t bytes[16];
+
+        if ( fread(bytes, sizeof(bytes), 1, fp) != 1 )
+        {
+            fprintf(stderr, "Failed to find MLE header in %s\n", file_path);
+            goto fail;
+        }
+
+        if ( memcmp(bytes, MLE_HEADER_UUID, 16) == 0 )
+        {
+            break;
+        }
+    }
+
+    if ( i >= PREFIX_SIZE )
+    {
+        fprintf(stderr, "Failed to find MLE header in %s\n", file_path);
+        goto fail;
+    }
+
+    if ( fseek(fp, -16, SEEK_CUR) )
+    {
+        fprintf(stderr, "Failed to seek back to MLE header in %s\n", file_path);
+        goto fail;
+    }
+
+    if ( fread(&header, sizeof(header), 1, fp) != 1 )
+    {
+        fprintf(stderr, "Failed to read MLE header from %s\n", file_path);
+        goto fail;
+    }
+
+    if ( fseek(fp, -(int)sizeof(header), SEEK_CUR) )
+    {
+        fprintf(stderr, "Failed to seek back again to MLE header in %s\n",
+                file_path);
+        goto fail;
+    }
+
+    header.entry_point += correction;
+
+    if ( fwrite(&header, sizeof(header), 1, fp) != 1 )
+    {
+        fprintf(stderr, "Failed to write MLE header in %s\n", file_path);
+        goto fail;
+    }
+
+    fclose(fp);
+    return 0;
+
+fail:
+    fclose(fp);
+    return 1;
+}
diff --git a/xen/arch/x86/slaunch.c b/xen/arch/x86/slaunch.c
index f0447f91d2..15cfe944a7 100644
--- a/xen/arch/x86/slaunch.c
+++ b/xen/arch/x86/slaunch.c
@@ -5,6 +5,7 @@
  */
 
 #include <xen/compiler.h>
+#include <xen/efi.h>
 #include <xen/init.h>
 #include <xen/macros.h>
 #include <xen/mm.h>
@@ -244,10 +245,23 @@ check_drtm_policy(struct slr_table *slrt,
 {
     uint32_t i;
     uint32_t num_mod_entries;
+    int min_entries;
 
-    if ( policy->nr_entries < 2 )
-        panic("DRTM policy in SLRT contains less than 2 entries (%d)!\n",
-              policy->nr_entries);
+    min_entries = efi_enabled(EFI_BOOT) ? 1 : 2;
+    if ( policy->nr_entries < min_entries )
+    {
+        panic("DRTM policy in SLRT contains less than %d entries (%d)!\n",
+              min_entries, policy->nr_entries);
+    }
+
+    if ( efi_enabled(EFI_BOOT) )
+    {
+        check_slrt_policy_entry(&policy_entry[0], 0, slrt);
+        /* SLRT was measured in tpm_measure_slrt(). */
+        return 1;
+    }
+
+    /* This must be legacy MultiBoot2 boot. */
 
     /*
      * MBI policy entry must be the first one, so that measuring order matches
@@ -316,6 +330,7 @@ void __init slaunch_process_drtm_policy(const struct boot_info *bi)
     struct slr_table *slrt;
     struct slr_entry_policy *policy;
     struct slr_policy_entry *policy_entry;
+    int rc;
     uint16_t i;
     unsigned int measured;
 
@@ -331,7 +346,6 @@ void __init slaunch_process_drtm_policy(const struct boot_info *bi)
 
     for ( i = measured; i < policy->nr_entries; i++ )
     {
-        int rc;
         uint64_t start = policy_entry[i].entity;
         uint64_t size = policy_entry[i].size;
 
@@ -376,6 +390,58 @@ void __init slaunch_process_drtm_policy(const struct boot_info *bi)
 
         policy_entry[i].flags |= SLR_POLICY_FLAG_MEASURED;
     }
+
+    /*
+     * On x86 EFI platforms Xen reads its command-line options and kernel/initrd
+     * from configuration files (several can be chained). Bootloader can't know
+     * contents of the configuration beforehand without parsing it, so there
+     * will be no corresponding policy entries. Instead, measure command-line
+     * and all modules here.
+     */
+    if ( efi_enabled(EFI_BOOT) )
+    {
+#define LOG_DATA(str) (uint8_t *)(str), (sizeof(str) - 1)
+
+        tpm_hash_extend(DRTM_LOC, DRTM_DATA_PCR,
+                        (const uint8_t *)bi->cmdline, strlen(bi->cmdline),
+                        DLE_EVTYPE_SLAUNCH, LOG_DATA("Xen's command line"));
+
+        for ( i = 0; i < bi->nr_modules; i++ )
+        {
+            const struct boot_module *mod = &bi->mods[i];
+
+            paddr_t string = mod->cmdline_pa;
+            paddr_t start = mod->start;
+            size_t size = mod->size;
+
+            if ( mod->relocated || mod->released )
+            {
+                panic("A module \"%s\" (#%d) was consumed before measurement\n",
+                      (const char *)__va(string), i);
+            }
+
+            /*
+             * Measuring module's name separately because module's command-line
+             * parameters are appended to its name when present.
+             *
+             * 2 MiB is minimally mapped size and it should more than suffice.
+             */
+            rc = slaunch_map_l2(string, 2 * 1024 * 1024);
+            BUG_ON(rc != 0);
+
+            tpm_hash_extend(DRTM_LOC, DRTM_DATA_PCR,
+                            __va(string), strlen(__va(string)),
+                            DLE_EVTYPE_SLAUNCH, LOG_DATA("MB module string"));
+
+            rc = slaunch_map_l2(start, size);
+            BUG_ON(rc != 0);
+
+            tpm_hash_extend(DRTM_LOC, DRTM_CODE_PCR, __va(start), size,
+                            DLE_EVTYPE_SLAUNCH, LOG_DATA("MB module"));
+        }
+
+#undef LOG_DATA
+    }
 }
 
 int __init slaunch_map_l2(unsigned long paddr, unsigned long size)
diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c
index e39fbc3529..35501ee4de 100644
--- a/xen/common/efi/boot.c
+++ b/xen/common/efi/boot.c
@@ -19,6 +19,7 @@
 #if EFI_PAGE_SIZE != PAGE_SIZE
 # error Cannot use xen/pfn.h here!
 #endif
+#include <xen/slr-table.h>
 #include <xen/string.h>
 #include <xen/stringify.h>
 #ifdef CONFIG_X86
@@ -1004,6 +1005,7 @@ static void __init efi_tables(void)
         static EFI_GUID __initdata mps_guid = MPS_TABLE_GUID;
         static EFI_GUID __initdata smbios_guid = SMBIOS_TABLE_GUID;
         static EFI_GUID __initdata smbios3_guid = SMBIOS3_TABLE_GUID;
+        static EFI_GUID __initdata slr_guid = UEFI_SLR_TABLE_GUID;
 
         if ( match_guid(&acpi2_guid, &efi_ct[i].VendorGuid) )
             efi.acpi20 = (unsigned long)efi_ct[i].VendorTable;
@@ -1015,6 +1017,8 @@ static void __init efi_tables(void)
             efi.smbios = (unsigned long)efi_ct[i].VendorTable;
         if ( match_guid(&smbios3_guid, &efi_ct[i].VendorGuid) )
             efi.smbios3 = (unsigned long)efi_ct[i].VendorTable;
+        if ( match_guid(&slr_guid, &efi_ct[i].VendorGuid) )
+            efi.slr = (unsigned long)efi_ct[i].VendorTable;
         if ( match_guid(&esrt_guid, &efi_ct[i].VendorGuid) )
             esrt = (UINTN)efi_ct[i].VendorTable;
     }
diff --git a/xen/common/efi/runtime.c b/xen/common/efi/runtime.c
index 7e1fce291d..e1b339f162 100644
--- a/xen/common/efi/runtime.c
+++ b/xen/common/efi/runtime.c
@@ -70,6 +70,7 @@ struct efi __read_mostly efi = {
 	.mps    = EFI_INVALID_TABLE_ADDR,
 	.smbios = EFI_INVALID_TABLE_ADDR,
 	.smbios3 = EFI_INVALID_TABLE_ADDR,
+	.slr    = EFI_INVALID_TABLE_ADDR,
 };
 
 const struct efi_pci_rom *__read_mostly efi_pci_roms;
diff --git a/xen/include/xen/efi.h b/xen/include/xen/efi.h
index 160804e294..614dfce66a 100644
--- a/xen/include/xen/efi.h
+++ b/xen/include/xen/efi.h
@@ -19,6 +19,7 @@ struct efi {
     unsigned long acpi20;       /* ACPI table (ACPI 2.0) */
     unsigned long smbios;       /* SM BIOS table */
     unsigned long smbios3;      /* SMBIOS v3 table */
+    unsigned long slr;          /* SLR table */
 };
 
 extern struct efi efi;
-- 
2.49.0
Re: [PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Demi Marie Obenour 7 months, 1 week ago
On 5/13/25 1:05 PM, Sergii Dmytruk wrote:
> When running on an EFI-enabled system, Xen needs to have access to Boot
> Services in order to initialize itself properly and reach a state in
> which a dom0 kernel can operate without issues.
> 
> This means that DRTM must be started in the middle of Xen's
> initialization process.  This effect is achieved via a callback into
> bootloader (GRUB) which is responsible for initiating DRTM and
> continuing Xen's initialization process.  The latter is done by
> branching in Slaunch entry point on a flag to switch back into long mode
> before calling the same function which Xen would execute as the next
> step without DRTM.

Depending on the bootloader for this unnecessarily ties DRTM to GRUB.
Instead, it would be much better for Xen to be able to perform DRTM
itself, which would allow DRTM to work without GRUB.  Pop! OS already
uses systemd-boot and the trend seems to be from GRUB to systemd-boot.
Furthermore, this would allow DRTM with Xen launched directly from
the UEFI firmware.
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)
Re: [PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Sergii Dmytruk 7 months, 1 week ago
On Tue, May 13, 2025 at 09:25:44PM -0400, Demi Marie Obenour wrote:
> On 5/13/25 1:05 PM, Sergii Dmytruk wrote:
> > When running on an EFI-enabled system, Xen needs to have access to Boot
> > Services in order to initialize itself properly and reach a state in
> > which a dom0 kernel can operate without issues.
> >
> > This means that DRTM must be started in the middle of Xen's
> > initialization process.  This effect is achieved via a callback into
> > bootloader (GRUB) which is responsible for initiating DRTM and
> > continuing Xen's initialization process.  The latter is done by
> > branching in Slaunch entry point on a flag to switch back into long mode
> > before calling the same function which Xen would execute as the next
> > step without DRTM.
>
> Depending on the bootloader for this unnecessarily ties DRTM to GRUB.
> Instead, it would be much better for Xen to be able to perform DRTM
> itself, which would allow DRTM to work without GRUB.  Pop! OS already
> uses systemd-boot and the trend seems to be from GRUB to systemd-boot.
> Furthermore, this would allow DRTM with Xen launched directly from
> the UEFI firmware.
> --
> Sincerely,
> Demi Marie Obenour (she/her/hers)

That sentence in the commit message is worth rewording.  GRUB isn't a
requirement, any TrenchBoot-enabled bootloader (or anything that wants
to act as a bootloader) can be used.  systemd-boot could implement
Secure Launch specification [0] and start Xen/Linux/something else via
DRTM.  Usage without a real bootloader could be implemented similarly
via some EFI stub that has binaries embedded into it or that can load
them from a drive.

Mind that at least Intel and AMD DRTM implementations require a DCE [1]
binary that depends on a vendor, firmware version or a CPU generation.
So even embedding all code into every kernel-like software won't produce
self-contained DRTM-capable images.

[0]: https://trenchboot.org/specifications/Secure_Launch/
[1]: https://trenchboot.org/theory/Glossary/#dynamic-configuration-environment-dce

Regards
Re: [PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Demi Marie Obenour 7 months, 1 week ago
On 5/14/25 10:24 AM, Sergii Dmytruk wrote:
> On Tue, May 13, 2025 at 09:25:44PM -0400, Demi Marie Obenour wrote:
>> On 5/13/25 1:05 PM, Sergii Dmytruk wrote:
>>> When running on an EFI-enabled system, Xen needs to have access to Boot
>>> Services in order to initialize itself properly and reach a state in
>>> which a dom0 kernel can operate without issues.
>>>
>>> This means that DRTM must be started in the middle of Xen's
>>> initialization process.  This effect is achieved via a callback into
>>> bootloader (GRUB) which is responsible for initiating DRTM and
>>> continuing Xen's initialization process.  The latter is done by
>>> branching in Slaunch entry point on a flag to switch back into long mode
>>> before calling the same function which Xen would execute as the next
>>> step without DRTM.
>>
>> Depending on the bootloader for this unnecessarily ties DRTM to GRUB.
>> Instead, it would be much better for Xen to be able to perform DRTM
>> itself, which would allow DRTM to work without GRUB.  Pop! OS already
>> uses systemd-boot and the trend seems to be from GRUB to systemd-boot.
>> Furthermore, this would allow DRTM with Xen launched directly from
>> the UEFI firmware.
>> --
>> Sincerely,
>> Demi Marie Obenour (she/her/hers)
> 
> That sentence in the commit message is worth rewording.  GRUB isn't a
> requirement, any TrenchBoot-enabled bootloader (or anything that wants
> to act as a bootloader) can be used.  systemd-boot could implement
> Secure Launch specification [0] and start Xen/Linux/something else via
> DRTM.  Usage without a real bootloader could be implemented similarly
> via some EFI stub that has binaries embedded into it or that can load
> them from a drive.
> 
> Mind that at least Intel and AMD DRTM implementations require a DCE [1]
> binary that depends on a vendor, firmware version or a CPU generation.
> So even embedding all code into every kernel-like software won't produce
> self-contained DRTM-capable images.
> 
> [0]: https://trenchboot.org/specifications/Secure_Launch/
> [1]: https://trenchboot.org/theory/Glossary/#dynamic-configuration-environment-dce

Why is it better for Xen to rely on the bootloader to implement the
specification, instead of xen.efi itself implementing secure launch?
That would make secure launch significantly more usable.  For an
initial implementation it makes sense to rely on the bootloader, but
in the future it would be better for xen.efi to have its own
implementation.

Is the code being added to GRUB for secure launch under a license
that would allow it to be used in Xen as well?
-- 
Sincerely,
Demi Marie Obenour (she/her/hers)
Re: [PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Sergii Dmytruk 7 months, 1 week ago
On Wed, May 14, 2025 at 11:58:49AM -0400, Demi Marie Obenour wrote:
> On 5/14/25 10:24 AM, Sergii Dmytruk wrote:
> > On Tue, May 13, 2025 at 09:25:44PM -0400, Demi Marie Obenour wrote:
> >> On 5/13/25 1:05 PM, Sergii Dmytruk wrote:
> > That sentence in the commit message is worth rewording.  GRUB isn't a
> > requirement, any TrenchBoot-enabled bootloader (or anything that wants
> > to act as a bootloader) can be used.  systemd-boot could implement
> > Secure Launch specification [0] and start Xen/Linux/something else via
> > DRTM.  Usage without a real bootloader could be implemented similarly
> > via some EFI stub that has binaries embedded into it or that can load
> > them from a drive.
> >
> > Mind that at least Intel and AMD DRTM implementations require a DCE [1]
> > binary that depends on a vendor, firmware version or a CPU generation.
> > So even embedding all code into every kernel-like software won't produce
> > self-contained DRTM-capable images.
> >
> > [0]: https://trenchboot.org/specifications/Secure_Launch/
> > [1]: https://trenchboot.org/theory/Glossary/#dynamic-configuration-environment-dce
>
> Why is it better for Xen to rely on the bootloader to implement the
> specification, instead of xen.efi itself implementing secure launch?
> That would make secure launch significantly more usable.  For an
> initial implementation it makes sense to rely on the bootloader, but
> in the future it would be better for xen.efi to have its own
> implementation.

That specification is not exactly about DRTM, which is specified by CPU
vendors.  It's about an interface between what starts DRTM (a bootloader
in a broad sense) and what uses on it (kernels, hypervisors, etc.).  If
the whole process is performed by a single entity, there is no need
for the specification.

What starts DRTM needs to ensure the system supports DRTM and should
then put it in a state suitable for DRTM start.  What uses DRTM
needs to know much less and can heavily rely on the information from a
bootloader.  Ideally, Xen/Linux would be able to handle DRTM uniformly
on different hardware, but the reality is that abstracting away some
differences is nearly impossible.

> Is the code being added to GRUB for secure launch under a license
> that would allow it to be used in Xen as well?

GRUB's changes are GPL3-or-later, but that shouldn't be a problem,
authors will likely agree to relicense it as GPL2-or-later for Xen.

Regards
Re: [PATCH v2 20/22] x86/slaunch: support EFI boot
Posted by Marek Marczykowski-Górecki 7 months, 1 week ago
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

On Wed, May 14, 2025 at 11:58:49AM -0400, Demi Marie Obenour wrote:
> Why is it better for Xen to rely on the bootloader to implement the
> specification, instead of xen.efi itself implementing secure launch?
> That would make secure launch significantly more usable.  For an
> initial implementation it makes sense to rely on the bootloader, but
> in the future it would be better for xen.efi to have its own
> implementation.

That might be true when looking at the very limited use case. But if you
look at the broader ecosystem, having a common part in the bootloader
makes much more sense, as it can be reused for different kernels without
duplicating a lot of work.

- -- 
Best Regards,
Marek Marczykowski-Górecki
Invisible Things Lab
-----BEGIN PGP SIGNATURE-----

iQEzBAEBCAAdFiEEhrpukzGPukRmQqkK24/THMrX1ywFAmgkwNEACgkQ24/THMrX
1yypmggAiBWdTT3yTZKYA/28IVhTXRPemEGGqdDcqyC2mDQt102kgF7m46LwSv5I
eK1xY7Hz6AG3Mp9pRH4Jltx5j7SMZ0zzesYuADEpDTNIEWx6Xh+W6AQ5ttAKco1M
tcUFiYaDujesLwRNHJpjH1D9Ih82d1SbUoNyjBgnn1cmX2hXXntVDyXttz6P+xUy
Vl8eF3NYC90+sdc0g8aaKKWe6GqDzsBRj+heISYtymiWRcePWgFWMVMrIDB4Yqmo
+KnDYWsDTQn4Bddu1O6AVVVhvlnhgzP8sHWb20gjQpZG2jwJd1DQcNwQy2Ae04xS
Fxx8mBC75utOGNqnft/Pv098BnmYqQ==
=AulS
-----END PGP SIGNATURE-----