1#include "PhysicalMemoryManager.h"
2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/panic.h"
4#include "pedigree/kernel/processor/MemoryRegion.h"
8#include "VirtualAddressSpace.h"
11EXPORTED_PUBLIC
size_t g_AllocedPages = 0;
14constexpr uint64_t MaximumPhysicalAddress = 64ULL << 30;
15constexpr uint64_t DirectMapBase = 0xffff000000000000ULL;
18Arm64PhysicalMemoryManager::Arm64PhysicalMemoryManager()
19 : m_References(nullptr),
20 m_FreeBitmap(nullptr),
25 m_NextRegion(KERNEL_VIRTUAL_MEMORYREGION_ADDRESS) {}
33 return Arm64PhysicalMemoryManager::instance();
38 for (
void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
39 if (info.getMemoryMapEntryType(entry) != 1) {
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;
47 if (start + length > highest) {
48 highest = start + length;
51 m_MaxPage = highest / PAGE_SIZE;
53 panic(
"ARM64: no usable RAM");
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) {
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;
69 if (start < end && end - start >= metadataBytes) {
75 panic(
"ARM64: cannot reserve page metadata");
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);
82 for (
void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
83 if (info.getMemoryMapEntryType(entry) != 1) {
86 uint64_t start = info.getMemoryMapEntryAddress(entry);
87 uint64_t end = start + info.getMemoryMapEntryLength(entry);
88 if (end > MaximumPhysicalAddress) {
89 end = MaximumPhysicalAddress;
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) {
97 markPage(address / PAGE_SIZE,
true);
102 m_NextPage = metadata / PAGE_SIZE + metadataBytes / PAGE_SIZE;
103 g_FreePages = m_FreePages;
107bool Arm64PhysicalMemoryManager::pageAvailable(
size_t page)
const {
108 return page < m_MaxPage && (m_FreeBitmap[page / 8] & (1U << (page % 8)));
111void Arm64PhysicalMemoryManager::markPage(
size_t page,
bool available) {
112 uint8_t& bits = m_FreeBitmap[page / 8];
114 bits |= 1U << (page % 8);
116 bits &= ~(1U << (page % 8));
120size_t Arm64PhysicalMemoryManager::pageLimit(
size_t constraints)
const {
121 size_t limit = m_MaxPage;
123 limit = limit < (1U << 8) ? limit : (1U << 8);
125 limit = limit < (1U << 12) ? limit : (1U << 12);
126 }
else if (constraints &
below4GB) {
127 limit = limit < (1U << 20) ? limit : (1U << 20);
132physical_uintptr_t Arm64PhysicalMemoryManager::allocatePageUnlocked(
size_t constraints) {
133 const size_t limit = pageLimit(constraints);
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)) {
145 markPage(page,
false);
146 m_References[page] = 1;
150 m_NextPage = page + 1;
151 return page * PAGE_SIZE;
157physical_uintptr_t Arm64PhysicalMemoryManager::allocateContinuousPagesUnlocked(
size_t pages,
158 size_t constraints) {
159 const size_t limit = pageLimit(constraints);
160 if (!pages || pages > limit) {
163 for (
size_t first = 1; first <= limit - pages; ++first) {
165 while (count < pages && pageAvailable(first + count)) {
168 if (count != pages) {
172 for (
size_t page = first; page < first + pages; ++page) {
173 markPage(page,
false);
174 m_References[page] = 1;
176 m_FreePages -= pages;
177 g_FreePages -= pages;
178 g_AllocedPages += pages;
179 m_NextPage = first + pages;
180 return first * PAGE_SIZE;
187 physical_uintptr_t page = allocatePageUnlocked(constraints);
189 panic(
"ARM64: out of physical pages");
194physical_uintptr_t Arm64PhysicalMemoryManager::tryAllocatePage() {
196 return allocatePageUnlocked(0);
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");
204 if (--m_References[index]) {
207 markPage(index,
true);
211 if (index < m_NextPage) {
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");
227 ++m_References[index];
230bool Arm64PhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page,
void* buffer) {
231 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
234 memcpy(buffer,
reinterpret_cast<const void*
>(DirectMapBase + page), PAGE_SIZE);
238bool Arm64PhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
239 const void* buffer) {
240 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
243 memcpy(
reinterpret_cast<void*
>(DirectMapBase + page), buffer, PAGE_SIZE);
249 return {m_TotalPages, m_FreePages, m_MaxPage != 0};
258 size_t constraints,
size_t flags,
259 physical_uintptr_t start) {
260 if (!pages || pages > (~
size_t(0) / PAGE_SIZE)) {
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) {
269 m_NextRegion = address + bytes;
271 const bool explicitPhysical = start !=
static_cast<physical_uintptr_t
>(-1);
272 const bool virtualOnlyRegion = constraints &
virtualOnly;
273 if (explicitPhysical && (start & (PAGE_SIZE - 1))) {
276 physical_uintptr_t continuousBase = 0;
277 if (!explicitPhysical && !virtualOnlyRegion && (constraints &
continuous)) {
279 continuousBase = allocateContinuousPagesUnlocked(pages, constraints);
280 if (!continuousBase) {
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;
293 physical = allocatePageUnlocked(constraints);
296 !space.
map(physical,
reinterpret_cast<void*
>(address + i * PAGE_SIZE),
299 if (!explicitPhysical && !continuousBase && physical) {
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;
306 if (space.
getMapping(virtualPage, old, oldFlags)) {
307 space.
unmap(virtualPage);
308 if (!explicitPhysical && !continuousBase) {
313 if (continuousBase) {
314 for (
size_t page = 0; page < pages; ++page) {
315 freePage(continuousBase + page * PAGE_SIZE);
325 region.m_bPageBacked = !explicitPhysical;
326 region.m_bNonRamMemory = explicitPhysical;
327 region.m_bForced = explicitPhysical;
328 region.m_bAnonymous = constraints &
anonymous;
334 if (!region || !region->
m_Size) {
339 for (
size_t i = 0; i < region->
m_Size; i += PAGE_SIZE) {
341 reinterpret_cast<void*
>(
reinterpret_cast<uintptr_t
>(region->
m_VirtualAddress) + i);
342 physical_uintptr_t physical = 0;
344 if (!space.
getMapping(address, physical, flags)) {
347 space.
unmap(address);
348 if (region->m_bPageBacked) {
physical_uintptr_t allocatePage(size_t constraints=0) override
void pin(physical_uintptr_t page) override
bool allocateRegion(MemoryRegion ®ion, 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
size_t freePageCount() const override
void unmapRegion(MemoryRegion *region) override
Special memory entity in the kernel's virtual address space.
physical_uintptr_t m_PhysicalAddress
static const size_t continuous
static PhysicalMemoryManager & instance()
static const size_t below1MB
static const size_t virtualOnly
static const size_t anonymous
Vector< MemoryRegion * > m_MemoryRegions
static const size_t nonRamMemory
static const size_t below16MB
static const size_t below4GB
static const size_t CacheDisable
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
static const size_t KernelMode
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
EXPORTED_PUBLIC size_t g_FreePages
void pushBack(const T &value)