20#include "PhysicalMemoryManager.h"
21#include "pedigree/kernel/BootstrapInfo.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/Metrics.h"
25#include "pedigree/kernel/debugger/commands/AllocationCommand.h"
26#include "pedigree/kernel/panic.h"
27#include "pedigree/kernel/process/MemoryPressureManager.h"
28#include "pedigree/kernel/process/Process.h"
29#include "pedigree/kernel/process/Thread.h"
30#include "pedigree/kernel/processor/MemoryRegion.h"
31#include "pedigree/kernel/processor/Processor.h"
32#include "pedigree/kernel/processor/ProcessorInformation.h"
33#include "pedigree/kernel/processor/VirtualAddressSpace.h"
34#include "pedigree/kernel/utilities/MemoryTracing.h"
35#include "pedigree/kernel/utilities/Vector.h"
36#include "pedigree/kernel/utilities/utility.h"
38#include "../x64/VirtualAddressSpace.h"
41EXPORTED_PUBLIC
size_t g_AllocedPages = 0;
45static void trackPages(ssize_t v, ssize_t p, ssize_t s) {
51 pProcess->trackPages(v, p, s);
73 physical_uintptr_t ptr;
79 if (
m_PageStack.freePages() < MemoryPressureManager::getHighWatermark()) {
83 WARNING_NOLOCK(
"Memory pressure encountered, performing a compact...");
85 ERROR_NOLOCK(
"Compact did not alleviate any memory pressure.");
87 NOTICE_NOLOCK(
"Compact was successful.");
91 bDidHitWatermark.compareAndSwap(
false,
true);
92 }
else if (bDidHitWatermark.compareAndSwap(
true,
false)) {
93 ERROR_NOLOCK(
"<pressure was hit, but is no longer being hit>");
100 Metrics::increment(Metrics::PhysicalPageAllocFailure);
102 panic(
"Out of memory.");
105 Metrics::increment(Metrics::PhysicalPageAlloc);
109 traceAllocation(
reinterpret_cast<void*
>(ptr), MemoryTracing::PageAlloc, 4096);
116 EMIT_IF(TRACK_PAGE_ALLOCATIONS) {
118 if (!g_AllocationCommand.isMallocing()) {
119 g_AllocationCommand.allocatePage(ptr);
126physical_uintptr_t X86CommonPhysicalMemoryManager::tryAllocatePage() {
128 physical_uintptr_t ptr;
133 Metrics::increment(Metrics::PhysicalPageAllocFailure);
138 Metrics::increment(Metrics::PhysicalPageAlloc);
142 traceAllocation(
reinterpret_cast<void*
>(ptr), MemoryTracing::PageAlloc, 4096);
149 EMIT_IF(TRACK_PAGE_ALLOCATIONS) {
151 if (!g_AllocationCommand.isMallocing()) {
152 g_AllocationCommand.allocatePage(ptr);
166bool X86CommonPhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page,
169 if (!page || (page & (
getPageSize() - 1)) || !buffer)
171 MemoryCopy(buffer,
reinterpret_cast<const void*
>(page + 0xffff800000000000ULL),
getPageSize());
177bool X86CommonPhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
178 const void* buffer) {
180 if (!page || (page & (
getPageSize() - 1)) || !buffer)
182 MemoryCopy(
reinterpret_cast<void*
>(page + 0xffff800000000000ULL), buffer,
getPageSize());
197 "X86CommonPhysicalMemoryManager::freePageUnlocked called without "
201 if (m_BootstrapPinnedPageRefcount &&
page == m_BootstrapPinnedPage) {
202 if (--m_BootstrapPinnedPageRefcount) {
205 m_BootstrapPinnedPage = 0;
211 if (result.hasValue()) {
212 struct page p = result.value();
230 Metrics::increment(Metrics::PhysicalPageFree);
233 trackPages(0, -1, 0);
239 if (m_BootstrapPinnedPageRefcount) {
240 if (m_BootstrapPinnedPage !=
page) {
241 FATAL_NOLOCK(
"PhysicalMemoryManager: multiple pages pinned during metadata bootstrap");
244 m_BootstrapPinnedPage =
page;
246 ++m_BootstrapPinnedPageRefcount;
252 if (result.hasValue()) {
253 struct page p = result.value();
265 size_t pageConstraints,
size_t Flags,
266 physical_uintptr_t start) {
270 if (start !=
static_cast<physical_uintptr_t
>(-1)) {
275 panic(
"PhysicalMemoryManager::allocateRegion(): function misused");
279 Region.setNonRamMemory(
true);
283 "PhysicalMemoryManager::allocateRegion() [specific] "
284 "- failed to get space from general range list and "
288 Region.setForced(
true);
291 if (start < 0x100000 && (start + cPages *
getPageSize()) < 0x100000) {
294 "PhysicalMemoryManager::allocateRegion() [specific] "
295 "- failed to get space from <1MB range list");
298 }
else if (start < 0x1000000 && (start + cPages *
getPageSize()) < 0x1000000) {
301 "PhysicalMemoryManager::allocateRegion() [specific] - "
303 << cPages <<
" pages of memory from <16MB range list at " <<
Hex << start);
306 }
else if (start < 0x1000000) {
308 "PhysicalMemoryManager: Memory region neither completely "
309 "below nor above 1MB");
314 Region.setNonRamMemory(
true);
315 Region.setForced(
true);
320 uintptr_t vAddress = 0;
324 WARNING(
"AllocateRegion: MemoryRegion allocation failed.");
330 for (
size_t i = 0; i < cPages; i++)
331 if (virtualAddressSpace.
map(
336 WARNING(
"AllocateRegion: VirtualAddressSpace::map failed.");
344 Region.m_bPageBacked =
false;
358 pageConstraints = (pageConstraints & ~addressConstraints) |
below16MB;
364 WARNING(
"AllocateRegion: MemoryRegion allocation failed.");
368 uint32_t allocatedStart = 0;
378 "PhysicalMemoryManager::allocateRegion() - "
379 "failed to get space from <1MB range list");
385 "PhysicalMemoryManager::allocateRegion() - "
386 "failed to get space from <16MB range list");
392 for (
size_t i = 0; i < cPages; i++)
393 if (virtualAddressSpace.
map(
397 WARNING(
"AllocateRegion: VirtualAddressSpace::map failed.");
402 for (
size_t i = 0; i < cPages; i++) {
404 if (virtualAddressSpace.
map(
409 for (
size_t mapped = 0; mapped < i; ++mapped) {
410 void* mappedAddress =
412 physical_uintptr_t mappedPage = 0;
413 size_t mappedFlags = 0;
414 virtualAddressSpace.
getMapping(mappedAddress, mappedPage, mappedFlags);
415 virtualAddressSpace.
unmap(mappedAddress);
419 WARNING(
"AllocateRegion: VirtualAddressSpace::map failed.");
431 Region.m_bPageBacked = (pageConstraints &
virtualOnly) ||
443 NOTICE(
"Shutting down X86CommonPhysicalMemoryManager");
449 NOTICE(
"memory-map:");
451 physical_uintptr_t top = 0;
458 void* MemoryMap = Info.getMemoryMap();
460 panic(
"no memory map provided by the bootloader");
464 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
465 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
466 uint32_t type = Info.getMemoryMapEntryType(MemoryMap);
468 NOTICE(
" " <<
Hex << addr <<
" - " << (addr + length) <<
", type: " << type);
470 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
477 uint64_t rangeTop = addr + length;
478 if (rangeTop < 0x1000000) {
481 }
else if (rangeTop >= 0x100000000ULL) {
486 if (addr < 0x1000000) {
488 length = rangeTop - 0x1000000;
492 if (rangeTop >= top) {
501 panic(
"No usable memory regions were discovered.");
512 if (m_BootstrapPinnedPageRefcount) {
515 p.refcount = m_BootstrapPinnedPageRefcount;
518 FATAL_NOLOCK(
"PhysicalMemoryManager: failed to publish bootstrap page pins");
520 m_BootstrapPinnedPage = 0;
521 m_BootstrapPinnedPageRefcount = 0;
527 MemoryMap = Info.getMemoryMap();
529 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
530 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
531 uint32_t type = Info.getMemoryMapEntryType(MemoryMap);
534 if (addr < 0x100000) {
538 if ((addr + length) >= 0x100000)
539 panic(
"PhysicalMemoryManager: strange memory-map");
542 }
else if (addr < 0x1000000) {
543 uint64_t upperBound = addr + length;
544 if (upperBound >= 0x1000000)
545 upperBound = 0x1000000;
549 }
else if (type == 3 || type == 4) {
553 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
558 extern void* kernel_start;
559 extern void* kernel_end;
561 reinterpret_cast<uintptr_t
>(KERNEL_VIRTUAL_ADDRESS),
562 reinterpret_cast<uintptr_t
>(&kernel_end) -
563 reinterpret_cast<uintptr_t
>(&kernel_start)) &&
566 "PhysicalMemoryManager: could not remove the kernel image from "
572 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
573 NOTICE(
"free memory ranges (below 1MB):");
580 NOTICE(
"free memory ranges (below 16MB):");
587 NOTICE(
"ACPI ranges:");
598 MemoryMap = Info.getMemoryMap();
600 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
601 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
605 WARNING(
"Memory region " << addr <<
" not used.");
606 }
else if (addr >= 0x100000000ULL) {
611 "PhysicalMemoryManager: Failed to create the list of ranges "
612 "of free physical space");
614 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
618 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
619 NOTICE(
"physical memory ranges:");
630 KERNEL_VIRTUAL_MEMORYREGION_SIZE);
634 NOTICE(
"64-bit memory-map:");
640 size_t numPagesOver4G = 0;
641 const uint64_t fourGiB = 0x100000000ULL;
642 const uint64_t sixtyFourGiB = 0x1000000000ULL;
644 uint64_t physicalRangeTop = fourGiB;
645 void* MemoryMap = Info.getMemoryMap();
647 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
648 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
649 uint32_t type = Info.getMemoryMapEntryType(MemoryMap);
650 uint64_t rangeTop = addr + length;
652 if (rangeTop < addr) {
653 panic(
"PhysicalMemoryManager: memory-map entry overflow");
656 if (rangeTop > fourGiB) {
657 uint64_t highAddr = addr < fourGiB ? fourGiB : addr;
658 if (rangeTop > physicalRangeTop) {
659 physicalRangeTop = rangeTop;
662 NOTICE(
" " <<
Hex << highAddr <<
" - " << rangeTop <<
", type: " << type);
665 uint64_t alignedHighAddr = (highAddr + pageSize - 1) & ~(pageSize - 1);
666 uint64_t alignedRangeTop = rangeTop & ~(pageSize - 1);
667 if (alignedHighAddr < alignedRangeTop) {
668 uint64_t highLength = alignedRangeTop - alignedHighAddr;
669 size_t numPages = highLength / pageSize;
670 if (alignedHighAddr < sixtyFourGiB && alignedRangeTop > sixtyFourGiB) {
671 m_PageStack.
free(alignedHighAddr, sixtyFourGiB - alignedHighAddr,
true);
677 numPagesOver4G += numPages;
682 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
688 MemoryMap = Info.getMemoryMap();
690 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
691 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
692 uint64_t rangeTop = addr + length;
694 if (rangeTop < addr) {
695 panic(
"PhysicalMemoryManager: memory-map entry overflow");
698 if (Info.getMemoryMapEntryType(MemoryMap) == 1 && rangeTop > fourGiB) {
699 uint64_t highAddr = addr < fourGiB ? fourGiB : addr;
700 uint64_t alignedHighAddr = (highAddr + pageSize - 1) & ~(pageSize - 1);
701 uint64_t alignedRangeTop = rangeTop & ~(pageSize - 1);
702 if (alignedHighAddr < alignedRangeTop) {
703 size_t numPages = (alignedRangeTop - alignedHighAddr) / pageSize;
705 alignedHighAddr,
reinterpret_cast<void*
>(0xFFFF800000000000ULL + alignedHighAddr),
707 FATAL(
"failed to map physical memory");
712 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
717 if (physicalRangeTop > fourGiB) {
721 NOTICE(
" --> " << numPagesOver4G <<
" pages exist above 4G!");
727 MemoryMap = Info.getMemoryMap();
729 if ((Info.getMemoryMapEntryType(MemoryMap) == 3 ||
730 Info.getMemoryMapEntryType(MemoryMap) == 4) &&
731 Info.getMemoryMapEntryAddress(MemoryMap) >= 0x100000000ULL) {
733 Info.getMemoryMapEntryLength(MemoryMap));
736 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
739 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
741 NOTICE(
"ACPI ranges (x64 added):");
751 MemoryMap = Info.getMemoryMap();
753 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
754 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
755 uint64_t rangeTop = addr + length;
757 if (rangeTop < addr) {
758 panic(
"PhysicalMemoryManager: memory-map entry overflow");
763 WARNING(
"Memory region " << addr <<
" not used.");
764 }
else if (rangeTop > fourGiB) {
765 uint64_t highAddr = addr < fourGiB ? fourGiB : addr;
768 "PhysicalMemoryManager: Failed to create the list of "
769 "ranges of free physical space");
773 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
777 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
778 NOTICE(
"physical memory ranges, 64-bit added:");
790 extern void* kernel_init;
791 extern void* kernel_init_end;
794 "PhysicalMemoryManager: kernel initialisation complete, cleaning "
799 size_t count = (
reinterpret_cast<uintptr_t
>(&kernel_init_end) -
800 reinterpret_cast<uintptr_t
>(&kernel_init)) /
802 for (
size_t i = 0; i < count; i++) {
803 void* vAddress = adjust_pointer(
reinterpret_cast<void*
>(&kernel_init), i *
getPageSize());
807 physical_uintptr_t pAddress;
808 kernelSpace.
getMapping(vAddress, pAddress, flags);
811 kernelSpace.
unmap(vAddress);
816 reinterpret_cast<uintptr_t
>(KERNEL_VIRTUAL_ADDRESS),
819 NOTICE(
"PhysicalMemoryManager: cleaned up " <<
Dec << (count * 4) <<
Hex
820 <<
"KB of init-only code.");
832 m_RegionLock(false, true),
834 m_PageMetadataReady(false),
835 m_BootstrapPinnedPage(0),
836 m_BootstrapPinnedPageRefcount(0) {}
844 if (*it == pRegion) {
846 uintptr_t start =
reinterpret_cast<uintptr_t
>(pRegion->
virtualAddress());
850 if (pRegion->m_bPageBacked) {
853 }
else if (pRegion->getNonRamMemory()) {
854 if (!pRegion->getForced())
857 if (phys < 0x100000 && (phys + cPages *
getPageSize()) < 0x100000) {
859 }
else if (phys < 0x1000000 && (phys + cPages *
getPageSize()) < 0x1000000) {
861 }
else if (phys < 0x1000000) {
863 "PhysicalMemoryManager: Memory region neither "
864 "completely below nor above 1MB");
869 for (
size_t i = 0; i < cPages; i++) {
871 if (!virtualAddressSpace.
isMapped(vAddr)) {
875 physical_uintptr_t pAddr;
877 virtualAddressSpace.
getMapping(vAddr, pAddr, flags);
879 virtualAddressSpace.
unmap(vAddr);
880 if (!pRegion->getNonRamMemory() && pRegion->m_bPageBacked) {
930 physical_uintptr_t result = 0;
955static void performPush(T* stack,
size_t& stackSize, uint64_t
physicalAddress,
size_t count) {
956 size_t nextEntry = stackSize /
sizeof(T);
958 for (
size_t i = 0; i < count; ++i) {
963 stackSize +=
sizeof(T) * count;
989 m_DesiredCapacity[index] += length /
getPageSize();
1000 size_t entrySize = index ?
sizeof(uint64_t) :
sizeof(uint32_t);
1001 if (m_StackSize[index] > m_StackMax[index] ||
1002 numPages > (m_StackMax[index] - m_StackSize[index]) / entrySize) {
1003 panic(
"PhysicalMemoryManager: page stack capacity exhausted");
1007 performPush(
reinterpret_cast<uint32_t*
>(m_Stack[index]), m_StackSize[index],
physicalAddress,
1010 performPush(
reinterpret_cast<uint64_t*
>(m_Stack[index]), m_StackSize[index],
physicalAddress,
1016 if (g_AllocedPages > 0) {
1017 if (g_AllocedPages >= numPages) {
1018 g_AllocedPages -= numPages;
1024 m_FreePages += numPages;
1026 m_TotalPages += numPages;
1030 for (
size_t i = 0; i < StackCount; i++) {
1031 m_Stack[i] =
nullptr;
1034 m_DesiredCapacity[i] = 0;
1035 m_StackReady[i] =
false;
1052 if (
LIKELY(m_Stack[0] !=
nullptr)) {
1065 for (
size_t i = 1; i < StackCount; ++i) {
1066 m_StackReady[i] =
true;
1071 m_StackReady[0] =
true;
1075 bool mapped =
false;
1076 size_t entrySize = index ?
sizeof(uint64_t) :
sizeof(uint32_t);
1078 void* virtualAddress = adjust_pointer(m_Stack[index], m_StackMax[index]);
1081 if (m_StackMax[index] / entrySize >= m_DesiredCapacity[index]) {
1105 if (X64AddressSpace.mapPageStructuresAbove4GB(
1114 if (AddressSpace.
isMapped(virtualAddress)) {
bool compactingForCurrentExecution() const
Special memory entity in the kernel's virtual address space.
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
physical_uintptr_t m_PhysicalAddress
static const size_t continuous
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
static const size_t below64GB
static const size_t below1MB
static const size_t force
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 addressConstraints
static const size_t below4GB
static ProcessorInformation & information()
static size_t m_Initialised
bool getRange(size_t index, Range &range) const
bool allocate(T length, T &address)
void free(T address, T length, bool merge=true)
bool allocateSpecific(T address, T length)
bool acquire(bool recurse=false, bool safe=true)
Process * getParent() const
virtual uintptr_t getKernelVirtualPagestackAdd2() const
virtual bool mapHuge(physical_uintptr_t physAddress, void *virtualAddress, size_t count, size_t flags)
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool isMapped(void *virtualAddress)=0
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
virtual uintptr_t getKernelVirtualPagestackAdd1() const
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual void unmap(void *virtualAddress)=0
virtual uintptr_t getKernelVirtualPagestack() const
bool mapPageStructures(physical_uintptr_t physAddress, void *virtualAddress, size_t flags)
void free(uint64_t physicalAddress, size_t length, bool newMemory=false)
bool maybeMap(size_t index, uint64_t physicalAddress)
Atomic< bool > m_StackReady[StackCount]
PageStack() INITIALISATION_ONLY
size_t m_StackSize[StackCount]
physical_uintptr_t allocate(size_t constraints, bool waitForReady=true)
void * m_Stack[StackCount]
Implementation of the PhysicalMemoryManager for common x86.
static X86CommonPhysicalMemoryManager & instance()
RangeList< uint32_t > m_RangeBelow1MB
virtual ~X86CommonPhysicalMemoryManager() override
void initialise64(const BootstrapStruct_t &Info) INITIALISATION_ONLY
static X86CommonPhysicalMemoryManager m_Instance
RangeList< uintptr_t > m_MemoryRegions
RangeList< uint64_t > m_PhysicalRanges
void initialise(const BootstrapStruct_t &Info) INITIALISATION_ONLY
X86CommonPhysicalMemoryManager() INITIALISATION_ONLY
void initialisationDone()
virtual size_t freePageCount() const override
RangeList< uint64_t > m_AcpiRanges
virtual void freePageUnlocked(physical_uintptr_t page) override
void unmapRegion(MemoryRegion *pRegion) override
virtual void pin(physical_uintptr_t page) override
virtual void freePage(physical_uintptr_t page) override
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1) override
HashTable< PageHashable, struct page > m_PageMetadata
RangeList< uint32_t > m_RangeBelow16MB
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0) override
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
uintptr_t physicalAddress(physical_uintptr_t address) PURE
EXPORTED_PUBLIC size_t g_FreePages
void pushBack(const T &value)
void clear(bool freeMem=false)