[PATCH v4 11/23] xen/arch/x86: reserve TXT memory during Slaunch

Sergii Dmytruk <[email protected]> Sun, 2 Aug 2026 16:09:27 +0300
Newsgroups gmane.comp.emulators.xen.devel
Message-ID <e9c888f8356e967d77a43ba5459d5c0a95c65347.1785668458.git.sergii.dmytruk@3mdeb.com>
From: Kacper Stojek <[email protected]>

TXT heap, SINIT and TXT private space are marked as reserved or unused
in e820 to protect from unintended uses.

Signed-off-by: Kacper Stojek <[email protected]>
Signed-off-by: Krystian Hebel <[email protected]>
Signed-off-by: Michał Żygowski <[email protected]>
Signed-off-by: Sergii Dmytruk <[email protected]>
---

Notes:
    v4: use CONFIG_SLAUNCH
    v4: use unsigned long constant in PREBUILT_MAP_LIMIT #define
    v4: add slaunch-tpm unit for TPM-related code specific to Slaunch (builds as normal and early code)
    v4: slaunch_get_slrt() now makes its first appearance in this commit
    v4: slaunch_find_log() is now defined in slaunch-tpm.c
    v4: improved signature and comment for slaunch_map_l2()
    v4: moved SPDX license comments to their own lines
    v4: reduced txt_heap_base and txt_heap_size from 64-bit to 32-bit
    v4: changed reserve_ram() to return bool and not take type (it's always the same) and skip already reserved memory
    v4: switch from "(from - to)" ranges to "[from, to)" in prints
    v4: verify that slaunch_map_l2() was passed a range below 4 GiB
    v4: move PREBUILT_MAP_LIMIT from asm/mm.h to asm/setup.h

 xen/arch/x86/Makefile                  |   2 +
 xen/arch/x86/include/asm/intel-txt.h   |   6 ++
 xen/arch/x86/include/asm/setup.h       |   3 +
 xen/arch/x86/include/asm/slaunch-tpm.h |  19 +++++
 xen/arch/x86/include/asm/slaunch.h     |  34 +++++++-
 xen/arch/x86/intel-txt.c               | 113 +++++++++++++++++++++++++
 xen/arch/x86/setup.c                   |  10 ++-
 xen/arch/x86/slaunch-tpm.c             |  36 ++++++++
 xen/arch/x86/slaunch.c                 | 105 ++++++++++++++++++++++-
 9 files changed, 323 insertions(+), 5 deletions(-)
 create mode 100644 xen/arch/x86/include/asm/slaunch-tpm.h
 create mode 100644 xen/arch/x86/intel-txt.c
 create mode 100644 xen/arch/x86/slaunch-tpm.c

diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index a03f5a91ef..8dbb76a3a0 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -42,6 +42,7 @@ obj-y += i387.o
 obj-y += i8259.o
 obj-$(CONFIG_INDIRECT_THUNK) += indirect-thunk.o
 obj-$(CONFIG_RETURN_THUNK) += indirect-thunk.o
+obj-$(CONFIG_SLAUNCH) += intel-txt.o
 obj-$(CONFIG_PV) += ioport_emulate.o
 obj-y += io_apic.o
 obj-y += irq.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_X86_PSR) += psr.o
 obj-y += setup.o
 obj-y += shutdown.o
 obj-$(CONFIG_SLAUNCH) += slaunch.o
+obj-$(CONFIG_SLAUNCH) += slaunch-tpm.o
 obj-y += smp.o
 obj-y += smpboot.o
 obj-y += spec_ctrl.o
diff --git a/xen/arch/x86/include/asm/intel-txt.h b/xen/arch/x86/include/asm/intel-txt.h
index 66039dbeee..db6b0defd0 100644
--- a/xen/arch/x86/include/asm/intel-txt.h
+++ b/xen/arch/x86/include/asm/intel-txt.h
@@ -383,6 +383,12 @@ static inline void txt_verify_pmr_ranges(
     */
 }
 
