Allow for 2MiB large pages

This commit is contained in:
Justin C. Miller
2018-05-18 23:21:37 -07:00
parent 4005e9e791
commit 0ddcf668cb
3 changed files with 57 additions and 20 deletions

View File

@@ -4,6 +4,7 @@
#include "page_manager.h" #include "page_manager.h"
const unsigned efi_page_size = 0x1000; 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 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; for (int i = 0; i < 512; ++i) new_table->entries[i] = 0;
} }
base->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb; base->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
} }
static uint64_t 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)++; page_table *new_table = (*free_pages)++;
for (int i=0; i<512; ++i) new_table->entries[i] = 0; for (int i=0; i<512; ++i) new_table->entries[i] = 0;
table->entries[index] = reinterpret_cast<uint64_t>(new_table) | 0xb; table->entries[index] = reinterpret_cast<uint64_t>(new_table) | ident_page_flags;
return 1; return 1;
} }
@@ -390,12 +391,24 @@ page_in_ident(
tables[1]->entries[idx[1]] & ~0xfffull); tables[1]->entries[idx[1]] & ~0xfffull);
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { 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); pages_consumed += check_needs_page_ident(tables[2], idx[2], &free_pages);
tables[3] = reinterpret_cast<page_table *>( tables[3] = reinterpret_cast<page_table *>(
tables[2]->entries[idx[2]] & ~0xfffull); tables[2]->entries[idx[2]] & ~0xfffull);
for (; idx[3] < 512; idx[3] += 1) { 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; phys_addr += page_manager::page_size;
if (--count == 0) return pages_consumed; if (--count == 0) return pages_consumed;
} }

View File

@@ -2,6 +2,7 @@
#include "kutil/assert.h" #include "kutil/assert.h"
#include "kutil/memory_manager.h" #include "kutil/memory_manager.h"
#include "console.h"
#include "log.h" #include "log.h"
#include "page_manager.h" #include "page_manager.h"
@@ -253,6 +254,12 @@ page_manager::dump_blocks()
page_block::dump(m_free, "free", true); page_block::dump(m_free, "free", true);
} }
void
page_manager::dump_pml4()
{
get_pml4()->dump();
}
page_block * page_block *
page_manager::get_block() page_manager::get_block()
{ {
@@ -480,36 +487,47 @@ page_manager::unmap_pages(void* address, size_t count)
} }
void 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; if ((table->entries[index] & 0x1) == 1) return;
page_table *new_table = get_table_page(); page_table *new_table = get_table_page();
for (int i=0; i<512; ++i) new_table->entries[i] = 0; 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 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_indices idx{virt_addr};
page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; page_table *tables[4] = {pml4, nullptr, nullptr, nullptr};
for (; idx[0] < 512; idx[0] += 1) { 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]); tables[1] = tables[0]->get(idx[0]);
for (; idx[1] < 512; idx[1] += 1, idx[2] = 0, idx[3] = 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]); tables[2] = tables[1]->get(idx[1]);
for (; idx[2] < 512; idx[2] += 1, idx[3] = 0) { 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]); tables[3] = tables[2]->get(idx[2]);
for (; idx[3] < 512; idx[3] += 1) { for (; idx[3] < 512; idx[3] += 1) {
tables[3]->entries[idx[3]] = phys_addr | 0xb; tables[3]->entries[idx[3]] = phys_addr | (user ? 0xf : 0xb);
phys_addr += page_manager::page_size; phys_addr += page_size;
if (--count == 0) return; if (--count == 0) return;
} }
} }
@@ -573,25 +591,25 @@ page_manager::pop_pages(size_t count, addr_t *address)
void void
page_table::dump(int level, uint64_t offset) 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) { for (int i=0; i<512; ++i) {
uint64_t ent = entries[i]; uint64_t ent = entries[i];
if (ent == 0) continue; if (ent == 0) continue;
if ((ent & 0x1) == 0) { if ((ent & 0x1) == 0) {
log::info(logs::memory, " %3d: %lx NOT PRESENT", i, ent); cons->printf(" %3d: %016lx NOT PRESENT\n", i, ent);
continue; continue;
} }
if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) { if ((level == 2 || level == 3) && (ent & 0x80) == 0x80) {
log::info(logs::memory, " %3d: %lx -> Large page at %lx", cons->printf(" %3d: %016lx -> Large page at %016lx\n", i, ent, ent & ~0xfffull);
i, ent, ent & ~0xfffull);
continue; continue;
} else if (level == 1) { } else if (level == 1) {
log::info(logs::memory, " %3d: %lx -> Page at %lx", cons->printf(" %3d: %016lx -> Page at %016lx\n", i, ent, ent & ~0xfffull);
i, ent, ent & ~0xfffull);
} else { } 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); i, ent, level - 1, (ent & ~0xfffull) + offset);
continue; continue;
} }

View File

@@ -70,6 +70,9 @@ public:
/// Log the current free/used block lists. /// Log the current free/used block lists.
void dump_blocks(); void dump_blocks();
/// Dump the current PML4 to the console
void dump_pml4();
/// Get the system page manager. /// Get the system page manager.
/// \returns A pointer to the system page manager /// \returns A pointer to the system page manager
static page_manager * get(); static page_manager * get();
@@ -127,18 +130,21 @@ private:
/// it. /// it.
/// \arg base Existing page table being indexed into /// \arg base Existing page table being indexed into
/// \arg i Index into the existing table to check /// \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. /// Low-level routine for mapping a number of pages into the given page table.
/// \arg pml4 The root page table to map into /// \arg pml4 The root page table to map into
/// \arg phys_addr The starting physical address of the pages to be mapped /// \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 virt_addr The starting virtual address ot the memory to be mapped
/// \arg count The number of pages to map /// \arg count The number of pages to map
/// \art user True if this is a userspace mapping
void page_in( void page_in(
page_table *pml4, page_table *pml4,
addr_t phys_addr, addr_t phys_addr,
addr_t virt_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. /// Low-level routine for unmapping a number of pages from the given page table.
/// \arg pml4 The root page table for this mapping /// \arg pml4 The root page table for this mapping