The Pedigree Project 0.1
MemoryMappedFile-rebias.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/process/Process.h"
3#include "pedigree/kernel/process/Thread.h"
4#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7
8#include "File.h"
9#include "MemoryMappedFile.h"
10
11MemoryMapManager::FileRemapStatus MemoryMapManager::remapFilePages(uintptr_t base, size_t length,
12 size_t byteOffset,
13 bool nonblock) {
14 TerminationDeferral lifetime;
15 OperationGuard operation(*this);
16 const size_t pageSize = PhysicalMemoryManager::getPageSize(), mask = pageSize - 1;
17 constexpr uint64_t MaximumFileOffset = 0x7fffffffffffffffULL;
18 if (!length || ((base | length | byteOffset) & mask) || length > ~uintptr_t(0) - base ||
19 byteOffset > MaximumFileOffset || length > MaximumFileOffset - byteOffset)
20 return FileRemapStatus::InvalidRange;
21 const size_t pages = length / pageSize;
22 if (pages > VirtualAddressSpace::MaximumRemapPages)
23 return FileRemapStatus::NoMemory;
24
25 auto& space = Processor::information().getVirtualAddressSpace();
26 auto* objects = m_MmObjectLists.lookup(&space);
27 if (objects && objects->count() > 4096)
28 return FileRemapStatus::NoMemory;
29 File* backing = nullptr;
30 FileMappingOrigin origin;
31 MemoryMappedObject::Permissions permissions = 0, maximum = 0;
32 MemoryLockMode inherited = MemoryLockMode::None;
33 const uintptr_t end = base + length;
34 for (uintptr_t cursor = base; cursor < end;) {
35 MemoryMappedObject* selected = nullptr;
36 uintptr_t selectedEnd = 0;
37 if (objects) {
38 for (auto* object : *objects) {
39 const uintptr_t objectEnd = (object->address() + object->length() + mask) & ~mask;
40 if (object->address() <= cursor && cursor < objectEnd) {
41 selected = object;
42 selectedEnd = objectEnd;
43 break;
44 }
45 }
46 }
47 if (!selected)
48 return space.isMapped(reinterpret_cast<void*>(cursor)) ? FileRemapStatus::Unsupported
49 : FileRemapStatus::InvalidRange;
50 auto* file = selected->backingFile();
51 if (!file)
52 return FileRemapStatus::InvalidRange;
53 auto* mapping = static_cast<MemoryMappedFile*>(selected);
54 if (mapping->m_Attachment || !mapping->m_Origin.openIdentity ||
55 !file->supportsRegularFileOperations())
56 return FileRemapStatus::Unsupported;
57 if (mapping->m_bCopyOnWrite || !mapping->m_Origin.writableOpen)
58 return FileRemapStatus::InvalidRange;
59 if (!backing) {
60 backing = file;
61 origin = mapping->m_Origin;
62 permissions = mapping->permissions();
63 maximum = mapping->maximumPermissions();
64 inherited = mapping->m_LockMode;
65 } else if (backing != file || origin.openIdentity != mapping->m_Origin.openIdentity ||
66 permissions != mapping->permissions() || maximum != mapping->maximumPermissions() ||
67 inherited != mapping->m_LockMode) {
68 return FileRemapStatus::InvalidRange;
69 }
70 cursor = selectedEnd < end ? selectedEnd : end;
71 }
72
73 auto* account = space.memoryLockAccount();
74 const MemoryLockMode future = account ? account->futureMode() : MemoryLockMode::None;
75 const MemoryLockMode mode = future == MemoryLockMode::OnFault ? MemoryLockMode::OnFault
76 : inherited != MemoryLockMode::None || future == MemoryLockMode::Eager
77 ? MemoryLockMode::Eager
78 : MemoryLockMode::None;
79 if (account) {
80 const bool privileged =
81 Processor::information().getCurrentThread()->getParent()->getEffectiveUserId() == 0;
82 if (inherited != MemoryLockMode::None && !account->permitsLocking(privileged))
83 return FileRemapStatus::PermissionDenied;
84 if (mode != MemoryLockMode::None) {
85 // Fixed mmap admits the entire new locked range before retiring victims.
86 // The publication path separately computes the final, net charge.
87 const auto charge = account->charge();
88 if (charge.rawPages > ~size_t(0) - charge.managedPages ||
89 pages > ~size_t(0) - charge.managedPages - charge.rawPages ||
90 !account->permitsTotalPages(charge.managedPages + charge.rawPages + pages, privileged))
91 return FileRemapStatus::LockLimit;
92 }
93 }
94
95 uintptr_t destination = base;
96 MapStatus status;
97 if (!mapFile(backing, destination, length, permissions, byteOffset, false,
98 Placement::FixedReplace, &status, maximum, SharedPointer<MappingAttachment>(), mode,
99 origin)) {
100 switch (status) {
101 case MapStatus::PolicyDenied:
102 return FileRemapStatus::PolicyDenied;
103 case MapStatus::LockLimit:
104 return FileRemapStatus::LockLimit;
105 case MapStatus::AddressInUse:
106 return FileRemapStatus::InvalidRange;
107 default:
108 return FileRemapStatus::NoMemory;
109 }
110 }
111 // Eager publication already populated, while ONFAULT must remain lazy even
112 // when the caller did not request MAP_NONBLOCK. Population is best effort.
113 if (mode == MemoryLockMode::None && !nonblock)
114 populateMemory(space, base, length);
115 return FileRemapStatus::Success;
116}
Memory-mapped file interface.
Definition File.h:75
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
MemoryMappedObject * mapFile(File *pFile, uintptr_t &address, size_t length, MemoryMappedObject::Permissions perms, size_t offset=0, bool bCopyOnWrite=true)
static ProcessorInformation & information()