The Pedigree Project 0.1
armv7/VirtualAddressSpace.cc
1#include "VirtualAddressSpace.h"
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/panic.h"
4#include "pedigree/kernel/processor/Processor.h"
5
6#include <string.h>
7
8#include "PhysicalMemoryManager.h"
9
10extern "C" char armv7_boot_l1;
11
12namespace {
13constexpr uint32_t PageTable = 1;
14constexpr uint32_t Section = 2;
15constexpr uint32_t SmallPage = 2;
16constexpr uint32_t AddressMask = 0xfffff000;
17constexpr uint32_t TableMask = 0xfffffc00;
18
19uint32_t* tableAt(physical_uintptr_t physical) {
20 return reinterpret_cast<uint32_t*>(ARMV7_DIRECT_MAP_BASE + physical);
21}
22
23uint32_t* shadowAt(uint32_t* table) {
24 return table + 256;
25}
26
27} // namespace
28
29Armv7VirtualAddressSpace Armv7VirtualAddressSpace::m_KernelSpace(true);
30
32 return Armv7VirtualAddressSpace::m_KernelSpace;
33}
34
36 auto* space = new Armv7VirtualAddressSpace(false);
37 if (!space || !space->root()) {
38 delete space;
39 return nullptr;
40 }
41 return space;
42}
43
44Armv7VirtualAddressSpace::Armv7VirtualAddressSpace(bool kernel)
46 reinterpret_cast<void*>(kernel ? KERNEL_VIRTUAL_HEAP : USERSPACE_VIRTUAL_HEAP)),
47 m_Root(kernel ? reinterpret_cast<uintptr_t>(&armv7_boot_l1)
48 : Armv7PhysicalMemoryManager::instance().allocateAlignedPages(2)),
49 m_StackTop(kernel ? KERNEL_VIRTUAL_STACK : USERSPACE_VIRTUAL_STACK) {
50 if (!kernel && m_Root) {
51 memset(tableAt(m_Root), 0, 2 * PAGE_SIZE);
52 }
53}
54
55Armv7VirtualAddressSpace::~Armv7VirtualAddressSpace() {
56 if (this != &m_KernelSpace && m_Root) {
57 freeUserTables();
58 Armv7PhysicalMemoryManager::instance().freePage(m_Root);
59 Armv7PhysicalMemoryManager::instance().freePage(m_Root + PAGE_SIZE);
60 }
61}
62
64 return true;
65}
66
67uint32_t* Armv7VirtualAddressSpace::findExistingEntry(uintptr_t address, bool* isSection) const {
68 const physical_uintptr_t root = address >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace.m_Root : m_Root;
69 uint32_t* first = &tableAt(root)[address >> 20];
70 if (isSection) {
71 *isSection = (*first & 3) == Section;
72 }
73 if ((*first & 3) != PageTable) {
74 return first;
75 }
76 return &tableAt(*first & TableMask)[(address >> 12) & 255];
77}
78
79uint32_t* Armv7VirtualAddressSpace::findEntry(uintptr_t address, bool create, size_t* newTables) {
80 const physical_uintptr_t root = address >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace.m_Root : m_Root;
81 uint32_t* first = &tableAt(root)[address >> 20];
82 if (!*first && create) {
83 physical_uintptr_t page = Armv7PhysicalMemoryManager::instance().tryAllocatePage();
84 if (!page) {
85 return nullptr;
86 }
87 memset(tableAt(page), 0, PAGE_SIZE);
88 *first = page | PageTable;
89 if (newTables) {
90 ++*newTables;
91 }
92 }
93 if ((*first & 3) != PageTable) {
94 return nullptr;
95 }
96 return &tableAt(*first & TableMask)[(address >> 12) & 255];
97}
98
99uint32_t Armv7VirtualAddressSpace::pageDescriptor(physical_uintptr_t physical, size_t flags) {
100 const bool kernel = flags & KernelMode;
101 uint32_t entry = (physical & AddressMask) | SmallPage | (1U << 4);
102 if (!kernel) {
103 entry |= 1U << 5;
104 entry |= 1U << 11;
105 }
106 if (!(flags & Write) || (flags & (CopyOnWrite | WriteProtected))) {
107 entry |= 1U << 9;
108 }
109 if (!(flags & Execute)) {
110 entry |= 1U;
111 }
112 if (!(flags & CacheDisable)) {
113 entry |= 1U << 6; // Normal, uncached memory while the bootstrap leaves caches disabled.
114 }
115 if (flags & (NoAccess | Swapped)) {
116 entry &= ~3U;
117 }
118 return entry;
119}
120
122 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
123 Armv7VirtualAddressSpace& owner = value >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace : *this;
124 LockGuard<Spinlock> guard(owner.m_Lock);
125 bool section = false;
126 uint32_t* entry = owner.findExistingEntry(value, &section);
127 if (!entry || !*entry) {
128 return false;
129 }
130 if (section) {
131 return true;
132 }
133 const unsigned int index = (value >> 12) & 255;
134 return !(shadowAt(entry - index)[index] & Swapped);
135}
136
137bool Armv7VirtualAddressSpace::map(physical_uintptr_t physical, void* address, size_t flags) {
138 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
139 if ((value & (PAGE_SIZE - 1)) || (physical & (PAGE_SIZE - 1)) ||
140 (value < ARMV7_DIRECT_MAP_BASE && (flags & KernelMode))) {
141 return false;
142 }
143 Armv7VirtualAddressSpace& owner = value >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace : *this;
144 LockGuard<Spinlock> guard(owner.m_Lock);
145 uint32_t* entry = owner.findEntry(value, true);
146 if (!entry || *entry) {
147 return false;
148 }
149 shadowAt(entry - ((value >> 12) & 255))[(value >> 12) & 255] = flags;
150 *entry = pageDescriptor(physical, flags);
151 Processor::invalidate(address);
152 return true;
153}
154
155bool Armv7VirtualAddressSpace::tryMapUserPage(physical_uintptr_t physical, void* address,
156 size_t flags, size_t* committedTablePages) {
157 if (committedTablePages) {
158 *committedTablePages = 0;
159 }
160 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
161 if ((flags & KernelMode) || value >= ARMV7_DIRECT_MAP_BASE || (value & (PAGE_SIZE - 1)) ||
162 (physical & (PAGE_SIZE - 1))) {
163 return false;
164 }
165 LockGuard<Spinlock> guard(m_Lock);
166 size_t newTables = 0;
167 uint32_t* entry = findEntry(value, true, &newTables);
168 if (!entry || *entry) {
169 return false;
170 }
171 const unsigned int index = (value >> 12) & 255;
172 shadowAt(entry - index)[index] = flags;
173 *entry = pageDescriptor(physical, flags);
174 Processor::invalidate(address);
175 if (committedTablePages) {
176 *committedTablePages = newTables;
177 }
178 return true;
179}
180
181bool Armv7VirtualAddressSpace::getMapping(void* address, physical_uintptr_t& physical,
182 size_t& flags) {
183 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
184 Armv7VirtualAddressSpace& owner = value >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace : *this;
185 LockGuard<Spinlock> guard(owner.m_Lock);
186 bool section = false;
187 uint32_t* entry = owner.findExistingEntry(value, &section);
188 if (!entry || !*entry) {
189 return false;
190 }
191 if (section) {
192 physical = (*entry & 0xfff00000) | (value & 0xfffff);
193 flags = KernelMode | Write | ((*entry & (1U << 4)) ? 0 : Execute);
194 } else {
195 uint32_t* table = entry - ((value >> 12) & 255);
196 physical = (*entry & AddressMask) | (value & (PAGE_SIZE - 1));
197 flags = shadowAt(table)[(value >> 12) & 255];
198 }
199 return true;
200}
201
202bool Armv7VirtualAddressSpace::trySetFlags(void* address, size_t flags) {
203 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
204 Armv7VirtualAddressSpace& owner = value >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace : *this;
205 LockGuard<Spinlock> guard(owner.m_Lock);
206 uint32_t* entry = owner.findEntry(value, false);
207 if (!entry || !*entry) {
208 return false;
209 }
210 const unsigned int index = (value >> 12) & 255;
211 shadowAt(entry - index)[index] = flags;
212 *entry = pageDescriptor(*entry & AddressMask, flags);
213 Processor::invalidate(address);
214 return true;
215}
216
217void Armv7VirtualAddressSpace::setFlags(void* address, size_t flags) {
218 if (!trySetFlags(address, flags)) {
219 panic("ARMv7: setFlags on absent mapping");
220 }
221}
222
223bool Armv7VirtualAddressSpace::detachMapping(void* address, physical_uintptr_t& physical,
224 size_t& flags, size_t requiredFlags) {
225 const uintptr_t value = reinterpret_cast<uintptr_t>(address);
226 Armv7VirtualAddressSpace& owner = value >= ARMV7_DIRECT_MAP_BASE ? m_KernelSpace : *this;
227 LockGuard<Spinlock> guard(owner.m_Lock);
228 uint32_t* entry = owner.findEntry(value, false);
229 if (!entry || !*entry) {
230 return false;
231 }
232 const unsigned int index = (value >> 12) & 255;
233 uint32_t* shadow = shadowAt(entry - index);
234 flags = shadow[index];
235 if ((flags & requiredFlags) != requiredFlags) {
236 return false;
237 }
238 physical = *entry & AddressMask;
239 *entry = 0;
240 shadow[index] = 0;
241 Processor::invalidate(address);
242 return true;
243}
244
245bool Armv7VirtualAddressSpace::tryDetachUserPage(void* address, physical_uintptr_t expected) {
246 physical_uintptr_t physical = 0;
247 size_t flags = 0;
248 if (!getMapping(address, physical, flags) || physical != expected || (flags & KernelMode)) {
249 return false;
250 }
251 return detachMapping(address, physical, flags);
252}
253
255 physical_uintptr_t physical = 0;
256 size_t flags = 0;
257 if (!detachMapping(address, physical, flags)) {
258 panic("ARMv7: unmap on absent mapping");
259 }
260}
261
262bool Armv7VirtualAddressSpace::handleCopyOnWriteFault(void* address, bool userMode) {
263 physical_uintptr_t old = 0;
264 size_t flags = 0;
265 if (!getMapping(address, old, flags) || !(flags & CopyOnWrite) ||
266 (flags & (NoAccess | WriteProtected | Swapped)) || (userMode && (flags & KernelMode))) {
267 return false;
268 }
269 physical_uintptr_t fresh = Armv7PhysicalMemoryManager::instance().tryAllocatePage();
270 if (!fresh) {
271 return false;
272 }
273 memcpy(reinterpret_cast<void*>(ARMV7_DIRECT_MAP_BASE + fresh),
274 reinterpret_cast<const void*>(ARMV7_DIRECT_MAP_BASE + old), PAGE_SIZE);
275 physical_uintptr_t detached = 0;
276 size_t detachedFlags = 0;
277 if (!detachMapping(address, detached, detachedFlags) || detached != old ||
278 !map(fresh, address, (flags | Write) & ~(CopyOnWrite | Borrowed | Shared))) {
279 Armv7PhysicalMemoryManager::instance().freePage(fresh);
280 return false;
281 }
282 Armv7PhysicalMemoryManager::instance().freePage(old);
283 return true;
284}
285
287 return allocateStack(this == &m_KernelSpace ? KERNEL_STACK_SIZE : USERSPACE_VIRTUAL_STACK_SIZE);
288}
289
291 if (!bytes) {
292 return allocateStack();
293 }
294 if (bytes > SIZE_MAX - PAGE_SIZE + 1) {
295 return nullptr;
296 }
297 bytes = (bytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
298 uintptr_t top;
299 {
300 LockGuard<Spinlock> guard(m_Lock);
301 if (m_StackTop < bytes + PAGE_SIZE) {
302 return nullptr;
303 }
304 top = m_StackTop;
305 m_StackTop -= bytes + PAGE_SIZE;
306 }
307 size_t flags = Write | (this == &m_KernelSpace ? KernelMode : 0);
308 for (size_t offset = PAGE_SIZE; offset <= bytes; offset += PAGE_SIZE) {
309 physical_uintptr_t physical = Armv7PhysicalMemoryManager::instance().tryAllocatePage();
310 if (!physical || !map(physical, reinterpret_cast<void*>(top - offset), flags)) {
311 if (physical) {
312 Armv7PhysicalMemoryManager::instance().freePage(physical);
313 }
314 for (size_t mapped = PAGE_SIZE; mapped < offset; mapped += PAGE_SIZE) {
315 void* page = reinterpret_cast<void*>(top - mapped);
316 physical_uintptr_t old = 0;
317 size_t oldFlags = 0;
318 if (detachMapping(page, old, oldFlags)) {
319 Armv7PhysicalMemoryManager::instance().freePage(old);
320 }
321 }
322 return nullptr;
323 }
324 }
325 return new Stack(reinterpret_cast<void*>(top), bytes);
326}
327
329 if (!stack) {
330 return;
331 }
332 const uintptr_t base = reinterpret_cast<uintptr_t>(stack->getBase());
333 for (size_t offset = 0; offset < stack->getSize(); offset += PAGE_SIZE) {
334 void* page = reinterpret_cast<void*>(base + offset);
335 physical_uintptr_t physical = 0;
336 size_t flags = 0;
337 if (detachMapping(page, physical, flags)) {
338 Armv7PhysicalMemoryManager::instance().freePage(physical);
339 }
340 }
341 delete stack;
342}
343
344bool Armv7VirtualAddressSpace::cloneUserTables(Armv7VirtualAddressSpace& destination,
345 bool copyOnWrite) {
346 uint32_t* sourceRoot = tableAt(m_Root);
347 uint32_t* targetRoot = tableAt(destination.m_Root);
348 for (size_t first = 0; first < 2048; ++first) {
349 const uint32_t entry = sourceRoot[first];
350 if (!entry) {
351 continue;
352 }
353 if ((entry & 3) != PageTable) {
354 return false;
355 }
356 physical_uintptr_t child = Armv7PhysicalMemoryManager::instance().tryAllocatePage();
357 if (!child) {
358 return false;
359 }
360 uint32_t* source = tableAt(entry & TableMask);
361 uint32_t* target = tableAt(child);
362 memset(target, 0, PAGE_SIZE);
363 targetRoot[first] = child | PageTable;
364 for (size_t second = 0; second < 256; ++second) {
365 if (!source[second]) {
366 continue;
367 }
368 const physical_uintptr_t old = source[second] & AddressMask;
369 const size_t flags = shadowAt(source)[second];
370 if (copyOnWrite && !(flags & (Shared | Borrowed))) {
371 physical_uintptr_t fresh = Armv7PhysicalMemoryManager::instance().tryAllocatePage();
372 if (!fresh) {
373 return false;
374 }
375 memcpy(reinterpret_cast<void*>(ARMV7_DIRECT_MAP_BASE + fresh),
376 reinterpret_cast<const void*>(ARMV7_DIRECT_MAP_BASE + old), PAGE_SIZE);
377 target[second] = pageDescriptor(fresh, flags);
378 shadowAt(target)[second] = flags;
379 } else {
380 if (!(flags & Borrowed)) {
381 Armv7PhysicalMemoryManager::instance().pin(old);
382 }
383 target[second] = source[second];
384 shadowAt(target)[second] = flags;
385 }
386 }
387 }
388 return true;
389}
390
391void Armv7VirtualAddressSpace::freeUserTables() {
392 uint32_t* root = tableAt(m_Root);
393 for (size_t first = 0; first < 2048; ++first) {
394 const uint32_t entry = root[first];
395 if ((entry & 3) != PageTable) {
396 continue;
397 }
398 uint32_t* table = tableAt(entry & TableMask);
399 for (size_t second = 0; second < 256; ++second) {
400 if (table[second] && !(shadowAt(table)[second] & Borrowed)) {
401 Armv7PhysicalMemoryManager::instance().freePage(table[second] & AddressMask);
402 }
403 }
404 root[first] = 0;
405 Armv7PhysicalMemoryManager::instance().freePage(entry & TableMask);
406 }
407}
408
410 auto* clone = new Armv7VirtualAddressSpace(false);
411 if (!clone || !clone->m_Root) {
412 delete clone;
413 return nullptr;
414 }
415 LockGuard<Spinlock> guard(m_Lock);
416 // The bootstrap's low identity mappings are sections, not userspace pages.
417 if (this != &m_KernelSpace && !cloneUserTables(*clone, copyOnWrite)) {
418 delete clone;
419 return nullptr;
420 }
423 clone->m_StackTop = m_StackTop;
424 return clone;
425}
426
428 if (this == &m_KernelSpace) {
429 return;
430 }
431 LockGuard<Spinlock> guard(m_Lock);
432 freeUserTables();
433 m_Heap = reinterpret_cast<void*>(USERSPACE_VIRTUAL_HEAP);
435 m_StackTop = USERSPACE_VIRTUAL_STACK;
436 Processor::invalidate(nullptr);
437}
438
440 uintptr_t value = reinterpret_cast<uintptr_t>(address);
441 return value >= KERNEL_VIRTUAL_HEAP && value < KERNEL_VIRTUAL_HEAP_END;
442}
443
445 uintptr_t value = reinterpret_cast<uintptr_t>(address);
446 return value >= reinterpret_cast<uintptr_t>(m_Heap) &&
447 value < reinterpret_cast<uintptr_t>(m_HeapEnd);
448}
449
451 return reinterpret_cast<void*>(this == &m_KernelSpace ? KERNEL_VIRTUAL_HEAP_END
452 : USERSPACE_DYNAMIC_END);
453}
void freePage(physical_uintptr_t page) override
void pin(physical_uintptr_t page) override
bool detachMapping(void *address, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0) override
void unmap(void *address) override
bool memIsInKernelHeap(void *address) override
void setFlags(void *address, size_t flags) override
bool getMapping(void *address, physical_uintptr_t &physical, size_t &flags) override
VirtualAddressSpace * clone(bool copyOnWrite=true) override
bool trySetFlags(void *address, size_t flags) override
bool isMapped(void *address) override
bool map(physical_uintptr_t physical, void *address, size_t flags) override
bool memIsInHeap(void *address) override
bool handleCopyOnWriteFault(void *address, bool userMode) override
void freeStack(Stack *stack) override
bool isAddressValid(void *address) override
static void invalidate(void *pAddress)
static VirtualAddressSpace * create()
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118