From 5dedd2e0e0d185ca69e3decb73208cee4849206d Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 21 Apr 2018 19:32:39 -0700 Subject: [PATCH] Finish memory bootstrap sequence. Now we're setting up all our own page tables, and handing off to page_manager's init function. (Which is still NYI.) --- NOTES.md | 1 + src/kernel/memory_bootstrap.cpp | 103 +++++++++++++++++++++++++++++--- src/kernel/memory_pages.cpp | 29 ++++++--- src/kernel/memory_pages.h | 15 ++++- 4 files changed, 128 insertions(+), 20 deletions(-) diff --git a/NOTES.md b/NOTES.md index a6ae2fd..330d32b 100644 --- a/NOTES.md +++ b/NOTES.md @@ -3,5 +3,6 @@ ## TODO - Better page-allocation model +- Reclaim skipped bootstrap scratch space - Allow for more than one IOAPIC in ACPI module diff --git a/src/kernel/memory_bootstrap.cpp b/src/kernel/memory_bootstrap.cpp index d26e5ea..d94c0f2 100644 --- a/src/kernel/memory_bootstrap.cpp +++ b/src/kernel/memory_bootstrap.cpp @@ -1,3 +1,4 @@ +#include "kutil/memory.h" #include "assert.h" #include "console.h" #include "memory.h" @@ -226,6 +227,67 @@ gather_block_lists( return reinterpret_cast(&block_list[i]); } +unsigned check_needs_page(page_table *table, unsigned index, page_table **free_pages) +{ + if (table->entries[index] & 0x1 == 1) return 0; + + kassert(*free_pages, "check_needs_page needed to allocate but had no free pages"); + + 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; + return 1; +} + +unsigned page_in(page_table *pml4, uint64_t phys_addr, uint64_t virt_addr, uint64_t count, page_table *free_pages) +{ + page_table_indices idx{virt_addr}; + page_table *tables[4] = {pml4, nullptr, nullptr, nullptr}; + + unsigned pages_consumed = 0; + for (; idx[0] < 512; idx[0] += 1) { + pages_consumed += check_needs_page(tables[0], idx[0], &free_pages); + tables[1] = reinterpret_cast( + tables[0]->entries[idx[0]] & ~0xfffull); + + for (; idx[1] < 512; idx[1] += 1) { + pages_consumed += check_needs_page(tables[1], idx[1], &free_pages); + tables[2] = reinterpret_cast( + tables[1]->entries[idx[1]] & ~0xfffull); + + for (; idx[2] < 512; idx[2] += 1) { + pages_consumed += check_needs_page(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; + phys_addr += 0x1000; + if (--count == 0) return pages_consumed; + } + } + } + } + + kassert(0, "Ran to end of page_in"); +} + +page_block * +fill_page_with_blocks(uint64_t start) { + uint64_t space = page_align(start) - start; + uint64_t count = space / sizeof(page_block); + page_block *blocks = reinterpret_cast(start); + kutil::memset(blocks, 0, sizeof(page_block)*count); + + page_block *head = nullptr, **insert = &head; + for (unsigned i = 0; i < count; ++i) { + *insert = &blocks[i]; + insert = &blocks[i].next; + } + + return head; +} + void memory_initialize_managers(const void *memory_map, size_t map_length, size_t desc_length) { @@ -251,10 +313,13 @@ memory_initialize_managers(const void *memory_map, size_t map_length, size_t des } kassert(desc < end, "Couldn't find 4MiB of contiguous scratch space."); - uint64_t free_region = page_table_align(desc->physical_start); - // Offset-map this region into the higher half. + uint64_t free_region_start = desc->physical_start; + uint64_t free_region = page_table_align(free_region_start); uint64_t next_free = free_region + 0xffff800000000000; + cons->puts("Skipping "); + cons->put_dec(free_region - free_region_start); + cons->puts(" bytes to get page-table-aligned.\n"); // We'll need to copy any existing tables (except the PML4 which the // bootloader gave us) into our 4 reserved pages so we can edit them. @@ -277,9 +342,9 @@ memory_initialize_managers(const void *memory_map, size_t map_length, size_t des } tables[1].entries[fr_idx[1]] = reinterpret_cast(&tables[2]) | 0xb; - for (int i = 0; i < 512; ++i) - tables[3].entries[i] = (free_region + 0x1000 * i) | 0xb; + // No need to copy the last-level page table, we're overwriting the whole thing tables[2].entries[fr_idx[2]] = reinterpret_cast(&tables[3]) | 0xb; + page_in(&tables[0], free_region, next_free, 512, nullptr); // We now have 2MiB starting at "free_region" to bootstrap ourselves. Start by // taking inventory of free pages. @@ -287,17 +352,37 @@ memory_initialize_managers(const void *memory_map, size_t map_length, size_t des page_block *used_head = nullptr; next_free = gather_block_lists(next_free, memory_map, map_length, desc_length, &free_head, &used_head); + + // Unused page_block structs go here - finish out the current page with them + page_block *cache_head = fill_page_with_blocks(next_free); next_free = page_align(next_free); // Now go back through these lists and consolidate - free_head->list_consolidate(); - used_head->list_consolidate(); + page_block **cache = &cache_head; + *cache = free_head->list_consolidate(); + while (*cache) cache = &(*cache)->next; + *cache = used_head->list_consolidate(); // Ok, now build an acutal set of kernel page tables that just contains // what the kernel actually has mapped. - unsigned table_page_count = count_table_pages_needed(used_head); - page_table *pages = reinterpret_cast(next_free); - next_free += table_page_count * 0x1000; + unsigned consumed_pages = 1; // We're about to make a PML4, start with 1:w + // Finally, remap the existing mappings, but making everything writable + // (especially the page tables themselves) + page_table *pml4 = pages++; + for (int i=0; i<512; ++i) pml4->entries[i] = 0; + + for (page_block *cur = used_head; cur; cur = cur->next) { + if (!cur->has_flag(page_block_flags::mapped)) continue; + consumed_pages += page_in(pml4, cur->physical_address, cur->virtual_address, + cur->count, pages + consumed_pages); + } + next_free += (consumed_pages * 0x1000); + + // We now have all used memory mapped ourselves. Let the page_manager take + // over from here. + page_manager::init( + free_head, used_head, cache_head, + free_region_start, 1024 * 0x1000, next_free); } diff --git a/src/kernel/memory_pages.cpp b/src/kernel/memory_pages.cpp index 46db592..5e661b5 100644 --- a/src/kernel/memory_pages.cpp +++ b/src/kernel/memory_pages.cpp @@ -4,14 +4,6 @@ page_manager g_page_manager; -page_manager::page_manager() : - m_free(nullptr), - m_used(nullptr), - m_pool(nullptr) -{ - kassert(this == &g_page_manager, "Attempt to create another page_manager."); -} - page_block * page_block::list_consolidate() @@ -68,3 +60,24 @@ page_block::list_dump(const char *name) cons->put_dec(count); cons->puts("\n"); } + + +page_manager::page_manager() : + m_free(nullptr), + m_used(nullptr), + m_block_cache(nullptr), + m_page_cache(nullptr) +{ + kassert(this == &g_page_manager, "Attempt to create another page_manager."); +} + +void +page_manager::init( + page_block *free, + page_block *used, + page_block *block_cache, + uint64_t scratch_start, + uint64_t scratch_length, + uint64_t scratch_cur) +{ +} diff --git a/src/kernel/memory_pages.h b/src/kernel/memory_pages.h index 1523abf..eb601d9 100644 --- a/src/kernel/memory_pages.h +++ b/src/kernel/memory_pages.h @@ -6,7 +6,8 @@ #include "kutil/enum_bitfields.h" -class page_block; +struct page_block; +struct free_page; /// Manager for allocation of physical pages. @@ -21,14 +22,22 @@ private: friend void memory_initialize_managers(const void *, size_t, size_t); /// Set up the memory manager from bootstraped memory - static void init(page_block *free, page_block *used, uint64_t scratch); + static void init( + page_block *free, + page_block *used, + page_block *block_cache, + uint64_t scratch_start, + uint64_t scratch_length, + uint64_t scratch_cur); /// Initialize the virtual memory manager based on this object's state void init_memory_manager(); page_block *m_free; ///< Free pages list page_block *m_used; ///< In-use pages list - page_block *m_pool; ///< Cache of unused page_block structs + + page_block *m_block_cache; ///< Cache of unused page_block structs + free_page *m_page_cache; ///< Cache of free pages to use for tables }; /// Global page manager.