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"
13bool MemoryMappedFile::backingRangeValid(uintptr_t source,
size_t length)
const {
19 uintptr_t destination,
size_t destinationLength) {
20 if (!backingRangeValid(source, destinationLength))
24 m_Permissions, m_MaximumPermissions, m_Attachment, m_Origin, m_ExecutableUse);
27 result->m_OwnerProcess = m_OwnerProcess;
28 result->m_OwnsMappings =
false;
29 result->m_LockMode = m_LockMode;
33 const size_t preserved = sourceLength < destinationLength ? sourceLength : destinationLength;
36 if (it.key() >= source && it.key() - source < preserved &&
37 !result->m_Mappings.tryInsert(destination + (it.key() - source), it.value())) {
47using Status = MemoryMapManager::VmStatus;
48using PageStatus = VirtualAddressSpace::RemapStatus;
51 return (object->
address() + object->
length() + pageMask) & ~pageMask;
53bool overlaps(uintptr_t a,
size_t aLength, uintptr_t b,
size_t bLength) {
54 return a < b + bLength && b < a + aLength;
58 if (length > ~uintptr_t(0) - base)
60 const uintptr_t end = base + length;
62 return &snapshot.dynamic;
64 return &snapshot.normal;
67bool allocateAnywhere(Snapshot& snapshot,
size_t length,
size_t pageMask, uintptr_t& address) {
70 uintptr_t allocation = 0;
71 if (!allocator->allocate(length + pageMask, allocation))
73 address = (allocation + pageMask) & ~pageMask;
74 if (address != allocation && !allocator->tryFree(allocation, address - allocation))
76 const uintptr_t tail = address + length;
77 if (tail < allocation + length + pageMask &&
78 !allocator->tryFree(tail, allocation + length + pageMask - tail))
84Status translate(PageStatus status) {
86 case PageStatus::Success:
87 return Status::Success;
88 case PageStatus::InvalidRange:
89 return Status::InvalidRange;
90 case PageStatus::Unsupported:
91 return Status::Unsupported;
93 return Status::NoMemory;
101 bool committed =
false;
103 for (
auto*
object : committed ? retired : staged)
106 bool appendSlice(
MemoryMappedObject* owner, uintptr_t from,
size_t length, uintptr_t destination,
117MemoryMapManager::VmStatus MemoryMapManager::remap(
const RemapRequest& request, uintptr_t& result) {
118 OperationGuard operation(*
this);
120 const size_t pageMask = pageSize - 1;
121 if (!request.oldLength)
122 return request.mayMove ? VmStatus::Unsupported : VmStatus::InvalidRange;
123 if (!request.newLength || ((request.source | request.oldLength | request.newLength) & pageMask) ||
124 request.oldLength > ~uintptr_t(0) - request.source ||
125 request.newLength > ~
size_t(0) - pageMask ||
127 (!request.mayMove || (request.destination & pageMask) ||
128 request.newLength > ~uintptr_t(0) - request.destination ||
129 overlaps(request.source, request.oldLength, request.destination, request.newLength))))
130 return VmStatus::InvalidRange;
136 for (
auto*
object : *objects) {
137 if (object->
address() <= request.source && request.source < roundedEnd(
object, pageMask) &&
138 request.oldLength <= roundedEnd(
object, pageMask) - request.source) {
145 return !
contains(request.source, request.oldLength) &&
146 space.
isMapped(
reinterpret_cast<void*
>(request.source))
147 ? VmStatus::Unsupported
148 : VmStatus::Unmapped;
149 if (source->backingFile() && source->backingFile()->isDirectPhysicalMapping())
150 return VmStatus::Unsupported;
151 if (!source->backingRangeValid(request.source, request.newLength))
152 return VmStatus::InvalidRange;
153 auto attachment = source->m_Attachment;
155 if (request.source != source->
address() ||
156 request.oldLength != roundedEnd(source, pageMask) - source->
address() ||
157 request.newLength > request.oldLength)
158 return VmStatus::Unsupported;
159 for (
auto*
object : *objects)
160 if (object != source && object->m_Attachment.get() == attachment.get())
161 return VmStatus::Unsupported;
163 if (!request.fixed && request.oldLength == request.newLength) {
164 result = request.source;
165 return VmStatus::Success;
169 constexpr size_t MaximumObjects = 4096;
170 if (request.oldLength / pageSize > VirtualAddressSpace::MaximumRemapPages ||
171 request.newLength / pageSize > VirtualAddressSpace::MaximumRemapPages ||
172 objects->count() > MaximumObjects)
173 return VmStatus::NoMemory;
174 for (
size_t attempt = 0; attempt < 32; ++attempt) {
176 if (!process->snapshotUserReservations(snapshot))
177 return VmStatus::NoMemory;
178 uintptr_t destination = request.fixed ? request.destination : request.source;
179 if (!request.fixed && request.newLength > request.oldLength) {
180 const uintptr_t extension = request.source + request.oldLength;
182 allocatorFor(snapshot, space, extension, request.newLength - request.oldLength);
184 extension == roundedEnd(source, pageMask) && allocator &&
185 allocator->
allocateSpecific(extension, request.newLength - request.oldLength);
187 if (!request.mayMove ||
188 !allocateAnywhere(snapshot, request.newLength, pageMask, destination))
189 return VmStatus::NoMemory;
193 allocatorFor(snapshot, space, destination, request.newLength);
194 if (!destinationAllocator)
195 return VmStatus::InvalidRange;
198 MetadataPlan metadata;
199 if (!victims.tryReserve(objects->count()) || !metadata.retired.tryReserve(objects->count()) ||
200 !metadata.staged.tryReserve(objects->count() * 3 + 1))
201 return VmStatus::NoMemory;
202 size_t metadataPages = request.newLength / pageSize;
203 for (
auto*
object : *objects) {
204 const size_t length = roundedEnd(
object, pageMask) -
object->address();
205 const bool victim = destination != request.source &&
206 overlaps(destination, request.newLength, object->
address(), length);
209 return VmStatus::NoMemory;
210 if (object->backingFile() && object->backingFile()->isDirectPhysicalMapping())
211 return VmStatus::Unsupported;
212 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
213 const uintptr_t last =
object->address() + length < destination + request.newLength
214 ?
object->address() + length
215 : destination + request.newLength;
216 victims.
pushBack({first, last - first});
218 if (
object == source || victim) {
219 if (length / pageSize > VirtualAddressSpace::MaximumRemapPages - metadataPages)
220 return VmStatus::NoMemory;
221 metadataPages += length / pageSize;
222 metadata.retired.pushBack(
object);
225 auto* account = space.memoryLockAccount();
229 if (source->m_LockMode != MemoryLockMode::None)
230 removed += request.oldLength / pageSize;
231 for (
auto*
object : metadata.retired) {
232 if (
object == source || object->m_LockMode == MemoryLockMode::None)
234 const uintptr_t first =
object->address() > destination ?
object->address() : destination;
235 const uintptr_t end = roundedEnd(
object, pageMask);
236 const uintptr_t last =
237 end < destination + request.newLength ? end : destination + request.newLength;
238 removed += (last - first) / pageSize;
240 assert(removed <= charge.managedPages);
241 charge.managedPages -= removed;
242 if (source->m_LockMode != MemoryLockMode::None) {
243 const size_t added = request.newLength / pageSize;
244 if (added > ~
size_t(0) - charge.managedPages)
245 return VmStatus::NoMemory;
246 charge.managedPages += added;
247 if (charge.rawPages > ~
size_t(0) - charge.managedPages ||
248 (request.newLength > request.oldLength &&
249 !account->permitsTotalPages(charge.managedPages + charge.rawPages,
250 process->getEffectiveUserId() == 0)))
251 return VmStatus::LockLimit;
255 uintptr_t cursor = destination;
256 const uintptr_t end = destination + request.newLength;
257 while (cursor < end) {
258 uintptr_t coveredEnd = cursor, next = end;
259 for (
const auto& victim : victims) {
260 if (victim.base <= cursor && cursor < victim.base + victim.length)
261 coveredEnd = victim.base + victim.length;
262 else if (victim.base > cursor && victim.base < next)
265 if (coveredEnd > cursor)
266 cursor = coveredEnd < end ? coveredEnd : end;
269 return VmStatus::NoMemory;
274 uintptr_t released = request.source;
275 size_t releasedLength = request.oldLength;
276 if (destination == request.source) {
277 released += request.newLength;
279 request.oldLength > request.newLength ? request.oldLength - request.newLength : 0;
281 if (releasedLength) {
282 auto* allocator = allocatorFor(snapshot, space, released, releasedLength);
283 if (!allocator || !allocator->tryFree(released, releasedLength))
284 return VmStatus::NoMemory;
286 for (
auto*
object : *objects) {
287 bool replaced =
false;
288 for (
auto* retired : metadata.retired)
289 replaced = replaced || retired == object;
291 if (!metadata.replacement.tryPushBack(
object))
292 return VmStatus::NoMemory;
297 if (
object == source)
298 cuts[cutCount++] = {request.source, request.oldLength};
299 if (destination != request.source)
300 cuts[cutCount++] = {destination, request.newLength};
301 if (cutCount == 2 && cuts[1].base < cuts[0].base) {
302 const auto first = cuts[0];
306 uintptr_t cursor =
object->address();
307 const uintptr_t end = roundedEnd(
object, pageMask);
308 for (
size_t i = 0; i < cutCount; ++i) {
309 const uintptr_t first = cuts[i].base > cursor ? cuts[i].base : cursor;
310 const uintptr_t last =
311 cuts[i].base + cuts[i].length < end ? cuts[i].base + cuts[i].length : end;
312 if (first >= end || last <= cursor)
314 if (first > cursor &&
315 !metadata.appendSlice(
object, cursor, first - cursor, cursor, first - cursor))
316 return VmStatus::NoMemory;
319 if (cursor < end && !metadata.appendSlice(
object, cursor, end - cursor, cursor, end - cursor))
320 return VmStatus::NoMemory;
322 if (!metadata.appendSlice(source, request.source, request.oldLength, destination,
324 return VmStatus::NoMemory;
327 auto* replacement =
new MmObjectList;
329 return VmStatus::NoMemory;
330 for (
auto*
object : metadata.replacement) {
333 return VmStatus::NoMemory;
337 request.source, destination, request.oldLength,
338 request.newLength, request.fixed, victims.
count() ? &victims[0] :
nullptr,
341 PageStatus status = space.
prepareRemap(pages, prepared);
342 if (status == PageStatus::Success) {
346 } admission{process, &snapshot};
347 status = prepared.get()->commit(
349 auto* state =
static_cast<Admission*
>(opaque);
350 return state->process->commitUserReservations(state->snapshot->generation,
355 if (status != PageStatus::Success) {
357 if (status == PageStatus::Retry)
359 return translate(status);
361 for (
auto*
object : metadata.retired)
362 object->setMappingOwnership(false);
363 for (
auto*
object : metadata.staged)
364 object->setMappingOwnership(true);
371 attachment->relocate(destination);
372 metadata.committed =
true;
373 for (
size_t i = 0; i < prepared.get()->detachedPageCount(); ++i) {
374 const auto& page = prepared.get()->detachedPages()[i];
375 for (
auto* owner : metadata.retired) {
376 if (owner->address() <= page.address && page.address < roundedEnd(owner, pageMask)) {
377 owner->releaseDetachedPage(page.address, page);
384 account->publish(charge, account->futureMode());
385 if (source->m_LockMode == MemoryLockMode::Eager && request.newLength > request.oldLength)
386 populateMemory(space, destination + request.oldLength, request.newLength - request.oldLength);
387 result = destination;
388 return VmStatus::Success;
390 return VmStatus::NoMemory;
Memory-mapped file interface.
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
bool contains(uintptr_t base, size_t length)
Tree< uintptr_t, physical_uintptr_t > m_Mappings
MemoryMappedObject * stageSlice(uintptr_t source, size_t sourceLength, uintptr_t destination, size_t destinationLength) override
uintptr_t address() const
Permissions m_Permissions
static constexpr size_t getPageSize() PURE
static ProcessorInformation & information()
bool allocateSpecific(T address, T length)
A vector / dynamic array.
virtual uintptr_t getUserReservedStart() const =0
virtual uintptr_t getDynamicStart() const
virtual bool isMapped(void *virtualAddress)=0
virtual uintptr_t getUserStart() const =0
virtual uintptr_t getDynamicEnd() const
virtual RemapStatus prepareRemap(const PageRemapRequest &request, UniquePointer< PreparedPageRemap > &plan)
bool tryPushBack(const T &value)
void pushBack(const T &value)