1#include "VirtualAddressSpace.h"
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/panic.h"
4#include "pedigree/kernel/processor/Processor.h"
8#include "PhysicalMemoryManager.h"
10extern "C" char arm64_boot_high_l0, arm64_boot_low_l0;
13constexpr uint64_t Valid = 1ULL;
14constexpr uint64_t Table = 2ULL;
15constexpr uint64_t AddressMask = 0x0000fffffffff000ULL;
16constexpr uint64_t Shareable = 3ULL << 8;
17constexpr uint64_t AccessFlag = 1ULL << 10;
18constexpr uint64_t NotGlobal = 1ULL << 11;
19constexpr uint64_t PrivilegedExecuteNever = 1ULL << 53;
20constexpr uint64_t UserExecuteNever = 1ULL << 54;
21constexpr uint64_t SoftwareNoAccess = 1ULL << 3;
22constexpr uint64_t SoftwareSwapped = 1ULL << 4;
23constexpr uint64_t SoftwareCopyOnWrite = 1ULL << 55;
24constexpr uint64_t SoftwareShared = 1ULL << 56;
25constexpr uint64_t SoftwareBorrowed = 1ULL << 57;
26constexpr uint64_t SoftwareWriteProtected = 1ULL << 58;
28uint64_t* tableAt(physical_uintptr_t physical) {
29 return reinterpret_cast<uint64_t*
>(ARM64_DIRECT_MAP_BASE + physical);
32size_t tableIndex(uintptr_t address,
size_t level) {
33 return (address >> (39 - level * 9)) & 511;
40 return Arm64VirtualAddressSpace::m_KernelSpace;
47Arm64VirtualAddressSpace::Arm64VirtualAddressSpace(
bool kernel)
49 reinterpret_cast<void*>(kernel ? KERNEL_VIRTUAL_HEAP : USERSPACE_VIRTUAL_HEAP)),
50 m_Root(kernel ? reinterpret_cast<uintptr_t>(&arm64_boot_high_l0)
52 m_StackTop(kernel ? KERNEL_VIRTUAL_STACK : USERSPACE_VIRTUAL_STACK) {
54 memset(tableAt(m_Root), 0, PAGE_SIZE);
58Arm64VirtualAddressSpace::~Arm64VirtualAddressSpace() {
59 if (
this != &m_KernelSpace && m_Root) {
60 freeTable(tableAt(m_Root), 0,
true);
61 Arm64PhysicalMemoryManager::instance().
freePage(m_Root);
66 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
67 return value < 0x0000800000000000ULL || value >= ARM64_DIRECT_MAP_BASE;
70uint64_t* Arm64VirtualAddressSpace::findExistingEntry(uintptr_t address,
size_t* depth)
const {
71 physical_uintptr_t root = m_Root;
72 if (address >= ARM64_DIRECT_MAP_BASE) {
73 root = m_KernelSpace.m_Root;
74 }
else if (
this == &m_KernelSpace) {
75 root =
reinterpret_cast<uintptr_t
>(&arm64_boot_low_l0);
77 uint64_t* table = tableAt(root);
78 for (
size_t level = 0; level < 4; ++level) {
79 uint64_t* entry = &table[tableIndex(address, level)];
80 if (level == 3 || !(*entry & Valid) || !(*entry & Table)) {
86 table = tableAt(*entry & AddressMask);
91uint64_t* Arm64VirtualAddressSpace::findEntry(uintptr_t address,
bool create,
size_t* newTables) {
92 physical_uintptr_t root = m_Root;
93 if (address >= ARM64_DIRECT_MAP_BASE) {
94 root = m_KernelSpace.m_Root;
95 }
else if (
this == &m_KernelSpace) {
96 root =
reinterpret_cast<uintptr_t
>(&arm64_boot_low_l0);
98 uint64_t* table = tableAt(root);
99 for (
size_t level = 0; level < 3; ++level) {
100 uint64_t* entry = &table[tableIndex(address, level)];
101 if (!(*entry & Valid) || !(*entry & Table)) {
105 physical_uintptr_t child = Arm64PhysicalMemoryManager::instance().tryAllocatePage();
109 uint64_t* childTable = tableAt(child);
110 if (*entry & Valid) {
112 const size_t shift = 39 - level * 9;
113 const uint64_t subSize = 1ULL << (shift - 9);
114 const uint64_t base = *entry & ~(subSize * 512 - 1);
115 const uint64_t attributes = *entry & ~AddressMask;
116 for (
size_t i = 0; i < 512; ++i) {
117 childTable[i] = (base + i * subSize) | attributes | Valid;
120 memset(childTable, 0, PAGE_SIZE);
122 *entry = child | Valid | Table;
127 table = tableAt(*entry & AddressMask);
129 return &table[tableIndex(address, 3)];
132uint64_t Arm64VirtualAddressSpace::pageDescriptor(physical_uintptr_t physical,
size_t flags) {
134 uint64_t entry = (physical & AddressMask) | Valid | Table | Shareable | AccessFlag;
148 if (kernel || !(flags &
Execute)) {
149 entry |= UserExecuteNever;
151 if (!kernel || !(flags &
Execute)) {
152 entry |= PrivilegedExecuteNever;
155 entry = (entry & ~Valid) | SoftwareNoAccess;
158 entry |= SoftwareCopyOnWrite;
161 entry |= SoftwareShared;
164 entry |= SoftwareBorrowed;
167 entry |= SoftwareWriteProtected;
170 entry = (entry & ~Valid) | SoftwareSwapped;
175size_t Arm64VirtualAddressSpace::descriptorFlags(uint64_t entry) {
177 if (!(entry & (1ULL << 6))) {
180 if (!(entry & (1ULL << 7))) {
183 if ((entry & (1ULL << 6)) ? !(entry & UserExecuteNever) : !(entry & PrivilegedExecuteNever)) {
186 if (!(entry & (1ULL << 2))) {
189 if (entry & SoftwareNoAccess) {
192 if (entry & SoftwareCopyOnWrite) {
195 if (entry & SoftwareShared) {
198 if (entry & SoftwareBorrowed) {
201 if (entry & SoftwareWriteProtected) {
204 if (entry & SoftwareSwapped) {
207 if ((entry & (1ULL << 6)) && !(entry & NotGlobal)) {
214 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
217 uint64_t* entry = owner.findExistingEntry(value);
218 return entry && *entry;
222 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
223 if (!
isAddressValid(address) || (value & (PAGE_SIZE - 1)) || (physical & (PAGE_SIZE - 1))) {
228 uint64_t* old = owner.findExistingEntry(value);
229 if (old && *old && (*old & Valid) && value >= ARM64_DIRECT_MAP_BASE) {
232 uint64_t* entry = owner.findEntry(value,
true);
233 if (!entry || (*entry && !(descriptorFlags(*entry) &
KernelMode))) {
236 *entry = pageDescriptor(physical, flags);
241bool Arm64VirtualAddressSpace::tryMapUserPage(physical_uintptr_t physical,
void* address,
242 size_t flags,
size_t* committedTablePages) {
243 if (flags &
KernelMode ||
reinterpret_cast<uintptr_t
>(address) >= ARM64_DIRECT_MAP_BASE) {
246 if (committedTablePages) {
247 *committedTablePages = 0;
249 return map(physical, address, flags);
254 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
258 uint64_t* entry = owner.findExistingEntry(value, &level);
259 if (!entry || !*entry || (level != 3 && !(*entry & Valid))) {
262 const uint64_t mask = (1ULL << (39 - level * 9)) - 1;
263 physical = (*entry & AddressMask & ~mask) | (
reinterpret_cast<uintptr_t
>(address) & mask);
264 flags = descriptorFlags(*entry);
269 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
272 uint64_t* entry = owner.findEntry(value,
false);
273 if (!entry || !*entry) {
276 *entry = pageDescriptor(*entry & AddressMask, flags);
283 panic(
"ARM64: setFlags on absent mapping");
288 size_t& flags,
size_t requiredFlags) {
289 const uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
292 uint64_t* entry = owner.findEntry(value,
false);
293 if (!entry || !*entry) {
296 flags = descriptorFlags(*entry);
297 if ((flags & requiredFlags) != requiredFlags) {
300 physical = *entry & AddressMask;
306bool Arm64VirtualAddressSpace::tryDetachUserPage(
void* address, physical_uintptr_t expected) {
307 physical_uintptr_t physical = 0;
316 physical_uintptr_t physical = 0;
319 panic(
"ARM64: unmap on absent mapping");
324 physical_uintptr_t old = 0;
330 physical_uintptr_t fresh = Arm64PhysicalMemoryManager::instance().tryAllocatePage();
334 memcpy(
reinterpret_cast<void*
>(ARM64_DIRECT_MAP_BASE + fresh),
335 reinterpret_cast<const void*
>(ARM64_DIRECT_MAP_BASE + old), PAGE_SIZE);
336 physical_uintptr_t detached = 0;
337 size_t detachedFlags = 0;
338 if (!
detachMapping(address, detached, detachedFlags) || detached != old ||
340 Arm64PhysicalMemoryManager::instance().
freePage(fresh);
343 Arm64PhysicalMemoryManager::instance().
freePage(old);
348 return allocateStack(
this == &m_KernelSpace ? KERNEL_STACK_SIZE : USERSPACE_VIRTUAL_STACK_SIZE);
355 bytes = (bytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
359 if (m_StackTop < bytes + PAGE_SIZE) {
363 m_StackTop -= bytes + PAGE_SIZE;
366 for (
size_t offset = PAGE_SIZE; offset <= bytes; offset += PAGE_SIZE) {
367 physical_uintptr_t physical = Arm64PhysicalMemoryManager::instance().tryAllocatePage();
368 if (!physical || !
map(physical,
reinterpret_cast<void*
>(top - offset), flags)) {
370 Arm64PhysicalMemoryManager::instance().
freePage(physical);
372 for (
size_t mapped = PAGE_SIZE; mapped < offset; mapped += PAGE_SIZE) {
373 void* page =
reinterpret_cast<void*
>(top - mapped);
374 physical_uintptr_t old = 0;
377 Arm64PhysicalMemoryManager::instance().
freePage(old);
383 return new Stack(
reinterpret_cast<void*
>(top), bytes);
390 const uintptr_t base =
reinterpret_cast<uintptr_t
>(stack->getBase());
391 for (
size_t offset = 0; offset < stack->getSize(); offset += PAGE_SIZE) {
392 void* page =
reinterpret_cast<void*
>(base + offset);
393 physical_uintptr_t physical = 0;
396 Arm64PhysicalMemoryManager::instance().
freePage(physical);
402bool Arm64VirtualAddressSpace::cloneTable(uint64_t* destination,
const uint64_t* source,
403 size_t level,
bool copyOnWrite) {
404 for (
size_t i = 0; i < 512; ++i) {
405 const uint64_t entry = source[i];
409 if (level < 3 && (entry & Valid) && (entry & Table)) {
410 physical_uintptr_t child = Arm64PhysicalMemoryManager::instance().tryAllocatePage();
414 memset(tableAt(child), 0, PAGE_SIZE);
415 destination[i] = child | Valid | Table;
416 if (!cloneTable(tableAt(child), tableAt(entry & AddressMask), level + 1, copyOnWrite)) {
419 }
else if (level == 3 && (entry & (1ULL << 6)) && (entry & Valid)) {
420 const physical_uintptr_t old = entry & AddressMask;
421 if (copyOnWrite && !(entry & SoftwareShared)) {
422 physical_uintptr_t fresh = Arm64PhysicalMemoryManager::instance().tryAllocatePage();
426 memcpy(
reinterpret_cast<void*
>(ARM64_DIRECT_MAP_BASE + fresh),
427 reinterpret_cast<const void*
>(ARM64_DIRECT_MAP_BASE + old), PAGE_SIZE);
428 destination[i] = ((entry & ~AddressMask) & ~SoftwareBorrowed) | fresh;
430 if (!(entry & SoftwareBorrowed)) {
431 Arm64PhysicalMemoryManager::instance().
pin(old);
433 destination[i] = entry;
436 destination[i] = entry;
442void Arm64VirtualAddressSpace::freeTable(uint64_t* table,
size_t level,
bool freeLeaves) {
443 for (
size_t i = 0; i < 512; ++i) {
444 uint64_t entry = table[i];
448 if (level < 3 && (entry & Valid) && (entry & Table)) {
449 freeTable(tableAt(entry & AddressMask), level + 1, freeLeaves);
450 Arm64PhysicalMemoryManager::instance().
freePage(entry & AddressMask);
451 }
else if (level == 3 && freeLeaves && (entry & (1ULL << 6)) && (entry & Valid) &&
452 !(entry & (SoftwareBorrowed | SoftwareShared))) {
453 Arm64PhysicalMemoryManager::instance().
freePage(entry & AddressMask);
465 if (!cloneTable(tableAt(
clone->m_Root), tableAt(m_Root), 0, copyOnWrite)) {
471 clone->m_StackTop = m_StackTop;
476 if (
this == &m_KernelSpace) {
480 uint64_t* root = tableAt(m_Root);
481 freeTable(root, 0,
true);
482 m_Heap =
reinterpret_cast<void*
>(USERSPACE_VIRTUAL_HEAP);
484 m_StackTop = USERSPACE_VIRTUAL_STACK;
489 uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
490 return value >= KERNEL_VIRTUAL_HEAP && value < KERNEL_VIRTUAL_HEAP_END;
494 uintptr_t value =
reinterpret_cast<uintptr_t
>(address);
495 return value >=
reinterpret_cast<uintptr_t
>(
m_Heap) &&
496 value <
reinterpret_cast<uintptr_t
>(
m_HeapEnd);
500 return reinterpret_cast<void*
>(
this == &m_KernelSpace ? KERNEL_VIRTUAL_HEAP_END
501 : USERSPACE_DYNAMIC_END);
void pin(physical_uintptr_t page) override
void freePage(physical_uintptr_t page) override
bool memIsInHeap(void *address) override
bool isMapped(void *address) override
void freeStack(Stack *stack) override
void unmap(void *address) override
void setFlags(void *address, size_t flags) override
Stack * allocateStack() override
bool getMapping(void *address, physical_uintptr_t &physical, size_t &flags) override
bool handleCopyOnWriteFault(void *address, bool userMode) override
void revertToKernelAddressSpace() override
VirtualAddressSpace * clone(bool copyOnWrite=true) override
bool trySetFlags(void *address, size_t flags) override
bool detachMapping(void *address, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0) override
bool map(physical_uintptr_t physical, void *address, size_t flags) override
bool isAddressValid(void *address) override
bool memIsInKernelHeap(void *address) override
void * getEndOfHeap() override
static void invalidate(void *pAddress)
static const size_t CopyOnWrite
static VirtualAddressSpace * create()
static const size_t CacheDisable
static const size_t Borrowed
static const size_t RuntimeMapping
static const size_t Shared
static const size_t KernelMode
static const size_t NoAccess
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
static const size_t WriteProtected
static const size_t Execute
static const size_t Swapped
void EXPORTED_PUBLIC panic(const char *msg) NORETURN