The Pedigree Project 0.1
armv7/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#include "pedigree/kernel/processor/VirtualAddressSpace.h"
8
9#include <string.h>
10
11#include "AddressLayout.h"
12
13EXPORTED_PUBLIC size_t g_FreePages = 0;
14EXPORTED_PUBLIC size_t g_AllocedPages = 0;
15
16namespace {
17constexpr uint64_t MaximumPhysicalAddress = 0x80000000ULL;
18} // namespace
19
20Armv7PhysicalMemoryManager::Armv7PhysicalMemoryManager()
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
29Armv7PhysicalMemoryManager& Armv7PhysicalMemoryManager::instance() {
30 static Armv7PhysicalMemoryManager manager;
31 return manager;
32}
33
35 return Armv7PhysicalMemoryManager::instance();
36}
37
38void Armv7PhysicalMemoryManager::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("ARMv7: 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("ARMv7: cannot reserve page metadata");
78 }
79
80 m_References = reinterpret_cast<uint16_t*>(ARMV7_DIRECT_MAP_BASE + 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 Armv7PhysicalMemoryManager::pageAvailable(size_t page) const {
110 return page < m_MaxPage && (m_FreeBitmap[page / 8] & (1U << (page % 8)));
111}
112
113void Armv7PhysicalMemoryManager::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 Armv7PhysicalMemoryManager::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 Armv7PhysicalMemoryManager::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 Armv7PhysicalMemoryManager::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 Armv7PhysicalMemoryManager::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("ARMv7: 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 Armv7PhysicalMemoryManager::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
211physical_uintptr_t Armv7PhysicalMemoryManager::allocateAlignedPages(size_t pages) {
212 if (!pages || pages > m_MaxPage) {
213 return 0;
214 }
215 LockGuard<Spinlock> guard(m_Lock);
216 const size_t startingPage = ((m_NextPage + pages - 1) / pages) * pages;
217 for (size_t pass = 0; pass < 2; ++pass) {
218 const size_t begin = pass ? pages : startingPage;
219 const size_t end = pass ? startingPage : m_MaxPage - pages + 1;
220 for (size_t first = begin; first < end; first += pages) {
221 size_t count = 0;
222 while (count < pages && pageAvailable(first + count)) {
223 ++count;
224 }
225 if (count != pages) {
226 continue;
227 }
228 for (size_t page = first; page < first + pages; ++page) {
229 markPage(page, false);
230 m_References[page] = 1;
231 }
232 m_FreePages -= pages;
233 g_FreePages -= pages;
234 g_AllocedPages += pages;
235 m_NextPage = first + pages;
236 return first * PAGE_SIZE;
237 }
238 }
239 return 0;
240}
241
242void Armv7PhysicalMemoryManager::freePageUnlocked(physical_uintptr_t page) {
243 const size_t index = page / PAGE_SIZE;
244 if (!page || (page & (PAGE_SIZE - 1)) || index >= m_MaxPage || !m_References[index]) {
245 panic("ARMv7: invalid physical page free");
246 }
247 if (--m_References[index]) {
248 return;
249 }
250 markPage(index, true);
251 ++m_FreePages;
252 ++g_FreePages;
253 --g_AllocedPages;
254 if (Processor::m_Initialised == 2) {
255 Metrics::increment(Metrics::PhysicalPageFree);
256 }
257 if (index < m_NextPage) {
258 m_NextPage = index;
259 }
260}
261
262void Armv7PhysicalMemoryManager::freePage(physical_uintptr_t page) {
263 LockGuard<Spinlock> guard(m_Lock);
264 freePageUnlocked(page);
265}
266
267void Armv7PhysicalMemoryManager::pin(physical_uintptr_t page) {
268 LockGuard<Spinlock> guard(m_Lock);
269 const size_t index = page / PAGE_SIZE;
270 if (index >= m_MaxPage || !m_References[index] || m_References[index] == 0xffff) {
271 panic("ARMv7: invalid physical page pin");
272 }
273 ++m_References[index];
274}
275
276bool Armv7PhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page, void* buffer) {
277 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
278 return false;
279 }
280 memcpy(buffer, reinterpret_cast<const void*>(ARMV7_DIRECT_MAP_BASE + page), PAGE_SIZE);
281 return true;
282}
283
284bool Armv7PhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
285 const void* buffer) {
286 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
287 return false;
288 }
289 memcpy(reinterpret_cast<void*>(ARMV7_DIRECT_MAP_BASE + page), buffer, PAGE_SIZE);
290 return true;
291}
292
293PhysicalMemoryManager::MemorySnapshot Armv7PhysicalMemoryManager::memorySnapshot() const {
294 LockGuard<Spinlock> guard(m_Lock);
295 return {m_TotalPages, m_FreePages, m_MaxPage != 0};
296}
297
299 LockGuard<Spinlock> guard(m_Lock);
300 return m_FreePages;
301}
302
304 size_t constraints, size_t flags,
305 physical_uintptr_t start) {
306 if (!pages || pages > (~size_t(0) / PAGE_SIZE)) {
307 return false;
308 }
309 LockGuard<Spinlock> guard(m_RegionLock);
310 const size_t bytes = pages * PAGE_SIZE;
311 const uintptr_t address = (m_NextRegion + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
312 if (address + bytes < address || address + bytes > KERNEL_VIRTUAL_MEMORYREGION_END) {
313 return false;
314 }
315 m_NextRegion = address + bytes;
316
317 const bool explicitPhysical = start != static_cast<physical_uintptr_t>(-1);
318 const bool virtualOnlyRegion = constraints & virtualOnly;
319 if (explicitPhysical && (start & (PAGE_SIZE - 1))) {
320 return false;
321 }
322 physical_uintptr_t continuousBase = 0;
323 if (!explicitPhysical && !virtualOnlyRegion && (constraints & continuous)) {
324 LockGuard<Spinlock> pagesGuard(m_Lock);
325 continuousBase = allocateContinuousPagesUnlocked(pages, constraints);
326 if (!continuousBase) {
327 return false;
328 }
329 }
331 for (size_t i = 0; i < pages && !virtualOnlyRegion; ++i) {
332 physical_uintptr_t physical = 0;
333 if (explicitPhysical) {
334 physical = start + i * PAGE_SIZE;
335 } else if (continuousBase) {
336 physical = continuousBase + i * PAGE_SIZE;
337 } else {
338 LockGuard<Spinlock> pagesGuard(m_Lock);
339 physical = allocatePageUnlocked(constraints);
340 }
341 if (!physical ||
342 !space.map(physical, reinterpret_cast<void*>(address + i * PAGE_SIZE),
344 ((constraints & nonRamMemory) ? VirtualAddressSpace::CacheDisable : 0))) {
345 if (!explicitPhysical && !continuousBase && physical) {
346 freePage(physical);
347 }
348 for (size_t mapped = 0; mapped < i; ++mapped) {
349 void* virtualPage = reinterpret_cast<void*>(address + mapped * PAGE_SIZE);
350 physical_uintptr_t old = 0;
351 size_t oldFlags = 0;
352 if (space.getMapping(virtualPage, old, oldFlags)) {
353 space.unmap(virtualPage);
354 if (!explicitPhysical && !continuousBase) {
355 freePage(old);
356 }
357 }
358 }
359 if (continuousBase) {
360 for (size_t page = 0; page < pages; ++page) {
361 freePage(continuousBase + page * PAGE_SIZE);
362 }
363 }
364 return false;
365 }
366 }
367
368 region.m_VirtualAddress = reinterpret_cast<void*>(address);
369 region.m_PhysicalAddress = explicitPhysical ? start : continuousBase;
370 region.m_Size = bytes;
371 region.m_bPageBacked = !explicitPhysical;
372 region.m_bNonRamMemory = explicitPhysical;
373 region.m_bForced = explicitPhysical;
374 region.m_bAnonymous = constraints & anonymous;
375 m_MemoryRegions.pushBack(&region);
376 return true;
377}
378
380 if (!region || !region->m_Size) {
381 return;
382 }
383 LockGuard<Spinlock> guard(m_RegionLock);
385 for (size_t i = 0; i < region->m_Size; i += PAGE_SIZE) {
386 void* address =
387 reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(region->m_VirtualAddress) + i);
388 physical_uintptr_t physical = 0;
389 size_t flags = 0;
390 if (!space.getMapping(address, physical, flags)) {
391 continue;
392 }
393 space.unmap(address);
394 if (region->m_bPageBacked) {
395 freePage(physical);
396 }
397 }
399 ++it) {
400 if (*it == region) {
402 break;
403 }
404 }
405 region->m_Size = 0;
406}
void freePage(physical_uintptr_t page) override
void unmapRegion(MemoryRegion *region) override
void freePageUnlocked(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 pin(physical_uintptr_t page) override
physical_uintptr_t allocatePage(size_t constraints=0) 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