The Pedigree Project 0.1
MemoryMappedFile-lock.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/Vector.h"
9
10#include "MemoryMappedFile.h"
11
13 public:
14 bool callerHasMemoryLockPrivilege() const override {
15 auto* thread = Processor::information().getCurrentThread();
16 return thread && thread->getParent()->getEffectiveUserId() == 0;
17 }
18 bool overlapsManagedMemory(VirtualAddressSpace& space, uintptr_t base,
19 size_t length) const override {
20 if (length > ~uintptr_t(0) - base)
21 return true;
22 auto* objects = MemoryMapManager::instance().m_MmObjectLists.lookup(&space);
23 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
24 if (objects)
25 for (auto* object : *objects) {
26 const uintptr_t end = (object->address() + object->length() + mask) & ~mask;
27 if (base < end && object->address() < base + length)
28 return true;
29 }
30 return false;
31 }
32 void enterOperation() override {
33 MemoryMapManager::instance().enterOperation();
34 }
35 void leaveOperation() override {
36 MemoryMapManager::instance().leaveOperation();
37 }
38};
39
40namespace {
42constexpr size_t MaximumLockObjects = 4096;
43constexpr size_t MaximumLockPages = 65536;
44uintptr_t objectEnd(MemoryMappedObject* object) {
45 const size_t mask = PhysicalMemoryManager::getPageSize() - 1;
46 return (object->address() + object->length() + mask) & ~mask;
47}
48MemoryLockStatus populationStatus(PopulationStatus status) {
49 switch (status) {
50 case PopulationStatus::Success:
51 return MemoryLockStatus::Success;
52 case PopulationStatus::NoMemory:
53 return MemoryLockStatus::PopulationNoMemory;
54 case PopulationStatus::Inaccessible:
55 return MemoryLockStatus::PopulationInaccessible;
56 case PopulationStatus::IoError:
57 return MemoryLockStatus::PopulationIoError;
58 }
59 return MemoryLockStatus::PopulationIoError;
60}
61bool addCharge(size_t previous, size_t removed, size_t added, size_t& result) {
62 if (removed > previous || added > ~size_t(0) - (previous - removed))
63 return false;
64 result = previous - removed + added;
65 return true;
66}
67} // namespace
68
69void MemoryMapManager::bindMemoryLockPolicy(VirtualAddressSpace& space) {
70 space.setUserMemoryPolicy(&policy);
71}
72
73PopulationStatus MemoryMappedObject::populatePage(VirtualAddressSpace& space, uintptr_t address) {
74 if (m_Permissions == None || beyondBackingEnd(address))
75 return PopulationStatus::Inaccessible;
76 const auto access = prepareResidentAccess(space, address);
77 if (access != PopulationStatus::Success)
78 return access;
79 const bool write = m_bCopyOnWrite && (m_Permissions & Write);
80 void* page = reinterpret_cast<void*>(address);
81 if (space.isMapped(page)) {
82 physical_uintptr_t physical;
83 size_t flags;
84 space.getMapping(page, physical, flags);
87 return PopulationStatus::Inaccessible;
88 if (!write || (flags & VirtualAddressSpace::Write))
89 return PopulationStatus::Success;
91 return space.handleCopyOnWriteFault(reinterpret_cast<void*>(address), true)
92 ? PopulationStatus::Success
93 : PopulationStatus::NoMemory;
94 }
95 PopulationStatus status = PopulationStatus::NoMemory;
96 auto* thread = Processor::information().getCurrentThread();
97 const size_t previousError = thread ? thread->getErrno() : 0;
98 if (thread)
99 thread->setErrno(0);
100 const bool populated = trap(space, address, write, &status);
101 if (thread)
102 thread->setErrno(previousError);
103 return populated ? PopulationStatus::Success : status;
104}
105
107 public:
108 LockPlan(MemoryMapManager& manager, VirtualAddressSpace& space, MemoryLockMode mode)
109 : m_Manager(manager), m_Space(space), m_Mode(mode) {}
110 ~LockPlan() override {
111 for (auto* object : m_Committed ? m_Retired : m_Staged)
112 delete object;
113 delete m_Replacement;
114 }
115 size_t removedPages() const override {
116 return m_Removed;
117 }
118 size_t addedPages() const override {
119 return m_Added;
120 }
121 size_t coveredPages() const override {
122 return m_Covered;
123 }
124 size_t eligiblePages() const override {
125 return m_Eligible;
126 }
127 void commit() override {
128 if (!m_Replacement) {
129 m_Committed = true;
130 return;
131 }
132 for (auto* object : m_Retired)
133 object->m_OwnsMappings = false;
134 for (auto* object : m_Staged)
135 object->m_OwnsMappings = true;
136 auto* previous = m_Manager.m_MmObjectLists.lookup(&m_Space);
137 {
138 LockGuard<Spinlock> guard(m_Manager.m_Lock);
139 m_Manager.m_MmObjectLists.insert(&m_Space, m_Replacement);
140 }
141 m_Replacement = nullptr;
142 delete previous;
143 m_Committed = true;
144 }
145 PopulationStatus populate() override {
146 if (m_Mode != MemoryLockMode::Eager)
147 return PopulationStatus::Success;
148 const size_t pageSize = PhysicalMemoryManager::getPageSize();
149 PopulationStatus result = PopulationStatus::Success;
150 for (auto* object : m_Populate) {
151 for (uintptr_t page = object->address(); page < objectEnd(object); page += pageSize) {
152 const auto status = object->populatePage(m_Space, page);
153 if (status != PopulationStatus::Success && result == PopulationStatus::Success)
154 result = status;
155 }
156 }
157 return result;
158 }
159 bool append(MemoryMappedObject* owner, uintptr_t first, uintptr_t last, MemoryLockMode mode,
160 bool populate) {
161 if (first == last)
162 return true;
163 auto* slice = owner->stageSlice(first, last - first, first, last - first);
164 if (!slice)
165 return false;
166 slice->m_LockMode = mode;
167 m_Staged.pushBack(slice);
168 if (populate)
169 m_Populate.pushBack(slice);
170 return m_Replacement->tryPushBack(slice);
171 }
172 MemoryMapManager& m_Manager;
173 VirtualAddressSpace& m_Space;
174 MemoryLockMode m_Mode;
175 MmObjectList* m_Replacement = nullptr;
176 Vector<MemoryMappedObject*> m_Retired, m_Staged, m_Populate;
177 size_t m_Removed = 0, m_Added = 0, m_Covered = 0, m_Eligible = 0;
178 bool m_Committed = false;
179};
180
181MemoryLockStatus MemoryMapManager::prepareManagedLocks(VirtualAddressSpace& space, uintptr_t base,
182 size_t length, MemoryLockMode mode, bool all,
184 auto* plan = new LockPlan(*this, space, mode);
186 if (!plan)
187 return MemoryLockStatus::NoMemory;
188 auto* objects = m_MmObjectLists.lookup(&space);
189 if (!objects || !objects->count()) {
190 result = pedigree_std::move(owned);
191 return MemoryLockStatus::Success;
192 }
193 if (objects->count() > MaximumLockObjects || !plan->m_Retired.tryReserve(objects->count()) ||
194 !plan->m_Staged.tryReserve(objects->count() * 3) ||
195 !plan->m_Populate.tryReserve(objects->count()))
196 return MemoryLockStatus::NoMemory;
197 plan->m_Replacement = new MmObjectList;
198 if (!plan->m_Replacement)
199 return MemoryLockStatus::NoMemory;
200 const size_t pageSize = PhysicalMemoryManager::getPageSize();
201 size_t stagedPages = 0;
202 for (auto* object : *objects) {
203 const uintptr_t end = objectEnd(object);
204 plan->m_Eligible += (end - object->address()) / pageSize;
205 const uintptr_t first = all || base < object->address() ? object->address() : base;
206 const uintptr_t last = all || base + length > end ? end : base + length;
207 if (first >= last) {
208 if (!plan->m_Replacement->tryPushBack(object))
209 return MemoryLockStatus::NoMemory;
210 continue;
211 }
212 const size_t pages = (last - first) / pageSize;
213 plan->m_Covered += pages;
214 if (object->m_LockMode != MemoryLockMode::None)
215 plan->m_Removed += pages;
216 if (mode != MemoryLockMode::None)
217 plan->m_Added += pages;
218 const size_t objectPages = (end - object->address()) / pageSize;
219 if (objectPages > MaximumLockPages - stagedPages)
220 return MemoryLockStatus::NoMemory;
221 stagedPages += objectPages;
222 plan->m_Retired.pushBack(object);
223 if (!plan->append(object, object->address(), first, object->m_LockMode, false) ||
224 !plan->append(object, first, last, mode, true) ||
225 !plan->append(object, last, end, object->m_LockMode, false))
226 return MemoryLockStatus::NoMemory;
227 }
228 if (plan->m_Replacement->count() > MaximumLockObjects)
229 return MemoryLockStatus::NoMemory;
230 result = pedigree_std::move(owned);
231 return MemoryLockStatus::Success;
232}
233
234MemoryLockStatus MemoryMapManager::lockMemory(VirtualAddressSpace& space, uintptr_t base,
235 size_t length, MemoryLockMode mode, bool privileged) {
236 OperationGuard operation(*this);
237 const size_t pageSize = PhysicalMemoryManager::getPageSize();
238 if ((base | length) & (pageSize - 1) || length > ~uintptr_t(0) - base)
239 return MemoryLockStatus::InvalidRange;
240 if (!length)
241 return MemoryLockStatus::Success;
242 auto* account = space.memoryLockAccount();
243 if (!account)
244 return MemoryLockStatus::Unsupported;
245 if (length / pageSize > MaximumLockPages)
246 return MemoryLockStatus::NoMemory;
248 auto status = prepareManagedLocks(space, base, length, mode, false, managed);
249 if (status != MemoryLockStatus::Success)
250 return status;
251 status = space.rawUserMemory().prepareLocks(base, length, mode, raw);
252 if (status != MemoryLockStatus::Success)
253 return status;
254 size_t covered = managed.get()->coveredPages() + raw.get()->coveredPages();
255 if (covered != length / pageSize)
256 return MemoryLockStatus::Unmapped;
257 auto charge = account->charge();
258 if (!addCharge(charge.managedPages, managed.get()->removedPages(), managed.get()->addedPages(),
259 charge.managedPages) ||
260 !addCharge(charge.rawPages, raw.get()->removedPages(), raw.get()->addedPages(),
261 charge.rawPages) ||
262 charge.rawPages > ~size_t(0) - charge.managedPages)
263 return MemoryLockStatus::NoMemory;
264 if (mode != MemoryLockMode::None &&
265 !account->permitsTotalPages(charge.managedPages + charge.rawPages, privileged))
266 return MemoryLockStatus::LockLimit;
267 managed.get()->commit();
268 raw.get()->commit();
269 account->publish(charge, account->futureMode());
270 const auto managedPopulation = managed.get()->populate();
271 const auto rawPopulation = raw.get()->populate();
272 return populationStatus(managedPopulation != PopulationStatus::Success ? managedPopulation
273 : rawPopulation);
274}
275
276MemoryLockStatus MemoryMapManager::lockAllMemory(VirtualAddressSpace& space, bool current,
277 MemoryLockMode currentMode,
278 MemoryLockMode futureMode, bool privileged) {
279 OperationGuard operation(*this);
280 auto* account = space.memoryLockAccount();
281 if (!account || ((currentMode != MemoryLockMode::None || futureMode != MemoryLockMode::None) &&
282 !space.rawUserMemory().completeInventory()))
283 return MemoryLockStatus::Unsupported;
284 if (!current) {
285 account->publish(account->charge(), futureMode);
286 return MemoryLockStatus::Success;
287 }
289 auto status = prepareManagedLocks(space, 0, 0, currentMode, true, managed);
290 if (status != MemoryLockStatus::Success)
291 return status;
292 status = space.rawUserMemory().prepareAllLocks(currentMode, raw);
293 if (status != MemoryLockStatus::Success)
294 return status;
295 const size_t eligible = managed.get()->eligiblePages();
296 if (raw.get()->eligiblePages() > ~size_t(0) - eligible)
297 return MemoryLockStatus::NoMemory;
298 if (currentMode != MemoryLockMode::None &&
299 !account->permitsTotalPages(eligible + raw.get()->eligiblePages(), privileged))
300 return MemoryLockStatus::LockLimit;
301 auto charge = account->charge();
302 if (!addCharge(charge.managedPages, managed.get()->removedPages(), managed.get()->addedPages(),
303 charge.managedPages) ||
304 !addCharge(charge.rawPages, raw.get()->removedPages(), raw.get()->addedPages(),
305 charge.rawPages))
306 return MemoryLockStatus::NoMemory;
307 managed.get()->commit();
308 raw.get()->commit();
309 account->publish(charge, futureMode);
310 // CURRENT publishes all lock state even if some pages cannot be populated.
311 managed.get()->populate();
312 raw.get()->populate();
313 return MemoryLockStatus::Success;
314}
315
316bool MemoryMapManager::hasLockedMemory(VirtualAddressSpace& space, uintptr_t base, size_t length) {
317 OperationGuard operation(*this);
318 if (!length)
319 return false;
320 if (length > ~uintptr_t(0) - base)
321 return true;
322 auto* objects = m_MmObjectLists.lookup(&space);
323 if (objects)
324 for (auto* object : *objects)
325 if (object->m_LockMode != MemoryLockMode::None && base < objectEnd(object) &&
326 object->address() < base + length)
327 return true;
328 return space.rawUserMemory().hasLockedMemory(base, length);
329}
330
331void MemoryMapManager::retireLockedPages(VirtualAddressSpace& space, size_t pages) {
332 auto* account = space.memoryLockAccount();
333 if (!account || !pages)
334 return;
335 auto charge = account->charge();
336 assert(pages <= charge.managedPages);
337 charge.managedPages -= pages;
338 account->publish(charge, account->futureMode());
339}
340
341PopulationStatus MemoryMapManager::populateMemory(VirtualAddressSpace& space, uintptr_t base,
342 size_t length) {
343 OperationGuard operation(*this);
344 const size_t pageSize = PhysicalMemoryManager::getPageSize();
345 if ((base | length) & (pageSize - 1) || length > ~uintptr_t(0) - base)
346 return PopulationStatus::Inaccessible;
347 auto* objects = m_MmObjectLists.lookup(&space);
348 if (!objects)
349 return length ? PopulationStatus::Inaccessible : PopulationStatus::Success;
350 PopulationStatus result = PopulationStatus::Success;
351 for (uintptr_t page = base; page < base + length; page += pageSize) {
352 MemoryMappedObject* selected = nullptr;
353 for (auto* object : *objects)
354 if (page >= object->address() && page < objectEnd(object)) {
355 selected = object;
356 break;
357 }
358 const auto status =
359 selected ? selected->populatePage(space, page) : PopulationStatus::Inaccessible;
360 if (result == PopulationStatus::Success && status != result)
361 result = status;
362 }
363 return result;
364}
Memory-mapped file interface.
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
static MemoryMapManager & instance()
size_t length() const
uintptr_t address() const
virtual bool trap(VirtualAddressSpace &space, uintptr_t address, bool bWrite, PopulationStatus *population=nullptr)=0
static ProcessorInformation & information()
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101
A vector / dynamic array.
Definition Vector.h:33
virtual bool isMapped(void *virtualAddress)=0
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
#define assert(x)
Definition assert.h:39
void pushBack(const T &value)
Definition Vector.h:275