The Pedigree Project 0.1
x86_common/PhysicalMemoryManager.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
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" // IWYU pragma: keep
35#include "pedigree/kernel/utilities/Vector.h"
36#include "pedigree/kernel/utilities/utility.h"
37
38#include "../x64/VirtualAddressSpace.h"
39
40EXPORTED_PUBLIC size_t g_FreePages = 0;
41EXPORTED_PUBLIC size_t g_AllocedPages = 0;
42
44
45static void trackPages(ssize_t v, ssize_t p, ssize_t s) {
46 // Track, if we can.
47 Thread* pThread = Processor::information().getCurrentThread();
48 if (pThread) {
49 Process* pProcess = pThread->getParent();
50 if (pProcess) {
51 pProcess->trackPages(v, p, s);
52 }
53 }
54}
55
56#if !HOSTED
59}
60#endif
61
63 return m_PageStack.freePages();
64}
65
66physical_uintptr_t X86CommonPhysicalMemoryManager::allocatePage(size_t pageConstraints) {
67 static Atomic<bool> bDidHitWatermark(false);
68
69 // Recursion allowed, to permit e.g. calls from the manager to the heap to
70 // succeed without needing to release/re-acquire the lock.
71 m_Lock.acquire(true);
72
73 physical_uintptr_t ptr;
74
75 // Some methods of handling memory pressure require allocating pages, so
76 // we need to not end up recursively trying to release the pressure.
77 MemoryPressureManager& pressureManager = MemoryPressureManager::instance();
78 if (!pressureManager.compactingForCurrentExecution()) {
79 if (m_PageStack.freePages() < MemoryPressureManager::getHighWatermark()) {
80 // Make sure the compact can trigger frees.
82
83 WARNING_NOLOCK("Memory pressure encountered, performing a compact...");
84 if (!pressureManager.compact())
85 ERROR_NOLOCK("Compact did not alleviate any memory pressure.");
86 else
87 NOTICE_NOLOCK("Compact was successful.");
88
89 m_Lock.acquire(true);
90
91 bDidHitWatermark.compareAndSwap(false, true);
92 } else if (bDidHitWatermark.compareAndSwap(true, false)) {
93 ERROR_NOLOCK("<pressure was hit, but is no longer being hit>");
94 }
95 }
96
97 ptr = m_PageStack.allocate(pageConstraints);
98 if (!ptr) {
99 if (Processor::m_Initialised == 2) {
100 Metrics::increment(Metrics::PhysicalPageAllocFailure);
101 }
102 panic("Out of memory.");
103 }
104 if (Processor::m_Initialised == 2) {
105 Metrics::increment(Metrics::PhysicalPageAlloc);
106 }
107
108 EMIT_IF(MEMORY_TRACING) {
109 traceAllocation(reinterpret_cast<void*>(ptr), MemoryTracing::PageAlloc, 4096);
110 }
111
112 trackPages(0, 1, 0);
113
114 m_Lock.release();
115
116 EMIT_IF(TRACK_PAGE_ALLOCATIONS) {
117 if (Processor::m_Initialised == 2) {
118 if (!g_AllocationCommand.isMallocing()) {
119 g_AllocationCommand.allocatePage(ptr);
120 }
121 }
122 }
123
124 return ptr;
125}
126physical_uintptr_t X86CommonPhysicalMemoryManager::tryAllocatePage() {
127 m_Lock.acquire(true);
128 physical_uintptr_t ptr;
129 ptr = m_PageStack.allocate(0, false);
130 if (!ptr) {
131 m_Lock.release();
132 if (Processor::m_Initialised == 2) {
133 Metrics::increment(Metrics::PhysicalPageAllocFailure);
134 }
135 return 0;
136 }
137 if (Processor::m_Initialised == 2) {
138 Metrics::increment(Metrics::PhysicalPageAlloc);
139 }
140
141 EMIT_IF(MEMORY_TRACING) {
142 traceAllocation(reinterpret_cast<void*>(ptr), MemoryTracing::PageAlloc, 4096);
143 }
144
145 trackPages(0, 1, 0);
146
147 m_Lock.release();
148
149 EMIT_IF(TRACK_PAGE_ALLOCATIONS) {
150 if (Processor::m_Initialised == 2) {
151 if (!g_AllocationCommand.isMallocing()) {
152 g_AllocationCommand.allocatePage(ptr);
153 }
154 }
155 }
156
157 return ptr;
158}
159
160PhysicalMemoryManager::MemorySnapshot X86CommonPhysicalMemoryManager::memorySnapshot() const {
161 auto& self = *const_cast<X86CommonPhysicalMemoryManager*>(this);
162 RecursingLockGuard<Spinlock> guard(self.m_Lock);
163 return {m_PageStack.totalPages(), m_PageStack.freePages(), true};
164}
165
166bool X86CommonPhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page,
167 void* buffer) {
168#if X64
169 if (!page || (page & (getPageSize() - 1)) || !buffer)
170 return false;
171 MemoryCopy(buffer, reinterpret_cast<const void*>(page + 0xffff800000000000ULL), getPageSize());
172 return true;
173#else
174 return false;
175#endif
176}
177bool X86CommonPhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
178 const void* buffer) {
179#if X64
180 if (!page || (page & (getPageSize() - 1)) || !buffer)
181 return false;
182 MemoryCopy(reinterpret_cast<void*>(page + 0xffff800000000000ULL), buffer, getPageSize());
183 return true;
184#else
185 return false;
186#endif
187}
188
195 if (!m_Lock.acquired())
196 FATAL(
197 "X86CommonPhysicalMemoryManager::freePageUnlocked called without "
198 "an acquired lock");
199
200 if (!m_PageMetadataReady) {
201 if (m_BootstrapPinnedPageRefcount && page == m_BootstrapPinnedPage) {
202 if (--m_BootstrapPinnedPageRefcount) {
203 return;
204 }
205 m_BootstrapPinnedPage = 0;
206 }
207 } else {
208 // Check for pinned page.
209 PageHashable index(page);
210 MetadataTable::LookupResult result = m_PageMetadata.lookup(index);
211 if (result.hasValue()) {
212 struct page p = result.value();
213 if (p.active) {
214 if (--p.refcount) {
215 // Still references.
216 m_PageMetadata.update(index, p);
217 return;
218 } else {
219 // No more references, stop tracking page.
220 p.active = false;
221 m_PageMetadata.update(index, p);
222 }
223 }
224 }
225 }
226
228
229 if (Processor::m_Initialised == 2) {
230 Metrics::increment(Metrics::PhysicalPageFree);
231 }
232
233 trackPages(0, -1, 0);
234}
237
238 if (!m_PageMetadataReady) {
239 if (m_BootstrapPinnedPageRefcount) {
240 if (m_BootstrapPinnedPage != page) {
241 FATAL_NOLOCK("PhysicalMemoryManager: multiple pages pinned during metadata bootstrap");
242 }
243 } else {
244 m_BootstrapPinnedPage = page;
245 }
246 ++m_BootstrapPinnedPageRefcount;
247 return;
248 }
249
250 PageHashable index(page);
251 MetadataTable::LookupResult result = m_PageMetadata.lookup(index);
252 if (result.hasValue()) {
253 struct page p = result.value();
254 ++p.refcount;
255 p.active = true;
256 m_PageMetadata.update(index, p);
257 } else {
258 struct page p;
259 p.refcount = 1;
260 p.active = true;
261 m_PageMetadata.insert(index, p);
262 }
263}
265 size_t pageConstraints, size_t Flags,
266 physical_uintptr_t start) {
267 LockGuard<Spinlock> guard(m_RegionLock);
268
269 // Allocate a specific physical memory region (always physically continuous)
270 if (start != static_cast<physical_uintptr_t>(-1)) {
271 // Page-align the start address.
272 start &= ~(getPageSize() - 1);
273
274 if (((pageConstraints & continuous) != continuous) || (pageConstraints & virtualOnly))
275 panic("PhysicalMemoryManager::allocateRegion(): function misused");
276
277 // Remove the memory from the range-lists (if desired/possible)
278 if ((pageConstraints & nonRamMemory) == nonRamMemory) {
279 Region.setNonRamMemory(true);
280 if (m_PhysicalRanges.allocateSpecific(start, cPages * getPageSize()) == false) {
281 if ((pageConstraints & force) != force) {
282 ERROR(
283 "PhysicalMemoryManager::allocateRegion() [specific] "
284 "- failed to get space from general range list and "
285 "force is not set");
286 return false;
287 } else
288 Region.setForced(true);
289 }
290 } else {
291 if (start < 0x100000 && (start + cPages * getPageSize()) < 0x100000) {
292 if (m_RangeBelow1MB.allocateSpecific(start, cPages * getPageSize()) == false) {
293 ERROR(
294 "PhysicalMemoryManager::allocateRegion() [specific] "
295 "- failed to get space from <1MB range list");
296 return false;
297 }
298 } else if (start < 0x1000000 && (start + cPages * getPageSize()) < 0x1000000) {
299 if (m_RangeBelow16MB.allocateSpecific(start, cPages * getPageSize()) == false) {
300 ERROR(
301 "PhysicalMemoryManager::allocateRegion() [specific] - "
302 "failed to get "
303 << cPages << " pages of memory from <16MB range list at " << Hex << start);
304 return false;
305 }
306 } else if (start < 0x1000000) {
307 ERROR(
308 "PhysicalMemoryManager: Memory region neither completely "
309 "below nor above 1MB");
310 return false;
311 } else {
312 // Ensure that free() does not attempt to free the given
313 // memory...
314 Region.setNonRamMemory(true);
315 Region.setForced(true);
316 }
317 }
318
319 // Allocate the virtual address space
320 uintptr_t vAddress = 0;
321
323 false) {
324 WARNING("AllocateRegion: MemoryRegion allocation failed.");
325 return false;
326 }
327
328 // Map the physical memory into the allocated space
329 VirtualAddressSpace& virtualAddressSpace = Processor::information().getVirtualAddressSpace();
330 for (size_t i = 0; i < cPages; i++)
331 if (virtualAddressSpace.map(
333 reinterpret_cast<void*>(vAddress + i * PhysicalMemoryManager::getPageSize()),
334 Flags) == false) {
336 WARNING("AllocateRegion: VirtualAddressSpace::map failed.");
337 return false;
338 }
339
340 // Set the memory-region's members
341 Region.m_VirtualAddress = reinterpret_cast<void*>(vAddress);
342 Region.m_PhysicalAddress = start;
343 Region.m_Size = cPages * PhysicalMemoryManager::getPageSize();
344 Region.m_bPageBacked = false;
345 Region.setAnonymous(pageConstraints & PhysicalMemoryManager::anonymous);
346 // NOTICE("MR: Allocated " << Hex << vAddress << " (phys " <<
347 // static_cast<uintptr_t>(start) << "), size " << (cPages*4096));
348
349 // Ownership tracking is required for unmapRegion(). Anonymous regions are
350 // filtered from diagnostic listings instead.
352 return true;
353 } else {
354 // If we need continuous memory, switch to below16 if not already
355 if ((pageConstraints & continuous) == continuous)
356 if ((pageConstraints & addressConstraints) != below1MB &&
357 (pageConstraints & addressConstraints) != below16MB)
358 pageConstraints = (pageConstraints & ~addressConstraints) | below16MB;
359
360 // Allocate the virtual address space
361 uintptr_t vAddress;
363 false) {
364 WARNING("AllocateRegion: MemoryRegion allocation failed.");
365 return false;
366 }
367
368 uint32_t allocatedStart = 0;
369 if (!(pageConstraints & virtualOnly)) {
370 VirtualAddressSpace& virtualAddressSpace = Processor::information().getVirtualAddressSpace();
371
372 if ((pageConstraints & addressConstraints) == below1MB ||
373 (pageConstraints & addressConstraints) == below16MB) {
374 // Allocate a range
375 if ((pageConstraints & addressConstraints) == below1MB) {
376 if (m_RangeBelow1MB.allocate(cPages * getPageSize(), allocatedStart) == false) {
377 ERROR(
378 "PhysicalMemoryManager::allocateRegion() - "
379 "failed to get space from <1MB range list");
380 return false;
381 }
382 } else if ((pageConstraints & addressConstraints) == below16MB) {
383 if (m_RangeBelow16MB.allocate(cPages * getPageSize(), allocatedStart) == false) {
384 ERROR(
385 "PhysicalMemoryManager::allocateRegion() - "
386 "failed to get space from <16MB range list");
387 return false;
388 }
389 }
390
391 // Map the physical memory into the allocated space
392 for (size_t i = 0; i < cPages; i++)
393 if (virtualAddressSpace.map(
394 allocatedStart + i * PhysicalMemoryManager::getPageSize(),
395 reinterpret_cast<void*>(vAddress + i * PhysicalMemoryManager::getPageSize()),
396 Flags) == false) {
397 WARNING("AllocateRegion: VirtualAddressSpace::map failed.");
398 return false;
399 }
400 } else {
401 // Map the physical memory into the allocated space
402 for (size_t i = 0; i < cPages; i++) {
403 physical_uintptr_t page = allocatePage(pageConstraints & addressConstraints);
404 if (virtualAddressSpace.map(
405 page,
406 reinterpret_cast<void*>(vAddress + i * PhysicalMemoryManager::getPageSize()),
407 Flags) == false) {
408 freePage(page);
409 for (size_t mapped = 0; mapped < i; ++mapped) {
410 void* mappedAddress =
411 reinterpret_cast<void*>(vAddress + mapped * PhysicalMemoryManager::getPageSize());
412 physical_uintptr_t mappedPage = 0;
413 size_t mappedFlags = 0;
414 virtualAddressSpace.getMapping(mappedAddress, mappedPage, mappedFlags);
415 virtualAddressSpace.unmap(mappedAddress);
416 freePage(mappedPage);
417 }
419 WARNING("AllocateRegion: VirtualAddressSpace::map failed.");
420 return false;
421 }
422 }
423 }
424 }
425
426 // Set the memory-region's members
427 Region.m_VirtualAddress = reinterpret_cast<void*>(vAddress);
428 Region.m_PhysicalAddress = allocatedStart;
429 Region.m_Size = cPages * PhysicalMemoryManager::getPageSize();
430 const size_t addressConstraint = pageConstraints & addressConstraints;
431 Region.m_bPageBacked = (pageConstraints & virtualOnly) ||
432 (addressConstraint != below1MB && addressConstraint != below16MB);
433 Region.setAnonymous(pageConstraints & PhysicalMemoryManager::anonymous);
434
435 // Ownership tracking is required for unmapRegion(). Anonymous regions are
436 // filtered from diagnostic listings instead.
438 return true;
439 }
440}
441
443 NOTICE("Shutting down X86CommonPhysicalMemoryManager");
445 m_PageMetadata.clear();
446}
447
449 NOTICE("memory-map:");
450
451 physical_uintptr_t top = 0;
452 size_t pageSize = getPageSize();
453
454 // Fill the page-stack (usable memory above 16MB)
455 // NOTE: We must do the page-stack first, because the range-lists already
456 // need the
457 // memory-management
458 void* MemoryMap = Info.getMemoryMap();
459 if (!MemoryMap)
460 panic("no memory map provided by the bootloader");
461
462 // Fill our stack with pages below the 4GB threshold.
463 while (MemoryMap) {
464 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
465 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
466 uint32_t type = Info.getMemoryMapEntryType(MemoryMap);
467
468 NOTICE(" " << Hex << addr << " - " << (addr + length) << ", type: " << type);
469
470 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
471
472 if (type != 1) {
473 continue;
474 }
475
476 // We don't want pages below 1MB, and don't want any over 4GB.
477 uint64_t rangeTop = addr + length;
478 if (rangeTop < 0x1000000) {
479 // Entire region is below 1MB.
480 continue;
481 } else if (rangeTop >= 0x100000000ULL) {
482 // Region is too high.
483 continue;
484 }
485
486 if (addr < 0x1000000) {
487 // Region crosses 1MB mark. Fix to base at 1MB instead.
488 length = rangeTop - 0x1000000;
489 addr = 0x1000000;
490 }
491
492 if (rangeTop >= top) {
493 // Update the "top of memory" value.
494 top = rangeTop;
495 }
496
497 m_PageStack.free(addr, length, true);
498 }
499
500 if (!top) {
501 panic("No usable memory regions were discovered.");
502 }
503
504 // Stack with <4GB is done.
506
508 m_PageMetadata.reserve(top >> 12); // number of 4k pages in this zone
509
510 {
512 if (m_BootstrapPinnedPageRefcount) {
513 PageHashable index(m_BootstrapPinnedPage);
514 struct page p;
515 p.refcount = m_BootstrapPinnedPageRefcount;
516 p.active = true;
517 if (!m_PageMetadata.insert(index, p)) {
518 FATAL_NOLOCK("PhysicalMemoryManager: failed to publish bootstrap page pins");
519 }
520 m_BootstrapPinnedPage = 0;
521 m_BootstrapPinnedPageRefcount = 0;
522 }
523 m_PageMetadataReady = true;
524 }
525
526 // Fill the range-lists (usable memory below 1/16MB & ACPI)
527 MemoryMap = Info.getMemoryMap();
528 while (MemoryMap) {
529 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
530 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
531 uint32_t type = Info.getMemoryMapEntryType(MemoryMap);
532
533 if (type == 1) {
534 if (addr < 0x100000) {
535 // NOTE: Assumes that the entry/entries starting below 1MB don't
536 // cross the
537 // 1MB barrier
538 if ((addr + length) >= 0x100000)
539 panic("PhysicalMemoryManager: strange memory-map");
540
541 m_RangeBelow1MB.free(addr, length);
542 } else if (addr < 0x1000000) {
543 uint64_t upperBound = addr + length;
544 if (upperBound >= 0x1000000)
545 upperBound = 0x1000000;
546
547 m_RangeBelow16MB.free(addr, upperBound - addr);
548 }
549 } else if (type == 3 || type == 4) {
550 m_AcpiRanges.free(addr, length);
551 }
552
553 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
554 }
555
556 // Remove the pages used by the kernel from the range-list (below 16MB)
557 EMIT_IF(!HOSTED) {
558 extern void* kernel_start;
559 extern void* kernel_end;
560 if (!m_RangeBelow16MB.allocateSpecific(reinterpret_cast<uintptr_t>(&kernel_start) -
561 reinterpret_cast<uintptr_t>(KERNEL_VIRTUAL_ADDRESS),
562 reinterpret_cast<uintptr_t>(&kernel_end) -
563 reinterpret_cast<uintptr_t>(&kernel_start)) &&
564 !Info.isUefi()) {
565 panic(
566 "PhysicalMemoryManager: could not remove the kernel image from "
567 "the range-list");
568 }
569 }
570
571 // Print the ranges
572 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
573 NOTICE("free memory ranges (below 1MB):");
574 for (size_t i = 0; i < m_RangeBelow1MB.size(); i++) {
575 RangeList<uint32_t>::Range range(0, 0);
576 if (m_RangeBelow1MB.getRange(i, range)) {
577 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
578 }
579 }
580 NOTICE("free memory ranges (below 16MB):");
581 for (size_t i = 0; i < m_RangeBelow16MB.size(); i++) {
582 RangeList<uint32_t>::Range range(0, 0);
583 if (m_RangeBelow16MB.getRange(i, range)) {
584 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
585 }
586 }
587 NOTICE("ACPI ranges:");
588 for (size_t i = 0; i < m_AcpiRanges.size(); i++) {
589 RangeList<uint64_t>::Range range(0, 0);
590 if (m_AcpiRanges.getRange(i, range)) {
591 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
592 }
593 }
594 }
595
596 // Initialise the free physical ranges
597 m_PhysicalRanges.free(0, 0x100000000ULL);
598 MemoryMap = Info.getMemoryMap();
599 while (MemoryMap) {
600 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
601 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
602
603 // Only map if the variable fits into a uintptr_t - no overflow!
604 if (addr > ~0ULL) {
605 WARNING("Memory region " << addr << " not used.");
606 } else if (addr >= 0x100000000ULL) {
607 // Skip >= 4 GB for now, done in initialise64
608 break;
609 } else if (m_PhysicalRanges.allocateSpecific(addr, length) == false)
610 panic(
611 "PhysicalMemoryManager: Failed to create the list of ranges "
612 "of free physical space");
613
614 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
615 }
616
617 // Print the ranges
618 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
619 NOTICE("physical memory ranges:");
620 for (size_t i = 0; i < m_PhysicalRanges.size(); i++) {
621 RangeList<uint64_t>::Range range(0, 0);
622 if (m_PhysicalRanges.getRange(i, range)) {
623 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
624 }
625 }
626 }
627
628 // Initialise the range of virtual space for MemoryRegions
629 m_MemoryRegions.free(reinterpret_cast<uintptr_t>(KERNEL_VIRTUAL_MEMORYREGION_ADDRESS),
630 KERNEL_VIRTUAL_MEMORYREGION_SIZE);
631}
632
634 NOTICE("64-bit memory-map:");
635
636 // Fill the page-stack (usable memory above 16MB)
637 // NOTE: We must do the page-stack first, because the range-lists already
638 // need the
639 // memory-management
640 size_t numPagesOver4G = 0;
641 const uint64_t fourGiB = 0x100000000ULL;
642 const uint64_t sixtyFourGiB = 0x1000000000ULL;
643 const uint64_t pageSize = getPageSize();
644 uint64_t physicalRangeTop = fourGiB;
645 void* MemoryMap = Info.getMemoryMap();
646 while (MemoryMap) {
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;
651
652 if (rangeTop < addr) {
653 panic("PhysicalMemoryManager: memory-map entry overflow");
654 }
655
656 if (rangeTop > fourGiB) {
657 uint64_t highAddr = addr < fourGiB ? fourGiB : addr;
658 if (rangeTop > physicalRangeTop) {
659 physicalRangeTop = rangeTop;
660 }
661
662 NOTICE(" " << Hex << highAddr << " - " << rangeTop << ", type: " << type);
663
664 if (type == 1) {
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);
672 m_PageStack.free(sixtyFourGiB, alignedRangeTop - sixtyFourGiB, true);
673 } else {
674 m_PageStack.free(alignedHighAddr, highLength, true);
675 }
676
677 numPagesOver4G += numPages;
678 }
679 }
680 }
681
682 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
683 }
684
685 // Direct-map every usable range independently. Aggregating their page
686 // counts would incorrectly map firmware holes as RAM.
688 MemoryMap = Info.getMemoryMap();
689 while (MemoryMap) {
690 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
691 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
692 uint64_t rangeTop = addr + length;
693
694 if (rangeTop < addr) {
695 panic("PhysicalMemoryManager: memory-map entry overflow");
696 }
697
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;
704 if (!kernelSpace.mapHuge(
705 alignedHighAddr, reinterpret_cast<void*>(0xFFFF800000000000ULL + alignedHighAddr),
707 FATAL("failed to map physical memory");
708 }
709 }
710 }
711
712 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
713 }
714
715 // This range tracks addresses available for non-RAM mappings. Seed the
716 // whole firmware-described span before removing every described region.
717 if (physicalRangeTop > fourGiB) {
718 m_PhysicalRanges.free(fourGiB, physicalRangeTop - fourGiB);
719 }
720
721 NOTICE(" --> " << numPagesOver4G << " pages exist above 4G!");
722
723 // Stacks >=4GB are done.
725
726 // Fill the range-lists (usable memory below 1/16MB & ACPI)
727 MemoryMap = Info.getMemoryMap();
728 while (MemoryMap) {
729 if ((Info.getMemoryMapEntryType(MemoryMap) == 3 ||
730 Info.getMemoryMapEntryType(MemoryMap) == 4) &&
731 Info.getMemoryMapEntryAddress(MemoryMap) >= 0x100000000ULL) {
732 m_AcpiRanges.free(Info.getMemoryMapEntryAddress(MemoryMap),
733 Info.getMemoryMapEntryLength(MemoryMap));
734 }
735
736 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
737 }
738
739 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
740 // Print the ranges
741 NOTICE("ACPI ranges (x64 added):");
742 for (size_t i = 0; i < m_AcpiRanges.size(); i++) {
743 RangeList<uint64_t>::Range range(0, 0);
744 if (m_AcpiRanges.getRange(i, range)) {
745 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
746 }
747 }
748 }
749
750 // Initialise the free physical ranges
751 MemoryMap = Info.getMemoryMap();
752 while (MemoryMap) {
753 uint64_t addr = Info.getMemoryMapEntryAddress(MemoryMap);
754 uint64_t length = Info.getMemoryMapEntryLength(MemoryMap);
755 uint64_t rangeTop = addr + length;
756
757 if (rangeTop < addr) {
758 panic("PhysicalMemoryManager: memory-map entry overflow");
759 }
760
761 // Only map if the variable fits into a uintptr_t - no overflow!
762 if (addr > ~0ULL) {
763 WARNING("Memory region " << addr << " not used.");
764 } else if (rangeTop > fourGiB) {
765 uint64_t highAddr = addr < fourGiB ? fourGiB : addr;
766 if (!m_PhysicalRanges.allocateSpecific(highAddr, rangeTop - highAddr)) {
767 panic(
768 "PhysicalMemoryManager: Failed to create the list of "
769 "ranges of free physical space");
770 }
771 }
772
773 MemoryMap = Info.nextMemoryMapEntry(MemoryMap);
774 }
775
776 // Print the ranges
777 EMIT_IF(VERBOSE_MEMORY_MANAGER) {
778 NOTICE("physical memory ranges, 64-bit added:");
779 for (size_t i = 0; i < m_PhysicalRanges.size(); i++) {
780 RangeList<uint64_t>::Range range(0, 0);
781 if (m_PhysicalRanges.getRange(i, range)) {
782 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
783 }
784 }
785 }
786}
787
789 EMIT_IF(!HOSTED) {
790 extern void* kernel_init;
791 extern void* kernel_init_end;
792
793 NOTICE(
794 "PhysicalMemoryManager: kernel initialisation complete, cleaning "
795 "up...");
796
797 // Unmap & free the .init section
799 size_t count = (reinterpret_cast<uintptr_t>(&kernel_init_end) -
800 reinterpret_cast<uintptr_t>(&kernel_init)) /
801 getPageSize();
802 for (size_t i = 0; i < count; i++) {
803 void* vAddress = adjust_pointer(reinterpret_cast<void*>(&kernel_init), i * getPageSize());
804
805 // Get the physical address
806 size_t flags;
807 physical_uintptr_t pAddress;
808 kernelSpace.getMapping(vAddress, pAddress, flags);
809
810 // Unmap the page
811 kernelSpace.unmap(vAddress);
812 }
813
814 // Free the physical pages
815 m_RangeBelow16MB.free(reinterpret_cast<uintptr_t>(&kernel_init) -
816 reinterpret_cast<uintptr_t>(KERNEL_VIRTUAL_ADDRESS),
817 count * getPageSize());
818
819 NOTICE("PhysicalMemoryManager: cleaned up " << Dec << (count * 4) << Hex
820 << "KB of init-only code.");
821 }
822}
823
825 : m_PageStack(),
826 m_RangeBelow1MB(),
827 m_RangeBelow16MB(),
828 m_PhysicalRanges(),
829 m_AcpiRanges(),
830 m_MemoryRegions(),
831 m_Lock(false, true),
832 m_RegionLock(false, true),
833 m_PageMetadata(),
834 m_PageMetadataReady(false),
835 m_BootstrapPinnedPage(0),
836 m_BootstrapPinnedPageRefcount(0) {}
838
840 LockGuard<Spinlock> guard(m_RegionLock);
841
844 if (*it == pRegion) {
845 size_t cPages = pRegion->size() / PhysicalMemoryManager::getPageSize();
846 uintptr_t start = reinterpret_cast<uintptr_t>(pRegion->virtualAddress());
847 physical_uintptr_t phys = pRegion->physicalAddress();
849
850 if (pRegion->m_bPageBacked) {
851 // Non-contiguous and lazy virtual-only regions own individual pages,
852 // not one physical range beginning at m_PhysicalAddress.
853 } else if (pRegion->getNonRamMemory()) {
854 if (!pRegion->getForced())
855 m_PhysicalRanges.free(phys, pRegion->size());
856 } else {
857 if (phys < 0x100000 && (phys + cPages * getPageSize()) < 0x100000) {
858 m_RangeBelow1MB.free(phys, cPages * getPageSize());
859 } else if (phys < 0x1000000 && (phys + cPages * getPageSize()) < 0x1000000) {
860 m_RangeBelow16MB.free(phys, cPages * getPageSize());
861 } else if (phys < 0x1000000) {
862 ERROR(
863 "PhysicalMemoryManager: Memory region neither "
864 "completely below nor above 1MB");
865 return;
866 }
867 }
868
869 for (size_t i = 0; i < cPages; i++) {
870 void* vAddr = reinterpret_cast<void*>(start + i * PhysicalMemoryManager::getPageSize());
871 if (!virtualAddressSpace.isMapped(vAddr)) {
872 // Can happen with virtualOnly mappings.
873 continue;
874 }
875 physical_uintptr_t pAddr;
876 size_t flags;
877 virtualAddressSpace.getMapping(vAddr, pAddr, flags);
878
879 virtualAddressSpace.unmap(vAddr);
880 if (!pRegion->getNonRamMemory() && pRegion->m_bPageBacked) {
881 freePage(pAddr);
882 }
883 }
884 // NOTICE("MR: Freed " << Hex << start << ", size " <<
885 // (cPages*4096));
886 m_MemoryRegions.free(start, pRegion->size());
888 break;
889 }
890 }
891}
892
893physical_uintptr_t X86CommonPhysicalMemoryManager::PageStack::allocate(size_t constraints,
894 bool waitForReady) {
895 initialise();
896
897 size_t index = 0;
899 index = 0;
900 else if (constraints == X86CommonPhysicalMemoryManager::below64GB)
901 index = 1;
902 else {
903 index = 2;
904
905 // Degrade quietly if this stack is not ready.
906 if (!m_StackReady[index]) {
907 index = 1;
908
909 if (!m_StackReady[index]) {
910 index = 0;
911 }
912 }
913 }
914
915 // Wait for the stack to be ready. With constraints, this will block until
916 // a specific page stack is ready. With no constraints, this will just
917 // block until the first page stack is ready (which should almost always
918 // be the case).
919 while (!m_StackReady[index]) {
920 if (!waitForReady)
921 return 0;
923 }
924
925 if (index == 2 && (!m_StackSize[2] || !m_StackReady[2]))
926 index = 1;
927 if (index == 1 && (!m_StackSize[1] || !m_StackReady[1]))
928 index = 0;
929
930 physical_uintptr_t result = 0;
931 if (m_StackSize[index]) {
932 if (index == 0) {
933 m_StackSize[0] -= 4;
934 result = *(reinterpret_cast<uint32_t*>(m_Stack[0]) + m_StackSize[0] / 4);
935 } else {
936 m_StackSize[index] -= 8;
937 result = *(reinterpret_cast<uint64_t*>(m_Stack[index]) + m_StackSize[index] / 8);
938 }
939 }
940
941 if (result) {
943 if (g_FreePages)
944 g_FreePages--;
945 g_AllocedPages++;
946
947 if (m_FreePages)
948 --m_FreePages;
949 }
950
951 return result;
952}
953
954template <class T>
955static void performPush(T* stack, size_t& stackSize, uint64_t physicalAddress, size_t count) {
956 size_t nextEntry = stackSize / sizeof(T);
957 T addend = 0;
958 for (size_t i = 0; i < count; ++i) {
959 stack[nextEntry + i] = static_cast<T>(physicalAddress + addend);
961 }
962
963 stackSize += sizeof(T) * count;
964}
965
967 bool newMemory) {
968 initialise();
969
970 // Select the right stack
972 size_t index = 0;
973 if (physicalAddress >= 0x100000000ULL) {
974 if (physicalAddress >= 0x1000000000ULL) {
975 index = 2;
976 } else {
977 index = 1;
978 }
979 }
980
981 // Don't attempt to map address zero.
982 if (UNLIKELY(!m_Stack[index])) {
983 return;
984 }
985
986 uint64_t topPhysical = physicalAddress + length;
987
988 if (newMemory) {
989 m_DesiredCapacity[index] += length / getPageSize();
990 }
991
992 for (; physicalAddress < topPhysical; physicalAddress += getPageSize()) {
993 // Expand the stack if necessary.
994 if (!maybeMap(index, physicalAddress)) {
995 break;
996 }
997 }
998
999 size_t numPages = (topPhysical - physicalAddress) / 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");
1004 }
1005
1006 if (index == 0) {
1007 performPush(reinterpret_cast<uint32_t*>(m_Stack[index]), m_StackSize[index], physicalAddress,
1008 numPages);
1009 } else {
1010 performPush(reinterpret_cast<uint64_t*>(m_Stack[index]), m_StackSize[index], physicalAddress,
1011 numPages);
1012 }
1013
1015 g_FreePages += numPages;
1016 if (g_AllocedPages > 0) {
1017 if (g_AllocedPages >= numPages) {
1018 g_AllocedPages -= numPages;
1019 } else {
1020 g_AllocedPages = 0;
1021 }
1022 }
1023
1024 m_FreePages += numPages;
1025 if (newMemory)
1026 m_TotalPages += numPages;
1027}
1028
1030 for (size_t i = 0; i < StackCount; i++) {
1031 m_Stack[i] = nullptr;
1032 m_StackMax[i] = 0;
1033 m_StackSize[i] = 0;
1034 m_DesiredCapacity[i] = 0;
1035 m_StackReady[i] = false;
1036 }
1037
1038 /*
1039 VirtualAddressSpace &AddressSpace = VirtualAddressSpace::getKernelAddressSpace();
1040
1041 // Set the locations for the page stacks in the virtual address space
1042 m_Stack[0] = reinterpret_cast<void *>(AddressSpace.getKernelVirtualPagestack());
1043 m_Stack[1] = reinterpret_cast<void *>(AddressSpace.getKernelVirtualPagestackAdd1());
1044 m_Stack[2] = reinterpret_cast<void *>(AddressSpace.getKernelVirtualPagestackAdd2());
1045 */
1046
1047 m_FreePages = 0;
1048 m_TotalPages = 0;
1049}
1050
1052 if (LIKELY(m_Stack[0] != nullptr)) {
1053 return;
1054 }
1055
1057
1058 // Set the locations for the page stacks in the virtual address space
1059 m_Stack[0] = reinterpret_cast<void*>(AddressSpace.getKernelVirtualPagestack());
1060 m_Stack[1] = reinterpret_cast<void*>(AddressSpace.getKernelVirtualPagestackAdd1());
1061 m_Stack[2] = reinterpret_cast<void*>(AddressSpace.getKernelVirtualPagestackAdd2());
1062}
1063
1065 for (size_t i = 1; i < StackCount; ++i) {
1066 m_StackReady[i] = true;
1067 }
1068}
1069
1071 m_StackReady[0] = true;
1072}
1073
1075 bool mapped = false;
1076 size_t entrySize = index ? sizeof(uint64_t) : sizeof(uint32_t);
1077
1078 void* virtualAddress = adjust_pointer(m_Stack[index], m_StackMax[index]);
1079
1080 // Do we even need to do this mapping?
1081 if (m_StackMax[index] / entrySize >= m_DesiredCapacity[index]) {
1082 return false;
1083 }
1084
1086
1087 EMIT_IF(HOSTED) {
1088 if (AddressSpace.map(physicalAddress, virtualAddress,
1090 mapped = true;
1091 }
1092 }
1093 else {
1094 // Get the kernel virtual address-space
1095 X64VirtualAddressSpace& X64AddressSpace =
1097
1098 if (!index) {
1099 if (X64AddressSpace.mapPageStructures(
1100 physicalAddress, virtualAddress,
1102 mapped = true;
1103 }
1104 } else {
1105 if (X64AddressSpace.mapPageStructuresAbove4GB(
1106 physicalAddress, virtualAddress,
1108 mapped = true;
1109 }
1110 }
1111 }
1112
1113 // Another page worth of entries is mapped - update capacity accordingly.
1114 if (AddressSpace.isMapped(virtualAddress)) {
1115 // This page is mapped, so we need to go ahead and start allocating the
1116 // next page in the stack. This way we always have the entire stack
1117 // mapped before we start pushing pages into it.
1118 m_StackMax[index] += getPageSize();
1119 }
1120
1121 // A page used for backing storage must never be pushed onto the free list,
1122 // including the final page that satisfies this stack's capacity demand.
1123 return mapped;
1124}
Special memory entity in the kernel's virtual address space.
void * m_VirtualAddress
void * virtualAddress() const
physical_uintptr_t physicalAddress() const
size_t size() const
physical_uintptr_t m_PhysicalAddress
static PhysicalMemoryManager & instance()
static ProcessorInformation & information()
static void pause()
static size_t m_Initialised
Definition Processor.h:483
bool getRange(size_t index, Range &range) const
Definition RangeList.h:419
bool allocate(T length, T &address)
Definition RangeList.h:318
size_t size() const
Definition RangeList.h:104
void free(T address, T length, bool merge=true)
Definition RangeList.h:157
bool allocateSpecific(T address, T length)
Definition RangeList.h:363
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
Process * getParent() const
Definition Thread.h:340
Iterator end()
Definition Vector.h:172
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
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual void unmap(void *virtualAddress)=0
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)
physical_uintptr_t allocate(size_t constraints, bool waitForReady=true)
Implementation of the PhysicalMemoryManager for common x86.
void initialise64(const BootstrapStruct_t &Info) INITIALISATION_ONLY
void initialise(const BootstrapStruct_t &Info) INITIALISATION_ONLY
virtual size_t freePageCount() const override
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
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0) override
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39
EXPORTED_PUBLIC size_t g_FreePages
void erase(size_t index)
Definition Vector.h:389
void pushBack(const T &value)
Definition Vector.h:275
void clear(bool freeMem=false)
Definition Vector.h:378