The Pedigree Project 0.1
VirtualAddressSpace-paging.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
3#include "pedigree/kernel/utilities/utility.h"
4
5#include "VirtualAddressSpace-internal.h"
6#include "VirtualAddressSpace.h"
7
8bool X64VirtualAddressSpace::tryMapUserPage(physical_uintptr_t physical, void* address,
9 size_t flags, size_t* committedTablePages) {
10 if (committedTablePages)
11 *committedTablePages = 0;
12 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
13 if (!physical || ((physical | value) & 4095) || value < getUserStart() || value >= (1ULL << 47) ||
14 (flags & (KernelMode | Swapped | Borrowed | Shared | CopyOnWrite)))
15 return false;
16 const size_t indexes[] = {PML4_INDEX(address), PAGE_DIRECTORY_POINTER_INDEX(address),
17 PAGE_DIRECTORY_INDEX(address), PAGE_TABLE_INDEX(address)};
18 uint64_t* leaf = nullptr;
19 auto missing = [&]() -> size_t {
20 physical_uintptr_t table = m_PhysicalPML4;
21 for (size_t level = 0; level < 3; ++level) {
22 const uint64_t entry = *TABLE_ENTRY(table, indexes[level]);
23 if (!(entry & PAGE_PRESENT))
24 return 3 - level;
25 if (entry & PAGE_2MB)
26 return 4;
27 table = entry & 0x000ffffffffff000ULL;
28 }
29 leaf = TABLE_ENTRY(table, indexes[3]);
30 return (*leaf & (PAGE_PRESENT | PAGE_NO_ACCESS | PAGE_SWAPPED)) ? 4 : 0;
31 };
32 size_t count;
33 {
35 mutation.lock(m_Lock);
36 count = missing();
37 if (!count) {
38 // Existing page tables need neither allocation nor a second locked walk.
39 *leaf = physical | toFlags(flags, address, true);
40 if (!invalidateMapping(address, mutation)) {
41 mutation.panicInvalidationFailure();
42 }
43 return true;
44 }
45 }
46 if (count > 3)
47 return false;
48 auto& memory = PhysicalMemoryManager::instance();
49 physical_uintptr_t pages[3] = {};
50 size_t prepared = 0;
51 for (; prepared < count; ++prepared) {
52 pages[prepared] = memory.tryAllocatePage();
53 if (!pages[prepared])
54 break;
55 ByteSet(reinterpret_cast<void*>(physicalAddress(pages[prepared])), 0, 4096);
56 }
57 size_t used = 0;
58 bool mapped = false;
59 if (prepared == count) {
61 mutation.lock(m_Lock);
62 if (missing() <= prepared) {
63 physical_uintptr_t table = m_PhysicalPML4;
64 for (size_t level = 0; level < 3; ++level) {
65 uint64_t* entry = TABLE_ENTRY(table, indexes[level]);
66 if (!(*entry & PAGE_PRESENT))
67 *entry = pages[used++] | PAGE_PRESENT | PAGE_USER | PAGE_WRITE;
68 table = PAGE_GET_PHYSICAL_ADDRESS(entry);
69 }
70 *TABLE_ENTRY(table, indexes[3]) = physical | toFlags(flags, address, true);
71 if (!invalidateMapping(address, mutation))
72 mutation.panicInvalidationFailure();
73 mapped = true;
74 }
75 }
76 for (size_t i = used; i < prepared; ++i)
77 memory.freePage(pages[i]);
78 if (mapped && committedTablePages)
79 *committedTablePages = used;
80 return mapped;
81}
82
83bool X64VirtualAddressSpace::tryDetachUserPage(void* address, physical_uintptr_t expected) {
84 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
85 if ((value & 4095) || value < getUserStart() || value >= (1ULL << 47))
86 return false;
87 {
89 mutation.lock(m_Lock);
90 uint64_t* entry = nullptr;
91 if (!getPageTableEntry(address, entry) || PAGE_GET_PHYSICAL_ADDRESS(entry) != expected ||
92 !(*entry & PAGE_NO_ACCESS) || (*entry & PAGE_SWAPPED))
93 return false;
94 *entry = 0;
95 if (!invalidateMapping(address, mutation))
96 mutation.panicInvalidationFailure();
97 }
98 return true;
99}
static PhysicalMemoryManager & instance()
uint64_t toFlags(size_t flags, void *virtualAddress, bool bFinal=false) const PURE
bool invalidateMapping(void *virtualAddress, X64MappingMutationScope &mutation)
bool getPageTableEntry(void *virtualAddress, uint64_t *&pageTableEntry) const
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39