From 0ddcf668cb4c492c2675ef59b1e6fc348349963a Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Fri, 18 May 2018 23:21:37 -0700 Subject: [PATCH] Allow for 2MiB large pages --- src/kernel/memory_bootstrap.cpp | 19 ++++++++++--- src/kernel/page_manager.cpp | 48 ++++++++++++++++++++++----------- src/kernel/page_manager.h | 10 +++++-- 3 files changed, 57 insertions(+), 20 deletions(-) diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index c9c0fbf..e5a324a 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -4,6 +4,7 @@ #include "page_manager.h" const unsigned efi_page_size = 0x1000; +const unsigned ident_page_flags = 0xf; // TODO: set to 0xb when user/kernel page tables are better sorted enum class efi_memory_type : uint32_t { @@ -306,7 +307,7 @@ copy_new_table(page_table *base, unsigned index, page_table *new_table) for (int i = 0; i < 512; ++i) new_table->entries[i] = 0; } - base->entries[index] = reinterpret_cast(new_table) | 0xb; + base->entries[index] = reinterpret_cast(new_table) | ident_page_flags; } static uint64_t @@ -363,7 +364,7 @@ check_needs_page_ident(page_table *table, unsigned index, page_table **free_page page_table *new_table = (*free_pages)++; for (int i=0; i<512; ++i) new_table->entries[i] = 0; - table->entries[index] = reinterpret_cast(new_table) | 0xb; + table->entries[index] = reinterpret_cast(new_table) | ident_page_flags; return 1; } @@ -390,12 +391,24 @@ page_in_ident( tables[1]->entries[idx[1]] & ~0xfffull); for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { + if (idx[3] == 0 && + count >= 512 && + tables[2]->get(idx[2]) == nullptr) { + // Do a 2MiB page instead + tables[2]->entries[idx[2]] = phys_addr | 0x80 | ident_page_flags; + + phys_addr += page_manager::page_size * 512; + count -= 512; + if (count == 0) return pages_consumed; + continue; + } + pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages); tables[3] = reinterpret_cast( tables[2]->entries[idx[2]] & ~0xfffull); for (; idx[3] < 512; idx[3] += 1) { - tables[3]->entries[idx[3]] = phys_addr | 0xb; + tables[3]->entries[idx[3]] = phys_addr | ident_page_flags; phys_addr += page_manager::page_size; if (--count == 0) return pages_consumed; } diff --git a/src/kernel/page_manager.cpp b/src/kernel/page_manager.cpp index ceab902..f8b2ee9 100644 --- a/src/kernel/page_manager.cpp +++ b/src/kernel/page_manager.cpp @@ -2,6 +2,7 @@ #include "kutil/assert.h" #include "kutil/memory_manager.h" +#include "console.h" #include "log.h" #include "page_manager.h" @@ -253,6 +254,12 @@ page_manager::dump_blocks() page_block::dump(m_free, "free", true); } +void +page_manager::dump_pml4() +{ + get_pml4()->dump(); +} + page_block * page_manager::get_block() { @@ -480,36 +487,47 @@ page_manager::unmap_pages(void* address, size_t count) } void -page_manager::check_needs_page(page_table *table, unsigned index) +page_manager::check_needs_page(page_table *table, unsigned index, bool user) { if ((table->entries[index] & 0x1) == 1) return; page_table *new_table = get_table_page(); for (int i=0; i<512; ++i) new_table->entries[i] = 0; - table->entries[index] = pt_to_phys(new_table) | 0xb; + table->entries[index] = pt_to_phys(new_table) | (user ? 0xf : 0xb); } void -page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count) +page_manager::page_in(page_table *pml4, addr_t phys_addr, addr_t virt_addr, size_t count, bool user) { page_table_indices idx{virt_addr}; page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; for (; idx[0] < 512; idx[0] += 1) { - check_needs_page(tables[0], idx[0]); + check_needs_page(tables[0], idx[0], user); tables[1] = tables[0]->get(idx[0]); for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 0) { - check_needs_page(tables[1], idx[1]); + check_needs_page(tables[1], idx[1], user); tables[2] = tables[1]->get(idx[1]); for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { - check_needs_page(tables[2], idx[2]); + if (idx[3] == 0 && + count >= 512 && + tables[2]->get(idx[2]) == nullptr) { + // Do a 2MiB page instead + tables[2]->entries[idx[2]] = phys_addr | (user ? 0x8f : 0x8b); + phys_addr += page_size * 512; + count -= 512; + if (count == 0) return; + continue; + } + + check_needs_page(tables[2], idx[2], user); tables[3] = tables[2]->get(idx[2]); for (; idx[3] < 512; idx[3] += 1) { - tables[3]->entries[idx[3]] = phys_addr | 0xb; - phys_addr += page_manager::page_size; + tables[3]->entries[idx[3]] = phys_addr | (user ? 0xf : 0xb); + phys_addr += page_size; if (--count == 0) return; } } @@ -573,25 +591,25 @@ page_manager::pop_pages(size_t count, addr_t *address) void page_table::dump(int level, uint64_t offset) { - log::info(logs::memory, "Level %d page table @ %lx (off %lx):", level, this, offset); + console *cons = console::get(); + + cons->printf("\nLevel %d page table @ %lx (off %lx):\n", level, this, offset); for (int i=0; i<512; ++i) { uint64_t ent = entries[i]; if (ent == 0) continue; if ((ent & 0x1) == 0) { - log::info(logs::memory, " %3d: %lx NOT PRESENT", i, ent); + cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent); continue; } if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) { - log::info(logs::memory, " %3d: %lx -> Large page at %lx", - i, ent, ent & ~0xfffull); + cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull); continue; } else if (level == 1) { - log::info(logs::memory, " %3d: %lx -> Page at %lx", - i, ent, ent & ~0xfffull); + cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull); } else { - log::info(logs::memory, " %3d: %lx -> Level %d table at %lx", + cons->printf(" %3d: %016lx -> Level %d table at %016lx\n", i, ent, level - 1, (ent & ~0xfffull) + offset); continue; } diff --git a/src/kernel/page_manager.h b/src/kernel/page_manager.h index 519245b..88d8897 100644 --- a/src/kernel/page_manager.h +++ b/src/kernel/page_manager.h @@ -70,6 +70,9 @@ public: /// Log the current free/used block lists. void dump_blocks(); + /// Dump the current PML4 to the console + void dump_pml4(); + /// Get the system page manager. /// \returns A pointer to the system page manager static page_manager * get(); @@ -127,18 +130,21 @@ private: /// it. /// \arg base Existing page table being indexed into /// \arg i Index into the existing table to check - void check_needs_page(page_table *base, unsigned i); + /// \art user True if this is a userspace mapping + void check_needs_page(page_table *base, unsigned i, bool user); /// Low-level routine for mapping a number of pages into the given page table. /// \arg pml4 The root page table to map into /// \arg phys_addr The starting physical address of the pages to be mapped /// \arg virt_addr The starting virtual address ot the memory to be mapped /// \arg count The number of pages to map + /// \art user True if this is a userspace mapping void page_in( page_table *pml4, addr_t phys_addr, addr_t virt_addr, - size_t count); + size_t count, + bool user = true); /// Low-level routine for unmapping a number of pages from the given page table. /// \arg pml4 The root page table for this mapping