feat: overhaul userspace heap and virtual memory
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user