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"
14 bool callerHasMemoryLockPrivilege()
const override {
16 return thread && thread->getParent()->getEffectiveUserId() == 0;
19 size_t length)
const override {
20 if (length > ~uintptr_t(0) - base)
25 for (
auto*
object : *objects) {
26 const uintptr_t end = (
object->address() +
object->length() + mask) & ~mask;
27 if (base < end && object->address() < base + length)
32 void enterOperation()
override {
35 void leaveOperation()
override {
42constexpr size_t MaximumLockObjects = 4096;
43constexpr size_t MaximumLockPages = 65536;
48MemoryLockStatus populationStatus(PopulationStatus 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;
59 return MemoryLockStatus::PopulationIoError;
61bool addCharge(
size_t previous,
size_t removed,
size_t added,
size_t& result) {
62 if (removed > previous || added > ~
size_t(0) - (previous - removed))
64 result = previous - removed + added;
70 space.setUserMemoryPolicy(&policy);
73PopulationStatus MemoryMappedObject::populatePage(
VirtualAddressSpace& space, uintptr_t address) {
75 return PopulationStatus::Inaccessible;
76 const auto access = prepareResidentAccess(space,
address);
77 if (access != PopulationStatus::Success)
80 void* page =
reinterpret_cast<void*
>(
address);
82 physical_uintptr_t physical;
87 return PopulationStatus::Inaccessible;
89 return PopulationStatus::Success;
92 ? PopulationStatus::Success
93 : PopulationStatus::NoMemory;
95 PopulationStatus status = PopulationStatus::NoMemory;
97 const size_t previousError = thread ? thread->getErrno() : 0;
100 const bool populated =
trap(space,
address, write, &status);
102 thread->setErrno(previousError);
103 return populated ? PopulationStatus::Success : status;
109 : m_Manager(manager), m_Space(space), m_Mode(mode) {}
111 for (
auto*
object : m_Committed ? m_Retired : m_Staged)
113 delete m_Replacement;
115 size_t removedPages()
const override {
118 size_t addedPages()
const override {
121 size_t coveredPages()
const override {
124 size_t eligiblePages()
const override {
127 void commit()
override {
128 if (!m_Replacement) {
132 for (
auto*
object : m_Retired)
133 object->m_OwnsMappings =
false;
134 for (
auto*
object : m_Staged)
135 object->m_OwnsMappings =
true;
141 m_Replacement =
nullptr;
145 PopulationStatus populate()
override {
146 if (m_Mode != MemoryLockMode::Eager)
147 return PopulationStatus::Success;
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)
159 bool append(
MemoryMappedObject* owner, uintptr_t first, uintptr_t last, MemoryLockMode mode,
163 auto* slice = owner->stageSlice(first, last - first, first, last - first);
166 slice->m_LockMode = mode;
170 return m_Replacement->tryPushBack(slice);
174 MemoryLockMode m_Mode;
177 size_t m_Removed = 0, m_Added = 0, m_Covered = 0, m_Eligible = 0;
178 bool m_Committed =
false;
181MemoryLockStatus MemoryMapManager::prepareManagedLocks(
VirtualAddressSpace& space, uintptr_t base,
182 size_t length, MemoryLockMode mode,
bool all,
184 auto* plan =
new LockPlan(*
this, space, mode);
187 return MemoryLockStatus::NoMemory;
189 if (!objects || !objects->count()) {
190 result = pedigree_std::move(owned);
191 return MemoryLockStatus::Success;
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;
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;
208 if (!plan->m_Replacement->tryPushBack(
object))
209 return MemoryLockStatus::NoMemory;
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;
228 if (plan->m_Replacement->count() > MaximumLockObjects)
229 return MemoryLockStatus::NoMemory;
230 result = pedigree_std::move(owned);
231 return MemoryLockStatus::Success;
235 size_t length, MemoryLockMode mode,
bool privileged) {
236 OperationGuard operation(*
this);
238 if ((base | length) & (pageSize - 1) || length > ~uintptr_t(0) - base)
239 return MemoryLockStatus::InvalidRange;
241 return MemoryLockStatus::Success;
242 auto* account = space.memoryLockAccount();
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)
251 status = space.rawUserMemory().prepareLocks(base, length, mode, raw);
252 if (status != MemoryLockStatus::Success)
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(),
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();
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
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;
285 account->publish(account->charge(), futureMode);
286 return MemoryLockStatus::Success;
289 auto status = prepareManagedLocks(space, 0, 0, currentMode,
true, managed);
290 if (status != MemoryLockStatus::Success)
292 status = space.rawUserMemory().prepareAllLocks(currentMode, raw);
293 if (status != MemoryLockStatus::Success)
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(),
306 return MemoryLockStatus::NoMemory;
307 managed.get()->commit();
309 account->publish(charge, futureMode);
311 managed.get()->populate();
312 raw.get()->populate();
313 return MemoryLockStatus::Success;
316bool MemoryMapManager::hasLockedMemory(
VirtualAddressSpace& space, uintptr_t base,
size_t length) {
317 OperationGuard operation(*
this);
320 if (length > ~uintptr_t(0) - base)
324 for (
auto*
object : *objects)
325 if (object->m_LockMode != MemoryLockMode::None && base < objectEnd(object) &&
326 object->address() < base + length)
328 return space.rawUserMemory().hasLockedMemory(base, length);
332 auto* account = space.memoryLockAccount();
333 if (!account || !pages)
335 auto charge = account->charge();
336 assert(pages <= charge.managedPages);
337 charge.managedPages -= pages;
338 account->publish(charge, account->futureMode());
341PopulationStatus MemoryMapManager::populateMemory(
VirtualAddressSpace& space, uintptr_t base,
343 OperationGuard operation(*
this);
345 if ((base | length) & (pageSize - 1) || length > ~uintptr_t(0) - base)
346 return PopulationStatus::Inaccessible;
349 return length ? PopulationStatus::Inaccessible : PopulationStatus::Success;
350 PopulationStatus result = PopulationStatus::Success;
351 for (uintptr_t page = base; page < base + length; page += pageSize) {
353 for (
auto*
object : *objects)
354 if (page >= object->address() && page < objectEnd(object)) {
359 selected ? selected->populatePage(space, page) : PopulationStatus::Inaccessible;
360 if (result == PopulationStatus::Success && status != result)
Memory-mapped file interface.
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
static MemoryMapManager & instance()
uintptr_t address() const
virtual bool trap(VirtualAddressSpace &space, uintptr_t address, bool bWrite, PopulationStatus *population=nullptr)=0
Permissions m_Permissions
static constexpr size_t getPageSize() PURE
static ProcessorInformation & information()
static UniquePointer< T > adopt(T *pointer)
A vector / dynamic array.
static const size_t CopyOnWrite
virtual bool isMapped(void *virtualAddress)=0
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
static const size_t Swapped
void pushBack(const T &value)