:: commit 273062a1a718cd17e514d447af29dcce1494dde1

Mintsuki <mintsuki@protonmail.com> — 2026-04-28 20:15

parents: cf31779516

mm/mtrr: Reintroduce logic to enable WC regions for FBs using MTRRs

diff --git a/common/lib/gterm.c b/common/lib/gterm.c
index c77d2dc6..e9027eaf 100644
--- a/common/lib/gterm.c
+++ b/common/lib/gterm.c
@@ -10,6 +10,7 @@
 #include <lib/image.h>
 #include <lib/rand.h>
 #include <mm/pmm.h>
+#include <mm/mtrr.h>
 #include <flanterm.h>
 #include <flanterm_backends/fb.h>
 #include <lib/term.h>
@@ -809,6 +810,19 @@ bool gterm_init(struct fb_info **_fbs, size_t *_fbs_count,
         return false;
     }
 
+#if defined (__i386__) || defined (__x86_64__)
+    for (size_t i = 0; i < fbs_count; i++) {
+        if (fbs[i].framebuffer_bpp != 32) {
+            continue;
+        }
+        uint64_t fb_size = (uint64_t)fbs[i].framebuffer_pitch * fbs[i].framebuffer_height;
+        if (fb_size == 0) {
+            continue;
+        }
+        mtrr_wc_add_fb_range(fbs[i].framebuffer_addr, fb_size);
+    }
+#endif
+
     struct gterm_config cfg;
     gterm_parse_config(config, &cfg);
 
diff --git a/common/lib/term.c b/common/lib/term.c
index 96f0e220..4b85cb33 100644
--- a/common/lib/term.c
+++ b/common/lib/term.c
@@ -6,6 +6,7 @@
 #include <lib/misc.h>
 #include <lib/fb.h>
 #include <mm/pmm.h>
+#include <mm/mtrr.h>
 #include <drivers/vga_textmode.h>
 #include <flanterm_backends/fb.h>
 
@@ -19,6 +20,10 @@ size_t terms_i = 0;
 int term_backend = _NOT_READY;
 
 void term_notready(void) {
+#if defined (__i386__) || defined (__x86_64__)
+    mtrr_wc_clear_fb_ranges();
+#endif
+
     for (size_t i = 0; i < terms_i; i++) {
         struct flanterm_context *term = terms[i];
 
diff --git a/common/mm/mtrr.c b/common/mm/mtrr.c
index 2ab123f8..4e35da4b 100644
--- a/common/mm/mtrr.c
+++ b/common/mm/mtrr.c
@@ -143,4 +143,168 @@ void mtrr_restore(void) {
     asm volatile ("mov %0, %%cr0" :: "r"(old_cr0) : "memory");
 }
 
+#define MTRR_TYPE_WC 1
+#define WC_MAX_SLOTS 4
+
+static struct {
+    uint8_t  slot;
+    uint64_t saved_base;
+    uint64_t saved_mask;
+} wc_saved[WC_MAX_SLOTS];
+static size_t wc_n_saved = 0;
+
+bool mtrr_wc_add_fb_range(uint64_t base, uint64_t size) {
+    if (size == 0 || !mtrr_supported()) {
+        return false;
+    }
+    if (wc_n_saved >= WC_MAX_SLOTS) {
+        return false;
+    }
+
+    uint64_t mtrrcap = rdmsr(0xfe);
+    if (!(mtrrcap & ((uint64_t)1 << 10))) {
+        return false;
+    }
+    uint8_t var_reg_count = mtrrcap & 0xff;
+
+    uint32_t eax, ebx, ecx, edx;
+    if (!cpuid(0x80000008, 0, &eax, &ebx, &ecx, &edx)) {
+        return false;
+    }
+    uint8_t maxphysaddr = eax & 0xff;
+    if (maxphysaddr < 32 || maxphysaddr > 52) {
+        return false;
+    }
+
+    base &= ~(uint64_t)0xfff;
+
+    uint64_t aligned_size = 0x1000;
+    while (aligned_size < size) {
+        aligned_size <<= 1;
+        if (aligned_size == 0) {
+            return false;
+        }
+    }
+    size = aligned_size;
+
+    // MTRR match is (addr & mask) == (base & mask); only correct when base is size-aligned.
+    if (base & (size - 1)) {
+        return false;
+    }
+
+    uint64_t mask = (((uint64_t)1 << maxphysaddr) - 1) & ~(size - 1);
+
+    for (uint8_t i = 0; i < var_reg_count; i++) {
+        uint64_t mb = rdmsr(0x200 + i * 2);
+        uint64_t mm = rdmsr(0x200 + i * 2 + 1);
+        if (!(mm & ((uint64_t)1 << 11))) {
+            continue;
+        }
+        uint64_t exist_mask = mm & ~(uint64_t)0xfff;
+        uint64_t exist_base = mb & ~(uint64_t)0xfff;
+        for (uint64_t a = base; a < base + size; a += 0x1000) {
+            if ((a & exist_mask) == (exist_base & exist_mask)) {
+                return false;
+            }
+        }
+    }
+
+    uint8_t slot;
+    bool found = false;
+    for (uint8_t i = 0; i < var_reg_count; i++) {
+        uint64_t mm = rdmsr(0x200 + i * 2 + 1);
+        if (!(mm & ((uint64_t)1 << 11))) {
+            slot = i;
+            found = true;
+            break;
+        }
+    }
+    if (!found) {
+        return false;
+    }
+
+#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));
+
+    wc_saved[wc_n_saved].slot       = slot;
+    wc_saved[wc_n_saved].saved_base = rdmsr(0x200 + slot * 2);
+    wc_saved[wc_n_saved].saved_mask = rdmsr(0x200 + slot * 2 + 1);
+    wc_n_saved++;
+
+    wrmsr(0x200 + slot * 2,     base | MTRR_TYPE_WC);
+    wrmsr(0x200 + slot * 2 + 1, mask | ((uint64_t)1 << 11));
+
+    wrmsr(0x2ff, mtrr_def | ((uint64_t)1 << 11));
+
+    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
+
+    return true;
+}
+
+void mtrr_wc_clear_fb_ranges(void) {
+    if (wc_n_saved == 0) {
+        return;
+    }
+    if (!mtrr_supported()) {
+        wc_n_saved = 0;
+        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 (size_t i = 0; i < wc_n_saved; i++) {
+        wrmsr(0x200 + wc_saved[i].slot * 2,     wc_saved[i].saved_base);
+        wrmsr(0x200 + wc_saved[i].slot * 2 + 1, wc_saved[i].saved_mask);
+    }
+
+    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_n_saved = 0;
+}
+
 #endif
diff --git a/common/mm/mtrr.h b/common/mm/mtrr.h
index 9cead124..2c005d78 100644
--- a/common/mm/mtrr.h
+++ b/common/mm/mtrr.h
@@ -1,11 +1,17 @@
 #ifndef MM__MTRR_H__
 #define MM__MTRR_H__
 
+#include <stdint.h>
+#include <stdbool.h>
+
 #if defined (__x86_64__) || defined (__i386__)
 
 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
 
 #endif
tab: 248 wrap: offon