From 424b4c46323f5aa637ddc495715f8fdca7fecf6b Mon Sep 17 00:00:00 2001 From: xamidev Date: Thu, 19 Mar 2026 19:34:31 +0100 Subject: [PATCH] Use MSR to map framebuffer as WC (write-combining) = huge speed diff on real HW --- include/arch/x86.h | 13 +++++++++ src/arch/x86/cpuid.c | 20 ++++++++++++++ src/arch/x86/init.c | 28 +++++++++++++++++++ src/arch/x86/msr.c | 66 ++++++++++++++++++++++++++++++++++++++++++++ src/kmain.c | 4 +++ src/mem/paging.c | 4 +-- 6 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 include/arch/x86.h create mode 100644 src/arch/x86/cpuid.c create mode 100644 src/arch/x86/init.c create mode 100644 src/arch/x86/msr.c diff --git a/include/arch/x86.h b/include/arch/x86.h new file mode 100644 index 0000000..9521859 --- /dev/null +++ b/include/arch/x86.h @@ -0,0 +1,13 @@ +#ifndef X86_H +#define X86_H + +#include +#include + +uint64_t rdmsr(uint32_t msr); +void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx); +void wrmsr(uint32_t msr, uint64_t value); +bool x86_has_msr(); +void x86_arch_init(); + +#endif \ No newline at end of file diff --git a/src/arch/x86/cpuid.c b/src/arch/x86/cpuid.c new file mode 100644 index 0000000..2d96368 --- /dev/null +++ b/src/arch/x86/cpuid.c @@ -0,0 +1,20 @@ +/* + * @author xamidev + * @brief x86 CPU identification + * @license GPL-3.0-only + */ + +#include + +/* + * cpuid - Wrapper for CPUID instruction + * @leaf: Requested leaf + * @eax: EAX register value + * @ebx: EBX register value + * @ecx: ECX register value + * @edx: EDX register value +*/ +void cpuid(uint32_t leaf, uint32_t* eax, uint32_t* ebx, uint32_t* ecx, uint32_t* edx) +{ + __asm__ volatile("cpuid" : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) : "a"(leaf)); +} \ No newline at end of file diff --git a/src/arch/x86/init.c b/src/arch/x86/init.c new file mode 100644 index 0000000..0a97bff --- /dev/null +++ b/src/arch/x86/init.c @@ -0,0 +1,28 @@ +/* + * @author xamidev + * @brief x86 architecture-dependant initialization + * @license GPL-3.0-only + */ + +#include +#include +#include + +/* + * x86_arch_init - Initialize x86 CPU structures + * + * This function is responsible for overriding a PAT entry + * (to put the framebuffer area in WC mode) only. + * + * Later, all architecture-dependant init (GDT, IDT, TSS, ...) + * should be initialized here, and separate function pointers + * should be set up for each arch. + */ +void x86_arch_init() +{ + uint64_t pat = rdmsr(0x277); + pat &= ~(0xFFULL << 8); // Clear PAT1 + pat |= (0x01ULL << 8); // PAT1 = 0x01 (WC) + wrmsr(0x277, pat); + DEBUG("Overrode PAT1 entry to set up Write-Combining"); +} \ No newline at end of file diff --git a/src/arch/x86/msr.c b/src/arch/x86/msr.c new file mode 100644 index 0000000..276239c --- /dev/null +++ b/src/arch/x86/msr.c @@ -0,0 +1,66 @@ +/* + * @author xamidev + * @brief x86 MSR C wrappers + * @description + * Wrapper functions to access Model Specific Registers + * + * @license GPL-3.0-only + */ + +#include +#include +#include + +/* + * rdmsr - Read from MSR + * @msr: model specific register number + * + * Read a 64-bit word from a Model Specific Register. + * Wrapper for the "rdmsr" instruction. It originally + * outputs to two 32-bit registers (EDX:EAX), so the + * function does the job of uniting them as a 64-bit + * value for us. + * + * Return: + * - value read from MSR + */ +uint64_t rdmsr(uint32_t msr) +{ + uint32_t low; + uint32_t high; + + __asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(msr)); + + return ((uint64_t)high << 32) | low; +} + +/* + * wrmsr - Write to MSR + * @msr: model specific register number + * + * Write a 64-bit value to a Model Specific Register. + */ +void wrmsr(uint32_t msr, uint64_t value) +{ + uint32_t low = (uint32_t)(value & 0xFFFFFFFF); + uint32_t high = (uint32_t)(value >> 32); + + __asm__ volatile("wrmsr" : : "c"(msr), "a"(low), "d"(high) : "memory"); +} + +/* + * x86_has_msr - Test for MSR support + * + * Checks if CPU supports Model Specific Registers + * using CPUID.01h:EDX[bit 5]. + * + * Return: + * true - MSR are supported + * false - MSR are not supported + */ +bool x86_has_msr() +{ + uint32_t eax, ebx, ecx, edx; + cpuid(1, &eax, &ebx, &ecx, &edx); + return (edx & (1 << 5)) != 0; +} \ No newline at end of file diff --git a/src/kmain.c b/src/kmain.c index 2926773..d4d3ea0 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -4,6 +4,7 @@ * @license GPL-3.0-only */ +#include "arch/x86.h" #include #include #include @@ -24,6 +25,7 @@ #include #include #include +#include // Limine version used __attribute__((used, section(".limine_requests"))) @@ -112,6 +114,8 @@ void kmain() serial_init(); timer_init(); + x86_arch_init(); + boot_mem_display(); pmm_init(boot_ctx); diff --git a/src/mem/paging.c b/src/mem/paging.c index ae75bb4..44290ea 100644 --- a/src/mem/paging.c +++ b/src/mem/paging.c @@ -202,9 +202,9 @@ void paging_init(struct boot_context boot_ctx) uint64_t fb_size = fb->pitch * fb->height; uint64_t fb_pages = (fb_size + PAGE_SIZE-1)/PAGE_SIZE; - // Map the framebuffer (with cache-disable & write-through) + // Map the framebuffer (PWT set, and no PCD means PAT1 [Write-Combining] for this region) for (uint64_t i=0; i