[PATCH v4 12/23] x86/slaunch: restore boot MTRRs after Intel TXT DRTM

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

In preparation for TXT SENTER call, GRUB had to modify MTRR settings
to be UC for everything except SINIT ACM. Old values are restored
from SLRT where they were saved by the bootloader.

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

Notes:
    v4: now this commit makes two functions of mtrr/generic.c public
    v4: take CONFIG_SLAUNCH into account
    v4: restore MTRRs earlier: move from mtrr_top_of_ram() to machine_specific_memory_setup()
    v4: renamed parameter of txt_restore_mtrrs(): e820_verbose => verbose
    v4: don't use rdmsrl() and wrmsrl()
    v4: extract part of an integer consistently (`mtrr_cap` in txt_restore_mtrrs(), was a mix of `(uint8_t)` cast and `& 0xff`)
    v4: use container_of()
    v4: manage MTRR count in txt_restore_mtrrs() better (separate variable, no weird ?: operator)

 xen/arch/x86/cpu/mtrr/generic.c      |  9 +--
 xen/arch/x86/e820.c                  |  5 ++
 xen/arch/x86/include/asm/intel-txt.h |  3 +
 xen/arch/x86/include/asm/mtrr.h      |  8 +++
 xen/arch/x86/include/asm/slaunch.h   |  8 +++
 xen/arch/x86/intel-txt.c             | 84 ++++++++++++++++++++++++++++
 6 files changed, 110 insertions(+), 7 deletions(-)

diff --git a/xen/arch/x86/cpu/mtrr/generic.c b/xen/arch/x86/cpu/mtrr/generic.c
index 86eb0f405b..c179935dd3 100644
--- a/xen/arch/x86/cpu/mtrr/generic.c
+++ b/xen/arch/x86/cpu/mtrr/generic.c
@@ -14,11 +14,6 @@
 #include <asm/cpufeature.h>
 #include "mtrr.h"
 
