The Pedigree Project 0.1
arm64/PhysicalMemoryManager.cc
1#include "PhysicalMemoryManager.h"
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/Metrics.h"
4#include "pedigree/kernel/panic.h"
5#include "pedigree/kernel/processor/MemoryRegion.h"
6#include "pedigree/kernel/processor/Processor.h"
7
8#include <string.h>
9
10#include "VirtualAddressSpace.h"
11
12EXPORTED_PUBLIC size_t g_FreePages = 0;
13EXPORTED_PUBLIC size_t g_AllocedPages = 0;
14
15namespace {
16constexpr uint64_t MaximumPhysicalAddress = 64ULL << 30;
17constexpr uint64_t DirectMapBase = 0xffff000000000000ULL;
18} // namespace
19
20Arm64PhysicalMemoryManager::Arm64PhysicalMemoryManager()
21 : m_References(nullptr),
22 m_FreeBitmap(nullptr),
23 m_MaxPage(0),
24 m_NextPage(0),
25 m_TotalPages(0),
26 m_FreePages(0),
27 m_NextRegion(KERNEL_VIRTUAL_MEMORYREGION_ADDRESS) {}
28
29Arm64PhysicalMemoryManager& Arm64PhysicalMemoryManager::instance() {
30 static Arm64PhysicalMemoryManager manager;
31 return manager;
32}
33
35 return Arm64PhysicalMemoryManager::instance();
36}
37
38void Arm64PhysicalMemoryManager::initialise(const BootstrapStruct_t& info) {
39 uint64_t highest = 0;
40 for (void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
41 if (info.getMemoryMapEntryType(entry) != 1) {
42 continue;
43 }
44 uint64_t start = info.getMemoryMapEntryAddress(entry);
45 uint64_t length = info.getMemoryMapEntryLength(entry);
46 if (start >= MaximumPhysicalAddress || length > MaximumPhysicalAddress - start) {
47 length = start < MaximumPhysicalAddress ? MaximumPhysicalAddress - start : 0;
48 }
49 if (start + length > highest) {
50 highest = start + length;
51 }
52 }
53 m_MaxPage = highest / PAGE_SIZE;
54 if (!m_MaxPage) {
55 panic("ARM64: no usable RAM");
56 }
57
58 const size_t referenceBytes = m_MaxPage * sizeof(uint16_t);
59 const size_t bitmapBytes = (m_MaxPage + 7) / 8;
60 const size_t metadataBytes = (referenceBytes + bitmapBytes + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
61 physical_uintptr_t metadata = 0;
62 for (void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
63 if (info.getMemoryMapEntryType(entry) != 1) {
64 continue;
65 }
66 uint64_t start = (info.getMemoryMapEntryAddress(entry) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
67 uint64_t end = info.getMemoryMapEntryAddress(entry) + info.getMemoryMapEntryLength(entry);
68 if (end > MaximumPhysicalAddress) {
69 end = MaximumPhysicalAddress;
70 }
71 if (start < end && end - start >= metadataBytes) {
72 metadata = start;
73 break;
74 }
75 }
76 if (!metadata) {
77 panic("ARM64: cannot reserve page metadata");
78 }
79
80 m_References = reinterpret_cast<uint16_t*>(DirectMapBase + metadata);
81 m_FreeBitmap = reinterpret_cast<uint8_t*>(m_References + m_MaxPage);
82 memset(m_References, 0, metadataBytes);
83
84 for (void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
85 if (info.getMemoryMapEntryType(entry) != 1) {
86 continue;
87 }
88 uint64_t start = info.getMemoryMapEntryAddress(entry);
89 uint64_t end = start + info.getMemoryMapEntryLength(entry);
90 if (end > MaximumPhysicalAddress) {
91 end = MaximumPhysicalAddress;
92 }
93 start = (start + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
94 end &= ~(PAGE_SIZE - 1);
95 for (uint64_t address = start; address < end; address += PAGE_SIZE) {
96 if (address >= metadata && address < metadata + metadataBytes) {
97 continue;
98 }
99 markPage(address / PAGE_SIZE, true);
100 ++m_TotalPages;
101 ++m_FreePages;
102 }
103 }
104 m_NextPage = metadata / PAGE_SIZE + metadataBytes / PAGE_SIZE;
105 g_FreePages = m_FreePages;
106 g_AllocedPages = 0;
107}
108
109bool Arm64PhysicalMemoryManager::pageAvailable(size_t page) const {
110 return page < m_MaxPage && (m_FreeBitmap[page / 8] & (1U << (page % 8)));
111}
112
113void Arm64PhysicalMemoryManager::markPage(size_t page, bool available) {
114 uint8_t& bits = m_FreeBitmap[page / 8];
115 if (available) {
116 bits |= 1U << (page % 8);
117 } else {
118 bits &= ~(1U << (page % 8));
119 }
120}
121
122size_t Arm64PhysicalMemoryManager::pageLimit(size_t constraints) const {
123 size_t limit = m_MaxPage;
124 if (constraints & below1MB) {
125 limit = limit < (1U << 8) ? limit : (1U << 8);
126 } else if (constraints & below16MB) {
127 limit = limit < (1U << 12) ? limit : (1U << 12);
128 } else if (constraints & below4GB) {
129 limit = limit < (1U << 20) ? limit : (1U << 20);
130 }
131 return limit;
132}
133
134physical_uintptr_t Arm64PhysicalMemoryManager::allocatePageUnlocked(size_t constraints) {
135 const size_t limit = pageLimit(constraints);
136 if (!limit) {
137 return 0;
138 }
139
140 size_t first = m_NextPage < limit ? m_NextPage : 0;
141 for (size_t pass = 0; pass < 2; ++pass) {
142 const size_t end = pass ? first : limit;
143 for (size_t page = pass ? 0 : first; page < end; ++page) {
144 if (!pageAvailable(page)) {
145 continue;
146 }
147 markPage(page, false);
148 m_References[page] = 1;
149 --m_FreePages;
150 --g_FreePages;
151 ++g_AllocedPages;
152 m_NextPage = page + 1;
153 return page * PAGE_SIZE;
154 }
155 }
156 return 0;
157}
158
159physical_uintptr_t Arm64PhysicalMemoryManager::allocateContinuousPagesUnlocked(size_t pages,
160 size_t constraints) {
161 const size_t limit = pageLimit(constraints);
162 if (!pages || pages > limit) {
163 return 0;
164 }
165 for (size_t first = 1; first <= limit - pages; ++first) {
166 size_t count = 0;
167 while (count < pages && pageAvailable(first + count)) {
168 ++count;
169 }
170 if (count != pages) {
171 first += count;
172 continue;
173 }
174 for (size_t page = first; page < first + pages; ++page) {
175 markPage(page, false);
176 m_References[page] = 1;
177 }
178 m_FreePages -= pages;
179 g_FreePages -= pages;
180 g_AllocedPages += pages;
181 m_NextPage = first + pages;
182 return first * PAGE_SIZE;
183 }
184 return 0;
185}
186
187physical_uintptr_t Arm64PhysicalMemoryManager::allocatePage(size_t constraints) {
188 LockGuard<Spinlock> guard(m_Lock);
189 physical_uintptr_t page = allocatePageUnlocked(constraints);
190 if (!page) {
191 if (Processor::m_Initialised == 2) {
192 Metrics::increment(Metrics::PhysicalPageAllocFailure);
193 }
194 panic("ARM64: out of physical pages");
195 }
196 if (Processor::m_Initialised == 2) {
197 Metrics::increment(Metrics::PhysicalPageAlloc);
198 }
199 return page;
200}
201
202physical_uintptr_t Arm64PhysicalMemoryManager::tryAllocatePage() {
203 LockGuard<Spinlock> guard(m_Lock);
204 physical_uintptr_t page = allocatePageUnlocked(0);
205 if (Processor::m_Initialised == 2) {
206 Metrics::increment(page ? Metrics::PhysicalPageAlloc : Metrics::PhysicalPageAllocFailure);
207 }
208 return page;
209}
210
211void Arm64PhysicalMemoryManager::freePageUnlocked(physical_uintptr_t page) {
212 const size_t index = page / PAGE_SIZE;
213 if (!page || (page & (PAGE_SIZE - 1)) || index >= m_MaxPage || !m_References[index]) {
214 panic("ARM64: invalid physical page free");
215 }
216 if (--m_References[index]) {
217 return;
218 }
219 markPage(index, true);
220 ++m_FreePages;
221 ++g_FreePages;
222 --g_AllocedPages;
223 if (Processor::m_Initialised == 2) {
224 Metrics::increment(Metrics::PhysicalPageFree);
225 }
226 if (index < m_NextPage) {
227 m_NextPage = index;
228 }
229}
230
231void Arm64PhysicalMemoryManager::freePage(physical_uintptr_t page) {
232 LockGuard<Spinlock> guard(m_Lock);
233 freePageUnlocked(page);
234}
235
236void Arm64PhysicalMemoryManager::pin(physical_uintptr_t page) {
237 LockGuard<Spinlock> guard(m_Lock);
238 const size_t index = page / PAGE_SIZE;
239 if (index >= m_MaxPage || !m_References[index] || m_References[index] == 0xffff) {
240 panic("ARM64: invalid physical page pin");
241 }
242 ++m_References[index];
243}
244
245bool Arm64PhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page, void* buffer) {
246 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
247 return false;
248 }
249 memcpy(buffer, reinterpret_cast<const void*>(DirectMapBase + page), PAGE_SIZE);
250 return true;
251}
252
253bool Arm64PhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
254 const void* buffer) {
255 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
256 return false;
257 }
258 memcpy(reinterpret_cast<void*>(DirectMapBase + page), buffer, PAGE_SIZE);
259 return true;
260}
261
262PhysicalMemoryManager::MemorySnapshot Arm64PhysicalMemoryManager::memorySnapshot() const {
263 LockGuard<Spinlock> guard(m_Lock);
264 return {m_TotalPages, m_FreePages, m_MaxPage != 0};
265}
266
268 LockGuard<Spinlock> guard(m_Lock);
269 return m_FreePages;
270}
271
273 size_t constraints, size_t flags,
274 physical_uintptr_t start) {
275 if (!pages || pages > (~size_t(0) / PAGE_SIZE)) {
276 return false;
277 }
278 LockGuard<Spinlock> guard(m_RegionLock);
279 const size_t bytes = pages * PAGE_SIZE;
280 const uintptr_t address = (m_NextRegion + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
281 if (address + bytes < address || address + bytes > KERNEL_VIRTUAL_MEMORYREGION_END) {
282 return false;
283 }
284 m_NextRegion = address + bytes;
285
286 const bool explicitPhysical = start != static_cast<physical_uintptr_t>(-1);
287 const bool virtualOnlyRegion = constraints & virtualOnly;
288 if (explicitPhysical && (start & (PAGE_SIZE - 1))) {
289 return false;
290 }
291 physical_uintptr_t continuousBase = 0;
292 if (!explicitPhysical && !virtualOnlyRegion && (constraints & continuous)) {
293 LockGuard<Spinlock> pagesGuard(m_Lock);
294 continuousBase = allocateContinuousPagesUnlocked(pages, constraints);
295 if (!continuousBase) {
296 return false;
297 }
298 }
300 for (size_t i = 0; i < pages && !virtualOnlyRegion; ++i) {
301 physical_uintptr_t physical = 0;
302 if (explicitPhysical) {
303 physical = start + i * PAGE_SIZE;
304 } else if (continuousBase) {
305 physical = continuousBase + i * PAGE_SIZE;
306 } else {
307 LockGuard<Spinlock> pagesGuard(m_Lock);
308 physical = allocatePageUnlocked(constraints);
309 }
310 if (!physical ||
311 !space.map(physical, reinterpret_cast<void*>(address + i * PAGE_SIZE),
313 ((constraints & nonRamMemory) ? VirtualAddressSpace::CacheDisable : 0))) {
314 if (!explicitPhysical && !continuousBase && physical) {
315 freePage(physical);
316 }
317 for (size_t mapped = 0; mapped < i; ++mapped) {
318 void* virtualPage = reinterpret_cast<void*>(address + mapped * PAGE_SIZE);
319 physical_uintptr_t old = 0;
320 size_t oldFlags = 0;
321 if (space.getMapping(virtualPage, old, oldFlags)) {
322 space.unmap(virtualPage);
323 if (!explicitPhysical && !continuousBase) {
324 freePage(old);
325 }
326 }
327 }
328 if (continuousBase) {
329 for (size_t page = 0; page < pages; ++page) {
330 freePage(continuousBase + page * PAGE_SIZE);
331 }
332 }
333 return false;
334 }
335 }
336
337 region.m_VirtualAddress = reinterpret_cast<void*>(address);
338 region.m_PhysicalAddress = explicitPhysical ? start : continuousBase;
339 region.m_Size = bytes;
340 region.m_bPageBacked = !explicitPhysical;
341 region.m_bNonRamMemory = explicitPhysical;
342 region.m_bForced = explicitPhysical;
343 region.m_bAnonymous = constraints & anonymous;
344 m_MemoryRegions.pushBack(&region);
345 return true;
346}
347
349 if (!region || !region->m_Size) {
350 return;
351 }
352 LockGuard<Spinlock> guard(m_RegionLock);
354 for (size_t i = 0; i < region->m_Size; i += PAGE_SIZE) {
355 void* address =
356 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(region->m_VirtualAddress) + i);
357 physical_uintptr_t physical = 0;
358 size_t flags = 0;
359 if (!space.getMapping(address, physical, flags)) {
360 continue;
361 }
362 space.unmap(address);
363 if (region->m_bPageBacked) {
364 freePage(physical);
365 }
366 }
368 ++it) {
369 if (*it == region) {
371 break;
372 }
373 }
374 region->m_Size = 0;
375}
physical_uintptr_t allocatePage(size_t constraints=0) override
void pin(physical_uintptr_t page) override
bool allocateRegion(MemoryRegion &region, size_t pages, size_t constraints, size_t flags, physical_uintptr_t start=-1) override
void freePageUnlocked(physical_uintptr_t page) override
void freePage(physical_uintptr_t page) override
void unmapRegion(MemoryRegion *region) override
Special memory entity in the kernel's virtual address space.
void * m_VirtualAddress
physical_uintptr_t m_PhysicalAddress
static PhysicalMemoryManager & instance()
static size_t m_Initialised
Definition Processor.h:483
Iterator end()
Definition Vector.h:172
Iterator begin()
Definition Vector.h:162
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual void unmap(void *virtualAddress)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
EXPORTED_PUBLIC size_t g_FreePages
void erase(size_t index)
Definition Vector.h:389
void pushBack(const T &value)
Definition Vector.h:275