8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/linker/Elf.h"
11#include "pedigree/kernel/process/Process.h"
12#include "pedigree/kernel/process/Semaphore.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/ProcessorInformation.h"
17#include "pedigree/kernel/processor/VirtualAddressSpace.h"
18#include "pedigree/kernel/utilities/utility.h"
20#include "DynamicLinker.h"
21#include "modules/system/vfs/File.h"
25bool fail(
const char* test,
const char* detail) {
26 ERROR(
"HOSTED-PAGE-CONTENT-TEST: FAIL " << test <<
": " << detail);
30size_t g_FailedAllocationCalls = 0;
32physical_uintptr_t failDemandPageAllocation() {
33 ++g_FailedAllocationCalls;
37class DemandPageElf final :
public Elf {
39 DemandPageElf(
size_t fileSize,
size_t memorySize) {
40 m_nProgramHeaders = 1;
41 m_pProgramHeaders =
new ElfProgramHeader_t[1];
42 ByteSet(m_pProgramHeaders, 0,
sizeof(ElfProgramHeader_t));
43 m_pProgramHeaders[0].type = PT_LOAD;
44 m_pProgramHeaders[0].filesz = fileSize;
45 m_pProgramHeaders[0].memsz = memorySize;
48 void addRelocations(uintptr_t relativeOffset, uintptr_t relativeAddend, uintptr_t narrowOffset,
49 uintptr_t narrowSymbol, uintptr_t narrowAddend) {
50 m_pRelaTable =
new ElfRela_t[2];
51 ByteSet(m_pRelaTable, 0,
sizeof(ElfRela_t) * 2);
52 m_pRelaTable[0].offset = relativeOffset;
53 m_pRelaTable[0].info = 8;
54 m_pRelaTable[0].addend = relativeAddend;
55 m_pRelaTable[1].offset = narrowOffset;
56 m_pRelaTable[1].info = (
static_cast<Elf_Xword
>(1) << 32) | 10;
57 m_pRelaTable[1].addend = narrowAddend;
58 m_nRelaTableSize =
sizeof(ElfRela_t) * 2;
61 ByteSet(m_pDynamicSymbolTable, 0,
sizeof(
ElfSymbol_t) * 2);
62 m_pDynamicSymbolTable[1].info = ST_INFO(STB_LOCAL, STT_SECTION);
63 m_pDynamicSymbolTable[1].shndx = 1;
64 m_nDynamicSymbolTableSize =
sizeof(
ElfSymbol_t) * 2;
68 m_pSectionHeaders[1].addr = narrowSymbol;
69 m_nSectionHeaders = 2;
78physical_uintptr_t g_DemandPageAllocations[2] = {};
79Semaphore* g_DemandPageFirstReady =
nullptr;
80Semaphore* g_DemandPageResumeFirst =
nullptr;
82physical_uintptr_t allocateControlledDemandPage() {
83 const size_t call = g_DemandPageAllocationCalls += 1;
84 return call <= 2 ? g_DemandPageAllocations[call - 1] : 0;
87void pauseFirstReadyDemandPage(uintptr_t) {
88 const size_t call = g_DemandPageReadyCalls += 1;
89 if (call == 1 && g_DemandPageFirstReady && g_DemandPageResumeFirst) {
90 g_DemandPageFirstReady->
release();
92 g_DemandPageReadyWaitFailed =
true;
97void observeFreedDemandPage(physical_uintptr_t page) {
98 g_DemandPageFreeCalls += 1;
99 for (
size_t i = 0; i < 2; ++i) {
100 if (page == g_DemandPageAllocations[i]) {
101 g_DemandPageFreeMask |=
static_cast<size_t>(1) << i;
106struct DemandPageLoadContext {
107 DemandPageLoadContext(
Elf* elf, uintptr_t buffer,
size_t size, uintptr_t address)
108 : elf(elf), buffer(buffer), size(size), address(address), completed(false), result(false) {}
118int demandPageLoadWorker(
void* parameter) {
119 DemandPageLoadContext* context =
reinterpret_cast<DemandPageLoadContext*
>(parameter);
120 context->result = DynamicLinker::loadDemandPageForTest(
121 context->elf, context->buffer, context->size, context->address,
nullptr, context->address);
122 context->completed =
true;
123 return context->result ? 0 : 1;
126class SentinelFile final :
public File {
128 SentinelFile(
size_t pageSize,
size_t dataSize)
129 :
File(), m_PageSize(pageSize), m_DataSize(dataSize), m_Reads(0), m_ReadShapeValid(true) {
133 size_t reads()
const {
137 bool readShapeValid()
const {
138 return m_ReadShapeValid;
146 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
148 if (location != 0 || size != m_DataSize || !buffer) {
149 m_ReadShapeValid =
false;
153 uint8_t* bytes =
reinterpret_cast<uint8_t*
>(buffer);
154 ByteSet(bytes, 0xA5, m_PageSize);
155 for (
size_t i = 0; i < size; ++i) {
156 bytes[i] =
static_cast<uint8_t
>(i + 1);
165 bool m_ReadShapeValid;
168class BlockingSentinelFile final :
public File {
170 explicit BlockingSentinelFile(
size_t dataSize)
172 m_DataSize(dataSize),
176 m_ReadShapeValid(true) {
180 bool waitUntilRead() {
181 return m_ReadEntered.acquireForCompletion(1, 2);
185 m_ResumeRead.release();
188 size_t reads()
const {
192 bool readShapeValid()
const {
193 return m_ReadShapeValid;
201 uint64_t
readBytewise(uint64_t location, uint64_t size, uintptr_t buffer,
bool)
override {
203 if (location != 0 || size != m_DataSize || !buffer) {
204 m_ReadShapeValid =
false;
208 m_ReadEntered.release();
209 if (!m_ResumeRead.acquireForCompletion()) {
210 m_ReadShapeValid =
false;
214 uint8_t* bytes =
reinterpret_cast<uint8_t*
>(buffer);
215 for (
size_t i = 0; i < size; ++i) {
216 bytes[i] =
static_cast<uint8_t
>(i + 1);
226 bool m_ReadShapeValid;
229struct PublishAfterInitialiseContext {
231 : mapping(mapping), address(address), completed(false), result(false) {}
239int publishAfterInitialiseWorker(
void* parameter) {
240 PublishAfterInitialiseContext* context =
241 reinterpret_cast<PublishAfterInitialiseContext*
>(parameter);
243 context->address,
false);
244 context->completed =
true;
245 return context->result ? 0 : 1;
248bool memoryMappedFileEofZeroFill() {
249 constexpr size_t DataSize = 37;
251 if (pageSize <= DataSize) {
252 return fail(
"mmap-eof-zero-fill",
"target page is too small for the fixture");
258 return fail(
"mmap-eof-zero-fill",
"no current process");
261 uintptr_t address = 0;
262 if (!process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
263 return fail(
"mmap-eof-zero-fill",
"could not reserve a target page");
266 bool trapped =
false;
267 bool dataIntact =
false;
268 bool tailZero =
false;
269 bool readValid =
false;
271 SentinelFile file(pageSize, DataSize);
272 MemoryMappedFile mapping(address, DataSize, 0, &file,
true, MemoryMappedObject::Read);
275 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(address);
277 for (
size_t i = 0; i < DataSize; ++i) {
278 if (bytes[i] !=
static_cast<uint8_t
>(i + 1)) {
285 for (
size_t i = DataSize; i < pageSize; ++i) {
292 readValid = file.reads() == 1 && file.readShapeValid();
295 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
296 if (!trapped || !readValid || !dataIntact || !tailZero) {
297 return fail(
"mmap-eof-zero-fill",
"the copied EOF page retained non-file data");
300 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS mmap-eof-zero-fill");
304bool memoryMappedFilePublishesAfterInitialise() {
305 constexpr size_t DataSize = 37;
307 if (pageSize <= DataSize) {
308 return fail(
"mmap-publish-after-init",
"target page is too small for the fixture");
314 return fail(
"mmap-publish-after-init",
"no current process");
317 uintptr_t address = 0;
318 if (!process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
319 return fail(
"mmap-publish-after-init",
"could not reserve a target page");
323 bool started =
false;
324 bool readEntered =
false;
325 bool absentWhileInitialising =
false;
327 bool completed =
false;
328 bool trapped =
false;
330 bool dataIntact =
false;
331 bool tailZero =
false;
332 bool readValid =
false;
334 BlockingSentinelFile file(DataSize);
335 MemoryMappedFile mapping(address, DataSize, 0, &file,
true, MemoryMappedObject::Read);
336 PublishAfterInitialiseContext context(&mapping, address);
338 new Thread(process, publishAfterInitialiseWorker, &context,
nullptr,
false,
true,
true);
339 worker->setName(
"hosted mmap publish-after-init");
340 started =
worker->start();
341 readEntered = started && file.waitUntilRead();
342 absentWhileInitialising = readEntered && !va.
isMapped(
reinterpret_cast<void*
>(address));
344 joined = started &&
worker->joinForCompletion();
349 completed = context.completed;
350 trapped = context.result;
351 mapped = va.
isMapped(
reinterpret_cast<void*
>(address));
355 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(address);
356 for (
size_t i = 0; i < DataSize; ++i) {
357 if (bytes[i] !=
static_cast<uint8_t
>(i + 1)) {
362 for (
size_t i = DataSize; i < pageSize; ++i) {
369 readValid = file.reads() == 1 && file.readShapeValid();
372 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
373 if (!started || !readEntered || !absentWhileInitialising || !joined || !completed || !trapped ||
374 !mapped || !dataIntact || !tailZero || !readValid) {
375 return fail(
"mmap-publish-after-init",
"the user mapping was visible before page population");
378 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS mmap-publish-after-init");
382bool dynamicDemandPagePublishesOnce() {
383 constexpr size_t FileSize = 64;
384 constexpr uintptr_t RelocationOffset = 16;
385 constexpr uintptr_t RelocationAddend = 0x1234;
386 constexpr uintptr_t NarrowRelocationSymbol = 0x10203040;
387 constexpr uintptr_t NarrowRelocationAddend = 0x55;
390 if (pageSize <= FileSize) {
391 return fail(
"dynamic-demand-page-publish",
"target page is too small for the fixture");
393 const uintptr_t NarrowRelocationOffset = pageSize -
sizeof(uint32_t);
398 return fail(
"dynamic-demand-page-publish",
"no current process");
401 uintptr_t address = 0;
402 if (!process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
403 return fail(
"dynamic-demand-page-publish",
"could not reserve a target page");
409 if (!g_DemandPageAllocations[0] || !g_DemandPageAllocations[1]) {
410 if (g_DemandPageAllocations[0]) {
411 memory.
freePage(g_DemandPageAllocations[0]);
413 if (g_DemandPageAllocations[1]) {
414 memory.
freePage(g_DemandPageAllocations[1]);
416 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
417 return fail(
"dynamic-demand-page-publish",
"could not allocate controlled pages");
420 uint8_t fileData[FileSize];
421 for (
size_t i = 0; i < FileSize; ++i) {
422 fileData[i] =
static_cast<uint8_t
>((i * 5) + 1);
425 DemandPageElf elf(FileSize, pageSize);
426 elf.addRelocations(RelocationOffset, RelocationAddend, NarrowRelocationOffset,
427 NarrowRelocationSymbol, NarrowRelocationAddend);
431 g_DemandPageFirstReady = &firstReady;
432 g_DemandPageResumeFirst = &resumeFirst;
433 g_DemandPageAllocationCalls = 0;
434 g_DemandPageReadyCalls = 0;
435 g_DemandPageFreeCalls = 0;
436 g_DemandPageFreeMask = 0;
437 g_DemandPageReadyWaitFailed =
false;
438 DynamicLinker::setDemandPageAllocationHookForTest(allocateControlledDemandPage);
439 DynamicLinker::setDemandPageReadyHookForTest(pauseFirstReadyDemandPage);
440 DynamicLinker::setDemandPageFreeHookForTest(observeFreedDemandPage);
442 DemandPageLoadContext firstContext(&elf,
reinterpret_cast<uintptr_t
>(fileData),
sizeof(fileData),
445 new Thread(process, demandPageLoadWorker, &firstContext,
nullptr,
false,
true,
true);
446 first->setName(
"hosted linker first page publisher");
447 const bool firstStarted = first->
start();
448 const bool firstReachedReady = firstStarted && firstReady.acquireForCompletion(1, 2);
451 const bool absentWhileFirstReady =
452 firstReachedReady && !va.
isMapped(
reinterpret_cast<void*
>(address));
454 DemandPageLoadContext secondContext(&elf,
reinterpret_cast<uintptr_t
>(fileData),
sizeof(fileData),
457 bool secondStarted =
false;
458 bool secondJoined =
false;
459 physical_uintptr_t pageAfterSecond = 0;
460 if (firstReachedReady) {
461 second =
new Thread(process, demandPageLoadWorker, &secondContext,
nullptr,
false,
true,
true);
462 second->setName(
"hosted linker second page publisher");
463 secondStarted = second->
start();
465 if (!secondStarted) {
468 if (va.
isMapped(
reinterpret_cast<void*
>(address))) {
470 va.
getMapping(
reinterpret_cast<void*
>(address), pageAfterSecond, flags);
474 resumeFirst.release();
480 DynamicLinker::setDemandPageReadyHookForTest(
nullptr);
481 DynamicLinker::setDemandPageAllocationHookForTest(
nullptr);
482 DynamicLinker::setDemandPageFreeHookForTest(
nullptr);
483 g_DemandPageFirstReady =
nullptr;
484 g_DemandPageResumeFirst =
nullptr;
486 const bool mapped = va.
isMapped(
reinterpret_cast<void*
>(address));
487 physical_uintptr_t finalPage = 0;
488 size_t finalFlags = 0;
490 va.
getMapping(
reinterpret_cast<void*
>(address), finalPage, finalFlags);
493 bool dataIntact = mapped;
494 bool tailZero = mapped;
495 bool relocationCorrect = mapped;
496 bool narrowRelocationCorrect = mapped;
498 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(address);
499 for (
size_t i = 0; i < FileSize; ++i) {
500 if (i >= RelocationOffset && i < RelocationOffset +
sizeof(uint64_t)) {
503 if (i >= NarrowRelocationOffset && i < NarrowRelocationOffset +
sizeof(uint32_t)) {
506 if (bytes[i] != fileData[i]) {
511 for (
size_t i = FileSize; i < pageSize; ++i) {
512 if (i >= NarrowRelocationOffset && i < NarrowRelocationOffset +
sizeof(uint32_t)) {
520 relocationCorrect = *
reinterpret_cast<const uint64_t*
>(address + RelocationOffset) ==
521 address + RelocationAddend;
522 narrowRelocationCorrect =
523 *
reinterpret_cast<const uint32_t*
>(address + NarrowRelocationOffset) ==
524 NarrowRelocationSymbol + NarrowRelocationAddend;
528 va.
unmap(
reinterpret_cast<void*
>(address));
531 for (
size_t i = 0; i < 2; ++i) {
532 const bool wasPublished = mapped && finalPage == g_DemandPageAllocations[i];
533 const bool wasFreed = (g_DemandPageFreeMask & (
static_cast<size_t>(1) << i)) != 0;
534 if (!wasPublished && !wasFreed) {
535 memory.
freePage(g_DemandPageAllocations[i]);
538 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
541 firstStarted && firstReachedReady && absentWhileFirstReady && secondStarted && secondJoined &&
542 secondContext.completed && secondContext.result && firstJoined && firstContext.completed &&
543 firstContext.result && g_DemandPageAllocationCalls == 2 && g_DemandPageReadyCalls == 2 &&
544 mapped && pageAfterSecond == g_DemandPageAllocations[1] && finalPage == pageAfterSecond &&
545 g_DemandPageFreeCalls == 1 && g_DemandPageFreeMask == 1 && !g_DemandPageReadyWaitFailed &&
546 dataIntact && tailZero && relocationCorrect && narrowRelocationCorrect;
549 "dynamic-demand-page-publish",
550 "a staging page was published early, overwritten by a loser, or relocated via its alias");
553 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS dynamic-demand-page-publish");
557bool memoryMapFaultReplay() {
559 uintptr_t address = 0;
562 manager.
mapAnon(address, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write);
564 return fail(
"mmap-fault-replay",
"could not create an anonymous mapping");
568 const bool initialRead = manager.trapForHostedTest(address,
false,
false);
569 const bool initialMapped = va.
isMapped(
reinterpret_cast<void*
>(address));
571 physical_uintptr_t readPage = 0;
572 size_t readFlags = 0;
573 if (initialRead && initialMapped) {
574 va.
getMapping(
reinterpret_cast<void*
>(address), readPage, readFlags);
577 const bool missingReplay = manager.trapForHostedTest(address,
false,
false);
578 const bool replayedReadMapped = va.
isMapped(
reinterpret_cast<void*
>(address));
579 physical_uintptr_t replayedReadPage = 0;
580 size_t replayedReadFlags = 0;
581 if (missingReplay && replayedReadMapped) {
582 va.
getMapping(
reinterpret_cast<void*
>(address), replayedReadPage, replayedReadFlags);
585 const bool copyOnWrite = manager.trapForHostedTest(address,
true,
true);
586 const bool writeMapped = va.
isMapped(
reinterpret_cast<void*
>(address));
587 physical_uintptr_t writePage = 0;
588 size_t writeFlags = 0;
589 if (copyOnWrite && writeMapped) {
590 va.
getMapping(
reinterpret_cast<void*
>(address), writePage, writeFlags);
593 const bool writeReplay = manager.trapForHostedTest(address,
true,
true);
594 const bool replayedWriteMapped = va.
isMapped(
reinterpret_cast<void*
>(address));
595 physical_uintptr_t replayedWritePage = 0;
596 size_t replayedWriteFlags = 0;
597 if (writeReplay && replayedWriteMapped) {
598 va.
getMapping(
reinterpret_cast<void*
>(address), replayedWritePage, replayedWriteFlags);
603 const bool passed = initialRead && initialMapped && missingReplay && replayedReadMapped &&
604 copyOnWrite && writeMapped && writeReplay && replayedWriteMapped &&
605 readPage == replayedReadPage && readFlags == replayedReadFlags &&
607 writePage == replayedWritePage && writeFlags == replayedWriteFlags;
609 return fail(
"mmap-fault-replay",
"a replay repeated or skipped demand-page work");
612 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS mmap-fault-replay");
616bool memoryMapDeferredWriteReplay() {
617 constexpr const char* Test =
"mmap-deferred-write-replay";
619 uintptr_t address = 0;
622 manager.
mapAnon(address, pageSize, MemoryMappedObject::Read | MemoryMappedObject::Write);
624 return fail(Test,
"could not create an anonymous mapping");
628 const bool initialRead = manager.trapForHostedTest(address,
false,
false);
629 physical_uintptr_t readPage = 0;
630 size_t readFlags = 0;
631 if (initialRead && va.
isMapped(
reinterpret_cast<void*
>(address))) {
632 va.
getMapping(
reinterpret_cast<void*
>(address), readPage, readFlags);
635 const bool deferredWrite = manager.trapForHostedTest(address,
true,
false);
636 physical_uintptr_t writePage = 0;
637 size_t writeFlags = 0;
638 if (deferredWrite && va.
isMapped(
reinterpret_cast<void*
>(address))) {
639 va.
getMapping(
reinterpret_cast<void*
>(address), writePage, writeFlags);
649 return fail(Test,
"a deferred write accepted the read-only shared zero page");
652 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS mmap-deferred-write-replay");
657bool runHostedPageContentRegressions() {
658 constexpr size_t FileSize = 48;
659 constexpr size_t MemorySize = 96;
661 if (pageSize <= MemorySize) {
662 return fail(
"dynamic-demand-page-zero-fill",
"target page is too small for the fixture");
668 return fail(
"dynamic-demand-page-zero-fill",
"no current process");
671 uintptr_t address = 0;
672 if (!process->allocateUserRange(Process::UserRegion::Normal, pageSize, address)) {
673 return fail(
"dynamic-demand-page-zero-fill",
"could not reserve a target page");
679 uint8_t fileData[FileSize];
680 for (
size_t i = 0; i < FileSize; ++i) {
681 fileData[i] =
static_cast<uint8_t
>((i * 7) + 3);
683 DemandPageElf elf(FileSize, MemorySize);
685 g_FailedAllocationCalls = 0;
686 DynamicLinker::setDemandPageAllocationHookForTest(failDemandPageAllocation);
687 const bool allocationFailureRejected = !DynamicLinker::loadDemandPageForTest(
688 &elf,
reinterpret_cast<uintptr_t
>(fileData),
sizeof(fileData), address,
nullptr, address);
689 DynamicLinker::setDemandPageAllocationHookForTest(
nullptr);
690 const bool allocationFailureClean = !va.
isMapped(
reinterpret_cast<void*
>(address));
691 if (!allocationFailureRejected || g_FailedAllocationCalls != 1 || !allocationFailureClean) {
692 if (!allocationFailureClean) {
693 va.
unmap(
reinterpret_cast<void*
>(address));
695 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
696 return fail(
"dynamic-demand-page-zero-fill",
"an allocation failure left demand-page state");
699 const physical_uintptr_t dirtyPage = memory.
allocatePage();
702 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
703 return fail(
"dynamic-demand-page-zero-fill",
"could not map the sentinel page");
706 ByteSet(
reinterpret_cast<void*
>(address), 0xA5, pageSize);
707 va.
unmap(
reinterpret_cast<void*
>(address));
710 const bool loaded = DynamicLinker::loadDemandPageForTest(
711 &elf,
reinterpret_cast<uintptr_t
>(fileData),
sizeof(fileData), address,
nullptr, address);
713 physical_uintptr_t loadedPage = 0;
715 const bool mapped = va.
isMapped(
reinterpret_cast<void*
>(address));
717 va.
getMapping(
reinterpret_cast<void*
>(address), loadedPage, flags);
720 bool fileDataIntact = loaded && mapped;
721 bool remainderZero = loaded && mapped;
722 if (loaded && mapped) {
723 const uint8_t* bytes =
reinterpret_cast<const uint8_t*
>(address);
724 for (
size_t i = 0; i < FileSize; ++i) {
725 if (bytes[i] != fileData[i]) {
726 fileDataIntact =
false;
730 for (
size_t i = FileSize; i < pageSize; ++i) {
732 remainderZero =
false;
739 va.
unmap(
reinterpret_cast<void*
>(address));
742 process->freeUserRange(Process::UserRegion::Normal, address, pageSize);
744 const bool reusedSentinel = mapped && loadedPage == dirtyPage;
745 const bool demandPassed = loaded && reusedSentinel && fileDataIntact && remainderZero;
747 return fail(
"dynamic-demand-page-zero-fill",
"the demand-loaded page retained prior data");
750 NOTICE(
"HOSTED-PAGE-CONTENT-TEST: PASS dynamic-demand-page-zero-fill");
751 return dynamicDemandPagePublishesOnce() && memoryMappedFileEofZeroFill() &&
752 memoryMappedFilePublishesAfterInitialise() && memoryMapFaultReplay() &&
753 memoryMapDeferredWriteReplay();
Memory-mapped file interface.
virtual uint64_t readBytewise(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true)
virtual bool isBytewise() const
MemoryMappedObject * mapAnon(uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms)
static MemoryMapManager & instance()
size_t removeAndRelease(uintptr_t base, size_t length, VmStatus *status=nullptr)
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
virtual void freePage(physical_uintptr_t page)=0
static ProcessorInformation & information()
MUST_USE_RESULT bool acquireForCompletion(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Process * getParent() const
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
static const size_t Shared
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t Write
virtual void unmap(void *virtualAddress)=0