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"
10#include "VirtualAddressSpace.h"
13EXPORTED_PUBLIC
size_t g_AllocedPages = 0;
16constexpr uint64_t MaximumPhysicalAddress = 64ULL << 30;
17constexpr uint64_t DirectMapBase = 0xffff000000000000ULL;
20Arm64PhysicalMemoryManager::Arm64PhysicalMemoryManager()
21 : m_References(nullptr),
22 m_FreeBitmap(nullptr),
27 m_NextRegion(KERNEL_VIRTUAL_MEMORYREGION_ADDRESS) {}
35 return Arm64PhysicalMemoryManager::instance();
40 for (
void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
41 if (info.getMemoryMapEntryType(entry) != 1) {
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;
49 if (start + length > highest) {
50 highest = start + length;
53 m_MaxPage = highest / PAGE_SIZE;
55 panic(
"ARM64: no usable RAM");
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) {
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;
71 if (start < end && end - start >= metadataBytes) {
77 panic(
"ARM64: cannot reserve page metadata");
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);
84 for (
void* entry = info.getMemoryMap(); entry; entry = info.nextMemoryMapEntry(entry)) {
85 if (info.getMemoryMapEntryType(entry) != 1) {
88 uint64_t start = info.getMemoryMapEntryAddress(entry);
89 uint64_t end = start + info.getMemoryMapEntryLength(entry);
90 if (end > MaximumPhysicalAddress) {
91 end = MaximumPhysicalAddress;
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) {
99 markPage(address / PAGE_SIZE,
true);
104 m_NextPage = metadata / PAGE_SIZE + metadataBytes / PAGE_SIZE;
105 g_FreePages = m_FreePages;
109bool Arm64PhysicalMemoryManager::pageAvailable(
size_t page)
const {
110 return page < m_MaxPage && (m_FreeBitmap[page / 8] & (1U << (page % 8)));
113void Arm64PhysicalMemoryManager::markPage(
size_t page,
bool available) {
114 uint8_t& bits = m_FreeBitmap[page / 8];
116 bits |= 1U << (page % 8);
118 bits &= ~(1U << (page % 8));
122size_t Arm64PhysicalMemoryManager::pageLimit(
size_t constraints)
const {
123 size_t limit = m_MaxPage;
125 limit = limit < (1U << 8) ? limit : (1U << 8);
127 limit = limit < (1U << 12) ? limit : (1U << 12);
128 }
else if (constraints &
below4GB) {
129 limit = limit < (1U << 20) ? limit : (1U << 20);
134physical_uintptr_t Arm64PhysicalMemoryManager::allocatePageUnlocked(
size_t constraints) {
135 const size_t limit = pageLimit(constraints);
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)) {
147 markPage(page,
false);
148 m_References[page] = 1;
152 m_NextPage = page + 1;
153 return page * PAGE_SIZE;
159physical_uintptr_t Arm64PhysicalMemoryManager::allocateContinuousPagesUnlocked(
size_t pages,
160 size_t constraints) {
161 const size_t limit = pageLimit(constraints);
162 if (!pages || pages > limit) {
165 for (
size_t first = 1; first <= limit - pages; ++first) {
167 while (count < pages && pageAvailable(first + count)) {
170 if (count != pages) {
174 for (
size_t page = first; page < first + pages; ++page) {
175 markPage(page,
false);
176 m_References[page] = 1;
178 m_FreePages -= pages;
179 g_FreePages -= pages;
180 g_AllocedPages += pages;
181 m_NextPage = first + pages;
182 return first * PAGE_SIZE;
189 physical_uintptr_t page = allocatePageUnlocked(constraints);
192 Metrics::increment(Metrics::PhysicalPageAllocFailure);
194 panic(
"ARM64: out of physical pages");
197 Metrics::increment(Metrics::PhysicalPageAlloc);
202physical_uintptr_t Arm64PhysicalMemoryManager::tryAllocatePage() {
204 physical_uintptr_t page = allocatePageUnlocked(0);
206 Metrics::increment(page ? Metrics::PhysicalPageAlloc : Metrics::PhysicalPageAllocFailure);
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");
216 if (--m_References[index]) {
219 markPage(index,
true);
224 Metrics::increment(Metrics::PhysicalPageFree);
226 if (index < m_NextPage) {
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");
242 ++m_References[index];
245bool Arm64PhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page,
void* buffer) {
246 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
249 memcpy(buffer,
reinterpret_cast<const void*
>(DirectMapBase + page), PAGE_SIZE);
253bool Arm64PhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
254 const void* buffer) {
255 if (!page || page >= MaximumPhysicalAddress || (page & (PAGE_SIZE - 1)) || !buffer) {
258 memcpy(
reinterpret_cast<void*
>(DirectMapBase + page), buffer, PAGE_SIZE);
264 return {m_TotalPages, m_FreePages, m_MaxPage != 0};
273 size_t constraints,
size_t flags,
274 physical_uintptr_t start) {
275 if (!pages || pages > (~
size_t(0) / PAGE_SIZE)) {
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) {
284 m_NextRegion = address + bytes;
286 const bool explicitPhysical = start !=
static_cast<physical_uintptr_t
>(-1);
287 const bool virtualOnlyRegion = constraints &
virtualOnly;
288 if (explicitPhysical && (start & (PAGE_SIZE - 1))) {
291 physical_uintptr_t continuousBase = 0;
292 if (!explicitPhysical && !virtualOnlyRegion && (constraints &
continuous)) {
294 continuousBase = allocateContinuousPagesUnlocked(pages, constraints);
295 if (!continuousBase) {
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;
308 physical = allocatePageUnlocked(constraints);
311 !space.
map(physical,
reinterpret_cast<void*
>(address + i * PAGE_SIZE),
314 if (!explicitPhysical && !continuousBase && physical) {
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;
321 if (space.
getMapping(virtualPage, old, oldFlags)) {
322 space.
unmap(virtualPage);
323 if (!explicitPhysical && !continuousBase) {
328 if (continuousBase) {
329 for (
size_t page = 0; page < pages; ++page) {
330 freePage(continuousBase + page * PAGE_SIZE);
340 region.m_bPageBacked = !explicitPhysical;
341 region.m_bNonRamMemory = explicitPhysical;
342 region.m_bForced = explicitPhysical;
343 region.m_bAnonymous = constraints &
anonymous;
349 if (!region || !region->
m_Size) {
354 for (
size_t i = 0; i < region->
m_Size; i += PAGE_SIZE) {
356 reinterpret_cast<void*
>(
reinterpret_cast<uintptr_t
>(region->
m_VirtualAddress) + i);
357 physical_uintptr_t physical = 0;
359 if (!space.
getMapping(address, physical, flags)) {
362 space.
unmap(address);
363 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 size_t m_Initialised
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)