+/* Prepares for accesses to TXT-specific memory. */
+void txt_map_mem_regions(void);
+
+/* Marks TXT-specific memory as used to avoid its corruption. */
+void txt_reserve_mem_regions(void);
+
 #endif /* !__ASSEMBLER__ */
 
 #endif /* X86_INTEL_TXT_H */
diff --git a/xen/arch/x86/include/asm/setup.h b/xen/arch/x86/include/asm/setup.h
index b01e83a8ed..431c0a26b5 100644
--- a/xen/arch/x86/include/asm/setup.h
+++ b/xen/arch/x86/include/asm/setup.h
@@ -4,6 +4,9 @@
 #include <xen/multiboot.h>
 #include <asm/numa.h>
 
+/* How much of the directmap is prebuilt at compile time. */
+#define PREBUILT_MAP_LIMIT (1UL << L2_PAGETABLE_SHIFT)
+
 extern const char __2M_text_start[], __2M_text_end[];
 extern const char __2M_rodata_start[], __2M_rodata_end[];
 extern char __2M_init_start[], __2M_init_end[];
diff --git a/xen/arch/x86/include/asm/slaunch-tpm.h b/xen/arch/x86/include/asm/slaunch-tpm.h
new file mode 100644
index 0000000000..68e9c8358a
--- /dev/null
+++ b/xen/arch/x86/include/asm/slaunch-tpm.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * TPM-related functions of Slaunch.  Can be used in both normal and early boot
+ * environments.
+ *
+ * Copyright (c) 2026 3mdeb Sp. z o.o.  All rights reserved.
+ */
+
+#ifndef X86_SLAUNCH_TPM_H
+#define X86_SLAUNCH_TPM_H
+
+#include <xen/types.h>
+
+struct slr_table;
+
+void slaunch_find_log(const struct slr_table *slrt, paddr_t *evt_log,
+                      uint32_t *evt_log_size);
+
+#endif /* X86_SLAUNCH_TPM_H */
diff --git a/xen/arch/x86/include/asm/slaunch.h b/xen/arch/x86/include/asm/slaunch.h
index 24ba164c0a..3df7174b4b 100644
--- a/xen/arch/x86/include/asm/slaunch.h
+++ b/xen/arch/x86/include/asm/slaunch.h
@@ -13,6 +13,8 @@
 #ifndef X86_SLAUNCH_H
 #define X86_SLAUNCH_H
 
+#include <xen/kernel.h>
+#include <xen/slr-table.h>
 #include <xen/types.h>
 
 struct slaunch_early_init_results
@@ -24,7 +26,37 @@ struct slaunch_early_init_results
 /* Indicates an active Secure Launch boot. */
 extern bool slaunch_active;
 
-/* Holds physical address of SLRT. */
+/*
+ * Holds physical address of SLRT.  Use slaunch_get_slrt() to access SLRT
+ * instead of mapping where this points to.
+ */
 extern uint32_t slaunch_slrt;
 
+/*
+ * Retrieves pointer to SLRT.  Checks table's validity and maps it as necessary.
+ */
+struct slr_table *slaunch_get_slrt(void);
+
+/*
+ * Prepares for accesses to essential data structures setup by boot environment.
+ */
+void slaunch_map_mem_regions(void);
+
+/* Marks regions of memory as used to avoid their corruption. */
+void slaunch_reserve_mem_regions(void);
+
+/*
+ * This helper function is used to map memory below 4 GiB using L2 page tables
+ * by aligning mapped regions to 2MB. This way page allocator (which at this
+ * point isn't yet initialized) isn't needed for creating new L1 mappings. The
+ * function also checks and skips memory already mapped by the prebuilt tables.
+ *
+ * There is no unmap_l2() because the function is meant to be used by the code
+ * that accesses DRTM-related memory soon after which Xen rebuilds memory maps,
+ * effectively dropping all existing mappings.
+ *
+ * Returns zero on success.
+ */
+int slaunch_map_l2(paddr_t paddr, size_t size);
+
 #endif /* X86_SLAUNCH_H */
