The Pedigree Project 0.1
MemoryMappedFile.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "MemoryMappedFile.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Spinlock.h"
24#include "pedigree/kernel/Subsystem.h"
25#include "pedigree/kernel/errors.h"
26#include "pedigree/kernel/process/MemoryPressureManager.h"
27#include "pedigree/kernel/process/Process.h"
28#include "pedigree/kernel/process/TerminationDeferral.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/process/Uninterruptible.h"
31#include "pedigree/kernel/processor/MemoryRegion.h"
32#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
33#include "pedigree/kernel/processor/Processor.h"
34#include "pedigree/kernel/processor/ProcessorInformation.h"
35#include "pedigree/kernel/processor/VirtualAddressSpace.h"
36#include "pedigree/kernel/processor/state.h"
37#include "pedigree/kernel/utilities/assert.h"
38#include "pedigree/kernel/utilities/utility.h"
39
40#include "File.h"
41#include "MountView.h"
42#include "VFS.h"
43
45
46physical_uintptr_t AnonymousMemoryMap::m_Zero = 0;
47
48#ifdef DEBUG_MMOBJECTS
49static constexpr bool DebugMemoryMappings = true;
50#else
51static constexpr bool DebugMemoryMappings = false;
52#endif
53
54namespace {
55class AddressSpaceRestorer {
56 public:
57 explicit AddressSpaceRestorer(VirtualAddressSpace& addressSpace) : m_AddressSpace(addressSpace) {}
58
59 ~AddressSpaceRestorer() {
60 Processor::switchAddressSpace(m_AddressSpace);
61 }
62
63 private:
64 NOT_COPYABLE_OR_ASSIGNABLE(AddressSpaceRestorer);
65
66 VirtualAddressSpace& m_AddressSpace;
67};
68
69class TemporaryPhysicalMapping {
70 public:
71 TemporaryPhysicalMapping(physical_uintptr_t page, const char* name)
72 : m_Region(name), m_Mapped(false) {
74 if (!memory.allocateRegion(
77 return;
78 }
79
81 m_Mapped = kernelSpace.map(page, m_Region.virtualAddress(),
83 if (!m_Mapped) {
84 m_Region.free();
85 }
86 }
87
88 ~TemporaryPhysicalMapping() {
89 if (m_Mapped) {
90 // A virtual-only MemoryRegion owns pages left mapped inside it. Remove
91 // this alias first so releasing the reservation does not free the page.
92 VirtualAddressSpace::getKernelAddressSpace().unmap(m_Region.virtualAddress());
93 }
94 m_Region.free();
95 }
96
97 bool valid() const {
98 return m_Mapped;
99 }
100
101 void* address() const {
102 return m_Region.virtualAddress();
103 }
104
105 private:
106 NOT_COPYABLE_OR_ASSIGNABLE(TemporaryPhysicalMapping);
107
108 MemoryRegion m_Region;
109 bool m_Mapped;
110};
111
112size_t protectionFlags(size_t flags, MemoryMappedObject::Permissions permissions,
113 bool sharedWritable) {
116 if (permissions == MemoryMappedObject::None) {
118 }
119 if (permissions & MemoryMappedObject::Exec) {
121 }
122 if (!(permissions & MemoryMappedObject::Write)) {
124 } else if (!(flags & VirtualAddressSpace::CopyOnWrite) &&
125 (!(flags & VirtualAddressSpace::Shared) || sharedWritable)) {
127 }
128 return flags;
129}
130
131void* currentOperationOwner() {
132 ProcessorInformation& information = Processor::information();
133 Thread* thread = information.getCurrentThread();
134 return thread ? static_cast<void*>(thread) : static_cast<void*>(&information);
135}
136} // namespace
137
138MemoryMappedObject::~MemoryMappedObject() {}
139
140AnonymousMemoryMap::AnonymousMemoryMap(uintptr_t address, size_t length,
142 : MemoryMappedObject(address, true, length, perms), m_Mappings() {
144
145 if (m_Zero == 0) {
147 if (!m_Zero) {
148 FATAL("AnonymousMemoryMap: could not allocate the shared zero page");
149 return;
150 }
151
152 TemporaryPhysicalMapping temporary(m_Zero, "Anonymous Shared Zero Page");
153 if (!temporary.valid()) {
155 m_Zero = 0;
156 FATAL("AnonymousMemoryMap: could not map the shared zero page");
157 return;
158 }
159
160 ByteSet(temporary.address(), 0, PhysicalMemoryManager::getPageSize());
162 }
163}
164
165bool AnonymousMemoryMap::initialisePhysicalPage(physical_uintptr_t physical) {
166#if X64 || HOSTED
167 return SwapStore::instance().zeroPage(physical);
168#else
169 TemporaryPhysicalMapping temporary(physical, "Anonymous Page Initialisation");
170 if (!temporary.valid())
171 return false;
172 ByteSet(temporary.address(), 0, PhysicalMemoryManager::getPageSize());
173 return true;
174#endif
175}
176
177MemoryMappedFile::MemoryMappedFile(uintptr_t address, size_t length, size_t offset, File* backing,
178 bool bCopyOnWrite, MemoryMappedObject::Permissions perms,
180 const SharedPointer<MappingAttachment>& attachment,
181 const FileMappingOrigin& origin, bool executableUse)
182 : MemoryMappedObject(address, bCopyOnWrite, length, perms, maximumPerms),
183 m_pBacking(backing),
184 m_Offset(offset),
185 m_Mappings(),
186 m_Origin(origin),
187 m_Lock(),
188 m_bVfsLease(backing && VFS::instance().retainTrackedFile(backing)),
189 m_ExecutableUse(executableUse || (bCopyOnWrite && (perms & Exec))),
190 m_SharedWriteUse(!bCopyOnWrite && (maximumPerms & Write)),
191 m_UseAdmitted(backing && backing->acquireMappingUse(m_ExecutableUse, m_SharedWriteUse)),
192 m_MountAdmitted(!m_SharedWriteUse || !backing->supportsRegularFileOperations() ||
193 backing->isBlockDevice() || !m_Origin.openingPath || m_Origin.writeLease ||
194 VfsMountView::retainWrite(m_Origin.openingPath, m_Origin.writeLease)) {
195 assert(m_pBacking);
196 m_Attachment = attachment;
197}
198
199MemoryMappedFile::~MemoryMappedFile() {
200 TerminationDeferral lifetime;
201 if (m_OwnsMappings)
202 unmap();
203 if (m_UseAdmitted)
204 m_pBacking->releaseMappingUse(m_ExecutableUse, m_SharedWriteUse);
205 if (m_bVfsLease) {
206 m_bVfsLease = false;
208 }
209 m_Origin.openingPath.reset();
210 m_Origin.writeLease.reset();
211}
212
214 TerminationDeferral terminationDeferral;
216
217 MemoryMappedFile* pResult =
219 m_MaximumPermissions, m_Attachment, m_Origin, m_ExecutableUse);
220 assert(pResult->m_UseAdmitted);
221 pResult->m_OwnerProcess = m_OwnerProcess;
222 pResult->m_Mappings = m_Mappings;
223
224 for (auto it = m_Mappings.begin(); it != m_Mappings.end(); ++it) {
225 // Bump reference count on backing file page if needed.
226 size_t fileOffset = (it.key() - m_Address) + m_Offset;
227 if (it.value() == ~0UL)
228 m_pBacking->getPhysicalPage(fileOffset);
229 }
230
231 return pResult;
232}
233
235 TerminationDeferral terminationDeferral;
237
238 size_t pageSz = PhysicalMemoryManager::getPageSize();
239
240 if (at < m_Address || at >= (m_Address + m_Length)) {
241 ERROR("MemoryMappedFile::split() given bad at parameter (at="
242 << at << ", address=" << m_Address << ", end=" << (m_Address + m_Length) << ")");
243 return 0;
244 }
245
246 if (at == m_Address) {
247 ERROR("MemoryMappedFile::split() misused, at == base address");
248 return 0;
249 }
250
251 uintptr_t oldEnd = m_Address + m_Length;
252
253 // Change our own object to fit in the new region.
254 size_t oldLength = m_Length;
255 m_Length = at - m_Address;
256
257 // New object.
258 MemoryMappedFile* pResult = new MemoryMappedFile(
260 m_MaximumPermissions, m_Attachment, m_Origin, m_ExecutableUse);
261 assert(pResult->m_UseAdmitted);
262
263 pResult->m_OwnerProcess = m_OwnerProcess;
264 pResult->m_LockMode = m_LockMode;
265
266 // Fix up mapping metadata.
267 for (uintptr_t virt = at; virt < oldEnd; virt += pageSz) {
268 if (m_Mappings.contains(virt)) {
269 physical_uintptr_t old = getMapping(virt);
270 untrackMapping(virt);
271 pResult->trackMapping(virt, old);
272 }
273 }
274
275 return pResult;
276}
277
278bool MemoryMappedFile::remove(size_t length) {
279 TerminationDeferral terminationDeferral;
281
282 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
283 size_t pageSz = PhysicalMemoryManager::getPageSize();
284
285 if (length & (pageSz - 1)) {
286 length += pageSz;
287 length &= ~(pageSz - 1);
288 }
289
290 if (length >= m_Length) {
291 unmapUnlocked();
292 return true;
293 }
294
295 uintptr_t oldStart = m_Address;
296 size_t oldOffset = m_Offset;
297
298 m_Address += length;
299 m_Offset += length;
300 m_Length -= length;
301
302 // Remove any existing mappings in this range.
303 for (uintptr_t virt = oldStart; virt < m_Address; virt += pageSz) {
304 void* v = reinterpret_cast<void*>(virt);
305 if (va.isMapped(v)) {
306 size_t flags;
307 physical_uintptr_t phys;
308
309 va.getMapping(v, phys, flags);
310 va.unmap(v);
311
312 physical_uintptr_t p = getMapping(virt);
313 if (p == ~0UL) {
314 size_t fileOffset = (virt - oldStart) + oldOffset;
315
316 if (!m_bCopyOnWrite) {
317 m_pBacking->syncAndReturnPhysicalPage(fileOffset, true);
318 } else {
319 m_pBacking->returnPhysicalPage(fileOffset);
320 }
321 } else
323 }
324
325 untrackMapping(virt);
326 }
327
328 return false;
329}
330
332 TerminationDeferral terminationDeferral;
333 if (m_bCopyOnWrite && (perms & Exec) && !m_ExecutableUse) {
334 // The manager preflights every backing while holding its operation gate,
335 // so no shared-write mapping can appear before this permission commit.
336 const bool admitted = m_pBacking->acquireMappingUse(true, false);
337 assert(admitted);
338 m_ExecutableUse = true;
339 }
341 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
342 for (auto it = m_Mappings.begin(); it != m_Mappings.end(); ++it) {
343 void* address = reinterpret_cast<void*>(it.key());
344 if (va.isMapped(address)) {
345 physical_uintptr_t physical;
346 size_t flags;
347 va.getMapping(address, physical, flags);
348 const size_t newFlags = protectionFlags(flags, perms, !m_bCopyOnWrite);
349 if (!m_bCopyOnWrite && it.value() == ~0UL && (newFlags & VirtualAddressSpace::Write) &&
350 !(flags & VirtualAddressSpace::Write)) {
352 }
353 va.setFlags(address, newFlags);
354 }
355 }
356 m_Permissions = perms;
357}
358
359bool MemoryMappedFile::preparePermissions(uintptr_t base, size_t length, Permissions perms) {
360 if (m_bCopyOnWrite || !(perms & Write) || (m_Permissions & Write)) {
361 return true;
362 }
364 const size_t pageSize = PhysicalMemoryManager::getPageSize();
365 for (auto it = m_Mappings.begin(); it != m_Mappings.end(); ++it) {
366 if (it.key() >= base && it.key() - base < length && it.value() == ~0UL &&
367 !m_pBacking->prepareSharedMapping(m_Offset + (it.key() - m_Address), pageSize)) {
368 return false;
369 }
370 }
371 return true;
372}
373
374bool MemoryMappedFile::sharedBacking(uintptr_t at, uintptr_t& identity, size_t& offset) const {
375 if (m_bCopyOnWrite || at < m_Address || at - m_Address >= m_Length) {
376 return false;
377 }
379 offset = m_Offset + (at - m_Address);
380 return true;
381}
382
383bool MemoryMappedFile::usesBacking(uintptr_t identity) const {
384 return m_pBacking->futexIdentity() == identity;
385}
386
387bool MemoryMappedFile::beyondBackingEnd(uintptr_t at) const {
388 const size_t pageSize = PhysicalMemoryManager::getPageSize();
389 const uintptr_t page = at & ~(pageSize - 1);
390 const size_t displacement = page - m_Address;
391 return displacement > ~static_cast<size_t>(0) - m_Offset ||
392 m_Offset + displacement >= m_pBacking->getSize();
393}
394
395void MemoryMappedFile::discardFilePages(VirtualAddressSpace& space, size_t end) {
397 const size_t pageSize = PhysicalMemoryManager::getPageSize();
398 const size_t firstDiscardedPage = end / pageSize + (end % pageSize != 0);
399 uintptr_t cursor = m_Address;
400 uintptr_t address = 0;
401 physical_uintptr_t tracked = 0;
402 while (m_Mappings.lowerBound(cursor, address, tracked)) {
403 if (address < m_Address || address - m_Address >= m_Length) {
404 break;
405 }
406 // The copied key survives rotations when this entry is removed below.
407 cursor = address + 1;
408 const size_t displacement = address - m_Address;
409 const size_t fileOffset = m_Offset + displacement;
410 if (fileOffset / pageSize < firstDiscardedPage) {
411 continue;
412 }
413 const bool loan = tracked == ~0UL;
414 physical_uintptr_t physical = 0;
415 size_t flags = 0;
416 const bool detached = space.detachMapping(reinterpret_cast<void*>(address), physical, flags);
417 // CoW can replace the PTE without updating its original loan tracker.
418 if (loan || (detached && (flags & VirtualAddressSpace::Borrowed))) {
419 m_pBacking->returnPhysicalPage(fileOffset);
420 }
421 if (detached && !(flags & VirtualAddressSpace::Borrowed)) {
423 }
425 }
426}
427
428physical_uintptr_t MemoryMappedFile::getBackingPage(size_t fileOffset, size_t mappingBytes) {
429 File* pBacking = m_pBacking;
430 size_t pageSz = PhysicalMemoryManager::getPageSize();
431 const size_t fileSize = pBacking->getSize();
432 if (fileOffset >= fileSize) {
433 return ~0UL;
434 }
435 size_t expected = fileSize - fileOffset < pageSz ? fileSize - fileOffset : pageSz;
436 if (expected > mappingBytes)
437 expected = mappingBytes;
438
439 physical_uintptr_t phys = pBacking->getPhysicalPage(fileOffset);
440 if (phys == ~0UL) {
441 // Grow only when faults consume the preceding window. A jump starts small
442 // to avoid reading large unused portions of executables and random mappings.
443 m_ReadAheadPages =
444 fileOffset == m_ReadAheadEnd ? (m_ReadAheadPages < 32 ? m_ReadAheadPages * 2 : 32) : 4;
445 const size_t window =
446 mappingBytes < m_ReadAheadPages * pageSz ? mappingBytes : m_ReadAheadPages * pageSz;
447 auto* thread = Processor::information().getCurrentThread();
448 const size_t previousError = thread ? thread->getErrno() : 0;
449 const size_t actual = pBacking->populateRange(fileOffset, window);
450 m_ReadAheadEnd = fileOffset + actual;
451 if (actual < expected) {
452 ERROR("Short read of " << pBacking->getName() << " in getBackingPage() - wanted " << expected
453 << " bytes but got " << actual << " instead");
454 }
455 phys = pBacking->getPhysicalPage(fileOffset);
456 if (phys == ~0UL) {
457 ERROR(
458 "*** Could not manage to get a physical page for a "
459 "MemoryMappedFile ("
460 << pBacking->getName() << ") - read got " << actual << " bytes!");
461 } else if (thread) {
462 // Failure of a speculative neighbour must not change a successful fault.
463 thread->setErrno(previousError);
464 }
465 }
466
467 return phys;
468}
469
470bool MemoryMappedObject::syncRange(uintptr_t at, size_t length, bool async) {
471 const size_t pageSize = PhysicalMemoryManager::getPageSize();
472 bool succeeded = true;
473 for (size_t offset = 0; offset < length; offset += pageSize)
474 succeeded = sync(at + offset, async) && succeeded;
475 return succeeded;
476}
477
478bool MemoryMappedFile::syncRange(uintptr_t at, size_t length, bool async) {
479 TerminationDeferral terminationDeferral;
481 if (m_bCopyOnWrite || !length)
482 return true;
483 if (at < m_Address || at - m_Address >= m_Length)
484 return false;
485 const size_t available = m_Length - (at - m_Address);
486 if (length > available)
487 length = available;
488 const size_t pageSize = PhysicalMemoryManager::getPageSize();
489 uint64_t offsets[Cache::MaxWritebackPages];
490 size_t count = 0;
491 bool succeeded = true;
492 for (size_t offset = 0; offset < length; offset += pageSize) {
493 const uintptr_t address = at + offset;
494 if (getMapping(address) != ~0UL)
495 continue;
496 const size_t fileOffset = m_Offset + address - m_Address;
497 if (async) {
498 succeeded = m_pBacking->sync(fileOffset, true) && succeeded;
499 } else {
500 offsets[count++] = fileOffset;
501 if (count == Cache::MaxWritebackPages) {
502 succeeded = m_pBacking->syncPages(offsets, count) && succeeded;
503 count = 0;
504 }
505 }
506 }
507 if (count)
508 succeeded = m_pBacking->syncPages(offsets, count) && succeeded;
509 return succeeded;
510}
511
512bool MemoryMappedFile::sync(uintptr_t at, bool async) {
513 TerminationDeferral terminationDeferral;
515 if (!m_bCopyOnWrite && at >= m_Address && at - m_Address < m_Length && getMapping(at) == ~0UL) {
516 // Write permission can have been removed since the page was dirtied.
517 return m_pBacking->sync(m_Offset + (at - m_Address), async);
518 }
519 return true;
520}
521
523 // Shared mappings already alias the page cache. Invalidating must not discard
524 // private modifications, which never belong to the backing file.
525}
526
528 TerminationDeferral terminationDeferral;
530
531 unmapUnlocked();
532}
533
534bool MemoryMappedFile::trap(VirtualAddressSpace& va, uintptr_t address, bool bWrite,
535 PopulationStatus* population) {
536 if (population)
537 *population = PopulationStatus::NoMemory;
538 TerminationDeferral terminationDeferral;
540
541 EMIT_IF(DebugMemoryMappings) {
542 NOTICE("MemoryMappedFile::trap(" << address << ", " << bWrite << ")");
543 }
544
545 size_t pageSz = PhysicalMemoryManager::getPageSize();
546
547 // Page-align the trap address
548 address = address & ~(pageSz - 1);
549 size_t mappingOffset = (address - m_Address);
550 size_t fileOffset = m_Offset + mappingOffset;
551
552 if (beyondBackingEnd(address)) {
553 if (population)
554 *population = PopulationStatus::Inaccessible;
555 return false;
556 }
557
558 bool bWillEof = (mappingOffset + pageSz) > m_Length;
559 bool bShouldCopy = m_bCopyOnWrite && (bWillEof || bWrite);
560
561 // Skip out on a few things if we can.
562 if (bWrite && !(m_Permissions & Write)) {
563 EMIT_IF(DebugMemoryMappings) {
564 DEBUG_LOG(" -> ignoring, was a write and this is not a writable mapping.");
565 }
566 return false;
567 } else if ((!bWrite) && !(m_Permissions & Read) && !population) {
568 EMIT_IF(DebugMemoryMappings) {
569 DEBUG_LOG(" -> ignoring, was a read and this is not a readable mapping.");
570 }
571 return false;
572 }
573
574 EMIT_IF(DebugMemoryMappings) {
575 DEBUG_LOG(" -> mapping offset is " << mappingOffset << ", file offset: " << fileOffset);
576 DEBUG_LOG(" -> will eof: " << bWillEof << ", should copy: " << bShouldCopy);
577 }
578
579 // Add execute flag.
580 size_t extraFlags = 0;
581 if (m_Permissions & Exec)
582 extraFlags |= VirtualAddressSpace::Execute;
583
584 if (!bShouldCopy) {
585 if (!m_bCopyOnWrite && (m_Permissions & Write) &&
586 !m_pBacking->prepareSharedMapping(fileOffset, pageSz)) {
587 if (population) {
588 auto* thread = Processor::information().getCurrentThread();
589 *population = thread && thread->getErrno() == Error::OutOfMemory
590 ? PopulationStatus::NoMemory
591 : PopulationStatus::IoError;
592 }
593 return false;
594 }
595 // No need to lock this section - only accessing m_Mappings once
596 physical_uintptr_t phys = getBackingPage(fileOffset, m_Length - mappingOffset);
597 if (phys == ~0UL) {
598 if (population) {
599 auto* thread = Processor::information().getCurrentThread();
600 *population = thread && thread->getErrno() == Error::OutOfMemory
601 ? PopulationStatus::NoMemory
602 : PopulationStatus::IoError;
603 }
604 ERROR("MemoryMappedFile::trap couldn't get a backing page");
605 return false; // Fail.
606 }
607
609 flags |= m_pBacking->physicalMappingFlags() &
612 if (!m_bCopyOnWrite && (m_Permissions & Write)) {
615 }
616
617 bool r = va.map(phys, reinterpret_cast<void*>(address), flags | extraFlags);
618 if (!r) {
619 ERROR("map() failed in MemoryMappedFile::trap (no-copy)");
620 m_pBacking->returnPhysicalPage(fileOffset);
621 return false;
622 }
623
624 if (!m_Mappings.tryInsert(address, ~0UL)) {
625 va.unmap(reinterpret_cast<void*>(address));
626 m_pBacking->returnPhysicalPage(fileOffset);
627 return false;
628 }
629 } else {
630 // Prepare the private page before exposing it to userspace.
631 physical_uintptr_t newPhys = PhysicalMemoryManager::instance().allocatePage();
632 if (!newPhys) {
633 ERROR("allocatePage() failed in MemoryMappedFile::trap (copy)");
634 return false;
635 }
636
637 size_t nBytes = m_Length - mappingOffset;
638 if (nBytes > pageSz)
639 nBytes = pageSz;
640
641 bool readSucceeded = false;
642 {
643 TemporaryPhysicalMapping temporary(newPhys, "Mapped File Page Initialisation");
644 if (!temporary.valid()) {
645 ERROR("temporary map failed in MemoryMappedFile::trap (copy)");
647 return false;
648 }
649
650 uintptr_t temporaryAddress = reinterpret_cast<uintptr_t>(temporary.address());
651 size_t nRead = m_pBacking->read(fileOffset, nBytes, temporaryAddress);
652 readSucceeded = nRead && nRead <= nBytes;
653 if (readSucceeded && nRead < pageSz) {
654 // Couldn't quite read in a page - zero out what's left.
655 ByteSet(reinterpret_cast<void*>(temporaryAddress + nRead), 0, pageSz - nRead);
656 }
657 }
658 if (!readSucceeded) {
659 if (population) {
660 auto* thread = Processor::information().getCurrentThread();
661 *population = thread && thread->getErrno() == Error::OutOfMemory
662 ? PopulationStatus::NoMemory
663 : PopulationStatus::IoError;
664 }
666 return false;
667 }
668
669 // Ditch an existing mapping only once its replacement is ready.
670 if (va.isMapped(reinterpret_cast<void*>(address))) {
671 va.unmap(reinterpret_cast<void*>(address));
672
673 // One less reference to the backing page.
674 m_pBacking->returnPhysicalPage(fileOffset);
676 }
677
678 bool r = va.map(newPhys, reinterpret_cast<void*>(address),
679 ((m_Permissions & Write) ? VirtualAddressSpace::Write : 0) | extraFlags);
680 if (!r) {
681 ERROR("map() failed in MemoryMappedFile::trap (copy)");
683 return false;
684 }
685
686 if (!m_Mappings.tryInsert(address, newPhys)) {
687 va.unmap(reinterpret_cast<void*>(address));
689 return false;
690 }
691 }
692
693 return true;
694}
695
697 if (m_LockMode != MemoryLockMode::None)
698 return false;
699 TerminationDeferral terminationDeferral;
700 if (!m_Lock.tryAcquire()) {
701 return false;
702 }
703
704 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
705 bool released = false;
706 const size_t pageSize = PhysicalMemoryManager::getPageSize();
707 for (uintptr_t address = m_Address; address < m_Address + m_Length; address += pageSize) {
708 if (getMapping(address) != ~0UL) {
709 continue;
710 }
711 void* page = reinterpret_cast<void*>(address);
713 break;
714 }
715 const size_t offset = m_Offset + (address - m_Address);
716 size_t flags = 0;
717 physical_uintptr_t physical = 0;
718 if (!va.detachMapping(page, physical, flags)) {
719 m_pBacking->endMappingRelease();
720 continue;
721 }
722 if (!m_bCopyOnWrite) {
723 m_pBacking->sync(offset, false);
724 }
726 m_pBacking->endMappingRelease();
728 released = true;
729 }
730 m_Lock.release();
731 return released;
732}
733
734void MemoryMappedFile::unmapUnlocked() {
735 EMIT_IF(DebugMemoryMappings) {
736 NOTICE("MemoryMappedFile::unmap()");
737 }
738
739 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
740
741 if (!getMappingCount())
742 return;
743
744 for (auto it = m_Mappings.begin(); it != m_Mappings.end(); ++it) {
745 void* v = reinterpret_cast<void*>(it.key());
746 if (!va.isMapped(v))
747 continue; // Already unmapped...
748
749 size_t flags = 0;
750 physical_uintptr_t phys = 0;
751 va.getMapping(v, phys, flags);
752 va.unmap(v);
753
754 physical_uintptr_t p = it.value();
755 if (p == ~0UL) {
756 size_t fileOffset = (it.key() - m_Address) + m_Offset;
757
758 if (!m_bCopyOnWrite) {
759 m_pBacking->syncAndReturnPhysicalPage(fileOffset, true);
760 } else {
761 m_pBacking->returnPhysicalPage(fileOffset);
762 }
763 } else
765 }
766
768}
769
770void MemoryMappedFile::trackMapping(uintptr_t addr, physical_uintptr_t phys) {
771 m_Mappings.insert(addr, phys);
772}
773
775 m_Mappings.remove(addr);
776}
777
778physical_uintptr_t MemoryMappedFile::getMapping(uintptr_t addr) {
779 return m_Mappings.lookup(addr);
780}
781
785
789
790MemoryMapManager::OperationGuard::OperationGuard(MemoryMapManager& manager, bool tryOnly)
791 : m_EventDeferral(), m_Manager(manager), m_Acquired(!tryOnly || manager.tryEnterOperation()) {
792 if (!tryOnly) {
793 m_Manager.enterOperation();
794 }
795}
796
797MemoryMapManager::OperationGuard::~OperationGuard() {
798 if (m_Acquired) {
799 m_Manager.leaveOperation();
800 }
801}
802
804 : m_MmObjectLists(),
805 m_Lock(),
808 m_pLifecycleOwner(nullptr),
809 m_LifecycleDepth(0) {
810 const bool registered = PageFaultHandler::instance().registerHandler(this);
811 assert(registered);
812 MemoryPressureManager::instance().registerHandler(MemoryPressureManager::HighPriority, this);
813}
814
815void MemoryMapManager::enterOperation() {
816 void* owner = currentOperationOwner();
817#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
818 Thread* diagnosticThread = Processor::information().getCurrentThread();
819 Process* diagnosticProcess = diagnosticThread ? diagnosticThread->getParent() : nullptr;
820 if (diagnosticProcess) {
821 diagnosticProcess->recordBenchmarkVmCounter(Process::VmGuardEntries);
822 }
823#endif
824 {
826 if (m_pLifecycleOwner == owner) {
827#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
828 if (diagnosticProcess) {
829 diagnosticProcess->recordBenchmarkVmCounter(Process::VmGuardRecursiveEntries);
830 }
831#endif
832 ++m_LifecycleDepth;
833 return;
834 }
835 }
836
837 const bool acquired = m_LifecycleLock.acquire();
838 assert(acquired);
839
841 assert(!m_pLifecycleOwner);
842 assert(!m_LifecycleDepth);
843 m_pLifecycleOwner = owner;
844 m_LifecycleDepth = 1;
845}
846
847bool MemoryMapManager::tryEnterOperation() {
848 void* owner = currentOperationOwner();
849 {
851 if (m_pLifecycleOwner == owner) {
852 // Pressure recovery must not revoke pages preflighted by an outer copy.
853 return false;
854 }
855 }
856
858 return false;
859 }
860
862 assert(!m_pLifecycleOwner);
863 assert(!m_LifecycleDepth);
864 m_pLifecycleOwner = owner;
865 m_LifecycleDepth = 1;
866 return true;
867}
868
869bool MemoryMapManager::operationOwnedByCurrentExecution() {
871 return m_LifecycleDepth && m_pLifecycleOwner == currentOperationOwner();
872}
873
874void MemoryMapManager::leaveOperation() {
875 bool release = false;
876 {
878 assert(m_pLifecycleOwner == currentOperationOwner());
879 assert(m_LifecycleDepth);
880 if (!--m_LifecycleDepth) {
881 m_pLifecycleOwner = nullptr;
882 release = true;
883 }
884 }
885
886 if (release) {
888 }
889}
890
891MemoryMapManager::~MemoryMapManager() {
892 const bool unregistered = PageFaultHandler::instance().unregisterHandler(this);
893 assert(unregistered);
894 MemoryPressureManager::instance().removeHandler(this);
895}
896
898 OperationGuard operation(*this);
899
900 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
901 VirtualAddressSpace* pOtherVa = pProcess->getAddressSpace();
902
903 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
904 if (!pMmObjectList)
905 return true;
906
907 MmObjectList* pMmObjectList2 = m_MmObjectLists.lookup(pOtherVa);
908 if (!pMmObjectList2) {
909 pMmObjectList2 = new MmObjectList();
910 if (!pMmObjectList2 || !m_MmObjectLists.tryInsert(pOtherVa, pMmObjectList2)) {
911 delete pMmObjectList2;
912 return false;
913 }
914 }
915
917 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin(); it != pMmObjectList->end();
918 it++) {
919 MemoryMappedObject* obj = *it;
920 if (!pMmObjectList2->reserveBack(obj->address()))
921 return false;
922 MemoryMappedObject* pNewObject = obj->clone();
923 if (!pNewObject) {
924 pMmObjectList2->popBack();
925 return false;
926 }
927 pNewObject->m_OwnerProcess = pProcess;
928 pMmObjectList2->publishBack(pNewObject);
929 if (obj->m_Attachment) {
930 auto attachment = clonedAttachments.lookup(obj->m_Attachment.get());
931 if (!attachment) {
932 attachment = obj->m_Attachment->clone(pProcess);
933 clonedAttachments.insert(obj->m_Attachment.get(), attachment);
934 }
935 pNewObject->m_Attachment = attachment;
936 }
937 }
938 return true;
939}
940
941size_t MemoryMapManager::remove(uintptr_t base, size_t length) {
942 return removeInternal(base, length, false);
943}
944
945size_t MemoryMapManager::removeAndRelease(uintptr_t base, size_t length, VmStatus* status) {
946 OperationGuard operation(*this);
947 if (status)
948 *status = VmStatus::InvalidRange;
949 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
950 if ((base & mask) || !length || length > ~size_t(0) - mask)
951 return 0;
952 length = (length + mask) & ~mask;
953 if (length > ~uintptr_t(0) - base)
954 return 0;
955 auto& space = Processor::information().getVirtualAddressSpace();
956 if (space.runtimeMappingPages(base, length)) {
957 if (status)
958 *status = VmStatus::Unsupported;
959 return 0;
960 }
962 const auto prepared = space.rawUserMemory().prepareReplacement(base, length, raw);
963 if (prepared != MemoryLockStatus::Success) {
964 if (status)
965 *status =
966 prepared == MemoryLockStatus::Unsupported ? VmStatus::Unsupported : VmStatus::NoMemory;
967 return 0;
968 }
969 VmStatus removedStatus;
970 const size_t affected = removeInternal(base, length, true, &removedStatus);
971 if (removedStatus != VmStatus::Success) {
972 if (status)
973 *status = removedStatus;
974 return 0;
975 }
976 if (raw) {
977 raw.get()->commit();
978 auto* process = Processor::information().getCurrentThread()->getParent();
979 for (size_t i = 0; i < raw.get()->removedRangeCount(); ++i) {
980 const auto& range = raw.get()->removedRanges()[i];
981 releaseReservation(process, space, range.base, range.length);
982 }
983 if (auto* account = space.memoryLockAccount()) {
984 auto charge = account->charge();
985 assert(raw.get()->removedPages() <= charge.rawPages);
986 charge.rawPages -= raw.get()->removedPages();
987 account->publish(charge, account->futureMode());
988 }
989 }
990 if (status)
991 *status = VmStatus::Success;
992 return affected;
993}
994
996 OperationGuard operation(*this);
997 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
998 MmObjectList* objects = m_MmObjectLists.lookup(&space);
999 MemoryMappedObject* first = nullptr;
1000 if (objects) {
1001 for (auto it = objects->begin(); it != objects->end(); ++it) {
1002 MemoryMappedObject* object = *it;
1003 if (object->m_Attachment && object->m_Attachment->baseAddress() == base &&
1004 (!first || object->address() < first->address())) {
1005 // A newer attachment may reuse the original base of an older suffix.
1006 first = object;
1007 }
1008 }
1009 }
1010 return first ? first->m_Attachment : SharedPointer<MappingAttachment>();
1011}
1012
1014 OperationGuard operation(*this);
1015 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
1016 Process* process = Processor::information().getCurrentThread()->getParent();
1017 MmObjectList* objects = m_MmObjectLists.lookup(&space);
1018 if (!objects || !attachment) {
1019 return 0;
1020 }
1021 size_t removed = 0;
1022 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
1023 for (auto it = objects->begin(); it != objects->end();) {
1024 MemoryMappedObject* object = *it;
1025 if (object->m_Attachment != attachment) {
1026 ++it;
1027 continue;
1028 }
1029 const uintptr_t base = object->address();
1030 const size_t length = (object->length() + pageMask) & ~pageMask;
1031 it = objects->erase(it);
1032 if (object->m_LockMode != MemoryLockMode::None)
1033 retireLockedPages(space, length / (pageMask + 1));
1034 object->unmap();
1035 delete object;
1036 releaseReservation(process, space, base, length);
1037 ++removed;
1038 }
1039 return removed;
1040}
1041
1042void MemoryMapManager::releaseReservation(Process* process, VirtualAddressSpace& addressSpace,
1043 uintptr_t base, size_t length) {
1044 const uintptr_t end = base + length;
1045
1046 auto releaseIntersection = [process, base, end](Process::UserRegion region, uintptr_t regionStart,
1047 uintptr_t regionEnd) {
1048 const uintptr_t releaseStart = base > regionStart ? base : regionStart;
1049 const uintptr_t releaseEnd = end < regionEnd ? end : regionEnd;
1050 if (releaseStart < releaseEnd) {
1051 process->freeUserRange(region, releaseStart, releaseEnd - releaseStart);
1052 }
1053 };
1054
1055 const uintptr_t dynamicStart = addressSpace.getDynamicStart();
1056 const uintptr_t dynamicEnd = addressSpace.getDynamicEnd();
1057 if (dynamicStart && dynamicStart < dynamicEnd) {
1058 releaseIntersection(Process::UserRegion::Dynamic, dynamicStart, dynamicEnd);
1059 }
1060 releaseIntersection(Process::UserRegion::Normal, addressSpace.getUserStart(),
1061 addressSpace.getUserReservedStart());
1062}
1063
1064size_t MemoryMapManager::setPermissions(uintptr_t base, size_t length,
1066 ProtectStatus* status) {
1067 OperationGuard operation(*this);
1068 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
1069 if (status) {
1070 *status = ProtectStatus::InvalidRange;
1071 }
1072 if (!length || (base & pageMask) || length > ~static_cast<size_t>(0) - pageMask ||
1073 (perms &
1074 ~(MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec))) {
1075 return 0;
1076 }
1077 length = (length + pageMask) & ~pageMask;
1078 if (length > ~static_cast<uintptr_t>(0) - base) {
1079 return 0;
1080 }
1081#if !(X64 || HOSTED)
1082 // These ports do not yet retain inaccessible physical mappings.
1083 if (perms == MemoryMappedObject::None) {
1084 if (status) {
1085 *status = ProtectStatus::Unsupported;
1086 }
1087 return 0;
1088 }
1089#endif
1090 if (!allows(base, length, MemoryMappedObject::None)) {
1091 if (status) {
1092 *status = ProtectStatus::Unmapped;
1093 }
1094 return 0;
1095 }
1096
1097 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1098 MmObjectList* objects = m_MmObjectLists.lookup(&va);
1099 const uintptr_t end = base + length;
1100 for (auto it = objects->begin(); it != objects->end(); ++it) {
1101 MemoryMappedObject* object = *it;
1102 const uintptr_t objectEnd = (object->address() + object->length() + pageMask) & ~pageMask;
1103 if (object->address() < end && objectEnd > base &&
1104 (object->maximumPermissions() & perms) != perms) {
1105 if (status) {
1106 *status = ProtectStatus::AccessDenied;
1107 }
1108 return 0;
1109 }
1110 if (object->address() < end && objectEnd > base && object->m_bCopyOnWrite &&
1111 (perms & MemoryMappedObject::Exec)) {
1112 File* backing = object->backingFile();
1113 if (backing) {
1114 if (!backing->acquireMappingUse(true, false)) {
1115 if (status)
1116 *status = ProtectStatus::TextBusy;
1117 return 0;
1118 }
1119 backing->releaseMappingUse(true, false);
1120 }
1121 }
1122 }
1123
1124 size_t affected = 0;
1125 for (auto it = objects->begin(); it != objects->end(); ++it) {
1126 MemoryMappedObject* object = *it;
1127 const uintptr_t objectEnd = (object->address() + object->length() + pageMask) & ~pageMask;
1128 if (object->address() < end && objectEnd > base &&
1129 !object->preparePermissions(base, length, perms)) {
1130 if (status) {
1131 *status = ProtectStatus::NoMemory;
1132 }
1133 return 0;
1134 }
1135 }
1136 size_t remaining = objects->count();
1137 for (auto it = objects->begin(); remaining; ++it, --remaining) {
1138 MemoryMappedObject* object = *it;
1139 const uintptr_t objectEnd = (object->address() + object->length() + pageMask) & ~pageMask;
1140 if (object->address() >= end || objectEnd <= base) {
1141 continue;
1142 }
1143 if (object->address() < base) {
1144 if (!objects->reserveBack(base)) {
1145 if (status)
1146 *status = ProtectStatus::NoMemory;
1147 return affected;
1148 }
1149 auto* split = object->split(base);
1150 if (!split) {
1151 objects->popBack();
1152 if (status)
1153 *status = ProtectStatus::NoMemory;
1154 return affected;
1155 }
1156 objects->publishBack(split);
1157 object = split;
1158 }
1159 if (objectEnd > end) {
1160 if (!objects->reserveBack(end)) {
1161 if (status)
1162 *status = ProtectStatus::NoMemory;
1163 return affected;
1164 }
1165 auto* split = object->split(end);
1166 if (!split) {
1167 objects->popBack();
1168 if (status)
1169 *status = ProtectStatus::NoMemory;
1170 return affected;
1171 }
1172 objects->publishBack(split);
1173 }
1174 object->setPermissions(perms);
1175 ++affected;
1176 }
1177 if (status) {
1178 *status = ProtectStatus::Success;
1179 }
1180 return affected;
1181}
1182
1183bool MemoryMapManager::contains(uintptr_t base, size_t length) {
1184 OperationGuard operation(*this);
1185
1186 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1187 size_t pageSz = PhysicalMemoryManager::getPageSize();
1188
1189 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1190 if (!pMmObjectList) {
1191 return false;
1192 }
1193
1194 for (uintptr_t address = base; address < (base + length); address += pageSz) {
1195 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin();
1196 it != pMmObjectList->end(); it++) {
1197 MemoryMappedObject* pObject = *it;
1198 if (pObject->matches(address & ~(pageSz - 1))) {
1199 return true;
1200 }
1201 }
1202 }
1203
1204 return false;
1205}
1206
1207bool MemoryMapManager::allows(uintptr_t base, size_t length,
1208 MemoryMappedObject::Permissions permissions) {
1209 OperationGuard operation(*this);
1210
1211 if (!length || length > (~static_cast<uintptr_t>(0) - base)) {
1212 return false;
1213 }
1214
1215 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1216 uintptr_t end = base + length;
1217
1218 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1219 if (!pMmObjectList) {
1220 return false;
1221 }
1222
1223#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1224 Process* process = Processor::information().getCurrentThread()->getParent();
1225 process->recordBenchmarkVmCounter(Process::VmAllowsCalls);
1226 process->recordBenchmarkVmCounter(Process::VmAllowsObjectCount, pMmObjectList->count());
1227 size_t objectVisits = 0;
1228#endif
1229
1230 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
1231 uintptr_t cursor = base;
1232 while (cursor < end) {
1233 uintptr_t coveredUntil = cursor;
1234 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin();
1235 it != pMmObjectList->end(); ++it) {
1236#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1237 ++objectVisits;
1238#endif
1239 MemoryMappedObject* pObject = *it;
1240 uintptr_t objectEnd = (pObject->address() + pObject->length() + pageMask) & ~pageMask;
1241 if (cursor >= pObject->address() && cursor < objectEnd &&
1242 (pObject->permissions() & permissions) == permissions) {
1243 if (objectEnd > coveredUntil) {
1244 coveredUntil = objectEnd;
1245 }
1246 if (coveredUntil >= end) {
1247#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1248 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1249#endif
1250 return true;
1251 }
1252 }
1253 }
1254
1255 if (coveredUntil == cursor) {
1256#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1257 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1258#endif
1259 return false;
1260 }
1261 cursor = coveredUntil < end ? coveredUntil : end;
1262 }
1263
1264#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1265 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1266#endif
1267 return true;
1268}
1269
1270bool MemoryMapManager::op(MemoryMapManager::Ops what, uintptr_t base, size_t length, bool async) {
1271 OperationGuard operation(*this);
1272
1273 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1274 size_t pageSz = PhysicalMemoryManager::getPageSize();
1275
1276 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1277 if (!pMmObjectList) {
1278 return false;
1279 }
1280
1281 bool success = true;
1282 if (what == Sync) {
1283 const uintptr_t end = base + length;
1284 for (MemoryMappedObject* object : *pMmObjectList) {
1285 const uintptr_t objectEnd =
1286 (object->address() + object->length() + pageSz - 1) & ~(pageSz - 1);
1287 const uintptr_t start = base > object->address() ? base : object->address();
1288 const uintptr_t stop = end < objectEnd ? end : objectEnd;
1289 if (start < stop)
1290 success = object->syncRange(start, stop - start, async) && success;
1291 }
1292 return success;
1293 }
1294 for (uintptr_t address = base; address < (base + length); address += pageSz) {
1295 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin();
1296 it != pMmObjectList->end(); it++) {
1297 MemoryMappedObject* pObject = *it;
1298 if (pObject->matches(address & ~(pageSz - 1))) {
1299 switch (what) {
1300 case Sync:
1301 success = pObject->sync(address, async) && success;
1302 break;
1303 case Invalidate:
1304 pObject->invalidate(address);
1305 break;
1306 default:
1307 WARNING("Bad 'what' in MemoryMapManager::op()");
1308 }
1309 break;
1310 }
1311 }
1312 }
1313 return success;
1314}
1315
1316bool MemoryMapManager::sync(uintptr_t base, size_t length, bool async, int* error) {
1317 OperationGuard operation(*this);
1318 if (error) {
1319 *error = static_cast<int>(Error::OutOfMemory);
1320 }
1321 const size_t pageMask = PhysicalMemoryManager::getPageSize() - 1;
1322 if (!length || (base & pageMask) || length > ~static_cast<size_t>(0) - pageMask) {
1323 return false;
1324 }
1325 length = (length + pageMask) & ~pageMask;
1326 if (!allows(base, length, MemoryMappedObject::None)) {
1327 return false;
1328 }
1329 const bool success = op(Sync, base, length, async);
1330 if (error) {
1331 *error = success ? 0 : static_cast<int>(Error::IoError);
1332 }
1333 return success;
1334}
1335
1336bool MemoryMapManager::sharedBacking(Process* process, uintptr_t address, uintptr_t& identity,
1337 size_t& offset) {
1338 OperationGuard operation(*this);
1339 if (!process) {
1340 return false;
1341 }
1342 MmObjectList* objects = m_MmObjectLists.lookup(process->getAddressSpace());
1343 if (!objects) {
1344 return false;
1345 }
1346 auto* object = objects->find(address);
1347 return object && object->sharedBacking(address, identity, offset);
1348}
1349
1350bool MemoryMapManager::faultInUnlocked(uintptr_t address, bool write, MemoryMappedObject*& selected,
1351 bool residentAnonymousOnly) {
1352 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1353#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1354 Process* diagnosticProcess = Processor::information().getCurrentThread()->getParent();
1355#endif
1356 const uintptr_t pageAddress = address & ~(PhysicalMemoryManager::getPageSize() - 1);
1357 void* page = reinterpret_cast<void*>(pageAddress);
1358 const bool pageAligned = address == pageAddress;
1359
1360 if (!selected || !selected->matches(address)) {
1361 selected = nullptr;
1362 auto* objects = m_MmObjectLists.lookup(&va);
1363 if (objects) {
1364#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1365 size_t objectVisits = 0;
1366#endif
1367#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1368 selected = objects->find(address, &objectVisits);
1369 Processor::information().getCurrentThread()->getParent()->recordBenchmarkVmCounter(
1370 Process::VmFaultInObjectVisits, objectVisits);
1371#else
1372 selected = objects->find(address);
1373#endif
1374 }
1375 }
1376
1377 if (residentAnonymousOnly && (!selected || selected->backingFile())) {
1378 return false;
1379 }
1380 const auto required = write ? MemoryMappedObject::Write : MemoryMappedObject::Read;
1381 if (selected && !(selected->permissions() & required))
1382 return false;
1383
1384 physical_uintptr_t physical = 0;
1385 size_t flags = 0;
1386 bool present = va.getMapping(page, physical, flags);
1387 if (residentAnonymousOnly) {
1388 return present && (flags & VirtualAddressSpace::Write) &&
1392 }
1393 if (selected && (!present || (flags & VirtualAddressSpace::NoAccess))) {
1394 if (selected->prepareResidentAccess(va, reinterpret_cast<uintptr_t>(page)) !=
1395 PopulationStatus::Success)
1396 return false;
1397 present = va.getMapping(page, physical, flags);
1398 }
1399 if (present) {
1402 (write && (flags & VirtualAddressSpace::WriteProtected))) {
1403 return false;
1404 }
1405 if (!write || (flags & VirtualAddressSpace::Write)) {
1406#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1407 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInPresent);
1408#endif
1409 return true;
1410 }
1412#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1413 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInCopyOnWrite);
1414#endif
1415 return va.handleCopyOnWriteFault(page, true);
1416 }
1417 }
1418
1419 // A page-aligned range keeps the selected object aligned with handleTrap's
1420 // own mapping lookup. Preserve the old fallback for unaligned direct calls.
1421 MemoryMappedObject* trapObject = pageAligned ? selected : nullptr;
1422#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1423 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInTrap);
1424#endif
1425 return handleTrapUnlocked(address, write, present, false, trapObject);
1426}
1427
1428bool MemoryMapManager::faultIn(uintptr_t address, bool write) {
1429 OperationGuard operation(*this);
1430 MemoryMappedObject* selected = nullptr;
1431 return faultInUnlocked(address, write, selected);
1432}
1433
1434bool MemoryMapManager::faultInRange(uintptr_t address, size_t length, bool write) {
1435 return accessRange(address, length, write, false);
1436}
1437
1438bool MemoryMapManager::writableAnonymousRange(uintptr_t address, size_t length) {
1439 return accessRange(address, length, true, true);
1440}
1441
1442bool MemoryMapManager::accessRange(uintptr_t address, size_t length, bool write,
1443 bool residentAnonymousOnly) {
1444 if (!length) {
1445 return true;
1446 }
1447 if (length - 1 > (~static_cast<uintptr_t>(0) - address)) {
1448 return false;
1449 }
1450
1451 auto faultRange = [&]() {
1452 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1453 const uintptr_t lastPage = (address + length - 1) & ~(pageSize - 1);
1454#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1455 Process* process = Processor::information().getCurrentThread()->getParent();
1456 process->recordBenchmarkVmCounter(Process::VmFaultInRangeCalls);
1457 process->recordBenchmarkVmCounter(Process::VmFaultInRangePages,
1458 (lastPage - (address & ~(pageSize - 1))) / pageSize + 1);
1459#endif
1460 MemoryMappedObject* selected = nullptr;
1461 for (uintptr_t page = address & ~(pageSize - 1);; page += pageSize) {
1462 if (!faultInUnlocked(page, write, selected, residentAnonymousOnly)) {
1463 return false;
1464 }
1465 if (page == lastPage) {
1466 return true;
1467 }
1468 }
1469 };
1470
1471 if (operationOwnedByCurrentExecution())
1472 return faultRange();
1473 OperationGuard operation(*this);
1474 return faultRange();
1475}
1476
1477MemoryMapManager::FaultResolution MemoryMapManager::resolveUserFault(uintptr_t address, bool write,
1478 bool wasPresent,
1479 bool execute) {
1480 if (!Processor::getInterrupts() || !Processor::information().getCurrentThread() ||
1481 (write && execute))
1482 return FaultResolution::Unhandled;
1483 OperationGuard operation(*this);
1484 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
1485#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1486 Process* process = Processor::information().getCurrentThread()->getParent();
1487#endif
1488#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1489 process->recordBenchmarkVmCounter(Process::VmFaultCalls);
1490#endif
1491 const bool normal = address >= space.getUserStart() && address < space.getUserReservedStart();
1492 const bool dynamic = space.getDynamicStart() && address >= space.getDynamicStart() &&
1493 address < space.getDynamicEnd();
1494 if (!normal && !dynamic) {
1495#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1496 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1497#endif
1498 return FaultResolution::Unhandled;
1499 }
1500 auto* objects = m_MmObjectLists.lookup(&space);
1501 if (!objects) {
1502#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1503 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1504#endif
1505 return FaultResolution::Unhandled;
1506 }
1507#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1508 process->recordBenchmarkVmCounter(Process::VmFaultObjectCount, objects->count());
1509 size_t objectVisits = 0;
1510#endif
1511 const uintptr_t pageAddress = address & ~(PhysicalMemoryManager::getPageSize() - 1);
1512 const auto required = execute ? MemoryMappedObject::Exec
1513 : write ? MemoryMappedObject::Write
1514 : MemoryMappedObject::Read;
1515 MemoryMappedObject* selected = nullptr;
1516#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1517 selected = objects->find(pageAddress, &objectVisits);
1518#else
1519 selected = objects->find(pageAddress);
1520#endif
1521#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1522 process->recordBenchmarkVmCounter(Process::VmFaultObjectVisits, objectVisits);
1523#endif
1524 if (!selected || !(selected->permissions() & required)) {
1525#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1526 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1527#endif
1528 return FaultResolution::Unhandled;
1529 }
1530 if (selected->beyondBackingEnd(pageAddress)) {
1531#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1532 process->recordBenchmarkVmCounter(Process::VmFaultBacking);
1533#endif
1534 return FaultResolution::BackingFault;
1535 }
1536 if (!handleTrapUnlocked(address, write, wasPresent, execute, selected)) {
1537#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1538 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1539#endif
1540 return FaultResolution::Unhandled;
1541 }
1542 void* page = reinterpret_cast<void*>(pageAddress);
1543 physical_uintptr_t physical;
1544 size_t flags;
1545 if (!space.getMapping(page, physical, flags)) {
1546#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1547 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1548#endif
1549 return FaultResolution::Unhandled;
1550 }
1551 const bool accessible =
1554 (!write ||
1556 (!execute || (flags & VirtualAddressSpace::Execute));
1557#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1558 process->recordBenchmarkVmCounter(accessible ? Process::VmFaultResolved
1559 : Process::VmFaultUnhandled);
1560#endif
1561 return accessible ? FaultResolution::Resolved : FaultResolution::Unhandled;
1562}
1563
1564void MemoryMapManager::invalidate(uintptr_t base, size_t length) {
1565 op(Invalidate, base, length, false);
1566}
1567
1569 OperationGuard operation(*this);
1570
1571 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1572
1573 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1574 if (!pMmObjectList)
1575 return;
1576
1577 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin(); it != pMmObjectList->end();
1578 ++it) {
1579 if ((*it) != pObj)
1580 continue;
1581
1582 MemoryMappedObject* object = *it;
1583 pMmObjectList->erase(it);
1584
1585 if (object->m_LockMode != MemoryLockMode::None) {
1586 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1587 retireLockedPages(va, (object->length() + pageSize - 1) / pageSize);
1588 }
1589 object->unmap();
1590 delete object;
1591 return;
1592 }
1593}
1594
1596 OperationGuard operation(*this);
1597
1599}
1600
1601bool MemoryMapManager::trap(InterruptState& state, uintptr_t address, bool bIsWrite,
1602 bool bWasPresent) {
1603 // User faults must not wait for the mapping gate, allocation or backing I/O
1604 // while the architecture's raw interrupt and accounting scopes are active.
1605 if (!state.kernelMode())
1606 return false;
1607 return handleTrap(address, bIsWrite, bWasPresent);
1608}
1609
1610#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1611bool MemoryMapManager::trapForHostedTest(uintptr_t address, bool bIsWrite, bool bWasPresent) {
1612 return handleTrap(address, bIsWrite, bWasPresent);
1613}
1614#endif
1615
1616bool MemoryMapManager::handleTrap(uintptr_t address, bool bIsWrite, bool bWasPresent, bool execute,
1617 MemoryMappedObject* selected) {
1618 // Can't take an event while we're trapping, as the event would otherwise
1619 // be in a minefield (can't touch *any* trap pages in userspace).
1620 Uninterruptible while_trapping;
1621 OperationGuard operation(*this);
1622
1623 return handleTrapUnlocked(address, bIsWrite, bWasPresent, execute, selected);
1624}
1625
1626bool MemoryMapManager::handleTrapUnlocked(uintptr_t address, bool bIsWrite, bool bWasPresent,
1627 bool execute, MemoryMappedObject* selected) {
1628 // Callers already own OperationGuard, so do not re-enter the event and
1629 // lifetime deferral scopes for every page fault in a user copy.
1630
1631 EMIT_IF(DebugMemoryMappings) {
1632 NOTICE("Trap start: " << Hex << address << ", pid:tid " << Dec
1633 << Processor::information().getCurrentThread()->getParent()->getId()
1634 << ":" << Processor::information().getCurrentThread()->getId());
1635 }
1636
1637 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1638 size_t pageSz = PhysicalMemoryManager::getPageSize();
1639 const uintptr_t pageAddress = address & ~(pageSz - 1);
1640
1641 MemoryMappedObject* pObject = selected;
1642 if (!pObject) {
1643 m_Lock.acquire();
1644 EMIT_IF(DebugMemoryMappings) {
1645 NOTICE_NOLOCK("trap: got lock");
1646 }
1647
1648 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1649 if (!pMmObjectList) {
1650 m_Lock.release();
1651 return false;
1652 }
1653
1654 EMIT_IF(DebugMemoryMappings) {
1655 NOTICE_NOLOCK("trap: lookup complete " << reinterpret_cast<uintptr_t>(pMmObjectList));
1656 }
1657
1658 // The final page can extend beyond the stored byte length of a file.
1659 pObject = pMmObjectList->find(pageAddress);
1660
1661 m_Lock.release();
1662 }
1663 if (!pObject) {
1664 EMIT_IF(DebugMemoryMappings) {
1665 ERROR("MemoryMapManager::trap() could not find an object for " << address);
1666 }
1667 return false;
1668 }
1669
1670 const MemoryMappedObject::Permissions required = execute ? MemoryMappedObject::Exec
1671 : bIsWrite ? MemoryMappedObject::Write
1672 : MemoryMappedObject::Read;
1673 if (!(pObject->permissions() & required)) {
1674 return false;
1675 }
1676
1677 if (pObject->prepareResidentAccess(va, pageAddress) != PopulationStatus::Success)
1678 return false;
1679
1680 // A mapping published while this fault waited is only a completed resolution
1681 // if it permits the access which originally faulted.
1682 physical_uintptr_t physicalAddress = 0;
1683 size_t flags = 0;
1684 if (va.getMapping(reinterpret_cast<void*>(pageAddress), physicalAddress, flags)) {
1685 const bool userAccessible =
1688 if (userAccessible && (!execute || (flags & VirtualAddressSpace::Execute))) {
1689 if (!bIsWrite && !bWasPresent) {
1690 return true;
1691 }
1692 if (bIsWrite && !(flags & VirtualAddressSpace::WriteProtected)) {
1693 if (flags & VirtualAddressSpace::Write) {
1694 return true;
1695 }
1697 return va.handleCopyOnWriteFault(reinterpret_cast<void*>(pageAddress), true);
1698 }
1699 }
1700 }
1701 }
1702
1703 PopulationStatus population = PopulationStatus::NoMemory;
1704 return pObject->trap(va, address, bIsWrite, execute ? &population : nullptr);
1705}
1706
1708 OperationGuard operation(*this);
1709 const uintptr_t identity = backing->futexIdentity();
1710 for (auto spaces = m_MmObjectLists.begin(); spaces != m_MmObjectLists.end(); ++spaces) {
1711 for (auto objects = spaces.value()->begin(); objects != spaces.value()->end(); ++objects) {
1712 const auto* object = *objects;
1713 if (!object->m_bCopyOnWrite && (object->maximumPermissions() & MemoryMappedObject::Write) &&
1714 object->usesBacking(identity)) {
1715 return true;
1716 }
1717 }
1718 }
1719 return false;
1720}
1721
1723 // Allocation may enter from a file mutation that another mapping operation
1724 // is waiting for. Pressure recovery cannot wait for that operation's gate.
1725 OperationGuard operation(*this, true);
1726 if (!operation) {
1727 return false;
1728 }
1729
1730 // Track current address space as we need to switch into each known address
1731 // space in order to compact them.
1732 VirtualAddressSpace& currva = Processor::information().getVirtualAddressSpace();
1733 AddressSpaceRestorer restoreAddressSpace(currva);
1734
1735 bool bCompact = false;
1737 it != m_MmObjectLists.end(); ++it) {
1739
1740 for (MmObjectList::Iterator it2 = it.value()->begin(); it2 != it.value()->end(); ++it2) {
1741 if ((*it2)->reclaimAnonymousPage(*it.key()))
1742 return true;
1743 bCompact = (*it2)->compact();
1744 if (bCompact)
1745 break;
1746 }
1747
1748 if (bCompact)
1749 break;
1750 }
1751
1752 // Memory mapped files tend to un-pin pages for the Cache system to
1753 // release, so we never return success (as we never actually released
1754 // pages and therefore didn't resolve any memory pressure).
1755 if (bCompact)
1756 NOTICE(" -> success, hoping for Cache eviction...");
1757 return false;
1758}
1759
1761 TerminationDeferral terminationDeferral;
1762
1763 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
1764
1765 MmObjectList* pMmObjectList = m_MmObjectLists.lookup(&va);
1766 if (!pMmObjectList)
1767 return;
1768
1769 // Detach first so a backing-store callback that re-enters the manager
1770 // cannot observe objects which are already being destroyed.
1771 m_MmObjectLists.remove(&va);
1772
1773 for (List<MemoryMappedObject*>::Iterator it = pMmObjectList->begin(); it != pMmObjectList->end();
1774 it = pMmObjectList->begin()) {
1775 MemoryMappedObject* object = *it;
1776 pMmObjectList->erase(it);
1777
1778 if (object->m_LockMode != MemoryLockMode::None) {
1779 const size_t pageSize = PhysicalMemoryManager::getPageSize();
1780 retireLockedPages(va, (object->length() + pageSize - 1) / pageSize);
1781 }
1782 object->unmap();
1783 delete object;
1784 }
1785
1786 delete pMmObjectList;
1787}
1788
1790 enterOperation();
1791}
1792
1794 leaveOperation();
1795}
1796
1797#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1798void MemoryMapManager::acquireLifecycleGateForHostedTest() {
1799 acquireLock();
1800}
1801
1802void MemoryMapManager::releaseLifecycleGateForHostedTest() {
1803 releaseLock();
1804}
1805#endif
Memory-mapped file interface.
Definition File.h:75
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
Definition File.cc:239
void markPageExternallyWritable(size_t offset)
Definition File.cc:1611
virtual bool syncPages(const uint64_t *offsets, size_t count)
Definition File.cc:688
size_t populateRange(size_t offset, size_t length)
Definition File.cc:1618
String getName() const
Definition File.cc:782
virtual bool tryBeginMappingRelease()
Definition File.cc:587
virtual uintptr_t futexIdentity()
Definition File.cc:75
virtual physical_uintptr_t getPhysicalPage(size_t offset)
Definition File.cc:486
virtual bool sync()
Definition File.cc:595
bool syncAndReturnPhysicalPage(size_t offset, bool async)
Definition File.cc:581
bool acquireMappingUse(bool executable, bool sharedWrite)
Definition File.cc:1181
virtual bool prepareSharedMapping(size_t offset, size_t length)
Definition File.cc:1143
virtual void returnPhysicalPage(size_t offset)
Definition File.cc:557
::Iterator< T, node_t > Iterator
Definition List.h:67
bool reserveBack(uintptr_t address)
Definition MappingList.h:44
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
static MemoryMapManager m_Instance
size_t remove(uintptr_t base, size_t length)
Spinlock m_LifecycleStateLock
bool allows(uintptr_t base, size_t length, MemoryMappedObject::Permissions permissions)
SharedPointer< MappingAttachment > findAttachment(uintptr_t base)
void unmap(MemoryMappedObject *pObj)
bool clone(Process *pTarget)
static MemoryMapManager & instance()
bool faultInRange(uintptr_t address, size_t length, bool write)
void invalidate(uintptr_t base, size_t length)
virtual bool trap(InterruptState &state, uintptr_t address, bool bIsWrite, bool bWasPresent)
virtual bool compact()
size_t removeAttachment(const SharedPointer< MappingAttachment > &attachment)
bool hasSharedWriteCapability(File *backing)
bool writableAnonymousRange(uintptr_t address, size_t length)
bool sync(uintptr_t base, size_t length, bool async, int *error=nullptr)
size_t removeAndRelease(uintptr_t base, size_t length, VmStatus *status=nullptr)
bool contains(uintptr_t base, size_t length)
virtual bool sync(uintptr_t at, bool async) override
virtual bool remove(size_t length) override
virtual void setPermissions(MemoryMappedObject::Permissions perms) override
virtual void invalidate(uintptr_t at) override
Tree< uintptr_t, physical_uintptr_t > m_Mappings
virtual MemoryMappedObject * split(uintptr_t at) override
void untrackMapping(uintptr_t)
physical_uintptr_t getMapping(uintptr_t)
virtual MemoryMappedObject * clone() override
virtual void unmap() override
bool syncRange(uintptr_t at, size_t length, bool async) override
virtual bool trap(VirtualAddressSpace &space, uintptr_t address, bool bWrite, PopulationStatus *population=nullptr) override
void trackMapping(uintptr_t, physical_uintptr_t)
virtual bool compact() override
size_t length() const
uintptr_t address() const
virtual bool sync(uintptr_t at, bool async)
virtual bool syncRange(uintptr_t at, size_t length, bool async)
virtual void invalidate(uintptr_t at)
bool matches(uintptr_t address)
virtual bool trap(VirtualAddressSpace &space, uintptr_t address, bool bWrite, PopulationStatus *population=nullptr)=0
virtual MemoryMappedObject * clone()=0
Permissions permissions() const
void registerHandler(size_t prio, MemoryPressureHandler *pHandler)
void removeHandler(MemoryPressureHandler *pHandler)
Special memory entity in the kernel's virtual address space.
EXPORTED_PUBLIC bool unregisterHandler(MemoryTrapHandler *pHandler)
EXPORTED_PUBLIC bool registerHandler(MemoryTrapHandler *pHandler)
static PageFaultHandler & instance()
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual void pin(physical_uintptr_t page)=0
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
Process * getParent()
Definition Process.h:620
VirtualAddressSpace * getAddressSpace()
Definition Process.h:530
static bool getInterrupts()
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
void release(size_t n=1)
Definition Semaphore.cc:549
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:484
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:355
T * get() const
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
Process * getParent() const
Definition Thread.h:340
An iterator applicable for many data structures.
Definition Iterator.h:147
A key/value dictionary.
Definition Tree.h:33
bool tryInsert(const K &key, const E &value)
Definition Tree.h:169
Iterator begin()
Definition Tree.h:402
void remove(const K &key)
Definition Tree.h:301
bool contains(const K &key) const
Definition Tree.h:287
bool lowerBound(const K &key, K &foundKey, E &foundValue) const
Definition Tree.h:239
E lookup(const K &key) const
Definition Tree.h:193
void clear()
Definition Tree.h:383
void insert(const K &key, const E &value)
Definition Tree.h:149
Iterator end()
Definition Tree.h:427
size_t count() const
Definition Tree.h:142
Definition VFS.h:58
bool untrackFile(File *pFile, bool destroy=true)
Definition VFS.cc:1670
static VFS & instance()
Definition VFS.cc:311
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
virtual uintptr_t getUserReservedStart() const =0
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool isMapped(void *virtualAddress)=0
virtual uintptr_t getUserStart() const =0
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
virtual void unmap(void *virtualAddress)=0
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39