feat: overhaul userspace heap and virtual memory

This commit is contained in:
2026-08-10 12:25:27 +02:00
parent 4ecec32c5c
commit 9b23d99082
21 changed files with 905 additions and 451 deletions
+55 -9
View File
@@ -13,13 +13,21 @@ namespace Memory::VMM {
static constexpr uint64_t LeafUser = 1ULL << 2;
static constexpr uint64_t LeafWriteThrough = 1ULL << 3;
static constexpr uint64_t LeafCacheDisabled = 1ULL << 4;
static constexpr uint64_t LeafNoExecute = 1ULL << 63;
// PageTableEntry's historical 52-bit Address bitfield also spans the
// architectural high flag bits. Mask those bits whenever extracting a
// physical address, in particular now that anonymous leaves use NX.
static constexpr uint64_t kPhysAddrMask = 0xFFFFFFFFFFULL; // PTE bits 12..51
static inline void SetLeafPte(PageTableEntry* entry, uint64_t physicalAddress,
bool user, bool writeThrough, bool cacheDisabled) {
uint64_t flags = LeafPresent | LeafWritable;
bool user, bool writeThrough, bool cacheDisabled,
bool writable = true, bool executable = true) {
uint64_t flags = LeafPresent;
if (writable) flags |= LeafWritable;
if (user) flags |= LeafUser;
if (writeThrough) flags |= LeafWriteThrough;
if (cacheDisabled) flags |= LeafCacheDisabled;
if (!executable) flags |= LeafNoExecute;
// Replace the complete PTE in one aligned store. Updating individual
// bitfields left old PWT/PCD/PAT/accessed state behind when a virtual
@@ -230,6 +238,12 @@ namespace Memory::VMM {
}
bool Paging::MapUserIn(std::uint64_t pml4Phys, std::uint64_t physicalAddress, std::uint64_t virtualAddress) {
return MapUserInPermissions(pml4Phys, physicalAddress, virtualAddress, true, true);
}
bool Paging::MapUserInPermissions(std::uint64_t pml4Phys, std::uint64_t physicalAddress,
std::uint64_t virtualAddress, bool writable,
bool executable) {
pagingLock.Acquire();
if (virtualAddress % 0x1000 != 0 || physicalAddress % 0x1000 != 0) {
pagingLock.Release();
@@ -266,7 +280,43 @@ namespace Memory::VMM {
if (!pml1) { pagingLock.Release(); return false; }
PageTableEntry* pageEntry = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
SetLeafPte(pageEntry, physicalAddress, true, false, false);
SetLeafPte(pageEntry, physicalAddress, true, false, false, writable, executable);
pagingLock.Release();
return true;
}
bool Paging::ProtectUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress,
bool writable, bool executable) {
if ((virtualAddress & 0xFFFULL) != 0) return false;
pagingLock.Acquire();
VirtualAddress va(virtualAddress);
auto walkRead = [](PageTable* table, uint64_t index) -> PageTable* {
PageTableEntry* entry = (PageTableEntry*)Memory::HHDM(&table->entries[index]);
if (!entry->Present || !entry->Supervisor || entry->LargerPages) return nullptr;
return (PageTable*)(entry->Address << 12);
};
PageTable* pml4 = (PageTable*)pml4Phys;
auto pml3 = walkRead(pml4, va.GetL4Index());
if (!pml3) { pagingLock.Release(); return false; }
auto pml2 = walkRead(pml3, va.GetL3Index());
if (!pml2) { pagingLock.Release(); return false; }
auto pml1 = walkRead(pml2, va.GetL2Index());
if (!pml1) { pagingLock.Release(); return false; }
PageTableEntry* pte = (PageTableEntry*)Memory::HHDM(&pml1->entries[va.GetPageIndex()]);
uint64_t raw = *(volatile uint64_t*)pte;
if ((raw & LeafPresent) == 0 || (raw & LeafUser) == 0) {
pagingLock.Release();
return false;
}
if (writable) raw |= LeafWritable;
else raw &= ~LeafWritable;
if (executable) raw &= ~LeafNoExecute;
else raw |= LeafNoExecute;
*(volatile uint64_t*)pte = raw;
asm volatile("invlpg (%0)" :: "r"(virtualAddress) : "memory");
pagingLock.Release();
return true;
}
@@ -376,7 +426,8 @@ namespace Memory::VMM {
// Skip MMIO/WC pages (not PFA-managed)
if (pte->WriteThrough || pte->CacheDisabled) continue;
uint64_t pagePhys = (uint64_t)pte->Address << 12;
uint64_t pagePhys =
(uint64_t)(pte->Address & kPhysAddrMask) << 12;
if (pagePhys != 0) {
Memory::g_pfa->Free((void*)Memory::HHDM(pagePhys));
}
@@ -442,11 +493,6 @@ namespace Memory::VMM {
return true;
}
// Mask to extract only the 40-bit physical address from a PTE Address field
// (bits 12-51 of the PTE). The PageTableEntry::Address field is 52 bits wide
// and includes NX, PK, and software-available bits that must be stripped.
static constexpr uint64_t kPhysAddrMask = 0xFFFFFFFFFFULL; // 40 bits
std::uint64_t Paging::GetPhysAddr(std::uint64_t pml4, std::uint64_t virtualAddress, bool use40BitL1) {
VirtualAddress virtualAddressObj(virtualAddress);
+11
View File
@@ -104,6 +104,17 @@ public:
// 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 ordinary user memory with explicit leaf permissions. Read access
// is implied by x86 paging; writable and executable are independent.
static bool MapUserInPermissions(std::uint64_t pml4Phys,
std::uint64_t physicalAddress,
std::uint64_t virtualAddress,
bool writable, bool executable);
// Change leaf permissions on an existing user page.
static bool ProtectUserIn(std::uint64_t pml4Phys, std::uint64_t virtualAddress,
bool writable, bool executable);
// 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);