[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
This commit is contained in:
Justin C. Miller
2021-01-17 20:49:47 -08:00
parent 1820972fb7
commit 3dffe564af
10 changed files with 152 additions and 38 deletions

View File

@@ -28,7 +28,7 @@ using ::memory::table_entries;
/// Page table entry flags for entries pointing at a page /// Page table entry flags for entries pointing at a page
constexpr uint16_t page_flags = 0x103; 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 // | IGN | | | | | | | | +- Present
// | | | | | | | | +--- Writeable // | | | | | | | | +--- Writeable
// | | | | | | | +----- Supervisor only // | | | | | | | +----- 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); bs->set_mem(addr, tables_needed*page_size, 0);
args->pml4 = addr; page_table *pml4 = reinterpret_cast<page_table*>(addr);
args->pml4 = pml4;
args->table_count = tables_needed - 1; args->table_count = tables_needed - 1;
args->page_tables = offset_ptr<void>(addr, page_size); args->page_tables = offset_ptr<void>(addr, page_size);
page_table *pml4 = reinterpret_cast<page_table*>(addr);
add_kernel_pds(pml4, args->page_tables, args->table_count); add_kernel_pds(pml4, args->page_tables, args->table_count);
add_offset_mappings(pml4, args->page_tables, args->table_count); add_offset_mappings(pml4, args->page_tables, args->table_count);

View File

@@ -7,8 +7,9 @@ CPU_FEATURE_REQ(pse, 0x00000001, 0, edx, 3)
CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4) CPU_FEATURE_OPT(tsc, 0x00000001, 0, edx, 4)
CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5) CPU_FEATURE_REQ(msr, 0x00000001, 0, edx, 5)
CPU_FEATURE_REQ(apic, 0x00000001, 0, edx, 9) 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_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_REQ(fxsr, 0x00000001, 0, edx, 24)
CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0) CPU_FEATURE_OPT(fsgsbase, 0x00000007, 0, ebx, 0)

View File

@@ -33,19 +33,10 @@ extern "C" {
void (*__ctors_end)(void); 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 *); extern void __kernel_assert(const char *, unsigned, const char *);
/// Bootstrap the memory managers. /// Bootstrap the memory managers.
void setup_pat();
void memory_initialize_pre_ctors(kernel::args::header *kargs); void memory_initialize_pre_ctors(kernel::args::header *kargs);
void memory_initialize_post_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"); 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; channel *std_out = nullptr;
void void
@@ -110,9 +111,23 @@ void
kernel_main(args::header *header) kernel_main(args::header *header)
{ {
kutil::assert_set_callback(__kernel_assert); kutil::assert_set_callback(__kernel_assert);
init_console(); init_console();
logger_init(); logger_init();
setup_pat();
bool has_video = false;
if (header->video.size > 0) {
has_video = true;
fb = memory::to_virtual<args::framebuffer>(reinterpret_cast<uintptr_t>(&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(); gdt_init();
interrupts_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<args::framebuffer>(reinterpret_cast<uintptr_t>(&header->video));
has_video = true;
logger_clear_immediate();
}
log::debug(logs::boot, " jsix header is at: %016lx", header); 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, " Memory map is at: %016lx", header->mem_map);
log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table); log::debug(logs::boot, "ACPI root table is at: %016lx", header->acpi_table);
@@ -152,7 +160,6 @@ kernel_main(args::header *header)
interrupts_enable(); interrupts_enable();
devices.init_drivers(); devices.init_drivers();
devices.get_lapic()->calibrate_timer(); devices.get_lapic()->calibrate_timer();
/* /*

View File

@@ -9,6 +9,7 @@
#include "frame_allocator.h" #include "frame_allocator.h"
#include "io.h" #include "io.h"
#include "log.h" #include "log.h"
#include "msr.h"
#include "objects/process.h" #include "objects/process.h"
#include "objects/vm_area.h" #include "objects/vm_area.h"
#include "vm_space.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 void
memory_initialize_pre_ctors(args::header *kargs) memory_initialize_pre_ctors(args::header *kargs)
{ {

View File

@@ -1,5 +1,11 @@
#include "msr.h" #include "msr.h"
msr
find_mtrr(msr type, unsigned index)
{
return static_cast<msr>(static_cast<uint32_t>(type) + (2 * index));
}
uint64_t uint64_t
rdmsr(msr addr) rdmsr(msr addr)
{ {

View File

@@ -6,22 +6,46 @@
enum class msr : uint32_t enum class msr : uint32_t
{ {
ia32_efer = 0xc0000080, ia32_mtrrcap = 0x000000fe,
ia32_star = 0xc0000081, ia32_mtrrdeftype = 0x000002ff,
ia32_lstar = 0xc0000082,
ia32_fmask = 0xc0000084,
ia32_gs_base = 0xc0000101, ia32_mtrrphysbase = 0x00000200,
ia32_kernel_gs_base = 0xc0000102 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 /// Read the value of a MSR
/// \arg addr The MSR address /// \arg addr The MSR address
/// \returns The current value of the MSR /// \returns The current value of the MSR
uint64_t rdmsr(msr addr); uint64_t rdmsr(msr addr);
/// Write to a MSR /// Write to a MSR
/// \arg addr The MSR address /// \arg addr The MSR address
/// \arg value The value to write /// \arg value The value to write
void wrmsr(msr addr, uint64_t value); void wrmsr(msr addr, uint64_t value);

View File

@@ -29,6 +29,7 @@ enum class vm_flags : uint32_t
huge_pages = 0x00000200, huge_pages = 0x00000200,
mmio = 0x00010000, mmio = 0x00010000,
write_combine = 0x00020000,
user_mask = 0x0000ffff ///< flags allowed via syscall user_mask = 0x0000ffff ///< flags allowed via syscall
}; };

View File

@@ -21,14 +21,21 @@ struct page_table
present = 0x0001, /// Entry is present in the table present = 0x0001, /// Entry is present in the table
write = 0x0002, /// Section may be written write = 0x0002, /// Section may be written
user = 0x0004, /// User-accessible user = 0x0004, /// User-accessible
mtrr0 = 0x0008, /// MTRR selector bit 0 pat0 = 0x0008, /// PAT selector bit 0
mtrr1 = 0x0010, /// MTRR selector bit 1 pat1 = 0x0010, /// PAT selector bit 1
accessed = 0x0020, /// Entry has been accessed accessed = 0x0020, /// Entry has been accessed
dirty = 0x0040, /// Page has been written to dirty = 0x0040, /// Page has been written to
page = 0x0080, /// Entry is a large page 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 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 /// Helper for getting the next level value

View File

@@ -141,7 +141,7 @@ load_process_image(uintptr_t phys, uintptr_t virt, size_t bytes, TCB *tcb)
// Crazypants framebuffer part // Crazypants framebuffer part
if (fb) { 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); space.add(0x100000000, vma);
vma->commit(fb->phys_addr, 0, memory::page_count(fb->size)); vma->commit(fb->phys_addr, 0, memory::page_count(fb->size));
} }

View File

@@ -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 flags =
page_table::flag::present | page_table::flag::present |
(m_kernel ? page_table::flag::none : page_table::flag::user) | (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}; page_table::iterator it {virt, m_pml4};