#pragma once #include #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; // Page-table index extraction works for any byte address. Callers that // truly require page alignment validate that explicitly before mapping. VirtualAddress(std::uint64_t newAddress) : 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(); return 0; } }; class Paging { PageTable* HandleLevel(VirtualAddress virtualAddress, PageTable* table, size_t level); PageTable* HandleLevelUser(VirtualAddress virtualAddress, PageTable* table, size_t level); public: PageTable* PML4{}; Paging(); void Init(std::uint64_t kernelBaseVirt, std::uint64_t kernelSize, limine_memmap_response* memMap); void Map(std::uint64_t physicalAddress, std::uint64_t virtualAddress); void MapMMIO(std::uint64_t physicalAddress, std::uint64_t virtualAddress); void MapWC(std::uint64_t physicalAddress, std::uint64_t virtualAddress); void MapUser(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); // Create a new PML4 with kernel-half (entries 256-511) copied from g_paging. // Returns the physical address of the new PML4. static std::uint64_t CreateUserPML4(); // Map a page into an arbitrary PML4 (specified by physical address) with User bit set. static bool MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress); // Map a page into an arbitrary PML4 with User + Write-Combining attributes. static bool MapUserInWC(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress); // Unmap a single page from an arbitrary PML4 (clears PTE + invalidates TLB). static void UnmapUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress); // Free all user-half page table structures and physical pages (PML4 entries 0-255). // Does NOT free the PML4 page itself (caller handles that). // Skips MMIO/WC pages (WriteThrough or CacheDisabled set in PTE). static void FreeUserHalf(std::uint64_t pml4Phys); // Validate that every page in the given user range is mapped with // user permissions. If requireWrite is true, all pages must also be // writable. static bool IsUserRangeAccessible(std::uint64_t pml4Phys, std::uint64_t virtualAddress, std::uint64_t size, bool requireWrite = false); // Identity-map EFI runtime service regions so firmware code can // reference its own data at physical addresses. void MapEfiRuntime(limine_efi_memmap_response* efiMemmap); }; extern Paging* g_paging; // Protects user page table modifications from concurrent access. // Required for SMP: e.g. WinServer::Resize unmaps pages from the // desktop's page tables while the desktop may be allocating pages // on another CPU. void LockUserPaging(); void UnlockUserPaging(); extern "C" uint64_t GetCR3(); extern "C" void LoadCR3(PageTable* PML4); inline void FlushTLB() { asm volatile("mov %%cr3, %%rax; mov %%rax, %%cr3" ::: "rax", "memory"); } };