The Pedigree Project 0.1
MemoryMappedFile-advice.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
9#include "File.h"
10#include "MemoryMappedFile.h"
11
12bool MemoryMappedObject::resident(VirtualAddressSpace& space, uintptr_t address,
13 FileResidencyAccess, void*) {
14 return space.isMapped(reinterpret_cast<void*>(address));
15}
16
17bool MemoryMappedFile::resident(VirtualAddressSpace& space, uintptr_t address,
18 FileResidencyAccess access, void* credentials) {
19 // Permission masking precedes both the PTE and backing-cache observation.
20 if (!access || !access(m_pBacking, credentials) || m_pBacking->isDirectPhysicalMapping())
21 return true;
22 if (space.isMapped(reinterpret_cast<void*>(address)))
23 return true;
24 const size_t displacement = address - m_Address;
25 if (displacement > ~size_t(0) - m_Offset)
26 return false;
27 const size_t offset = m_Offset + displacement;
28 const physical_uintptr_t physical = m_pBacking->getPhysicalPage(offset);
29 if (physical == ~physical_uintptr_t(0))
30 return false;
32 return true;
33}
34
35void MemoryMappedFile::releaseDetachedPageUnlocked(uintptr_t oldAddress,
37 const bool loan = m_Mappings.contains(oldAddress) && getMapping(oldAddress) == ~0UL;
38 const size_t offset = m_Offset + (oldAddress - m_Address);
39 const bool borrowed = page.mapped && (page.flags & VirtualAddressSpace::Borrowed);
40 if (borrowed && !m_bCopyOnWrite)
41 m_pBacking->sync(offset, false);
42 // Generic CoW can replace the PTE while the original cache loan remains.
43 // Both references must be retired, independently, using the final PTE.
44 if (loan || borrowed)
46 if (page.mapped && !borrowed)
48 untrackMapping(oldAddress);
49}
50
51void MemoryMappedFile::releaseDetachedPage(uintptr_t oldAddress,
54 releaseDetachedPageUnlocked(oldAddress, page);
55}
56
57void MemoryMappedFile::discardRange(VirtualAddressSpace& space, uintptr_t base, size_t length) {
59#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
60 size_t trackedPages = 0, mappedPages = 0;
61#endif
62 uintptr_t cursor = base;
63 uintptr_t address = 0;
64 physical_uintptr_t tracked = 0;
65 while (m_Mappings.lowerBound(cursor, address, tracked)) {
66 if (address - base >= length)
67 break;
68 // The copied key survives rotations when retirement removes the entry.
69 cursor = address + 1;
70#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
71 ++trackedPages;
72#endif
74 page.mapped = space.detachMapping(reinterpret_cast<void*>(address), page.physical, page.flags);
75#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
76 if (page.mapped)
77 ++mappedPages;
78#endif
79 releaseDetachedPageUnlocked(address, page);
80 }
81#if PEDIGREE_BENCHMARK_VM_DIAGNOSTICS
82 Process* process = Processor::information().getCurrentThread()->getParent();
83 process->recordBenchmarkVmCounter(Process::VmDiscardTrackedPages, trackedPages);
84 process->recordBenchmarkVmCounter(Process::VmDiscardMappedPages, mappedPages);
85#endif
86}
87
88MemoryMapManager::VmStatus MemoryMapManager::residency(uintptr_t base, size_t length,
89 unsigned char* kernelVector,
90 FileResidencyAccess access,
91 void* credentials) {
92 OperationGuard operation(*this);
93 const size_t pageSize = PhysicalMemoryManager::getPageSize();
94 if ((base & (pageSize - 1)) || (length & (pageSize - 1)) || length > ~uintptr_t(0) - base)
95 return VmStatus::InvalidRange;
96 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
97 MmObjectList* objects = m_MmObjectLists.lookup(&space);
98 for (uintptr_t address = base; address < base + length; address += pageSize) {
99 MemoryMappedObject* selected = nullptr;
100 if (objects) {
101 for (auto* object : *objects) {
102 if (object->matches(address)) {
103 selected = object;
104 break;
105 }
106 }
107 }
108 bool present = false;
109 if (selected)
110 present = selected->resident(space, address, access, credentials);
111 else if (!(present = space.isMapped(reinterpret_cast<void*>(address))))
112 return VmStatus::Unmapped;
113 kernelVector[(address - base) / pageSize] = present ? 1 : 0;
114 }
115 return VmStatus::Success;
116}
117
118MemoryMapManager::VmStatus MemoryMapManager::discard(uintptr_t base, size_t length) {
119 OperationGuard operation(*this);
120 const size_t pageSize = PhysicalMemoryManager::getPageSize();
121 const size_t pageMask = pageSize - 1;
122 if ((base & pageMask) || (length & pageMask) || length > ~uintptr_t(0) - base)
123 return VmStatus::InvalidRange;
124 if (!length)
125 return VmStatus::Success;
126#if !(X64 || HOSTED)
127 return VmStatus::Unsupported;
128#endif
129 VirtualAddressSpace& space = Processor::information().getVirtualAddressSpace();
130 if (hasLockedMemory(space, base, length))
131 return VmStatus::InvalidRange;
132 MmObjectList* objects = m_MmObjectLists.lookup(&space);
133 if (!objects)
134 return VmStatus::Unmapped;
135 const uintptr_t end = base + length;
136 uintptr_t cursor = base;
137 while (cursor < end) {
138 MemoryMappedObject* selected = nullptr;
139 uintptr_t selectedEnd = 0;
140 for (auto* object : *objects) {
141 const uintptr_t objectEnd = (object->address() + object->length() + pageMask) & ~pageMask;
142 if (object->address() <= cursor && cursor < objectEnd) {
143 selected = object;
144 selectedEnd = objectEnd;
145 break;
146 }
147 }
148 if (!selected)
149 return VmStatus::Unmapped;
150 if (selected->backingFile() && selected->backingFile()->isDirectPhysicalMapping())
151 return VmStatus::Unsupported;
152 cursor = selectedEnd < end ? selectedEnd : end;
153 }
154 for (auto* object : *objects) {
155 const uintptr_t objectEnd = (object->address() + object->length() + pageMask) & ~pageMask;
156 const uintptr_t first = object->address() > base ? object->address() : base;
157 const uintptr_t last = objectEnd < end ? objectEnd : end;
158 if (first < last)
159 object->discardRange(space, first, last - first);
160 }
161 return VmStatus::Success;
162}
Memory-mapped file interface.
virtual physical_uintptr_t getPhysicalPage(size_t offset)
Definition File.cc:458
virtual bool sync()
Definition File.cc:563
virtual void returnPhysicalPage(size_t offset)
Definition File.cc:525
Tree< VirtualAddressSpace *, MmObjectList * > m_MmObjectLists
Tree< uintptr_t, physical_uintptr_t > m_Mappings
void untrackMapping(uintptr_t)
physical_uintptr_t getMapping(uintptr_t)
size_t length() const
uintptr_t address() const
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
Process * getParent()
Definition Process.h:567
static ProcessorInformation & information()
bool contains(const K &key) const
Definition Tree.h:287
bool lowerBound(const K &key, K &foundKey, E &foundValue) const
Definition Tree.h:239
virtual bool isMapped(void *virtualAddress)=0
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)