]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: MTRR write-combining support — mtrr_init/mtrr_set_range for variable-range...
authorTulio A M Mendes <[email protected]>
Thu, 12 Feb 2026 04:09:08 +0000 (01:09 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 02:20:50 +0000 (23:20 -0300)
include/mtrr.h [new file with mode: 0644]
src/arch/x86/mtrr.c [new file with mode: 0644]

diff --git a/include/mtrr.h b/include/mtrr.h
new file mode 100644 (file)
index 0000000..f8798f5
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef MTRR_H
+#define MTRR_H
+
+#include <stdint.h>
+
+#define MTRR_TYPE_UC  0  /* Uncacheable */
+#define MTRR_TYPE_WC  1  /* Write-Combining */
+#define MTRR_TYPE_WT  4  /* Write-Through */
+#define MTRR_TYPE_WP  5  /* Write-Protect */
+#define MTRR_TYPE_WB  6  /* Write-Back */
+
+void mtrr_init(void);
+int  mtrr_set_range(uint64_t base, uint64_t size, uint8_t type);
+
+#endif
diff --git a/src/arch/x86/mtrr.c b/src/arch/x86/mtrr.c
new file mode 100644 (file)
index 0000000..0939151
--- /dev/null
@@ -0,0 +1,87 @@
+#include "mtrr.h"
+#include "uart_console.h"
+
+#define IA32_MTRRCAP       0xFE
+#define IA32_MTRR_DEF_TYPE 0x2FF
+#define IA32_MTRR_PHYS_BASE(n) (0x200 + 2*(n))
+#define IA32_MTRR_PHYS_MASK(n) (0x201 + 2*(n))
+
+static inline uint64_t rdmsr(uint32_t msr) {
+    uint32_t lo, hi;
+    __asm__ volatile("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr));
+    return ((uint64_t)hi << 32) | lo;
+}
+
+static inline void wrmsr(uint32_t msr, uint64_t val) {
+    __asm__ volatile("wrmsr" : : "c"(msr), "a"((uint32_t)val), "d"((uint32_t)(val >> 32)));
+}
+
+static uint8_t mtrr_count = 0;
+static uint8_t mtrr_enabled = 0;
+
+void mtrr_init(void) {
+    uint32_t eax, edx;
+    __asm__ volatile("cpuid" : "=a"(eax) : "a"(1) : "ebx", "ecx", "edx");
+    /* Check MTRR support: CPUID.1:EDX bit 12 */
+    __asm__ volatile("cpuid" : "=a"(eax), "=d"(edx) : "a"(1) : "ebx", "ecx");
+    if (!(edx & (1U << 12))) {
+        uart_print("[MTRR] Not supported by CPU\n");
+        return;
+    }
+
+    uint64_t cap = rdmsr(IA32_MTRRCAP);
+    mtrr_count = (uint8_t)(cap & 0xFF);
+    if (mtrr_count == 0) {
+        uart_print("[MTRR] No variable-range MTRRs available\n");
+        return;
+    }
+
+    mtrr_enabled = 1;
+    uart_print("[MTRR] Initialized, ");
+    /* Simple decimal print for count */
+    char buf[4];
+    buf[0] = (char)('0' + mtrr_count / 10);
+    buf[1] = (char)('0' + mtrr_count % 10);
+    buf[2] = '\0';
+    uart_print(buf);
+    uart_print(" variable-range registers\n");
+}
+
+int mtrr_set_range(uint64_t base, uint64_t size, uint8_t type) {
+    if (!mtrr_enabled) return -1;
+    if (size == 0) return -1;
+    /* Size must be a power of 2 and base must be aligned to size */
+    if (size & (size - 1)) return -1;
+    if (base & (size - 1)) return -1;
+
+    /* Find a free variable-range MTRR register */
+    int slot = -1;
+    for (int i = 0; i < mtrr_count; i++) {
+        uint64_t mask = rdmsr(IA32_MTRR_PHYS_MASK(i));
+        if (!(mask & (1ULL << 11))) { /* Valid bit not set = free */
+            slot = i;
+            break;
+        }
+    }
+    if (slot < 0) return -1; /* No free MTRR slots */
+
+    /* 36-bit physical address mask (common for 32-bit x86) */
+    uint64_t addr_mask = 0x0000000FFFFFFFFFULL;
+    uint64_t phys_mask = (~(size - 1)) & addr_mask;
+
+    /* Disable interrupts during MTRR programming */
+    __asm__ volatile("cli");
+
+    /* Flush caches */
+    __asm__ volatile("wbinvd");
+
+    wrmsr(IA32_MTRR_PHYS_BASE(slot), (base & addr_mask) | type);
+    wrmsr(IA32_MTRR_PHYS_MASK(slot), phys_mask | (1ULL << 11)); /* Set valid bit */
+
+    /* Flush caches again */
+    __asm__ volatile("wbinvd");
+
+    __asm__ volatile("sti");
+
+    return 0;
+}