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