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"
46physical_uintptr_t AnonymousMemoryMap::m_Zero = 0;
49static constexpr bool DebugMemoryMappings =
true;
51static constexpr bool DebugMemoryMappings =
false;
55class AddressSpaceRestorer {
57 explicit AddressSpaceRestorer(
VirtualAddressSpace& addressSpace) : m_AddressSpace(addressSpace) {}
59 ~AddressSpaceRestorer() {
64 NOT_COPYABLE_OR_ASSIGNABLE(AddressSpaceRestorer);
69class TemporaryPhysicalMapping {
71 TemporaryPhysicalMapping(physical_uintptr_t page,
const char* name)
72 : m_Region(name), m_Mapped(false) {
81 m_Mapped = kernelSpace.
map(page, m_Region.virtualAddress(),
88 ~TemporaryPhysicalMapping() {
101 void* address()
const {
102 return m_Region.virtualAddress();
106 NOT_COPYABLE_OR_ASSIGNABLE(TemporaryPhysicalMapping);
113 bool sharedWritable) {
116 if (permissions == MemoryMappedObject::None) {
119 if (permissions & MemoryMappedObject::Exec) {
122 if (!(permissions & MemoryMappedObject::Write)) {
131void* currentOperationOwner() {
133 Thread* thread = information.getCurrentThread();
134 return thread ?
static_cast<void*
>(thread) : static_cast<void*>(&information);
138MemoryMappedObject::~MemoryMappedObject() {}
140AnonymousMemoryMap::AnonymousMemoryMap(uintptr_t address,
size_t length,
148 FATAL(
"AnonymousMemoryMap: could not allocate the shared zero page");
152 TemporaryPhysicalMapping temporary(m_Zero,
"Anonymous Shared Zero Page");
153 if (!temporary.valid()) {
156 FATAL(
"AnonymousMemoryMap: could not map the shared zero page");
165bool AnonymousMemoryMap::initialisePhysicalPage(physical_uintptr_t physical) {
167 return SwapStore::instance().zeroPage(physical);
169 TemporaryPhysicalMapping temporary(physical,
"Anonymous Page Initialisation");
170 if (!temporary.valid())
177MemoryMappedFile::MemoryMappedFile(uintptr_t address,
size_t length,
size_t offset,
File* backing,
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)) {
196 m_Attachment = attachment;
199MemoryMappedFile::~MemoryMappedFile() {
204 m_pBacking->releaseMappingUse(m_ExecutableUse, m_SharedWriteUse);
209 m_Origin.openingPath.
reset();
210 m_Origin.writeLease.
reset();
219 m_MaximumPermissions, m_Attachment, m_Origin, m_ExecutableUse);
220 assert(pResult->m_UseAdmitted);
221 pResult->m_OwnerProcess = m_OwnerProcess;
227 if (it.value() == ~0UL)
241 ERROR(
"MemoryMappedFile::split() given bad at parameter (at="
247 ERROR(
"MemoryMappedFile::split() misused, at == base address");
260 m_MaximumPermissions, m_Attachment, m_Origin, m_ExecutableUse);
261 assert(pResult->m_UseAdmitted);
263 pResult->m_OwnerProcess = m_OwnerProcess;
264 pResult->m_LockMode = m_LockMode;
267 for (uintptr_t virt = at; virt < oldEnd; virt += pageSz) {
285 if (
length & (pageSz - 1)) {
303 for (uintptr_t virt = oldStart; virt <
m_Address; virt += pageSz) {
304 void* v =
reinterpret_cast<void*
>(virt);
307 physical_uintptr_t phys;
314 size_t fileOffset = (virt - oldStart) + oldOffset;
338 m_ExecutableUse =
true;
343 void*
address =
reinterpret_cast<void*
>(it.key());
345 physical_uintptr_t physical;
348 const size_t newFlags = protectionFlags(flags, perms, !
m_bCopyOnWrite);
359bool MemoryMappedFile::preparePermissions(uintptr_t base,
size_t length, Permissions perms) {
366 if (it.key() >= base && it.key() - base <
length && it.value() == ~0UL &&
374bool MemoryMappedFile::sharedBacking(uintptr_t at, uintptr_t&
identity,
size_t& offset)
const {
383bool MemoryMappedFile::usesBacking(uintptr_t
identity)
const {
387bool MemoryMappedFile::beyondBackingEnd(uintptr_t at)
const {
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 ||
398 const size_t firstDiscardedPage = end / pageSize + (end % pageSize != 0);
401 physical_uintptr_t tracked = 0;
403 if (address < m_Address || address - m_Address >=
m_Length) {
409 const size_t fileOffset =
m_Offset + displacement;
410 if (fileOffset / pageSize < firstDiscardedPage) {
413 const bool loan = tracked == ~0UL;
414 physical_uintptr_t physical = 0;
428physical_uintptr_t MemoryMappedFile::getBackingPage(
size_t fileOffset,
size_t mappingBytes) {
431 const size_t fileSize = pBacking->getSize();
432 if (fileOffset >= fileSize) {
435 size_t expected = fileSize - fileOffset < pageSz ? fileSize - fileOffset : pageSz;
436 if (expected > mappingBytes)
437 expected = mappingBytes;
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;
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");
458 "*** Could not manage to get a physical page for a "
460 << pBacking->
getName() <<
") - read got " << actual <<
" bytes!");
463 thread->setErrno(previousError);
472 bool succeeded =
true;
473 for (
size_t offset = 0; offset <
length; offset += pageSize)
474 succeeded =
sync(at + offset, async) && succeeded;
483 if (at < m_Address || at - m_Address >=
m_Length)
489 uint64_t offsets[Cache::MaxWritebackPages];
491 bool succeeded =
true;
492 for (
size_t offset = 0; offset <
length; offset += pageSize) {
493 const uintptr_t
address = at + offset;
500 offsets[count++] = fileOffset;
501 if (count == Cache::MaxWritebackPages) {
535 PopulationStatus* population) {
537 *population = PopulationStatus::NoMemory;
542 NOTICE(
"MemoryMappedFile::trap(" <<
address <<
", " << bWrite <<
")");
550 size_t fileOffset =
m_Offset + mappingOffset;
552 if (beyondBackingEnd(
address)) {
554 *population = PopulationStatus::Inaccessible;
558 bool bWillEof = (mappingOffset + pageSz) >
m_Length;
564 DEBUG_LOG(
" -> ignoring, was a write and this is not a writable mapping.");
567 }
else if ((!bWrite) && !(
m_Permissions & Read) && !population) {
569 DEBUG_LOG(
" -> ignoring, was a read and this is not a readable mapping.");
575 DEBUG_LOG(
" -> mapping offset is " << mappingOffset <<
", file offset: " << fileOffset);
576 DEBUG_LOG(
" -> will eof: " << bWillEof <<
", should copy: " << bShouldCopy);
580 size_t extraFlags = 0;
589 *population = thread && thread->getErrno() == Error::OutOfMemory
590 ? PopulationStatus::NoMemory
591 : PopulationStatus::IoError;
596 physical_uintptr_t phys = getBackingPage(fileOffset,
m_Length - mappingOffset);
600 *population = thread && thread->getErrno() == Error::OutOfMemory
601 ? PopulationStatus::NoMemory
602 : PopulationStatus::IoError;
604 ERROR(
"MemoryMappedFile::trap couldn't get a backing page");
617 bool r = va.
map(phys,
reinterpret_cast<void*
>(
address), flags | extraFlags);
619 ERROR(
"map() failed in MemoryMappedFile::trap (no-copy)");
633 ERROR(
"allocatePage() failed in MemoryMappedFile::trap (copy)");
637 size_t nBytes =
m_Length - mappingOffset;
641 bool readSucceeded =
false;
643 TemporaryPhysicalMapping temporary(newPhys,
"Mapped File Page Initialisation");
644 if (!temporary.valid()) {
645 ERROR(
"temporary map failed in MemoryMappedFile::trap (copy)");
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) {
655 ByteSet(
reinterpret_cast<void*
>(temporaryAddress + nRead), 0, pageSz - nRead);
658 if (!readSucceeded) {
661 *population = thread && thread->getErrno() == Error::OutOfMemory
662 ? PopulationStatus::NoMemory
663 : PopulationStatus::IoError;
678 bool r = va.
map(newPhys,
reinterpret_cast<void*
>(
address),
681 ERROR(
"map() failed in MemoryMappedFile::trap (copy)");
697 if (m_LockMode != MemoryLockMode::None)
705 bool released =
false;
711 void* page =
reinterpret_cast<void*
>(
address);
717 physical_uintptr_t physical = 0;
734void MemoryMappedFile::unmapUnlocked() {
736 NOTICE(
"MemoryMappedFile::unmap()");
745 void* v =
reinterpret_cast<void*
>(it.key());
750 physical_uintptr_t phys = 0;
754 physical_uintptr_t p = it.value();
790MemoryMapManager::OperationGuard::OperationGuard(
MemoryMapManager& manager,
bool tryOnly)
791 : m_EventDeferral(), m_Manager(manager), m_Acquired(!tryOnly || manager.tryEnterOperation()) {
793 m_Manager.enterOperation();
797MemoryMapManager::OperationGuard::~OperationGuard() {
799 m_Manager.leaveOperation();
808 m_pLifecycleOwner(nullptr),
809 m_LifecycleDepth(0) {
812 MemoryPressureManager::instance().
registerHandler(MemoryPressureManager::HighPriority,
this);
815void MemoryMapManager::enterOperation() {
816 void* owner = currentOperationOwner();
817#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
819 Process* diagnosticProcess = diagnosticThread ? diagnosticThread->
getParent() :
nullptr;
820 if (diagnosticProcess) {
821 diagnosticProcess->recordBenchmarkVmCounter(Process::VmGuardEntries);
826 if (m_pLifecycleOwner == owner) {
827#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
828 if (diagnosticProcess) {
829 diagnosticProcess->recordBenchmarkVmCounter(Process::VmGuardRecursiveEntries);
841 assert(!m_pLifecycleOwner);
842 assert(!m_LifecycleDepth);
843 m_pLifecycleOwner = owner;
844 m_LifecycleDepth = 1;
847bool MemoryMapManager::tryEnterOperation() {
848 void* owner = currentOperationOwner();
851 if (m_pLifecycleOwner == owner) {
862 assert(!m_pLifecycleOwner);
863 assert(!m_LifecycleDepth);
864 m_pLifecycleOwner = owner;
865 m_LifecycleDepth = 1;
869bool MemoryMapManager::operationOwnedByCurrentExecution() {
871 return m_LifecycleDepth && m_pLifecycleOwner == currentOperationOwner();
874void MemoryMapManager::leaveOperation() {
875 bool release =
false;
878 assert(m_pLifecycleOwner == currentOperationOwner());
879 assert(m_LifecycleDepth);
880 if (!--m_LifecycleDepth) {
881 m_pLifecycleOwner =
nullptr;
891MemoryMapManager::~MemoryMapManager() {
893 assert(unregistered);
908 if (!pMmObjectList2) {
910 if (!pMmObjectList2 || !
m_MmObjectLists.tryInsert(pOtherVa, pMmObjectList2)) {
911 delete pMmObjectList2;
924 pMmObjectList2->popBack();
927 pNewObject->m_OwnerProcess = pProcess;
928 pMmObjectList2->publishBack(pNewObject);
929 if (obj->m_Attachment) {
930 auto attachment = clonedAttachments.
lookup(obj->m_Attachment.
get());
932 attachment = obj->m_Attachment->clone(pProcess);
933 clonedAttachments.
insert(obj->m_Attachment.
get(), attachment);
935 pNewObject->m_Attachment = attachment;
942 return removeInternal(base, length,
false);
948 *status = VmStatus::InvalidRange;
950 if ((base & mask) || !length || length > ~
size_t(0) - mask)
952 length = (length + mask) & ~mask;
953 if (length > ~uintptr_t(0) - base)
956 if (space.runtimeMappingPages(base, length)) {
958 *status = VmStatus::Unsupported;
963 if (prepared != MemoryLockStatus::Success) {
966 prepared == MemoryLockStatus::Unsupported ? VmStatus::Unsupported : VmStatus::NoMemory;
969 VmStatus removedStatus;
970 const size_t affected = removeInternal(base, length,
true, &removedStatus);
971 if (removedStatus != VmStatus::Success) {
973 *status = removedStatus;
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);
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());
991 *status = VmStatus::Success;
1001 for (
auto it = objects->begin(); it != objects->end(); ++it) {
1003 if (object->m_Attachment && object->m_Attachment->baseAddress() == base &&
1018 if (!objects || !attachment) {
1023 for (
auto it = objects->begin(); it != objects->end();) {
1025 if (object->m_Attachment != attachment) {
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));
1036 releaseReservation(process, space, base, length);
1043 uintptr_t base,
size_t length) {
1044 const uintptr_t end = base + length;
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);
1057 if (dynamicStart && dynamicStart < dynamicEnd) {
1058 releaseIntersection(Process::UserRegion::Dynamic, dynamicStart, dynamicEnd);
1060 releaseIntersection(Process::UserRegion::Normal, addressSpace.
getUserStart(),
1064size_t MemoryMapManager::setPermissions(uintptr_t base,
size_t length,
1066 ProtectStatus* status) {
1067 OperationGuard operation(*
this);
1070 *status = ProtectStatus::InvalidRange;
1072 if (!length || (base & pageMask) || length > ~
static_cast<size_t>(0) - pageMask ||
1074 ~(MemoryMappedObject::Read | MemoryMappedObject::Write | MemoryMappedObject::Exec))) {
1077 length = (length + pageMask) & ~pageMask;
1078 if (length > ~
static_cast<uintptr_t
>(0) - base) {
1083 if (perms == MemoryMappedObject::None) {
1085 *status = ProtectStatus::Unsupported;
1090 if (!
allows(base, length, MemoryMappedObject::None)) {
1092 *status = ProtectStatus::Unmapped;
1099 const uintptr_t end = base + length;
1100 for (
auto it = objects->begin(); it != objects->end(); ++it) {
1102 const uintptr_t objectEnd = (
object->address() +
object->length() + pageMask) & ~pageMask;
1103 if (object->
address() < end && objectEnd > base &&
1104 (
object->maximumPermissions() & perms) != perms) {
1106 *status = ProtectStatus::AccessDenied;
1110 if (object->
address() < end && objectEnd > base &&
object->m_bCopyOnWrite &&
1111 (perms & MemoryMappedObject::Exec)) {
1112 File* backing =
object->backingFile();
1116 *status = ProtectStatus::TextBusy;
1119 backing->releaseMappingUse(
true,
false);
1124 size_t affected = 0;
1125 for (
auto it = objects->begin(); it != objects->end(); ++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)) {
1131 *status = ProtectStatus::NoMemory;
1136 size_t remaining = objects->count();
1137 for (
auto it = objects->begin(); remaining; ++it, --remaining) {
1139 const uintptr_t objectEnd = (
object->address() +
object->length() + pageMask) & ~pageMask;
1140 if (object->
address() >= end || objectEnd <= base) {
1143 if (object->
address() < base) {
1144 if (!objects->reserveBack(base)) {
1146 *status = ProtectStatus::NoMemory;
1149 auto* split =
object->split(base);
1153 *status = ProtectStatus::NoMemory;
1156 objects->publishBack(split);
1159 if (objectEnd > end) {
1160 if (!objects->reserveBack(end)) {
1162 *status = ProtectStatus::NoMemory;
1165 auto* split =
object->split(end);
1169 *status = ProtectStatus::NoMemory;
1172 objects->publishBack(split);
1174 object->setPermissions(perms);
1178 *status = ProtectStatus::Success;
1190 if (!pMmObjectList) {
1194 for (uintptr_t address = base; address < (base + length); address += pageSz) {
1196 it != pMmObjectList->end(); it++) {
1198 if (pObject->
matches(address & ~(pageSz - 1))) {
1211 if (!length || length > (~
static_cast<uintptr_t
>(0) - base)) {
1216 uintptr_t end = base + length;
1219 if (!pMmObjectList) {
1223#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1225 process->recordBenchmarkVmCounter(Process::VmAllowsCalls);
1226 process->recordBenchmarkVmCounter(Process::VmAllowsObjectCount, pMmObjectList->count());
1227 size_t objectVisits = 0;
1231 uintptr_t cursor = base;
1232 while (cursor < end) {
1233 uintptr_t coveredUntil = cursor;
1235 it != pMmObjectList->end(); ++it) {
1236#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
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;
1246 if (coveredUntil >= end) {
1247#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1248 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1255 if (coveredUntil == cursor) {
1256#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1257 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1261 cursor = coveredUntil < end ? coveredUntil : end;
1264#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1265 process->recordBenchmarkVmCounter(Process::VmAllowsObjectVisits, objectVisits);
1270bool MemoryMapManager::op(MemoryMapManager::Ops what, uintptr_t base,
size_t length,
bool async) {
1271 OperationGuard operation(*
this);
1277 if (!pMmObjectList) {
1281 bool success =
true;
1283 const uintptr_t end = base + length;
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;
1290 success =
object->syncRange(start, stop - start, async) && success;
1294 for (uintptr_t address = base; address < (base + length); address += pageSz) {
1296 it != pMmObjectList->end(); it++) {
1298 if (pObject->
matches(address & ~(pageSz - 1))) {
1301 success = pObject->
sync(address, async) && success;
1307 WARNING(
"Bad 'what' in MemoryMapManager::op()");
1319 *error =
static_cast<int>(Error::OutOfMemory);
1322 if (!length || (base & pageMask) || length > ~
static_cast<size_t>(0) - pageMask) {
1325 length = (length + pageMask) & ~pageMask;
1326 if (!
allows(base, length, MemoryMappedObject::None)) {
1329 const bool success = op(Sync, base, length, async);
1331 *error = success ? 0 :
static_cast<int>(Error::IoError);
1336bool MemoryMapManager::sharedBacking(
Process* process, uintptr_t address, uintptr_t&
identity,
1338 OperationGuard operation(*
this);
1346 auto*
object = objects->find(address);
1347 return object &&
object->sharedBacking(address,
identity, offset);
1350bool MemoryMapManager::faultInUnlocked(uintptr_t address,
bool write,
MemoryMappedObject*& selected,
1351 bool residentAnonymousOnly) {
1353#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1357 void* page =
reinterpret_cast<void*
>(pageAddress);
1358 const bool pageAligned = address == pageAddress;
1360 if (!selected || !selected->
matches(address)) {
1364#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1365 size_t objectVisits = 0;
1367#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1368 selected = objects->find(address, &objectVisits);
1370 Process::VmFaultInObjectVisits, objectVisits);
1372 selected = objects->find(address);
1377 if (residentAnonymousOnly && (!selected || selected->backingFile())) {
1380 const auto required = write ? MemoryMappedObject::Write : MemoryMappedObject::Read;
1381 if (selected && !(selected->
permissions() & required))
1384 physical_uintptr_t physical = 0;
1386 bool present = va.
getMapping(page, physical, flags);
1387 if (residentAnonymousOnly) {
1394 if (selected->prepareResidentAccess(va,
reinterpret_cast<uintptr_t
>(page)) !=
1395 PopulationStatus::Success)
1397 present = va.
getMapping(page, physical, flags);
1406#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1407 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInPresent);
1412#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1413 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInCopyOnWrite);
1422#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1423 diagnosticProcess->recordBenchmarkVmCounter(Process::VmFaultInTrap);
1425 return handleTrapUnlocked(address, write, present,
false, trapObject);
1428bool MemoryMapManager::faultIn(uintptr_t address,
bool write) {
1429 OperationGuard operation(*
this);
1431 return faultInUnlocked(address, write, selected);
1435 return accessRange(address, length, write,
false);
1439 return accessRange(address, length,
true,
true);
1442bool MemoryMapManager::accessRange(uintptr_t address,
size_t length,
bool write,
1443 bool residentAnonymousOnly) {
1447 if (length - 1 > (~
static_cast<uintptr_t
>(0) - address)) {
1451 auto faultRange = [&]() {
1453 const uintptr_t lastPage = (address + length - 1) & ~(pageSize - 1);
1454#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1456 process->recordBenchmarkVmCounter(Process::VmFaultInRangeCalls);
1457 process->recordBenchmarkVmCounter(Process::VmFaultInRangePages,
1458 (lastPage - (address & ~(pageSize - 1))) / pageSize + 1);
1461 for (uintptr_t page = address & ~(pageSize - 1);; page += pageSize) {
1462 if (!faultInUnlocked(page, write, selected, residentAnonymousOnly)) {
1465 if (page == lastPage) {
1471 if (operationOwnedByCurrentExecution())
1472 return faultRange();
1473 OperationGuard operation(*
this);
1474 return faultRange();
1477MemoryMapManager::FaultResolution MemoryMapManager::resolveUserFault(uintptr_t address,
bool write,
1482 return FaultResolution::Unhandled;
1483 OperationGuard operation(*
this);
1485#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1488#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1489 process->recordBenchmarkVmCounter(Process::VmFaultCalls);
1494 if (!normal && !dynamic) {
1495#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1496 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1498 return FaultResolution::Unhandled;
1502#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1503 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1505 return FaultResolution::Unhandled;
1507#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1508 process->recordBenchmarkVmCounter(Process::VmFaultObjectCount, objects->count());
1509 size_t objectVisits = 0;
1512 const auto required = execute ? MemoryMappedObject::Exec
1513 : write ? MemoryMappedObject::Write
1514 : MemoryMappedObject::Read;
1516#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1517 selected = objects->find(pageAddress, &objectVisits);
1519 selected = objects->find(pageAddress);
1521#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1522 process->recordBenchmarkVmCounter(Process::VmFaultObjectVisits, objectVisits);
1524 if (!selected || !(selected->
permissions() & required)) {
1525#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1526 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1528 return FaultResolution::Unhandled;
1530 if (selected->beyondBackingEnd(pageAddress)) {
1531#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1532 process->recordBenchmarkVmCounter(Process::VmFaultBacking);
1534 return FaultResolution::BackingFault;
1536 if (!handleTrapUnlocked(address, write, wasPresent, execute, selected)) {
1537#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1538 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1540 return FaultResolution::Unhandled;
1542 void* page =
reinterpret_cast<void*
>(pageAddress);
1543 physical_uintptr_t physical;
1545 if (!space.
getMapping(page, physical, flags)) {
1546#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1547 process->recordBenchmarkVmCounter(Process::VmFaultUnhandled);
1549 return FaultResolution::Unhandled;
1551 const bool accessible =
1557#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
1558 process->recordBenchmarkVmCounter(accessible ? Process::VmFaultResolved
1561 return accessible ? FaultResolution::Resolved : FaultResolution::Unhandled;
1565 op(Invalidate, base, length,
false);
1583 pMmObjectList->erase(it);
1585 if (object->m_LockMode != MemoryLockMode::None) {
1587 retireLockedPages(va, (object->
length() + pageSize - 1) / pageSize);
1605 if (!state.kernelMode())
1607 return handleTrap(address, bIsWrite, bWasPresent);
1610#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1611bool MemoryMapManager::trapForHostedTest(uintptr_t address,
bool bIsWrite,
bool bWasPresent) {
1612 return handleTrap(address, bIsWrite, bWasPresent);
1616bool MemoryMapManager::handleTrap(uintptr_t address,
bool bIsWrite,
bool bWasPresent,
bool execute,
1621 OperationGuard operation(*
this);
1623 return handleTrapUnlocked(address, bIsWrite, bWasPresent, execute, selected);
1626bool MemoryMapManager::handleTrapUnlocked(uintptr_t address,
bool bIsWrite,
bool bWasPresent,
1631 EMIT_IF(DebugMemoryMappings) {
1632 NOTICE(
"Trap start: " <<
Hex << address <<
", pid:tid " <<
Dec
1639 const uintptr_t pageAddress = address & ~(pageSz - 1);
1644 EMIT_IF(DebugMemoryMappings) {
1645 NOTICE_NOLOCK(
"trap: got lock");
1649 if (!pMmObjectList) {
1654 EMIT_IF(DebugMemoryMappings) {
1655 NOTICE_NOLOCK(
"trap: lookup complete " <<
reinterpret_cast<uintptr_t
>(pMmObjectList));
1659 pObject = pMmObjectList->find(pageAddress);
1664 EMIT_IF(DebugMemoryMappings) {
1665 ERROR(
"MemoryMapManager::trap() could not find an object for " << address);
1671 : bIsWrite ? MemoryMappedObject::Write
1672 : MemoryMappedObject::Read;
1677 if (pObject->prepareResidentAccess(va, pageAddress) != PopulationStatus::Success)
1685 const bool userAccessible =
1689 if (!bIsWrite && !bWasPresent) {
1703 PopulationStatus population = PopulationStatus::NoMemory;
1704 return pObject->
trap(va, address, bIsWrite, execute ? &population : nullptr);
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) &&
1733 AddressSpaceRestorer restoreAddressSpace(currva);
1735 bool bCompact =
false;
1740 for (MmObjectList::Iterator it2 = it.value()->begin(); it2 != it.value()->end(); ++it2) {
1741 if ((*it2)->reclaimAnonymousPage(*it.key()))
1743 bCompact = (*it2)->compact();
1756 NOTICE(
" -> success, hoping for Cache eviction...");
1774 it = pMmObjectList->begin()) {
1776 pMmObjectList->erase(it);
1778 if (object->m_LockMode != MemoryLockMode::None) {
1780 retireLockedPages(va, (object->
length() + pageSize - 1) / pageSize);
1786 delete pMmObjectList;
1797#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1798void MemoryMapManager::acquireLifecycleGateForHostedTest() {
1802void MemoryMapManager::releaseLifecycleGateForHostedTest() {
Memory-mapped file interface.
virtual uint64_t read(uint64_t location, uint64_t size, uintptr_t buffer, bool bCanBlock=true) final
void markPageExternallyWritable(size_t offset)
virtual bool syncPages(const uint64_t *offsets, size_t count)
size_t populateRange(size_t offset, size_t length)
virtual bool tryBeginMappingRelease()
virtual uintptr_t futexIdentity()
virtual physical_uintptr_t getPhysicalPage(size_t offset)
bool syncAndReturnPhysicalPage(size_t offset, bool async)
bool acquireMappingUse(bool executable, bool sharedWrite)
virtual bool prepareSharedMapping(size_t offset, size_t length)
virtual void returnPhysicalPage(size_t offset)
::Iterator< T, node_t > Iterator
bool reserveBack(uintptr_t address)
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)
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
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 m_Permissions
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()
static constexpr size_t getPageSize() PURE
static const size_t virtualOnly
static const size_t anonymous
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
VirtualAddressSpace * getAddressSpace()
static bool getInterrupts()
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
bool tryAcquire(size_t n=1)
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
bool acquire(bool recurse=false, bool safe=true)
Process * getParent() const
An iterator applicable for many data structures.
bool tryInsert(const K &key, const E &value)
void remove(const K &key)
bool contains(const K &key) const
bool lowerBound(const K &key, K &foundKey, E &foundValue) const
E lookup(const K &key) const
void insert(const K &key, const E &value)
bool untrackFile(File *pFile, bool destroy=true)
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
static const size_t CopyOnWrite
virtual uintptr_t getUserReservedStart() const =0
static const size_t CacheDisable
static const size_t Borrowed
virtual uintptr_t getDynamicStart() 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 uintptr_t getUserStart() const =0
static const size_t WriteCombine
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t NoAccess
static const size_t Write
virtual uintptr_t getDynamicEnd() const
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
static const size_t WriteProtected
static const size_t Execute
static const size_t Swapped
virtual void unmap(void *virtualAddress)=0
static const size_t WriteThrough
uintptr_t physicalAddress(physical_uintptr_t address) PURE