[PATCH v4 21/23] x86/slaunch: support EFI boot

Sergii Dmytruk <[email protected]> Sun, 2 Aug 2026 16:09:37 +0300
Newsgroups gmane.comp.emulators.xen.devel
Message-ID <e29edd81eeb55927fef2c56b7493a8db65edfc90.1785668458.git.sergii.dmytruk@3mdeb.com>
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
a TrenchBoot-enabled 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 <[email protected]>
Signed-off-by: Sergii Dmytruk <[email protected]>
---

Notes:
    v4: -DXEN_BUILD_EFI => -DXEN_BUILD_EFI=1
    v4: use CONFIG_SLAUNCH
    v4: use pointers to `const` SLRT and TXT heap data
    v4: use container_of()
    v4: take updates to `struct boot_module` into account
    v4: now UEFI_SLR_TABLE_GUID is defined here in efi/boot.c

 .gitignore                                    |   1 +
 .../eclair_analysis/ECLAIR/out_of_scope.ecl   |   1 +
 docs/hypervisor-guide/x86/how-xen-boots.rst   |  14 +-
 xen/arch/x86/Makefile                         |  12 +-
 xen/arch/x86/boot/head.S                      | 124 +++++++++++++++++
 xen/arch/x86/boot/x86_64.S                    |  14 +-
 xen/arch/x86/efi/efi-boot.h                   |  94 ++++++++++++-
 xen/arch/x86/efi/fixmlehdr.c                  | 127 ++++++++++++++++++
 xen/arch/x86/slaunch.c                        |  75 ++++++++++-
 xen/common/efi/boot.c                         |   6 +
 xen/common/efi/runtime.c                      |   1 +
 xen/include/xen/efi.h                         |   1 +
 12 files changed, 455 insertions(+), 15 deletions(-)
 create mode 100644 xen/arch/x86/efi/fixmlehdr.c

diff --git a/.gitignore b/.gitignore
index bfc7bdf043..76bc97d5ea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -177,6 +177,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 a841d1e9f8..0b1b62971f 100644
--- a/docs/hypervisor-guide/x86/how-xen-boots.rst
+++ b/docs/hypervisor-guide/x86/how-xen-boots.rst
@@ -56,12 +56,14 @@ 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 Measured Launched Environment (MLE) headers
-is used to support Dynamic Root of Trust for Measurement (DRTM) for legacy
-(BIOS) boot.  DRTM is a way to establish hardware root of trust which
-excludes firmware and is not directly tied to hardware's boot process.  The
-separate entry point called ``slaunch_stub_entry`` is used mainly to
-differentiate from other kinds of boots.  It moves a magic number to ``EAX``
-before jumping into common startup code.  More details about Secure Launch
+is used to support Dynamic Root of Trust for Measurement (DRTM).  DRTM is a
+way to establish hardware root of trust which excludes firmware and is not
+directly tied to hardware's boot process.  The separate entry point called
+``slaunch_stub_entry`` 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.  More details about Secure Launch
 data structures processed by Xen in this boot mode can be found in
 `<https://trenchboot.org/specifications/Secure_Launch/>`_.
 
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 8dbb76a3a0..4f7db9420e 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
 
@@ -123,6 +124,11 @@ $(TARGET): $(TARGET)-syms $(efi-y) $(obj)/boot/mkelf32
 
 CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
 
+# Expose this build flag as a macro when compiling assembly files.
+ifeq ($(XEN_BUILD_EFI),y)
+XEN_AFLAGS += -DXEN_BUILD_EFI=1
+endif
+
 $(TARGET)-syms: $(objtree)/prelink.o $(obj)/xen.lds
 	$(objtree)/tools/symbols $(all_symbols) --empty > $(dot-target).0.S
 	$(MAKE) $(build)=$(@D) $(dot-target).0.o
@@ -196,7 +202,7 @@ note_file_option ?= $(note_file)
 extra-$(XEN_BUILD_PE) += efi.lds
 ifeq ($(XEN_BUILD_PE),y)
 $(TARGET).efi: $(obj)/efi/relocs-dummy.o $(obj)/efi/relocs-empty.o $(obj)/efi/mkreloc
-$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds
+$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds $(obj)/efi/fixmlehdr
 ifeq ($(CONFIG_DEBUG_INFO),y)
 	$(if $(filter --strip-debug,$(EFI_LDFLAGS)),echo,:) "Will strip debug info from $(@F)"
 endif
@@ -230,6 +236,10 @@ endif
 	$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) -T $(obj)/efi.lds $< $(obj)/efi/relocs-empty.o \
 	      $(dot-target).2r.o $(dot-target).2s.o $(orphan-handling-y) \
 	      $(note_file_option) -o $@
+ifeq ($(CONFIG_SLAUNCH),y)
+	# update entry point's address by taking image offset into account
+	$(obj)/efi/fixmlehdr $@ $(XEN_IMG_OFFSET)
+endif
 	$(NM) -pa --format=sysv $@ \
 		| $(objtree)/tools/symbols --all-symbols --xensyms --sysv --sort \
 		> [email protected]
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index bf38aef21c..22b331a45c 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -408,6 +408,12 @@ slaunch_stub_entry:
         mov     %ebx, %esi
         sub     $sym_offs(slaunch_stub_entry), %esi
 
