The Pedigree Project 0.1
RawUserMemory.cc
1/* Copyright (c) 2026, Pedigree Developers. */
2#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
3#include "pedigree/kernel/processor/UserMemoryPolicy.h"
4#include "pedigree/kernel/processor/VirtualAddressSpace.h"
5#include "pedigree/kernel/utilities/Vector.h"
6#include "pedigree/kernel/utilities/assert.h"
7
8namespace {
9constexpr size_t MaximumRawSegments = 4096;
10size_t pageSize() {
12}
13bool validRange(uintptr_t base, size_t length) {
14 return !(base % pageSize()) && !(length % pageSize()) && length <= ~uintptr_t(0) - base;
15}
16} // namespace
17
19 struct Entry {
20 UserRegion region;
21 MemoryLockMode mode;
22 };
23 Vector<Entry> entries;
24 uint64_t nextId = 1;
25 uint64_t epoch = 0;
26 bool complete = false;
27};
28
30 public:
31 explicit Plan(RawUserMemory& owner) : owner(owner), epoch(owner.m_State.get()->epoch) {}
32 size_t removedPages() const override {
33 return removed;
34 }
35 size_t addedPages() const override {
36 return added;
37 }
38 size_t coveredPages() const override {
39 return covered;
40 }
41 size_t eligiblePages() const override {
42 return eligible;
43 }
44 size_t removedRangeCount() const override {
45 return retired.count();
46 }
47 const MemoryLockRange* removedRanges() const override {
48 return retired.count() ? &retired[0] : nullptr;
49 }
50 void commit() override {
51 assert(!committed && owner.m_State.get()->epoch == epoch);
52 for (const MemoryLockRange& region : retired) {
53 for (size_t offset = 0; offset < region.length; offset += pageSize()) {
54 physical_uintptr_t physical = 0;
55 size_t flags = 0;
56 void* address = reinterpret_cast<void*>(region.base + offset);
57 if (owner.m_Space.detachMapping(address, physical, flags) &&
61 }
62 }
63 owner.m_State.get()->entries.swap(entries);
64 ++owner.m_State.get()->epoch;
65 committed = true;
66 }
67 PopulationStatus populate() override {
68 for (const UserRegion& region : populateRegions) {
69 for (size_t offset = 0; offset < region.length; offset += pageSize()) {
70 void* address = reinterpret_cast<void*>(region.base + offset);
71 if (!owner.m_Space.isMapped(address))
72 return PopulationStatus::Inaccessible;
73 physical_uintptr_t physical = 0;
74 size_t flags = 0;
75 owner.m_Space.getMapping(address, physical, flags);
77 return PopulationStatus::Inaccessible;
78 if (region.privateWritable && (flags & VirtualAddressSpace::CopyOnWrite)) {
80 return PopulationStatus::Inaccessible;
81 if (!owner.m_Space.handleCopyOnWriteFault(address, true))
82 return PopulationStatus::NoMemory;
83 }
84 }
85 }
86 return PopulationStatus::Success;
87 }
88 bool append(const State::Entry& entry) {
89 if (!entry.region.length)
90 return true;
91 if (entries.count() == MaximumRawSegments || !entries.tryReserve(entries.count() + 1))
92 return false;
93 entries.pushBack(entry);
94 return true;
95 }
96 bool appendPart(const State::Entry& entry, uintptr_t base, size_t length, MemoryLockMode mode) {
97 State::Entry part = entry;
98 part.region.base = base;
99 part.region.length = length;
100 part.mode = mode;
101 return append(part);
102 }
103 bool retire(const State::Entry& entry, uintptr_t base, size_t length) {
104 if (!length)
105 return true;
106 if (!retired.tryReserve(retired.count() + 1))
107 return false;
108 retired.pushBack({base, length});
109 if (entry.mode != MemoryLockMode::None)
110 removed += length / pageSize();
111 return true;
112 }
113 bool populateLater(UserRegion region) {
114 if (!region.length)
115 return true;
116 if (!populateRegions.tryReserve(populateRegions.count() + 1))
117 return false;
118 populateRegions.pushBack(region);
119 return true;
120 }
121 bool addRegion(UserRegion region, MemoryLockMode mode) {
122 if (!region.length)
123 return true;
124 if (UserMemoryPolicy* policy = owner.m_Space.userMemoryPolicy()) {
125 if (policy->overlapsManagedMemory(owner.m_Space, region.base, region.length))
126 return false;
127 }
128 for (const auto& entry : entries) {
129 if (entry.region.base < region.base + region.length &&
130 region.base < entry.region.base + entry.region.length)
131 return false;
132 }
133 if (!append({region, mode}))
134 return false;
135 if (mode != MemoryLockMode::None)
136 added += region.length / pageSize();
137 return mode != MemoryLockMode::Eager || populateLater(region);
138 }
139 void sort() {
140 for (size_t i = 1; i < entries.count(); ++i) {
141 State::Entry value = entries[i];
142 size_t j = i;
143 while (j && entries[j - 1].region.base > value.region.base) {
144 entries[j] = entries[j - 1];
145 --j;
146 }
147 entries[j] = value;
148 }
149 size_t kept = 0;
150 for (size_t i = 0; i < entries.count(); ++i) {
151 const auto& current = entries[i];
152 if (kept) {
153 auto& previous = entries[kept - 1];
154 if (previous.region.id == current.region.id && previous.mode == current.mode &&
155 previous.region.base + previous.region.length == current.region.base &&
156 previous.region.kind == current.region.kind &&
157 previous.region.privateWritable == current.region.privateWritable) {
158 previous.region.length += current.region.length;
159 continue;
160 }
161 }
162 entries[kept++] = current;
163 }
164 while (entries.count() > kept)
165 entries.popBack();
166 }
167 static Plan* create(RawUserMemory& owner, UniquePointer<PreparedMemoryLock>& result) {
168 result.reset();
169 if (!owner.m_State)
170 owner.m_State = UniquePointer<State>::adopt(new State);
171 if (!owner.m_State)
172 return nullptr;
173 Plan* plan = new Plan(owner);
175 return plan;
176 }
177 RawUserMemory& owner;
178 uint64_t epoch;
179 Vector<State::Entry> entries;
181 Vector<UserRegion> populateRegions;
182 size_t removed = 0, added = 0, covered = 0, eligible = 0;
183 bool committed = false;
184};
185
186RawUserMemory::RawUserMemory(VirtualAddressSpace& space) : m_Space(space), m_State() {}
187RawUserMemory::~RawUserMemory() = default;
188
189uint64_t RawUserMemory::nextRegionId() {
190 if (!m_State)
191 m_State = UniquePointer<State>::adopt(new State);
192 if (!m_State || !m_State.get()->nextId)
193 return 0;
194 return m_State.get()->nextId++;
195}
196
197bool RawUserMemory::completeInventory() const {
198 return m_State && m_State.get()->complete;
199}
200void RawUserMemory::setCompleteInventory(bool complete) {
201 if (!m_State)
202 m_State = UniquePointer<State>::adopt(new State);
203 if (m_State)
204 m_State.get()->complete = complete;
205}
206
207bool RawUserMemory::covers(uintptr_t base, size_t length) const {
208 if (!m_State || !length || length > ~uintptr_t(0) - base)
209 return false;
210 const uintptr_t end = base + length;
211 for (const auto& entry : m_State.get()->entries) {
212 const uintptr_t regionEnd = entry.region.base + entry.region.length;
213 if (regionEnd <= base)
214 continue;
215 if (entry.region.base > base)
216 return false;
217 if (regionEnd >= end)
218 return true;
219 base = regionEnd;
220 }
221 return false;
222}
223
224bool RawUserMemory::hasLockedMemory(uintptr_t base, size_t length) const {
225 if (!m_State || length > ~uintptr_t(0) - base)
226 return false;
227 for (const auto& entry : m_State.get()->entries) {
228 if (entry.mode != MemoryLockMode::None && entry.region.base < base + length &&
229 base < entry.region.base + entry.region.length)
230 return true;
231 }
232 return false;
233}
234
235MemoryLockStatus RawUserMemory::prepareChange(const UserRegion* previous,
236 const UserRegion* replacement,
238 if ((!previous && !replacement) ||
239 (previous && (!previous->id || !validRange(previous->base, previous->length))) ||
240 (replacement && (!replacement->id || !validRange(replacement->base, replacement->length))) ||
241 (previous && replacement && previous->id != replacement->id))
242 return MemoryLockStatus::InvalidRange;
244 Plan* plan = Plan::create(*this, prepared);
245 if (!plan)
246 return MemoryLockStatus::NoMemory;
247 for (const auto& entry : m_State.get()->entries) {
248 if (!previous || entry.region.id != previous->id) {
249 if (!plan->append(entry))
250 return MemoryLockStatus::NoMemory;
251 continue;
252 }
253 const uintptr_t end = entry.region.base + entry.region.length;
254 const uintptr_t keepStart = replacement && replacement->base > entry.region.base
255 ? replacement->base
256 : entry.region.base;
257 const uintptr_t replacementEnd = replacement ? replacement->base + replacement->length : 0;
258 const uintptr_t keepEnd = replacementEnd < end ? replacementEnd : end;
259 if (!replacement || keepStart >= keepEnd) {
260 if (!plan->retire(entry, entry.region.base, entry.region.length))
261 return MemoryLockStatus::NoMemory;
262 } else if (!plan->retire(entry, entry.region.base, keepStart - entry.region.base) ||
263 !plan->appendPart(entry, keepStart, keepEnd - keepStart, entry.mode) ||
264 !plan->retire(entry, keepEnd, end - keepEnd)) {
265 return MemoryLockStatus::NoMemory;
266 }
267 }
268 if (replacement) {
269 MemoryLockAccount* account = m_Space.memoryLockAccount();
270 const MemoryLockMode future = account ? account->futureMode() : MemoryLockMode::None;
271 UserRegion added = *replacement;
272 if (!previous) {
273 if (!plan->addRegion(added, future))
274 return MemoryLockStatus::NoMemory;
275 } else {
276 // A fixed mapping may have punched holes in an allocation. Preserve them
277 // when its logical heap end grows; only admit the newly added tail.
278 if (replacement->base < previous->base) {
279 added.length = previous->base - replacement->base;
280 if (added.length > replacement->length)
281 added.length = replacement->length;
282 if (!plan->addRegion(added, future))
283 return MemoryLockStatus::NoMemory;
284 }
285 const uintptr_t oldEnd = previous->base + previous->length;
286 const uintptr_t newEnd = replacement->base + replacement->length;
287 if (newEnd > oldEnd) {
288 added.base = oldEnd > replacement->base ? oldEnd : replacement->base;
289 added.length = newEnd - added.base;
290 if (!plan->addRegion(added, future))
291 return MemoryLockStatus::NoMemory;
292 }
293 }
294 }
295 plan->sort();
296 result = pedigree_std::move(prepared);
297 return MemoryLockStatus::Success;
298}
299
300MemoryLockStatus RawUserMemory::prepareReplacement(uintptr_t base, size_t length,
302 if (!validRange(base, length))
303 return MemoryLockStatus::InvalidRange;
304 bool overlaps = false;
305 if (m_State && length) {
306 const uintptr_t end = base + length;
307 for (const auto& entry : m_State.get()->entries) {
308 if (entry.region.base >= end)
309 break;
310 if (base < entry.region.base + entry.region.length) {
311 overlaps = true;
312 break;
313 }
314 }
315 }
316 if (!overlaps) {
317 result.reset();
318 return MemoryLockStatus::Success;
319 }
321 Plan* plan = Plan::create(*this, prepared);
322 if (!plan)
323 return MemoryLockStatus::NoMemory;
324 for (const auto& entry : m_State.get()->entries) {
325 const uintptr_t end = entry.region.base + entry.region.length;
326 const uintptr_t first = base > entry.region.base ? base : entry.region.base;
327 const uintptr_t last = base + length < end ? base + length : end;
328 if (first >= last) {
329 if (!plan->append(entry))
330 return MemoryLockStatus::NoMemory;
331 } else {
332 plan->covered += (last - first) / pageSize();
333 if (!plan->appendPart(entry, entry.region.base, first - entry.region.base, entry.mode) ||
334 !plan->retire(entry, first, last - first) ||
335 !plan->appendPart(entry, last, end - last, entry.mode))
336 return MemoryLockStatus::NoMemory;
337 }
338 }
339 plan->sort();
340 result = pedigree_std::move(prepared);
341 return MemoryLockStatus::Success;
342}
343
344MemoryLockStatus RawUserMemory::prepareLocks(uintptr_t base, size_t length, MemoryLockMode mode,
346 if (!validRange(base, length))
347 return MemoryLockStatus::InvalidRange;
349 Plan* plan = Plan::create(*this, prepared);
350 if (!plan)
351 return MemoryLockStatus::NoMemory;
352 for (const auto& entry : m_State.get()->entries) {
353 const uintptr_t end = entry.region.base + entry.region.length;
354 const uintptr_t first = base > entry.region.base ? base : entry.region.base;
355 const uintptr_t last = base + length < end ? base + length : end;
356 if (first >= last) {
357 if (!plan->append(entry))
358 return MemoryLockStatus::NoMemory;
359 continue;
360 }
361 const size_t pages = (last - first) / pageSize();
362 plan->covered += pages;
363 plan->eligible += pages;
364 if (entry.mode != MemoryLockMode::None)
365 plan->removed += pages;
366 if (mode != MemoryLockMode::None)
367 plan->added += pages;
368 if (!plan->appendPart(entry, entry.region.base, first - entry.region.base, entry.mode) ||
369 !plan->appendPart(entry, first, last - first, mode) ||
370 !plan->appendPart(entry, last, end - last, entry.mode))
371 return MemoryLockStatus::NoMemory;
372 if (mode == MemoryLockMode::Eager) {
373 UserRegion region = entry.region;
374 region.base = first;
375 region.length = last - first;
376 if (!plan->populateLater(region))
377 return MemoryLockStatus::NoMemory;
378 }
379 }
380 // Skip absent page-table subtrees when the range spans large user holes.
381 plan->covered += m_Space.runtimeMappingPages(base, length);
382 plan->sort();
383 result = pedigree_std::move(prepared);
384 return MemoryLockStatus::Success;
385}
386
387MemoryLockStatus RawUserMemory::prepareAllLocks(MemoryLockMode mode,
389 if (mode != MemoryLockMode::None && !completeInventory())
390 return MemoryLockStatus::Unsupported;
392 Plan* plan = Plan::create(*this, prepared);
393 if (!plan)
394 return MemoryLockStatus::NoMemory;
395 for (const auto& entry : m_State.get()->entries) {
396 const size_t pages = entry.region.length / pageSize();
397 plan->covered += pages;
398 plan->eligible += pages;
399 if (entry.mode != MemoryLockMode::None)
400 plan->removed += pages;
401 if (mode != MemoryLockMode::None)
402 plan->added += pages;
403 if (!plan->appendPart(entry, entry.region.base, entry.region.length, mode) ||
404 (mode == MemoryLockMode::Eager && !plan->populateLater(entry.region)))
405 return MemoryLockStatus::NoMemory;
406 }
407 plan->sort();
408 result = pedigree_std::move(prepared);
409 return MemoryLockStatus::Success;
410}
411
412MemoryLockStatus RawUserMemory::cloneInto(RawUserMemory& target) {
413 if (&target == this)
414 return MemoryLockStatus::InvalidRange;
415 auto copied = UniquePointer<State>::adopt(new State);
416 if (!copied)
417 return MemoryLockStatus::NoMemory;
418 if (m_State) {
419 copied.get()->complete = m_State.get()->complete;
420 copied.get()->nextId = m_State.get()->nextId;
421 if (!copied.get()->entries.tryReserve(m_State.get()->entries.count()))
422 return MemoryLockStatus::NoMemory;
423 for (auto entry : m_State.get()->entries) {
424 entry.mode = MemoryLockMode::None;
425 copied.get()->entries.pushBack(entry);
426 }
427 }
428 target.m_State = pedigree_std::move(copied);
429 return MemoryLockStatus::Success;
430}
431
432void RawUserMemory::clear() {
433 if (m_State) {
434 m_State.get()->entries.clear();
435 m_State.get()->complete = false;
436 ++m_State.get()->epoch;
437 }
438 if (MemoryLockAccount* account = m_Space.memoryLockAccount()) {
439 auto charge = account->charge();
440 charge.rawPages = 0;
441 account->publish(charge, account->futureMode());
442 }
443}
444
445size_t RawUserMemory::retireRegion(uint64_t id) {
446 if (!m_State || !id)
447 return 0;
448 State& state = *m_State.get();
449 size_t kept = 0, removed = 0;
450 for (size_t i = 0; i < state.entries.count(); ++i) {
451 const State::Entry entry = state.entries[i];
452 if (entry.region.id != id) {
453 state.entries[kept++] = entry;
454 continue;
455 }
456 if (entry.mode != MemoryLockMode::None)
457 removed += entry.region.length / pageSize();
458 for (size_t offset = 0; offset < entry.region.length; offset += pageSize()) {
459 physical_uintptr_t physical = 0;
460 size_t flags = 0;
461 if (m_Space.detachMapping(reinterpret_cast<void*>(entry.region.base + offset), physical,
462 flags) &&
466 }
467 }
468 while (state.entries.count() > kept)
469 state.entries.popBack();
470 ++state.epoch;
471 return removed;
472}
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
bool covers(uintptr_t base, size_t length) const
MemoryLockStatus prepareReplacement(uintptr_t base, size_t length, UniquePointer< PreparedMemoryLock > &result)
static UniquePointer< T > adopt(T *pointer)
Definition Pointers.h:101
A vector / dynamic array.
Definition Vector.h:33
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
void pushBack(const T &value)
Definition Vector.h:275
size_t count() const
Definition Vector.h:270