From 2515149f28f004878e85e7e24ad1322fcfb81f12 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Thu, 17 Apr 2025 21:54:25 +0200 Subject: [PATCH] feat(vmm): Add Paging support --- .vscode/settings.json | 28 ++++++- GNUmakefile | 2 +- README.md | 2 +- kernel/linker-x86_64.ld | 3 + kernel/src/Common/Panic.cpp | 4 +- kernel/src/Hal/GDT.cpp | 2 +- kernel/src/Hal/IDT.cpp | 4 +- kernel/src/Main.cpp | 21 ++++- kernel/src/Memory/PageFrameAllocator.cpp | 7 ++ kernel/src/Memory/PageFrameAllocator.hpp | 1 + kernel/src/Memory/Paging.asm | 12 +++ kernel/src/Memory/Paging.cpp | 91 ++++++++++++++++++++++ kernel/src/Memory/Paging.hpp | 98 ++++++++++++++++++++++++ kernel/src/Platform/Limine.hpp | 2 +- 14 files changed, 266 insertions(+), 11 deletions(-) create mode 100644 kernel/src/Memory/Paging.asm create mode 100644 kernel/src/Memory/Paging.cpp create mode 100644 kernel/src/Memory/Paging.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index 58e978a..f2d9c3f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -69,6 +69,32 @@ "rope": "cpp", "slist": "cpp", "stacktrace": "cpp", - "regex": "cpp" + "regex": "cpp", + "coroutine": "cpp", + "expected": "cpp", + "generator": "cpp", + "scoped_allocator": "cpp", + "source_location": "cpp", + "typeindex": "cpp", + "any": "cpp", + "barrier": "cpp", + "cfenv": "cpp", + "codecvt": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cuchar": "cpp", + "forward_list": "cpp", + "unordered_set": "cpp", + "future": "cpp", + "iostream": "cpp", + "latch": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "spanstream": "cpp", + "stop_token": "cpp", + "syncstream": "cpp", + "valarray": "cpp" } } \ No newline at end of file diff --git a/GNUmakefile b/GNUmakefile index add7591..f7c84fd 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -6,7 +6,7 @@ MAKEFLAGS += -rR ARCH := x86_64 # Default user QEMU flags. These are appended to the QEMU command calls. -QEMUFLAGS := -m 2G -s -S +QEMUFLAGS := -m 2G -s -S -d int -D qemu.log override IMAGE_NAME := os220-$(ARCH) diff --git a/README.md b/README.md index 1615630..8f2bb18 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Research project aiming to create a modern kernel and operating system in C++ - [x] Kernel-mode heap allocator - [x] ISR handling (interrupts) - [x] Exception handling -- [ ] Virtual memory paging +- [x] Virtual memory paging - [ ] UEFI runtime service support - [ ] ACPI & AML support - [ ] PCI-e diff --git a/kernel/linker-x86_64.ld b/kernel/linker-x86_64.ld index ec5284a..0b364c5 100644 --- a/kernel/linker-x86_64.ld +++ b/kernel/linker-x86_64.ld @@ -22,6 +22,7 @@ SECTIONS /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ /* that is the beginning of the region. */ . = 0xffffffff80000000; + KernelStartSymbol = .; /* Define a section to contain the Limine requests and assign it to its own PHDR */ .limine_requests : { @@ -74,4 +75,6 @@ SECTIONS *(.eh_frame*) *(.note .note.*) } + + KernelEndSymbol = .; } diff --git a/kernel/src/Common/Panic.cpp b/kernel/src/Common/Panic.cpp index d41f027..df8e1b5 100644 --- a/kernel/src/Common/Panic.cpp +++ b/kernel/src/Common/Panic.cpp @@ -1,9 +1,9 @@ #include "Panic.hpp" void Panic(const char *meditationString, System::PanicFrame* frame) { - kerr << "\nGuru Meditation" << "\n" << "\n"; + kerr << "\nKernel panic" << "\n" << "\n"; - kerr << "\t" << "MeditationString: " << meditationString << Kt::newline; + kerr << "\t" << meditationString << Kt::newline; #if defined (__x86_64__) if (frame != nullptr) { diff --git a/kernel/src/Hal/GDT.cpp b/kernel/src/Hal/GDT.cpp index 2028a41..907bfbb 100644 --- a/kernel/src/Hal/GDT.cpp +++ b/kernel/src/Hal/GDT.cpp @@ -38,6 +38,6 @@ namespace Hal { LoadGDT(&gdtPointer); ReloadSegments(); - KernelLogStream(DEBUG, "HardwareAbstraction") << "Set new GDT (0x" << base::hex << (uint64_t)&kernelGDT << ")"; + KernelLogStream(DEBUG, "Hal") << "Set new GDT (0x" << base::hex << (uint64_t)&kernelGDT << ")"; } }; \ No newline at end of file diff --git a/kernel/src/Hal/IDT.cpp b/kernel/src/Hal/IDT.cpp index eccad21..8cbc554 100644 --- a/kernel/src/Hal/IDT.cpp +++ b/kernel/src/Hal/IDT.cpp @@ -115,10 +115,10 @@ namespace Hal { SetHandler<0, 31>::run(); - Kt::KernelLogStream(Kt::OK, "HardwareAbstraction") << "Created exception interrupt vectors"; + Kt::KernelLogStream(Kt::OK, "Hal") << "Created exception interrupt vectors"; LoadIDT(IDTR); - Kt::KernelLogStream(Kt::OK, "HardwareAbstraction") << "Loaded new IDT"; + Kt::KernelLogStream(Kt::OK, "Hal") << "Loaded new IDT"; } }; \ No newline at end of file diff --git a/kernel/src/Main.cpp b/kernel/src/Main.cpp index efddf84..4e2bc51 100644 --- a/kernel/src/Main.cpp +++ b/kernel/src/Main.cpp @@ -25,6 +25,7 @@ #include #include #include +#include using namespace Kt; @@ -41,6 +42,9 @@ KernelErrorStream kerr{}; extern void (*__init_array[])(); extern void (*__init_array_end[])(); +extern "C" uint64_t KernelStartSymbol; +extern "C" uint64_t KernelEndSymbol; + extern "C" void kmain() { if (LIMINE_BASE_REVISION_SUPPORTED == false) { Hal::Halt(); @@ -80,12 +84,12 @@ extern "C" void kmain() { Memory::HHDMBase = hhdm_offset; if (memmap_request.response != nullptr) { - Kt::KernelLogStream(OK, "MemoryManagement") << "Creating PageFrameAllocator"; + Kt::KernelLogStream(OK, "Mem") << "Creating PageFrameAllocator"; Memory::PageFrameAllocator pmm(Memory::Scan(memmap_request.response)); Memory::g_pfa = &pmm; - Kt::KernelLogStream(OK, "MemoryManagement") << "Creating HeapAllocator"; + Kt::KernelLogStream(OK, "Mem") << "Creating HeapAllocator"; Memory::HeapAllocator heap{}; Memory::g_heap = &heap; @@ -95,9 +99,22 @@ extern "C" void kmain() { Panic("System memory map missing!", nullptr); } + #if defined (__x86_64__) Hal::IDTInitialize(); #endif + // auto kaddr_request_response = kernel_address_request.response; + + Memory::VMM::Paging g_paging{}; + + g_paging.Init((uint64_t)&KernelStartSymbol, ((uint64_t)&KernelEndSymbol - (uint64_t)&KernelStartSymbol)); + // g_paging.Map(0xcfc0000, 0xcfc0000); + + // kout << "0x" << base::hex << Memory::VMM::GetCR3(); + + kout << "0x" << base::hex << 0xdeadbeef << "\n"; + kout << "0x" << base::hex << Memory::HHDM(0xdeadbeef) << "\n"; + Hal::Halt(); } diff --git a/kernel/src/Memory/PageFrameAllocator.cpp b/kernel/src/Memory/PageFrameAllocator.cpp index e323a83..27d41df 100644 --- a/kernel/src/Memory/PageFrameAllocator.cpp +++ b/kernel/src/Memory/PageFrameAllocator.cpp @@ -61,6 +61,13 @@ namespace Memory { return nullptr; } + void* PageFrameAllocator::AllocateZeroed() { + auto page = Allocate(); + memset(page, 0, 0x1000); + + return page; + } + void* PageFrameAllocator::ReallocConsecutive(void* ptr, int n) { auto first = Allocate(); diff --git a/kernel/src/Memory/PageFrameAllocator.hpp b/kernel/src/Memory/PageFrameAllocator.hpp index 5a23e5c..650d743 100644 --- a/kernel/src/Memory/PageFrameAllocator.hpp +++ b/kernel/src/Memory/PageFrameAllocator.hpp @@ -23,6 +23,7 @@ public: PageFrameAllocator(LargestSection section); void* Allocate(); + void* AllocateZeroed(); void* ReallocConsecutive(void* ptr, int n); void Free(void* ptr); void Free(void* ptr, int n); diff --git a/kernel/src/Memory/Paging.asm b/kernel/src/Memory/Paging.asm new file mode 100644 index 0000000..c151296 --- /dev/null +++ b/kernel/src/Memory/Paging.asm @@ -0,0 +1,12 @@ +[bits 64] +section .text +global GetCR3 +global LoadCR3 + +GetCR3: + mov rax, cr3 + ret + +LoadCR3: + mov cr3, rdi + ret \ No newline at end of file diff --git a/kernel/src/Memory/Paging.cpp b/kernel/src/Memory/Paging.cpp new file mode 100644 index 0000000..04ff2c2 --- /dev/null +++ b/kernel/src/Memory/Paging.cpp @@ -0,0 +1,91 @@ +#include "Paging.hpp" +#include +#include +#include + +namespace Memory::VMM { + extern "C" uint64_t KernelStartSymbol; + + std::uint64_t GetPhysKernelAddress(std::uint64_t virtualAddress) { + return Paging::GetPhysAddr(GetCR3(), (std::uint64_t)virtualAddress, true); + } + + Paging::Paging() { + PML4 = (PageTable*)SubHHDM((PageTable*)Memory::g_pfa->AllocateZeroed()); + } + + void Paging::Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize) { + // Map kernel + Kt::KernelLogStream(Kt::DEBUG, "VMM") << "Paging::Init called with kernelBaseVirt as 0x" << base::hex << kernelBaseVirt << " and kernelSize as " << base::dec << kernelSize; + + for (std::uint64_t pageAddr = kernelBaseVirt; pageAddr < (kernelBaseVirt + kernelSize); pageAddr += 0x1000) { + Kt::KernelLogStream(Kt::DEBUG, "VMM") << "Mapping 0x" << base::hex << GetPhysKernelAddress(pageAddr) << " to 0x" << pageAddr; + Map(GetPhysKernelAddress(pageAddr), pageAddr); + } + + // TODO map HHDM + // TODO map ACPI tables + + LoadCR3((PageTable*)&PML4); + } + + PageTable* Paging::HandleLevel(VirtualAddress virtualAddress, PageTable* table, const size_t level) { + PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[virtualAddress.GetIndex(level)]); + + entry->Present = true; + entry->Writable = true; + + uint64_t downLevelAddr = Memory::SubHHDM((uint64_t)Memory::g_pfa->AllocateZeroed()); + + entry->Address = downLevelAddr >> 12; + + return (PageTable*)downLevelAddr; + } + + void Paging::Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress) { + if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) { + Panic("Value that isn't page-aligned passed as address to Paging::Map!", nullptr); + } + + VirtualAddress virtualAddressObj(virtualAddress); + + auto PML3 = HandleLevel(virtualAddressObj, PML4, 4); + auto PML2 = HandleLevel(virtualAddressObj, PML3, 3); + auto PML1 = HandleLevel(virtualAddressObj, PML2, 2); + + PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&PML1->entries[virtualAddressObj.GetPageIndex()]); + + pageEntry->Present = true; + pageEntry->Writable = true; + + pageEntry->Address = virtualAddress >> 12; + } + + std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) { + VirtualAddress virtualAddressObj(virtualAddress); + + PageTable* pml4Virt = (PageTable*)HHDM(pml4); + + PageTableEntry* pml4_entry = &pml4Virt->entries[virtualAddressObj.GetL4Index()]; + + PageTable* pml3 = (PageTable*)HHDM(pml4_entry->Address << 12); + PageTableEntry* pml3_entry = &pml3->entries[virtualAddressObj.GetL3Index()]; + + PageTable* pml2 = (PageTable*)HHDM(pml3_entry->Address << 12); + PageTableEntry* pml2_entry = &pml2->entries[virtualAddressObj.GetL2Index()]; + + PageTable* pml1 = (PageTable*)HHDM(pml2_entry->Address << 12); + + if (use40BitL1 == true) { + PageTableEntry40Bit* pml1_entry = (PageTableEntry40Bit*)&pml1->entries[virtualAddressObj.GetPageIndex()]; + return (uint64_t)pml1_entry->Address << 12; + } + + PageTableEntry* pml1_entry = &pml1->entries[virtualAddressObj.GetPageIndex()]; + return (uint64_t)pml1_entry->Address << 12; + } + + std::uint64_t Paging::GetPhysAddr(std::uint64_t virtualAddress) { + return GetPhysAddr((std::uint64_t)PML4, virtualAddress, false); + } +}; \ No newline at end of file diff --git a/kernel/src/Memory/Paging.hpp b/kernel/src/Memory/Paging.hpp new file mode 100644 index 0000000..23580cd --- /dev/null +++ b/kernel/src/Memory/Paging.hpp @@ -0,0 +1,98 @@ +#pragma once +#include +#include + +namespace Memory::VMM { + struct PageTableEntry { + std::uint8_t Present : 1; + std::uint8_t Writable : 1; + std::uint8_t Supervisor : 1; + std::uint8_t WriteThrough : 1; + std::uint8_t CacheDisabled : 1; + std::uint8_t Accessed : 1; + std::uint8_t Ignore : 1; + std::uint8_t LargerPages : 1; + std::uint8_t PageSize : 1; + std::uint8_t Available : 3; + std::uint64_t Address : 52; + }; + + struct PageTableEntry40Bit { + std::uint8_t Present : 1; + std::uint8_t Writable : 1; + std::uint8_t Supervisor : 1; + std::uint8_t WriteThrough : 1; + std::uint8_t CacheDisabled : 1; + std::uint8_t Accessed : 1; + std::uint8_t Ignore : 1; + std::uint8_t LargerPages : 1; + std::uint8_t PageSize : 1; + std::uint8_t Available : 3; + std::uint64_t Address : 40; + std::uint8_t AvailableHigh : 7; + std::uint8_t PK : 4; + std::uint8_t NX : 1; + }; + + + struct PageTable { + PageTableEntry entries[512]; + } __attribute__((packed)) __attribute__((aligned(0x1000))); + + struct VirtualAddress { + std::uint64_t address; + + VirtualAddress(std::uint64_t newAddress) { + if (newAddress % 0x1000 != 0) { + Kt::KernelLogStream(Kt::WARNING, "VMM") << "VirtualAddress object created with non-aligned value."; + } + + address = newAddress; + } + + uint64_t GetL4Index() { + return (address >> 39) & 0x1ff; + } + + uint64_t GetL3Index() { + return (address >> 30) & 0x1ff; + } + + uint64_t GetL2Index() { + return (address >> 21) & 0x1ff; + } + + uint64_t GetPageIndex() { + return (address >> 12) & 0x1ff; + } + + uint64_t GetIndex(size_t level) { + if (level == 4) + return GetL4Index(); + + else if (level == 3) + return GetL3Index(); + + else if (level == 2) + return GetL2Index(); + + else if (level == 1) + return GetPageIndex(); + } + }; + + class Paging { + PageTable* PML4{}; + + PageTable* HandleLevel(VirtualAddress virtualAddress, PageTable* table, size_t level); +public: + Paging(); + void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize); + void Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress); + static std::uint64_t GetPhysAddr(std::uint64_t PML4, std::uint64_t virtualAddress, bool use40BitL1 = false); + std::uint64_t GetPhysAddr(std::uint64_t virtualAddress); + }; + + extern "C" uint64_t GetCR3(); + extern "C" void LoadCR3(PageTable* PML4); +}; \ No newline at end of file diff --git a/kernel/src/Platform/Limine.hpp b/kernel/src/Platform/Limine.hpp index 843ee18..1ab925e 100644 --- a/kernel/src/Platform/Limine.hpp +++ b/kernel/src/Platform/Limine.hpp @@ -51,7 +51,7 @@ namespace { .revision = 0, .response = nullptr }; - + } // Finally, define the start and end markers for the Limine requests.