The Pedigree Project 0.1
hosted/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/LockGuard.h"
22#include "pedigree/kernel/Log.h"
23#include "pedigree/kernel/Metrics.h"
24#include "pedigree/kernel/machine/Trace.h"
25#include "pedigree/kernel/panic.h"
26#include "pedigree/kernel/processor/MemoryRegion.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/utilities/Cache.h"
29#include "pedigree/kernel/utilities/assert.h"
30#include "pedigree/kernel/utilities/utility.h"
31
32#if TRACK_PAGE_ALLOCATIONS
33#include "pedigree/kernel/debugger/commands/AllocationCommand.h"
34#endif
35
36#include "pedigree/kernel/core/SlamAllocator.h"
37#include "pedigree/kernel/process/MemoryPressureManager.h"
38
39#include "VirtualAddressSpace.h"
40
41namespace __pedigree_hosted {};
42using namespace __pedigree_hosted;
43
44#include <fcntl.h>
45#include <stdio.h>
46#include <unistd.h>
47
48uint32_t g_PageBitmap[16384] = {0};
49
51
52static char instanceStorage[sizeof(HostedPhysicalMemoryManager)] = {0};
53
56}
57
59 if (!m_Instance) {
60 m_Instance = new (reinterpret_cast<void*>(instanceStorage)) HostedPhysicalMemoryManager();
61 }
62 return *m_Instance;
63}
64
65physical_uintptr_t HostedPhysicalMemoryManager::allocatePage(size_t pageConstraints) {
66 static Atomic<bool> bDidHitWatermark(false);
67
68 m_Lock.acquire(true);
69
70 physical_uintptr_t ptr;
71
72 // Some methods of handling memory pressure require allocating pages, so
73 // we need to not end up recursively trying to release the pressure.
74 MemoryPressureManager& pressureManager = MemoryPressureManager::instance();
75 if (!pressureManager.compactingForCurrentExecution()) {
76 if (m_PageStack.freePages() < MemoryPressureManager::getHighWatermark()) {
77 // Make sure the compact can trigger frees.
79
80 WARNING_NOLOCK("Memory pressure encountered, performing a compact...");
81 if (!pressureManager.compact())
82 ERROR_NOLOCK("Compact did not alleviate any memory pressure.");
83 else
84 NOTICE_NOLOCK("Compact was successful.");
85
86 m_Lock.acquire(true);
87
88 bDidHitWatermark.compareAndSwap(false, true);
89 } else if (bDidHitWatermark.compareAndSwap(true, false)) {
90 ERROR_NOLOCK("<pressure was hit, but is no longer being hit>");
91 }
92 }
93
94 ptr = m_PageStack.allocate(0);
95 if (!ptr) {
96 if (Processor::m_Initialised == 2) {
97 Metrics::increment(Metrics::PhysicalPageAllocFailure);
98 }
99 panic("Out of memory.");
100 }
101 physical_uintptr_t ptr_bitmap = ptr / getPageSize();
102 size_t idx = ptr_bitmap / 32;
103 size_t bit = ptr_bitmap % 32;
104 if (g_PageBitmap[idx] & (1 << bit)) {
105 m_Lock.release();
106 FATAL_NOLOCK("PhysicalMemoryManager allocate()d a page twice");
107 }
108 g_PageBitmap[idx] |= (1 << bit);
109 if (Processor::m_Initialised == 2) {
110 Metrics::increment(Metrics::PhysicalPageAlloc);
111 }
112
113 m_Lock.release();
114
115#if TRACK_PAGE_ALLOCATIONS
116 if (Processor::m_Initialised == 2) {
117 if (!g_AllocationCommand.isMallocing()) {
118 g_AllocationCommand.allocatePage(ptr);
119 }
120 }
121#endif
122
123 return ptr;
124}
125#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
126namespace {
127ssize_t tryAllocationFailure = -1;
128}
129void PhysicalMemoryManager::setTryAllocationFailureForTest(ssize_t after) {
131 RecursingLockGuard<Spinlock> guard(memory.m_Lock);
132 tryAllocationFailure = after;
133}
134#endif
135physical_uintptr_t HostedPhysicalMemoryManager::tryAllocatePage() {
136 m_Lock.acquire(true);
137#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
138 if (!tryAllocationFailure) {
139 m_Lock.release();
140 if (Processor::m_Initialised == 2) {
141 Metrics::increment(Metrics::PhysicalPageAllocFailure);
142 }
143 return 0;
144 }
145 if (tryAllocationFailure > 0)
146 --tryAllocationFailure;
147#endif
148 physical_uintptr_t ptr;
149 ptr = m_PageStack.allocate(0, false);
150 if (!ptr) {
151 m_Lock.release();
152 if (Processor::m_Initialised == 2) {
153 Metrics::increment(Metrics::PhysicalPageAllocFailure);
154 }
155 return 0;
156 }
157 physical_uintptr_t ptr_bitmap = ptr / getPageSize();
158 size_t idx = ptr_bitmap / 32;
159 size_t bit = ptr_bitmap % 32;
160 if (g_PageBitmap[idx] & (1 << bit)) {
161 m_Lock.release();
162 FATAL_NOLOCK("PhysicalMemoryManager allocate()d a page twice");
163 }
164 g_PageBitmap[idx] |= (1 << bit);
165 if (Processor::m_Initialised == 2) {
166 Metrics::increment(Metrics::PhysicalPageAlloc);
167 }
168
169 m_Lock.release();
170
171#if TRACK_PAGE_ALLOCATIONS
172 if (Processor::m_Initialised == 2) {
173 if (!g_AllocationCommand.isMallocing()) {
174 g_AllocationCommand.allocatePage(ptr);
175 }
176 }
177#endif
178
179 return ptr;
180}
181
182PhysicalMemoryManager::MemorySnapshot HostedPhysicalMemoryManager::memorySnapshot() const {
183 auto& self = *const_cast<HostedPhysicalMemoryManager*>(this);
184 RecursingLockGuard<Spinlock> guard(self.m_Lock);
185 return {m_PageStack.totalPages(), m_PageStack.freePages(), true};
186}
187
188bool HostedPhysicalMemoryManager::copyPhysicalPageToBuffer(physical_uintptr_t page, void* buffer) {
189 return page && !(page & (getPageSize() - 1)) && page < HOSTED_PHYSICAL_MEMORY_SIZE && buffer &&
190 pread(m_BackingFile, buffer, getPageSize(), page) == static_cast<ssize_t>(getPageSize());
191}
192bool HostedPhysicalMemoryManager::copyPhysicalPageFromBuffer(physical_uintptr_t page,
193 const void* buffer) {
194 return page && !(page & (getPageSize() - 1)) && page < HOSTED_PHYSICAL_MEMORY_SIZE && buffer &&
195 pwrite(m_BackingFile, buffer, getPageSize(), page) == static_cast<ssize_t>(getPageSize());
196}
197
204 if (!m_Lock.acquired())
205 FATAL(
206 "HostedPhysicalMemoryManager::freePageUnlocked called without an "
207 "acquired lock");
208
209 // Check for pinned page.
210 PageHashable index(page);
211 MetadataTable::LookupResult result = m_PageMetadata.lookup(index);
212 if (result.hasValue()) {
213 struct page p = result.value();
214 if (p.active) {
215 if (--p.refcount) {
216 // Still references.
217 m_PageMetadata.update(index, p);
218 return;
219 } else {
220 // No more references, stop tracking page.
221 p.active = false;
222 m_PageMetadata.update(index, p);
223 }
224 }
225 }
226
227 physical_uintptr_t ptr_bitmap = page / getPageSize();
228 size_t idx = ptr_bitmap / 32;
229 size_t bit = ptr_bitmap % 32;
230 if (!(g_PageBitmap[idx] & (1 << bit))) {
231 m_Lock.release();
232 FATAL_NOLOCK("PhysicalMemoryManager DOUBLE FREE");
233 }
234
235 g_PageBitmap[idx] &= ~(1 << bit);
236
238 if (Processor::m_Initialised == 2) {
239 Metrics::increment(Metrics::PhysicalPageFree);
240 }
241}
242
243void HostedPhysicalMemoryManager::pin(physical_uintptr_t page) {
245
246 PageHashable index(page);
247 MetadataTable::LookupResult result = m_PageMetadata.lookup(index);
248 if (result.hasValue()) {
249 struct page p = result.value();
250 ++p.refcount;
251 p.active = true;
252 m_PageMetadata.update(index, p);
253 } else {
254 struct page p;
255 p.refcount = 1;
256 p.active = true;
257 m_PageMetadata.insert(index, p);
258 }
259}
260
261#if PEDIGREE_HOSTED_SMOKE_TESTS
262size_t PhysicalMemoryManager::pageReferenceCountForTest(physical_uintptr_t page) {
263 return HostedPhysicalMemoryManager::instance().pageReferenceCountForTestImpl(page);
264}
265
266size_t HostedPhysicalMemoryManager::pageReferenceCountForTestImpl(physical_uintptr_t page) {
268
269 if (page >= HOSTED_PHYSICAL_MEMORY_SIZE) {
270 return 0;
271 }
272
273 const PageHashable index(page);
274 const MetadataTable::LookupResult result = m_PageMetadata.lookup(index);
275 if (result.hasValue() && result.value().active) {
276 return result.value().refcount;
277 }
278
279 const physical_uintptr_t bitmapPage = page / getPageSize();
280 const size_t bitmapIndex = bitmapPage / 32;
281 const size_t bitmapBit = bitmapPage % 32;
282 return (g_PageBitmap[bitmapIndex] & (1U << bitmapBit)) ? 1 : 0;
283}
284#endif
285
287 size_t pageConstraints, size_t Flags,
288 physical_uintptr_t start) {
289 LockGuard<Spinlock> guard(m_RegionLock);
290
291 // Allocate a specific physical memory region (always physically continuous)
292 if (start != static_cast<physical_uintptr_t>(-1)) {
293 // Page-align the start address.
294 start &= ~(getPageSize() - 1);
295
296 if ((pageConstraints & continuous) != continuous)
297 panic("PhysicalMemoryManager::allocateRegion(): function misused");
298
299 // Remove the memory from the range-lists (if desired/possible)
300 if ((pageConstraints & nonRamMemory) == nonRamMemory) {
301 Region.setNonRamMemory(true);
302 if (m_PhysicalRanges.allocateSpecific(start, cPages * getPageSize()) == false) {
303 if ((pageConstraints & force) != force)
304 return false;
305 else
306 Region.setForced(true);
307 }
308 } else {
309 // Ensure that free() does not attempt to free the given memory...
310 Region.setNonRamMemory(true);
311 Region.setForced(true);
312 }
313
314 // Allocate the virtual address space
315 uintptr_t vAddress;
316
318 false) {
319 WARNING("AllocateRegion: MemoryRegion allocation failed.");
320 return false;
321 }
322
323 // Map the physical memory into the allocated space
324 VirtualAddressSpace& virtualAddressSpace = Processor::information().getVirtualAddressSpace();
325 for (size_t i = 0; i < cPages; i++)
326 if (virtualAddressSpace.map(
328 reinterpret_cast<void*>(vAddress + i * PhysicalMemoryManager::getPageSize()),
329 Flags) == false) {
331 WARNING("AllocateRegion: VirtualAddressSpace::map failed.");
332 return false;
333 }
334
335 // Set the memory-region's members
336 Region.m_VirtualAddress = reinterpret_cast<void*>(vAddress);
337 Region.m_PhysicalAddress = start;
338 Region.m_Size = cPages * PhysicalMemoryManager::getPageSize();
339 Region.m_bPageBacked = false;
340 Region.setAnonymous(pageConstraints & PhysicalMemoryManager::anonymous);
341 // Add to the list of memory-regions
343 return true;
344 } else {
345 // Allocate the virtual address space
346 uintptr_t vAddress;
348 false) {
349 WARNING("AllocateRegion: MemoryRegion allocation failed.");
350 return false;
351 }
352
353 const bool virtualOnlyRegion = pageConstraints & virtualOnly;
354 if (!virtualOnlyRegion) {
355 VirtualAddressSpace& virtualAddressSpace = Processor::information().getVirtualAddressSpace();
356
357 // Map the physical memory into the allocated space.
358 for (size_t i = 0; i < cPages; i++) {
359 physical_uintptr_t page = allocatePage(pageConstraints & addressConstraints);
360 if (virtualAddressSpace.map(page, reinterpret_cast<void*>(vAddress + i * getPageSize()),
361 Flags) == false) {
362 freePage(page);
363 for (size_t mapped = 0; mapped < i; ++mapped) {
364 void* mappedAddress = reinterpret_cast<void*>(vAddress + mapped * getPageSize());
365 physical_uintptr_t mappedPage = 0;
366 size_t mappedFlags = 0;
367 virtualAddressSpace.getMapping(mappedAddress, mappedPage, mappedFlags);
368 virtualAddressSpace.unmap(mappedAddress);
369 freePage(mappedPage);
370 }
371 m_MemoryRegions.free(vAddress, cPages * getPageSize());
372 WARNING("AllocateRegion: VirtualAddressSpace::map failed.");
373 return false;
374 }
375 }
376 }
377
378 // Set the memory-region's members
379 Region.m_VirtualAddress = reinterpret_cast<void*>(vAddress);
380 Region.m_PhysicalAddress = 0;
381 Region.m_Size = cPages * PhysicalMemoryManager::getPageSize();
382 Region.m_bPageBacked = true;
383 Region.setAnonymous(pageConstraints & PhysicalMemoryManager::anonymous);
384
385 // Add to the list of memory-regions
387 return true;
388 }
389 return false;
390}
391
393 TRACE("Hosted PMM: init");
394
395 NOTICE("memory-map:");
396
397 size_t pageSize = getPageSize();
398
399 // Free pages into the page stack first.
400 m_PageStack.free(0, HOSTED_PHYSICAL_MEMORY_SIZE, true);
402 TRACE("Hosted PMM: page stack done");
403
404 m_PageMetadata.reserve(HOSTED_PHYSICAL_MEMORY_SIZE / pageSize);
405
406 // Initialise the free physical ranges
407 m_PhysicalRanges.free(0, 0x100000000ULL);
408 m_PhysicalRanges.allocateSpecific(0, HOSTED_PHYSICAL_MEMORY_SIZE);
409
410// Print the ranges
411#if VERBOSE_MEMORY_MANAGER
412 NOTICE("physical memory ranges:");
413 for (size_t i = 0; i < m_PhysicalRanges.size(); i++) {
414 RangeList<uint64_t>::Range range(0, 0);
415 if (m_PhysicalRanges.getRange(i, range)) {
416 NOTICE(" " << Hex << range.address << " - " << (range.address + range.length));
417 }
418 }
419#endif
420
421 // Initialise the range of virtual space for MemoryRegions
422 m_MemoryRegions.free(reinterpret_cast<uintptr_t>(KERNEL_VIRTUAL_MEMORYREGION_ADDRESS),
423 KERNEL_VIRTUAL_MEMORYREGION_SIZE);
424}
425
427#if PEDIGREE_HOSTED_SMOKE_TESTS
428 const size_t trackedBefore = PhysicalMemoryManager::m_MemoryRegions.count();
429 MemoryRegion first("Hosted anonymous-region regression");
432 FATAL("HOSTED-MEMORY-TEST: FAIL anonymous-region allocation");
433 }
434
435 void* firstAddress = first.virtualAddress();
437 if (PhysicalMemoryManager::m_MemoryRegions.count() != trackedBefore + 1 ||
438 !addressSpace.isMapped(firstAddress)) {
439 FATAL("HOSTED-MEMORY-TEST: FAIL anonymous-region ownership");
440 }
441
442 first.free();
443 if (PhysicalMemoryManager::m_MemoryRegions.count() != trackedBefore ||
444 addressSpace.isMapped(firstAddress)) {
445 FATAL("HOSTED-MEMORY-TEST: FAIL anonymous-region release");
446 }
447
448 MemoryRegion second("Hosted anonymous-region reuse regression");
451 second.virtualAddress() != firstAddress) {
452 FATAL("HOSTED-MEMORY-TEST: FAIL anonymous-region reuse");
453 }
454 second.free();
455 if (PhysicalMemoryManager::m_MemoryRegions.count() != trackedBefore ||
456 addressSpace.isMapped(firstAddress)) {
457 FATAL("HOSTED-MEMORY-TEST: FAIL anonymous-region final release");
458 }
459
460 NOTICE("HOSTED-MEMORY-TEST: PASS anonymous-region-release");
461#endif
462 NOTICE("PhysicalMemoryManager: kernel initialisation complete");
463}
464
466 : m_PhysicalRanges(),
467 m_MemoryRegions(),
468 m_Lock(false, true),
469 m_RegionLock(false, true),
470 m_PageMetadata(),
471 m_BackingFile(-1) {
472 // Create our backing memory file.
473 // This lseek/write creates a sparse file on disk.
474 m_BackingFile = open("physical.bin", O_RDWR | O_CREAT, 0644);
475 lseek(m_BackingFile, HOSTED_PHYSICAL_MEMORY_SIZE - 1, SEEK_SET);
476 write(m_BackingFile, "\0", 1);
477 lseek(m_BackingFile, 0, SEEK_SET);
478}
479
488
490 LockGuard<Spinlock> guard(m_RegionLock);
491
494 if (*it == pRegion) {
495 size_t cPages = pRegion->size() / PhysicalMemoryManager::getPageSize();
496 uintptr_t start = reinterpret_cast<uintptr_t>(pRegion->virtualAddress());
497 physical_uintptr_t phys = pRegion->physicalAddress();
499
500 if (pRegion->m_bPageBacked) {
501 // Non-contiguous and lazy virtual-only regions own individual pages,
502 // not one physical range beginning at m_PhysicalAddress.
503 } else if (pRegion->getNonRamMemory()) {
504 if (!pRegion->getForced())
505 m_PhysicalRanges.free(phys, pRegion->size());
506 }
507
508 for (size_t i = 0; i < cPages; i++) {
509 void* vAddr = reinterpret_cast<void*>(start + i * PhysicalMemoryManager::getPageSize());
510 if (!virtualAddressSpace.isMapped(vAddr))
511 continue; // Can happen with virtualOnly mappings.
512 physical_uintptr_t pAddr;
513 size_t flags;
514 virtualAddressSpace.getMapping(vAddr, pAddr, flags);
515
516 virtualAddressSpace.unmap(vAddr);
517 if (!pRegion->getNonRamMemory() && pRegion->m_bPageBacked)
518 freePage(pAddr);
519 }
520 m_MemoryRegions.free(start, pRegion->size());
522 break;
523 }
524 }
525}
Implementation of the PhysicalMemoryManager for common x86.
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0) override
virtual void freePage(physical_uintptr_t page) override
void unmapRegion(MemoryRegion *pRegion) override
static HostedPhysicalMemoryManager & instance()
virtual void pin(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
void initialise(const BootstrapStruct_t &Info) INITIALISATION_ONLY
virtual void freePageUnlocked(physical_uintptr_t page) override
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 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
Iterator end()
Definition Vector.h:172
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
void free(uint64_t physicalAddress, size_t length, bool newMemory=false)
physical_uintptr_t allocate(size_t constraints, bool waitForReady=true)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Hex
Definition Log.h:124
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
size_t count() const
Definition Vector.h:270