diff --git a/xen/arch/x86/intel-txt.c b/xen/arch/x86/intel-txt.c
new file mode 100644
index 0000000000..4a42abf8df
--- /dev/null
+++ b/xen/arch/x86/intel-txt.c
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Functions related to DRTM on Intel using its TXT (Trusted eXecution
+ * Technology).
+ *
+ * Copyright (c) 2022-2026 3mdeb Sp. z o.o.  All rights reserved.
+ */
+
+#include <xen/bug.h>
+#include <xen/init.h>
+#include <xen/lib.h>
+#include <xen/types.h>
+#include <asm/e820.h>
+#include <asm/intel-txt.h>
+#include <asm/slaunch.h>
+
+/*
+ * Corresponding TXT registers seem to have 64-bits allocated for them, yet the
+ * actual values are 32-bit long, so using the latter.
+ */
+static uint32_t __initdata txt_heap_base, txt_heap_size;
+
+void __init txt_map_mem_regions(void)
+{
+    int rc;
+
+    rc = slaunch_map_l2(TXT_PRIV_CONFIG_REGS_BASE, TXT_CONFIG_SPACE_SIZE);
+    BUG_ON(rc != 0);
+
+    txt_heap_base = txt_read(TXTCR_HEAP_BASE);
+    BUG_ON(txt_heap_base == 0);
+
+    txt_heap_size = txt_read(TXTCR_HEAP_SIZE);
+    BUG_ON(txt_heap_size == 0);
+
+    rc = slaunch_map_l2(txt_heap_base, txt_heap_size);
+    BUG_ON(rc != 0);
+}
+
+/* Mark a RAM region as reserved if it isn't marked that way already. */
+static bool __init reserve_ram(struct e820map *map, uint64_t start,
+                               uint64_t end)
+{
+    unsigned int i;
+
+    for ( i = 0; i < map->nr_map; i++ )
+    {
+        uint64_t rs = map->map[i].addr;
+        uint64_t re = rs + map->map[i].size;
+
+        /* The entry includes the range. */
+        if ( start >= rs && end <= re )
+            break;
+
+        /* The entry intersects the range. */
+        if ( end > rs && start < re )
+        {
+            /* Fatal failure. */
+            return false;
+        }
+    }
+
+    /*
+     * If the range is not included by any entry and no entry intersects it,
+     * then it's not listed in the memory map.  Consider this case as a success
+     * since we're only preventing RAM from being used and unlisted range should
+     * not be used.
+     */
+    if ( i == map->nr_map )
+        return true;
+
+    /*
+     * e820_change_range_type() fails if the range is already marked with the
+     * desired type.  Don't consider it an error if firmware has done it for us.
+     */
+    if ( map->map[i].type == E820_RESERVED )
+        return true;
+
+    return e820_change_range_type(map, start, end, E820_RAM, E820_RESERVED);
+}
+
+void __init txt_reserve_mem_regions(void)
+{
+    bool ok;
+    uint32_t sinit_base, sinit_size;
+
+    /* TXT Heap */
+    BUG_ON(txt_heap_base == 0);
+    printk("SLAUNCH: reserving TXT heap range [%#x, %#x)\n", txt_heap_base,
+           txt_heap_base + txt_heap_size);
+    ok = reserve_ram(&e820_raw, txt_heap_base, txt_heap_base + txt_heap_size);
+    BUG_ON(!ok);
+
+    sinit_base = txt_read(TXTCR_SINIT_BASE);
+    BUG_ON(sinit_base == 0);
+
+    sinit_size = txt_read(TXTCR_SINIT_SIZE);
+    BUG_ON(sinit_size == 0);
+
+    /* SINIT */
+    printk("SLAUNCH: reserving SINIT memory range [%#x, %#x)\n", sinit_base,
+           sinit_base + sinit_size);
+    ok = reserve_ram(&e820_raw, sinit_base, sinit_base + sinit_size);
+    BUG_ON(!ok);
+
+    /* TXT Private Space */
+    printk("SLAUNCH: reserving private TXT registers range [%#x, %#x)\n",
+           TXT_PRIV_CONFIG_REGS_BASE,
+           TXT_PRIV_CONFIG_REGS_BASE + TXT_CONFIG_SPACE_SIZE);
+    ok = reserve_ram(&e820_raw, TXT_PRIV_CONFIG_REGS_BASE,
+                     TXT_PRIV_CONFIG_REGS_BASE + TXT_CONFIG_SPACE_SIZE);
+    BUG_ON(!ok);
+}
diff --git a/xen/arch/x86/setup.c b/xen/arch/x86/setup.c
index 7d71fea6c0..5494fa1621 100644
--- a/xen/arch/x86/setup.c
+++ b/xen/arch/x86/setup.c
@@ -50,6 +50,7 @@
 #include <asm/pv/domain.h>
 #include <asm/setup.h>
 #include <asm/shstk.h>
