From 03b2d0dac713b9c9cd3a969c2ef5db785c557e3b Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sun, 17 Jan 2021 20:49:47 -0800 Subject: [PATCH] [kernel] Set framebuffer to write-combining Several changes were needed to make this work: - Update the page_table::flags to understand memory caching types - Set up the PAT MSR to add the WC option - Make page-offset area mapped as WT - Add all the MTRR and PAT MSRs, and log the MTRRs for verification - Add a vm_area flag for write_combining --- src/boot/paging.cpp | 7 ++-- src/kernel/cpu_features.inc | 3 +- src/kernel/main.cpp | 43 ++++++++++++--------- src/kernel/memory_bootstrap.cpp | 66 +++++++++++++++++++++++++++++++++ src/kernel/msr.cpp | 6 +++ src/kernel/msr.h | 44 +++++++++++++++++----- src/kernel/objects/vm_area.h | 1 + src/kernel/page_table.h | 15 ++++++-- src/kernel/scheduler.cpp | 2 +- src/kernel/vm_space.cpp | 3 +- 10 files changed, 152 insertions(+), 38 deletions(-) diff --git a/src/boot/paging.cpp b/src/boot/paging.cpp index 21f693d..de61509 100644 --- a/src/boot/paging.cpp +++ b/src/boot/paging.cpp @@ -28,7 +28,7 @@ using ::memory::table_entries; /// Page table entry flags for entries pointing at a page constexpr uint16_t page_flags = 0x103; -// Flags: 0 0 0 0 1 1 0 0 0 0 0 1 1 = 0x0183 +// Flags: 0 0 0 0 1 1 0 0 0 1 0 1 1 = 0x018b // | IGN | | | | | | | | +- Present // | | | | | | | | +--- Writeable // | | | | | | | +----- Supervisor only @@ -209,11 +209,12 @@ allocate_tables(kernel::args::header *args, uefi::boot_services *bs) bs->set_mem(addr, tables_needed*page_size, 0); - args->pml4 = addr; + page_table *pml4 = reinterpret_cast(addr); + + args->pml4 = pml4; args->table_count = tables_needed - 1; args->page_tables = offset_ptr(addr, page_size); - page_table *pml4 = reinterpret_cast(addr); add_kernel_pds(pml4, args->page_tables, args->table_count); add_offset_mappings(pml4, args->page_tables, args->table_count); diff --git a/src/kernel/cpu_features.inc b/src/kernel/cpu_features.inc index b455f53..34d3f54 100644 --- a/src/kernel/cpu_features.inc +++ b/src/kernel/cpu_features.inc @@ -7,8 +7,9 @@ CPU_FEATURE_REQ(pse, 0x00000001, 0, edx, 3) CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4) CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5) CPU_FEATURE_REQ(apic, 0x00000001, 0, edx, 9) +CPU_FEATURE_REQ(mtrr, 0x00000001, 0, edx, 12) CPU_FEATURE_REQ(pge, 0x00000001, 0, edx, 13) -CPU_FEATURE_OPT(pat, 0x00000001, 0, edx, 16) +CPU_FEATURE_REQ(pat, 0x00000001, 0, edx, 16) CPU_FEATURE_REQ(fxsr, 0x00000001, 0, edx, 24) CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0) diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 6cd52f2..120a2b2 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -33,19 +33,10 @@ extern "C" { void (*__ctors_end)(void); } -void -run_constructors() -{ - void (**p)(void) = &__ctors; - while (p < &__ctors_end) { - void (*ctor)(void) = *p++; - ctor(); - } -} - extern void __kernel_assert(const char *, unsigned, const char *); /// Bootstrap the memory managers. +void setup_pat(); void memory_initialize_pre_ctors(kernel::args::header *kargs); void memory_initialize_post_ctors(kernel::args::header *kargs); @@ -66,6 +57,16 @@ init_console() cons->puts(GIT_VERSION " booting...\n"); } +void +run_constructors() +{ + void (**p)(void) = &__ctors; + while (p < &__ctors_end) { + void (*ctor)(void) = *p++; + ctor(); + } +} + channel *std_out = nullptr; void @@ -110,9 +111,23 @@ void kernel_main(args::header *header) { kutil::assert_set_callback(__kernel_assert); + init_console(); logger_init(); + setup_pat(); + + bool has_video = false; + if (header->video.size > 0) { + has_video = true; + fb = memory::to_virtual(reinterpret_cast(&header->video)); + + const args::framebuffer &video = header->video; + log::debug(logs::boot, "Framebuffer: %dx%d[%d] type %s @ %016llx", + video.horizontal, video.vertical, video.scanline, video.type, video.phys_addr); + logger_clear_immediate(); + } + gdt_init(); interrupts_init(); @@ -135,13 +150,6 @@ kernel_main(args::header *header) } } - bool has_video = false; - if (header->video.size > 0) { - fb = memory::to_virtual(reinterpret_cast(&header->video)); - has_video = true; - logger_clear_immediate(); - } - log::debug(logs::boot, " jsix header is at: %016lx", header); log::debug(logs::boot, " Memory map is at: %016lx", header->mem_map); log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table); @@ -152,7 +160,6 @@ kernel_main(args::header *header) interrupts_enable(); devices.init_drivers(); - devices.get_lapic()->calibrate_timer(); /* diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index 114a5ce..df8d726 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -9,6 +9,7 @@ #include "frame_allocator.h" #include "io.h" #include "log.h" +#include "msr.h" #include "objects/process.h" #include "objects/vm_area.h" #include "vm_space.h" @@ -100,6 +101,71 @@ void walk_page_table( } */ +static void +log_mtrrs() +{ + uint64_t mtrrcap = rdmsr(msr::ia32_mtrrcap); + uint64_t mtrrdeftype = rdmsr(msr::ia32_mtrrdeftype); + unsigned vcap = mtrrcap & 0xff; + log::debug(logs::boot, "MTRRs: vcap=%d %s %s def=%02x %s %s", + vcap, + (mtrrcap & (1<< 8)) ? "fix" : "", + (mtrrcap & (1<<10)) ? "wc" : "", + mtrrdeftype & 0xff, + (mtrrdeftype & (1<<10)) ? "fe" : "", + (mtrrdeftype & (1<<11)) ? "enabled" : "" + ); + + for (unsigned i = 0; i < vcap; ++i) { + uint64_t base = rdmsr(find_mtrr(msr::ia32_mtrrphysbase, i)); + uint64_t mask = rdmsr(find_mtrr(msr::ia32_mtrrphysmask, i)); + log::debug(logs::boot, " vcap[%2d] base:%016llx mask:%016llx type:%02x %s", i, + (base & ~0xfffull), + (mask & ~0xfffull), + (base & 0xff), + (mask & (1<<11)) ? "valid" : ""); + } + + msr mtrr_fixed[] = { + msr::ia32_mtrrfix64k_00000, + msr::ia32_mtrrfix16k_80000, + msr::ia32_mtrrfix16k_a0000, + msr::ia32_mtrrfix4k_c0000, + msr::ia32_mtrrfix4k_c8000, + msr::ia32_mtrrfix4k_d0000, + msr::ia32_mtrrfix4k_d8000, + msr::ia32_mtrrfix4k_e0000, + msr::ia32_mtrrfix4k_e8000, + msr::ia32_mtrrfix4k_f0000, + msr::ia32_mtrrfix4k_f8000, + }; + + for (int i = 0; i < 11; ++i) { + uint64_t v = rdmsr(mtrr_fixed[i]); + log::debug(logs::boot, " fixed[%2d] %02x %02x %02x %02x %02x %02x %02x %02x", i, + ((v << 0) & 0xff), ((v << 8) & 0xff), ((v << 16) & 0xff), ((v << 24) & 0xff), + ((v << 32) & 0xff), ((v << 40) & 0xff), ((v << 48) & 0xff), ((v << 56) & 0xff)); + } + + uint64_t pat = rdmsr(msr::ia32_pat); + static const char *pat_names[] = {"UC ","WC ","XX ","XX ","WT ","WP ","WB ","UC-"}; + log::debug(logs::boot, " PAT: 0:%s 1:%s 2:%s 3:%s 4:%s 5:%s 6:%s 7:%s", + pat_names[(pat >> (0*8)) & 7], pat_names[(pat >> (1*8)) & 7], + pat_names[(pat >> (2*8)) & 7], pat_names[(pat >> (3*8)) & 7], + pat_names[(pat >> (4*8)) & 7], pat_names[(pat >> (5*8)) & 7], + pat_names[(pat >> (6*8)) & 7], pat_names[(pat >> (7*8)) & 7]); +} + +void +setup_pat() +{ + uint64_t pat = rdmsr(msr::ia32_pat); + pat = (pat & 0x00ffffffffffffffull) | (0x01ull << 56); // set PAT 7 to WC + wrmsr(msr::ia32_pat, pat); + log_mtrrs(); +} + + void memory_initialize_pre_ctors(args::header *kargs) { diff --git a/src/kernel/msr.cpp b/src/kernel/msr.cpp index d43c1fc..4efb175 100644 --- a/src/kernel/msr.cpp +++ b/src/kernel/msr.cpp @@ -1,5 +1,11 @@ #include "msr.h" +msr +find_mtrr(msr type, unsigned index) +{ + return static_cast(static_cast(type) + (2 * index)); +} + uint64_t rdmsr(msr addr) { diff --git a/src/kernel/msr.h b/src/kernel/msr.h index 4d26425..ee02d45 100644 --- a/src/kernel/msr.h +++ b/src/kernel/msr.h @@ -6,22 +6,46 @@ enum class msr : uint32_t { - ia32_efer = 0xc0000080, - ia32_star = 0xc0000081, - ia32_lstar = 0xc0000082, - ia32_fmask = 0xc0000084, + ia32_mtrrcap = 0x000000fe, + ia32_mtrrdeftype = 0x000002ff, - ia32_gs_base = 0xc0000101, - ia32_kernel_gs_base = 0xc0000102 + ia32_mtrrphysbase = 0x00000200, + ia32_mtrrphysmask = 0x00000201, + + ia32_mtrrfix64k_00000 = 0x00000250, + + ia32_mtrrfix16k_80000 = 0x00000258, + ia32_mtrrfix16k_a0000 = 0x00000259, + + ia32_mtrrfix4k_c0000 = 0x00000268, + ia32_mtrrfix4k_c8000 = 0x00000269, + ia32_mtrrfix4k_d0000 = 0x0000026A, + ia32_mtrrfix4k_d8000 = 0x0000026B, + ia32_mtrrfix4k_e0000 = 0x0000026C, + ia32_mtrrfix4k_e8000 = 0x0000026D, + ia32_mtrrfix4k_f0000 = 0x0000026E, + ia32_mtrrfix4k_f8000 = 0x0000026F, + + ia32_pat = 0x00000277, + ia32_efer = 0xc0000080, + ia32_star = 0xc0000081, + ia32_lstar = 0xc0000082, + ia32_fmask = 0xc0000084, + + ia32_gs_base = 0xc0000101, + ia32_kernel_gs_base = 0xc0000102 }; +/// Find the msr for MTRR physical base or mask +msr find_mtrr(msr type, unsigned index); + /// Read the value of a MSR -/// \arg addr The MSR address -/// \returns The current value of the MSR +/// \arg addr The MSR address +/// \returns The current value of the MSR uint64_t rdmsr(msr addr); /// Write to a MSR -/// \arg addr The MSR address -/// \arg value The value to write +/// \arg addr The MSR address +/// \arg value The value to write void wrmsr(msr addr, uint64_t value); diff --git a/src/kernel/objects/vm_area.h b/src/kernel/objects/vm_area.h index e8b34c2..dbeb18d 100644 --- a/src/kernel/objects/vm_area.h +++ b/src/kernel/objects/vm_area.h @@ -29,6 +29,7 @@ enum class vm_flags : uint32_t huge_pages = 0x00000200, mmio = 0x00010000, + write_combine = 0x00020000, user_mask = 0x0000ffff ///< flags allowed via syscall }; diff --git a/src/kernel/page_table.h b/src/kernel/page_table.h index 5e3f010..8114f36 100644 --- a/src/kernel/page_table.h +++ b/src/kernel/page_table.h @@ -21,14 +21,21 @@ struct page_table present = 0x0001, /// Entry is present in the table write = 0x0002, /// Section may be written user = 0x0004, /// User-accessible - mtrr0 = 0x0008, /// MTRR selector bit 0 - mtrr1 = 0x0010, /// MTRR selector bit 1 + pat0 = 0x0008, /// PAT selector bit 0 + pat1 = 0x0010, /// PAT selector bit 1 accessed = 0x0020, /// Entry has been accessed dirty = 0x0040, /// Page has been written to page = 0x0080, /// Entry is a large page - pte_mtrr2 = 0x0080, /// MTRR selector bit 2 on PT entries + pat2 = 0x0080, /// PAT selector bit 2 on PT entries global = 0x0100, /// Entry is not PCID-specific - mtrr2 = 0x1000 /// MTRR selector bit 2 on PD and PDP entries + pat2_lg = 0x1000, /// PAT selector bit 2 on large/huge pages + + wb = none, + wt = pat0, + uc_ = pat1, + uc = pat0 | pat1, + wc = pat0 | pat1 | pat2, + wc_lg = pat0 | pat1 | pat2_lg, }; /// Helper for getting the next level value diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index b8e203f..6ce2183 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -141,7 +141,7 @@ load_process_image(uintptr_t phys, uintptr_t virt, size_t bytes, TCB *tcb) // Crazypants framebuffer part if (fb) { - vma = new vm_area_open(fb->size, space, vm_flags::write|vm_flags::mmio); + vma = new vm_area_open(fb->size, space, vm_flags::write|vm_flags::mmio|vm_flags::write_combine); space.add(0x100000000, vma); vma->commit(fb->phys_addr, 0, memory::page_count(fb->size)); } diff --git a/src/kernel/vm_space.cpp b/src/kernel/vm_space.cpp index fd14f30..98fd6c9 100644 --- a/src/kernel/vm_space.cpp +++ b/src/kernel/vm_space.cpp @@ -170,7 +170,8 @@ vm_space::page_in(const vm_area &vma, uintptr_t offset, uintptr_t phys, size_t c page_table::flag flags = page_table::flag::present | (m_kernel ? page_table::flag::none : page_table::flag::user) | - ((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none); + ((vma.flags() && vm_flags::write) ? page_table::flag::write : page_table::flag::none) | + ((vma.flags() && vm_flags::write_combine) ? page_table::flag::wc : page_table::flag::none); page_table::iterator it {virt, m_pml4};