+#if 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
 
@@ -855,6 +861,124 @@ trampoline_setup:
         /* Jump into the relocated trampoline. */
         lret
 
+#if XEN_BUILD_EFI && CONFIG_SLAUNCH
+
+        /*
+         * 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 && CONFIG_SLAUNCH */
+
 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 886960c22f..d23cfebdb6 100644
--- a/xen/arch/x86/boot/x86_64.S
+++ b/xen/arch/x86/boot/x86_64.S
@@ -267,14 +267,22 @@ GLOBAL(__page_tables_end)
 /* Init pagetables. Enough page directories to map into 4GB. */
         .section .init.data.page_aligned, "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 d738b839ee..9653de4ca9 100644
--- a/xen/arch/x86/efi/efi-boot.h
+++ b/xen/arch/x86/efi/efi-boot.h
@@ -7,8 +7,15 @@
 #ifndef X86_EFI_EFI_BOOT_H
 #define X86_EFI_EFI_BOOT_H
 
+#include <xen/kernel.h>
 #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>
@@ -17,8 +24,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
 };
@@ -234,10 +244,31 @@ 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 )
+    {
+        const struct slr_table *slrt = (const struct slr_table *)efi.slr;
+        const struct slr_entry_hdr *entry;
+
+        entry = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO);
+        if ( entry != NULL )
+        {
+            const struct slr_entry_intel_info *intel_info =
+                container_of(entry, const struct slr_entry_intel_info, hdr);
+            void *txt_heap = txt_init();
+            const struct txt_os_mle_data *os_mle =
+                txt_start(txt_heap, TXT_OS2MLE);
+            const struct txt_os_sinit_data *os_sinit =
+                txt_start(txt_heap, TXT_OS2SINIT);
+
+            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);
 
@@ -283,6 +314,66 @@ static void __init noreturn efi_arch_post_exit_boot(void)
     unreachable();
 }
 
+static void __init attempt_secure_launch(void)
+{
+#ifdef CONFIG_SLAUNCH
+    const struct slr_table *slrt;
+    const struct slr_entry_hdr *entry;
+    const struct slr_entry_dl_info *dlinfo;
+    dl_handler_func handler_callback;
+
+    /* The presence of this table indicates a Secure Launch boot. */
+    slrt = (const 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. */
+    entry = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DL_INFO);
+    dlinfo = container_of(entry, const struct slr_entry_dl_info, hdr);
+
+    handler_callback = (dl_handler_func)dlinfo->dl_handler;
+    handler_callback(&dlinfo->bl_context);
+
+    unreachable();
+#endif
+}
+
+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)
@@ -783,6 +874,7 @@ static void noreturn __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 af88ca9caa..e506fb4293 100644
--- a/xen/arch/x86/slaunch.c
+++ b/xen/arch/x86/slaunch.c
@@ -6,6 +6,7 @@
  */
 
 #include <xen/compiler.h>
+#include <xen/efi.h>
 #include <xen/init.h>
 #include <xen/kernel.h>
 #include <xen/macros.h>
@@ -252,10 +253,23 @@ check_drtm_policy(const 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 slaunch_measure_slrt(). */
+        return 1;
+    }
+
+    /* This must be legacy MultiBoot2 boot. */
 
     /*
      * MBI policy entry must be the first one, so that measuring order matches
@@ -324,6 +338,7 @@ void __init slaunch_process_drtm_policy(const struct boot_info *bi)
     const struct slr_table *slrt;
     const struct slr_entry_policy *policy;
     struct slr_policy_entry *policy_entry;
+    int rc;
     uint16_t i;
     unsigned int measured;
 
@@ -338,7 +353,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;
 
@@ -384,6 +398,59 @@ 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)
+
+        slaunch_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->arch.cmdline_pa;
+            paddr_t start = mod->start;
+            size_t size = mod->size;
+
+            if ( mod->arch.relocated || mod->arch.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);
+
+            slaunch_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);
+
+            slaunch_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(paddr_t paddr, size_t size)
diff --git a/xen/common/efi/boot.c b/xen/common/efi/boot.c
index 8f24df9bc2..41ccc6dcd3 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
@@ -45,6 +46,8 @@
 #define EFI_SYSTEM_RESOURCE_TABLE_GUID    \
   { 0xb122a263U, 0x3661, 0x4f68, {0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80} }
 #define EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION 1
+#define UEFI_SLR_TABLE_GUID \
+  { 0x877a9b2aU, 0x0385, 0x45d1, { 0xa0, 0x34, 0x9d, 0xac, 0x9c, 0x9e, 0x56, 0x5f } }
 
 typedef struct {
     EFI_GUID FwClass;
@@ -1154,6 +1157,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;
@@ -1165,6 +1169,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 596f2710fb..b2cebaad02 100644
--- a/xen/common/efi/runtime.c
+++ b/xen/common/efi/runtime.c
@@ -72,6 +72,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 87146172ad..37d016d885 100644
--- a/xen/include/xen/efi.h
+++ b/xen/include/xen/efi.h
@@ -17,6 +17,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.55.0