+#include <asm/slaunch.h>
 #include <asm/smp.h>
 #include <asm/spec_ctrl.h>
 #include <asm/stubs.h>
@@ -1128,9 +1129,6 @@ static struct domain *__init create_dom0(struct boot_info *bi)
     return d;
 }
 
-/* How much of the directmap is prebuilt at compile time. */
-#define PREBUILT_MAP_LIMIT (1 << L2_PAGETABLE_SHIFT)
-
 void asmlinkage __init noreturn __start_xen(void)
 {
     const char *memmap_type = NULL;
@@ -1472,6 +1470,12 @@ void asmlinkage __init noreturn __start_xen(void)
 #endif
     }
 
+    if ( slaunch_active )
+    {
+        slaunch_map_mem_regions();
+        slaunch_reserve_mem_regions();
+    }
+
     /* Sanitise the raw E820 map to produce a final clean version. */
     max_page = raw_max_page = init_e820(memmap_type, &e820_raw);
 
diff --git a/xen/arch/x86/slaunch-tpm.c b/xen/arch/x86/slaunch-tpm.c
new file mode 100644
index 0000000000..f59170594d
--- /dev/null
+++ b/xen/arch/x86/slaunch-tpm.c
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Slaunch functions related to TPM.
+ *
+ * Copyright (c) 2022-2026 3mdeb Sp. z o.o.  All rights reserved.
+ */
+
+#include <xen/macros.h>
+#include <xen/slr-table.h>
+#include <xen/types.h>
+
+void slaunch_find_log(const struct slr_table *slrt, paddr_t *evt_log,
+                      uint32_t *evt_log_size)
+{
+    const struct slr_entry_hdr *hdr;
+
+    hdr = slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_LOG_INFO);
+    if ( hdr != NULL )
+    {
+        const struct slr_entry_log_info *log_info;
+        log_info = container_of(hdr, const struct slr_entry_log_info, hdr);
+
+        *evt_log = (uintptr_t)_p(log_info->addr);
+        *evt_log_size = log_info->size;
+    }
+    else
+    {
+        /*
+         * Event log is used to verify measurements, but values of PCRs is the
+         * real authoritative source of information, so keep going if there is
+         * no log as secrets may still be correctly unsealed by TPM.
+         */
+        *evt_log = 0;
+        *evt_log_size = 0;
+    }
+}
diff --git a/xen/arch/x86/slaunch.c b/xen/arch/x86/slaunch.c
index acf751804f..ba1ba61c47 100644
--- a/xen/arch/x86/slaunch.c
+++ b/xen/arch/x86/slaunch.c
@@ -7,11 +7,17 @@
 
 #include <xen/compiler.h>
 #include <xen/init.h>
-#include <xen/inttypes.h>
 #include <xen/macros.h>
+#include <xen/mm.h>
 #include <xen/sections.h>
+#include <xen/types.h>
 
+#include <asm/e820.h>
+#include <asm/intel-txt.h>
+#include <asm/page.h>
+#include <asm/setup.h>
 #include <asm/slaunch.h>
