:: commit 529fc81297ff698ce8e19f51c59b97899ebcf420

Mintsuki <mintsuki@protonmail.com> — 2026-05-14 01:22

parents: 68ff52f51b

mm/mtrr: Reuse mtrr_save/mtrr_restore for WC FB undo

diff --git a/common/lib/term.c b/common/lib/term.c
index 99c5b1c1..84951b93 100644
--- a/common/lib/term.c
+++ b/common/lib/term.c
@@ -33,7 +33,7 @@ void term_notready(bool preserve_screen) {
     }
 
 #if defined (__i386__) || defined (__x86_64__)
-    mtrr_wc_clear_fb_ranges();
+    mtrr_restore();
 #endif
 
     for (size_t i = 0; i < terms_i; i++) {
diff --git a/common/mm/mtrr.c b/common/mm/mtrr.c
index e249671b..a83b21b7 100644
--- a/common/mm/mtrr.c
+++ b/common/mm/mtrr.c
@@ -17,9 +17,20 @@ static bool mtrr_supported(void) {
     return !!(edx & (1 << 12));
 }
 
-uint64_t *saved_mtrrs = NULL;
+// Sized for the architectural maximum MTRRCAP.VCNT (8 bits -> 256), plus the
+// 11 fixed-range MTRRs and the default-type MSR. .no_unwind so the snapshot
+// survives a panic-rewind: the MSR state we mutated persists across the
+// rewind, and so does the data we need to undo it.
+#define SAVED_MTRRS_MAX (256 * 2 + 11 + 1)
+
+no_unwind uint64_t saved_mtrrs[SAVED_MTRRS_MAX];
+static no_unwind uint8_t saved_mtrrs_var_count = 0;
+static no_unwind bool saved_mtrrs_valid = false;
 
 void mtrr_save(void) {
+    if (saved_mtrrs_valid) {
+        return;
+    }
     if (!mtrr_supported()) {
         return;
     }
@@ -29,14 +40,6 @@ void mtrr_save(void) {
     uint8_t var_reg_count = ia32_mtrrcap & 0xff;
     bool fix_supported = !!(ia32_mtrrcap & ((uint64_t)1 << 8));
 
-    if (saved_mtrrs == NULL) {
-        saved_mtrrs = ext_mem_alloc((
-            (var_reg_count * 2) /* variable MTRRs, 2 MSRs each */
-          + 11                  /* 11 fixed MTRRs */
-          + 1                   /* 1 default type MTRR */
-        ) * sizeof(uint64_t));
-    }
-
     /* save variable range MTRRs */
     for (uint8_t i = 0; i < var_reg_count * 2; i += 2) {
         saved_mtrrs[i] = rdmsr(0x200 + i);
@@ -63,6 +66,9 @@ void mtrr_save(void) {
 
     /* make sure that the saved MTRR default has MTRRs off */
     saved_mtrrs[var_reg_count * 2 + 11] &= ~((uint64_t)1 << 11);
+
+    saved_mtrrs_var_count = var_reg_count;
+    saved_mtrrs_valid = true;
 }
 
 void mtrr_restore(void) {
@@ -70,15 +76,15 @@ void mtrr_restore(void) {
         return;
     }
 
+    if (!saved_mtrrs_valid) {
+        return;
+    }
+
     uint64_t ia32_mtrrcap = rdmsr(0xfe);
 
-    uint8_t var_reg_count = ia32_mtrrcap & 0xff;
+    uint8_t var_reg_count = saved_mtrrs_var_count;
     bool fix_supported = !!(ia32_mtrrcap & ((uint64_t)1 << 8));
 
-    if (saved_mtrrs == NULL) {
-        panic(true, "mtrr: Attempted restore without prior save");
-    }
-
     /* according to the Intel SDM 12.11.7.2 "MemTypeSet() Function",
        we need to follow this procedure before changing MTRR set up */
 
@@ -144,11 +150,6 @@ void mtrr_restore(void) {
 }
 
 #define MTRR_TYPE_WC 1
-#define MTRR_VAR_MAX 256
-
-static no_unwind uint64_t wc_full_saved_var[MTRR_VAR_MAX * 2];
-static no_unwind uint8_t wc_full_saved_var_i = 0;
-static no_unwind bool wc_full_saved_valid = false;
 
 static bool wc_add_mtrr(uint64_t base, uint64_t size, uint8_t type,
                         uint8_t maxphysaddr, uint8_t var_reg_count) {
@@ -223,14 +224,7 @@ bool mtrr_wc_add_fb_range(uint64_t base, uint64_t size) {
         }
     }
 
-    if (!wc_full_saved_valid) {
-        for (uint8_t i = 0; i < var_reg_count; i++) {
-            wc_full_saved_var[i * 2] = rdmsr(0x200 + i * 2);
-            wc_full_saved_var[i * 2 + 1] = rdmsr(0x200 + i * 2 + 1);
-        }
-        wc_full_saved_var_i = var_reg_count;
-        wc_full_saved_valid = true;
-    }
+    mtrr_save();
 
 #if defined (UEFI)
     asm volatile ("cli");
@@ -265,48 +259,4 @@ bool mtrr_wc_add_fb_range(uint64_t base, uint64_t size) {
     return ok;
 }
 
-void mtrr_wc_clear_fb_ranges(void) {
-    if (!wc_full_saved_valid) {
-        return;
-    }
-    if (!mtrr_supported()) {
-        wc_full_saved_valid = false;
-        return;
-    }
-
-#if defined (UEFI)
-    asm volatile ("cli");
-#endif
-
-    uintptr_t old_cr0;
-    asm volatile ("mov %%cr0, %0" : "=r"(old_cr0) :: "memory");
-    uintptr_t new_cr0 = (old_cr0 | (1U << 30)) & ~((uintptr_t)1 << 29);
-    asm volatile ("mov %0, %%cr0" :: "r"(new_cr0) : "memory");
-    asm volatile ("wbinvd" ::: "memory");
-
-    uintptr_t cr3;
-    asm volatile ("mov %%cr3, %0" : "=r"(cr3) :: "memory");
-    asm volatile ("mov %0, %%cr3" :: "r"(cr3) : "memory");
-
-    uint64_t mtrr_def = rdmsr(0x2ff);
-    wrmsr(0x2ff, mtrr_def & ~((uint64_t)1 << 11));
-
-    for (uint8_t i = 0; i < wc_full_saved_var_i; i++) {
-        wrmsr(0x200 + i * 2, wc_full_saved_var[i * 2]);
-        wrmsr(0x200 + i * 2 + 1, wc_full_saved_var[i * 2 + 1]);
-    }
-    wrmsr(0x2ff, mtrr_def);
-
-    asm volatile ("mov %%cr3, %0" : "=r"(cr3) :: "memory");
-    asm volatile ("mov %0, %%cr3" :: "r"(cr3) : "memory");
-    asm volatile ("wbinvd" ::: "memory");
-    asm volatile ("mov %0, %%cr0" :: "r"(old_cr0) : "memory");
-
-#if defined (UEFI)
-    asm volatile ("sti");
-#endif
-
-    wc_full_saved_valid = false;
-}
-
 #endif
diff --git a/common/mm/mtrr.h b/common/mm/mtrr.h
index 2c005d78..ac5341f3 100644
--- a/common/mm/mtrr.h
+++ b/common/mm/mtrr.h
@@ -10,7 +10,6 @@ void mtrr_save(void);
 void mtrr_restore(void);
 
 bool mtrr_wc_add_fb_range(uint64_t base, uint64_t size);
-void mtrr_wc_clear_fb_ranges(void);
 
 #endif
 
tab: 248 wrap: offon