-struct mtrr_pausing_state {
-	bool pge;
-	uint64_t def_type;
-};
-
 static const struct fixed_range_block {
 	uint32_t base_msr;   /* start address of an MTRR block */
 	unsigned int ranges; /* number of MTRRs in this block  */
@@ -440,7 +435,7 @@ static DEFINE_SPINLOCK(set_atomicity_lock);
  * has been called.
  */
 
-static void mtrr_pause_caching(struct mtrr_pausing_state *state)
+void mtrr_pause_caching(struct mtrr_pausing_state *state)
 {
 	unsigned long cr4;
 
@@ -481,7 +476,7 @@ static void mtrr_pause_caching(struct mtrr_pausing_state *state)
 	alternative("wbinvd", "", X86_FEATURE_XEN_SELFSNOOP);
 }
 
-static void mtrr_resume_caching(struct mtrr_pausing_state state)
+void mtrr_resume_caching(struct mtrr_pausing_state state)
 {
 	/* Intel (P6) standard MTRRs */
 	mtrr_wrmsr(MSR_MTRRdefType, state.def_type);
diff --git a/xen/arch/x86/e820.c b/xen/arch/x86/e820.c
index 872208ab37..c63b0b12cc 100644
--- a/xen/arch/x86/e820.c
+++ b/xen/arch/x86/e820.c
@@ -11,6 +11,8 @@
 #include <asm/mtrr.h>
 #include <asm/msr.h>
 #include <asm/guest.h>
+#include <asm/intel-txt.h>
+#include <asm/slaunch.h>
 
 /*
  * opt_mem: Limit maximum address of physical RAM.
@@ -499,6 +501,9 @@ static void __init machine_specific_memory_setup(struct e820map *raw)
     uint64_t top_of_ram, size;
     unsigned int i;
 
+    if ( slaunch_active )
+        txt_restore_mtrrs(e820_verbose);
+
     sanitize_e820_map(raw->map, &raw->nr_map);
     copy_e820_map(raw->map, raw->nr_map);
 
diff --git a/xen/arch/x86/include/asm/intel-txt.h b/xen/arch/x86/include/asm/intel-txt.h
index db6b0defd0..406929fac2 100644
--- a/xen/arch/x86/include/asm/intel-txt.h
+++ b/xen/arch/x86/include/asm/intel-txt.h
@@ -389,6 +389,9 @@ void txt_map_mem_regions(void);
 /* Marks TXT-specific memory as used to avoid its corruption. */
 void txt_reserve_mem_regions(void);
 
+/* Restores original MTRR values saved by a bootloader before starting DRTM. */
+void txt_restore_mtrrs(bool verbose);
+
 #endif /* !__ASSEMBLER__ */
 
 #endif /* X86_INTEL_TXT_H */
diff --git a/xen/arch/x86/include/asm/mtrr.h b/xen/arch/x86/include/asm/mtrr.h
index 3a5b4f5b6e..bf82af8c47 100644
--- a/xen/arch/x86/include/asm/mtrr.h
+++ b/xen/arch/x86/include/asm/mtrr.h
@@ -63,6 +63,14 @@ extern uint8_t pat_type_2_pte_flags(uint8_t pat_type);
 extern void mtrr_aps_sync_begin(void);
 extern void mtrr_aps_sync_end(void);
 
+struct mtrr_pausing_state {
+	bool pge;
+	uint64_t def_type;
+};
+
+extern void mtrr_pause_caching(struct mtrr_pausing_state *state);
+extern void mtrr_resume_caching(struct mtrr_pausing_state state);
+
 extern bool mtrr_var_range_msr_set(struct domain *d, struct mtrr_state *m,
                                    uint32_t msr, uint64_t msr_content);
 extern bool mtrr_fix_range_msr_set(struct domain *d, struct mtrr_state *m,
diff --git a/xen/arch/x86/include/asm/slaunch.h b/xen/arch/x86/include/asm/slaunch.h
index 3df7174b4b..459fc83388 100644
--- a/xen/arch/x86/include/asm/slaunch.h
+++ b/xen/arch/x86/include/asm/slaunch.h
@@ -23,8 +23,16 @@ struct slaunch_early_init_results
     uint32_t slrt_pa;
 } __packed;
 
+#ifdef CONFIG_SLAUNCH
 /* Indicates an active Secure Launch boot. */
 extern bool slaunch_active;
+#else
+/*
+ * This avoids `#ifdef CONFIG_SLAUNCH` around `if ( slaunch_active )` thanks to
+ * dead code elimination.
+ */
+static bool slaunch_active = false;
+#endif
 
 /*
  * Holds physical address of SLRT.  Use slaunch_get_slrt() to access SLRT
diff --git a/xen/arch/x86/intel-txt.c b/xen/arch/x86/intel-txt.c
index 4a42abf8df..e0344d3421 100644
--- a/xen/arch/x86/intel-txt.c
+++ b/xen/arch/x86/intel-txt.c
@@ -8,10 +8,13 @@
 
 #include <xen/bug.h>
 #include <xen/init.h>
+#include <xen/kernel.h>
 #include <xen/lib.h>
 #include <xen/types.h>
 #include <asm/e820.h>
 #include <asm/intel-txt.h>
+#include <asm/msr.h>
+#include <asm/mtrr.h>
 #include <asm/slaunch.h>
 
 /*
@@ -111,3 +114,84 @@ void __init txt_reserve_mem_regions(void)
                      TXT_PRIV_CONFIG_REGS_BASE + TXT_CONFIG_SPACE_SIZE);
     BUG_ON(!ok);
 }
+
+void __init txt_restore_mtrrs(bool verbose)
+{
+    const struct slr_entry_hdr *entry;
+    const struct slr_entry_intel_info *intel_info;
+    uint64_t mtrr_cap, mtrr_def, base, mask;
+    unsigned int i;
+    unsigned int vcnt;
+    uint64_t def_type;
+    struct mtrr_pausing_state pausing_state;
+
+    mtrr_cap = rdmsr(MSR_MTRRcap);
+    mtrr_def = rdmsr(MSR_MTRRdefType);
+
+    vcnt = mtrr_cap & 0xFF;
+
+    if ( verbose )
+    {
+        printk("MTRRs set previously for SINIT ACM:\n");
+        printk(" MTRR cap: %"PRIx64" type: %"PRIx64"\n", mtrr_cap, mtrr_def);
+
+        for ( i = 0; i < vcnt; i++ )
+        {
+            base = rdmsr(MSR_IA32_MTRR_PHYSBASE(i));
+            mask = rdmsr(MSR_IA32_MTRR_PHYSMASK(i));
+
+            printk(" MTRR[%d]: base %"PRIx64" mask %"PRIx64"\n",
+                   i, base, mask);
+        }
+    }
+
+    entry =
+        slr_next_entry_by_tag(slaunch_get_slrt(), NULL, SLR_ENTRY_INTEL_INFO);
+    intel_info = container_of(entry, const struct slr_entry_intel_info, hdr);
+
+    if ( vcnt != intel_info->saved_bsp_mtrrs.mtrr_vcnt )
+    {
+        printk("Bootloader saved %ld MTRR values, but there should be %d\n",
+               intel_info->saved_bsp_mtrrs.mtrr_vcnt, vcnt);
+        /* Choose the smaller one to be on the safe side. */
+        if ( intel_info->saved_bsp_mtrrs.mtrr_vcnt < vcnt )
+            vcnt = intel_info->saved_bsp_mtrrs.mtrr_vcnt;
+    }
+
+    def_type = intel_info->saved_bsp_mtrrs.default_mem_type;
+    mtrr_pause_caching(&pausing_state);
+
+    for ( i = 0; i < vcnt; i++ )
+    {
+        base = intel_info->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physbase;
+        mask = intel_info->saved_bsp_mtrrs.mtrr_pair[i].mtrr_physmask;
+        wrmsr(MSR_IA32_MTRR_PHYSBASE(i), base);
+        wrmsr(MSR_IA32_MTRR_PHYSMASK(i), mask);
+    }
+
+    pausing_state.def_type = def_type;
+    mtrr_resume_caching(pausing_state);
+
+    if ( verbose )
+    {
+        printk("Restored MTRRs:\n");
+
+        /*
+         * If MTRRs are not enabled or WB is not the default, MTRRs won't be
+         * printed.
+         */
+        if ( !test_bit(11, &def_type) || (def_type & 0x7) == X86_MT_WB )
+        {
+            for ( i = 0; i < vcnt; i++ )
+            {
+                base = rdmsr(MSR_IA32_MTRR_PHYSBASE(i));
+                mask = rdmsr(MSR_IA32_MTRR_PHYSMASK(i));
+                printk(" MTRR[%d]: base %"PRIx64" mask %"PRIx64"\n",
+                       i, base, mask);
+            }
+        }
+    }
+
+    /* Restore IA32_MISC_ENABLES */
+    wrmsr(MSR_IA32_MISC_ENABLE, intel_info->saved_misc_enable_msr);
+}
-- 
2.55.0