+#include <asm/slaunch-tpm.h>
 
 /*
  * These variables are assigned to by the code near Xen's entry point.
@@ -30,3 +36,100 @@ static void __maybe_unused compile_time_checks(void)
 {
     BUILD_BUG_ON(sizeof(slaunch_active) != 1);
 }
+
+struct slr_table *__init slaunch_get_slrt(void)
+{
+    static struct slr_table *__initdata slrt;
+
+    if ( slrt == NULL )
+    {
+        int rc;
+
+        slrt = __va(slaunch_slrt);
+
+        rc = slaunch_map_l2(slaunch_slrt, PAGE_SIZE);
+        BUG_ON(rc != 0);
+
+        if ( slrt->magic != SLR_TABLE_MAGIC )
+            panic("SLRT has invalid magic value: %#x!\n", slrt->magic);
+        /* XXX: are newer revisions allowed? */
+        if ( slrt->revision != SLR_TABLE_REVISION )
+            panic("SLRT is of unsupported revision: %#x!\n", slrt->revision);
+        if ( slrt->architecture != SLR_INTEL_TXT )
+            panic("SLRT is for unexpected architecture: %#x!\n",
+                  slrt->architecture);
+        if ( slrt->size > slrt->max_size )
+            panic("SLRT is larger than its max size: %#x > %#x!\n",
+                  slrt->size, slrt->max_size);
+
+        if ( slrt->size > PAGE_SIZE )
+        {
+            rc = slaunch_map_l2(slaunch_slrt, slrt->size);
+            BUG_ON(rc != 0);
+        }
+    }
+
+    return slrt;
+}
+
+void __init slaunch_map_mem_regions(void)
+{
+    int rc;
+    paddr_t evt_log_addr;
+    uint32_t evt_log_size;
+
+    /* Vendor-specific part. */
+    txt_map_mem_regions();
+
+    slaunch_find_log(slaunch_get_slrt(), &evt_log_addr, &evt_log_size);
+    if ( evt_log_addr != 0 )
+    {
+        rc = slaunch_map_l2(evt_log_addr, evt_log_size);
+        BUG_ON(rc != 0);
+    }
+}
+
+void __init slaunch_reserve_mem_regions(void)
+{
+    paddr_t evt_log_addr;
+    uint32_t evt_log_size;
+
+    /* Vendor-specific part. */
+    txt_reserve_mem_regions();
+
+    slaunch_find_log(slaunch_get_slrt(), &evt_log_addr, &evt_log_size);
+    if ( evt_log_addr != 0 )
+    {
+        int ok;
+
+        printk("SLAUNCH: reserving event log [%#lx, %#lx)\n", evt_log_addr,
+               evt_log_addr + evt_log_size);
+        ok = reserve_e820_ram(&e820_raw, evt_log_addr,
+                              evt_log_addr + evt_log_size);
+        BUG_ON(!ok);
+    }
+}
+
+int __init slaunch_map_l2(paddr_t paddr, size_t size)
+{
+    unsigned long aligned_paddr = paddr & ~((1ULL << L2_PAGETABLE_SHIFT) - 1);
+    unsigned long pages = ((paddr + size) - aligned_paddr);
+
+    pages = ROUNDUP(pages, 1ULL << L2_PAGETABLE_SHIFT) >> PAGE_SHIFT;
+
+    BUG_ON(paddr >= (1ULL << 32));
+    BUG_ON(paddr + pages * PAGE_SIZE >= (1ULL << 32));
+
+    if ( aligned_paddr + pages * PAGE_SIZE <= PREBUILT_MAP_LIMIT )
+        return 0;
+
+    if ( aligned_paddr < PREBUILT_MAP_LIMIT )
+    {
+        pages -= (PREBUILT_MAP_LIMIT - aligned_paddr) >> PAGE_SHIFT;
+        aligned_paddr = PREBUILT_MAP_LIMIT;
+    }
+
+    return map_pages_to_xen((uintptr_t)__va(aligned_paddr),
+                            maddr_to_mfn(aligned_paddr),
+                            pages, PAGE_HYPERVISOR);
+}
-- 
2.55.0