1#include "pedigree/kernel/BootstrapInfo.h"
2#include "pedigree/kernel/Log.h"
3#include "pedigree/kernel/process/Process.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
6#include "pedigree/kernel/processor/Processor.h"
7#include "pedigree/kernel/processor/ProcessorInformation.h"
8#include "pedigree/kernel/utilities/Pointers.h"
10#include "VirtualAddressSpace.h"
13#if PEDIGREE_VM_REMAP_TESTS
15constexpr size_t PageSize = 4096;
16using Status = VirtualAddressSpace::RemapStatus;
18bool retainedTablesRegression() {
21 const auto physical = memory.allocatePage();
22 if (!space || !physical) {
24 memory.freePage(physical);
27 void* address =
reinterpret_cast<void*
>(space.get()->getDynamicStart());
28 bool ownsPhysical =
true, passed =
true;
29 const size_t expectedTables[] = {3, 0, 3};
30 for (
size_t expected : expectedTables) {
32 if (!space.get()->tryMapUserPage(physical, address,
39 if (!space.get()->tryDetachUserPage(address, physical)) {
44 if (tables != expected ||
45 !space.get()->map(physical, address,
50 physical_uintptr_t detached = 0;
52 if (!space.get()->detachMapping(address, detached, flags) || detached != physical ||
53 space.get()->isMapped(address)) {
62 const size_t before = singleProcessor ? memory.freePageCount() : 0;
63 space.get()->revertToKernelAddressSpace();
64 const size_t after = singleProcessor ? memory.freePageCount() : 0;
66 if (singleProcessor && after != before + 3) {
74 memory.freePage(physical);
78bool hugePageTableReplacementRegression() {
79 constexpr size_t HugePageSize = 2 * 1024 * 1024;
80 physical_uintptr_t hugePhysical = 0;
82 if (!g_pBootstrapInfo)
84 for (
void* entry = g_pBootstrapInfo->getMemoryMap(); entry;
85 entry = g_pBootstrapInfo->nextMemoryMapEntry(entry)) {
86 const uint64_t base = g_pBootstrapInfo->getMemoryMapEntryAddress(entry);
87 const uint64_t length = g_pBootstrapInfo->getMemoryMapEntryLength(entry);
88 if (g_pBootstrapInfo->getMemoryMapEntryType(entry) != 1 || length < HugePageSize ||
89 base > ~uint64_t{0} - (HugePageSize - 1) || length > ~uint64_t{0} - base)
91 const uint64_t aligned = (base + HugePageSize - 1) & ~(uint64_t{HugePageSize} - 1);
92 if (aligned - base <= length - HugePageSize) {
93 hugePhysical = aligned;
103 const auto physical = memory.allocatePage();
104 if (!space || !physical) {
106 memory.freePage(physical);
109 const uintptr_t base = space.get()->getDynamicStart();
110 void* address =
reinterpret_cast<void*
>(base);
112 bool passed = !(base & (HugePageSize - 1)) && !(hugePhysical & (HugePageSize - 1)) &&
113 space.get()->map(physical, address, flags);
120 const size_t before = singleProcessor ? memory.freePageCount() : 0;
121 passed = space.get()->mapHuge(hugePhysical, address, HugePageSize / PageSize, flags);
122 const size_t after = singleProcessor ? memory.freePageCount() : 0;
124 passed = passed && (!singleProcessor || after == before + 1) &&
125 space.get()->isMapped(address) &&
126 space.get()->isMapped(
reinterpret_cast<void*
>(base + HugePageSize - PageSize)) &&
127 !space.get()->isMapped(
reinterpret_cast<void*
>(base - PageSize)) &&
128 !space.get()->isMapped(
reinterpret_cast<void*
>(base + HugePageSize));
131 memory.freePage(physical);
135bool reserveAligned(
Process& process,
size_t length, uintptr_t& result) {
139 if (!process.snapshotUserReservations(snapshot) ||
140 !snapshot.dynamic.allocate(length + PageSize - 1, raw)) {
143 const uintptr_t aligned = (raw + PageSize - 1) & ~(PageSize - 1);
144 const size_t prefix = aligned - raw;
145 const size_t suffix = PageSize - 1 - prefix;
146 if ((prefix && !snapshot.dynamic.tryFree(raw, prefix)) ||
147 (suffix && !snapshot.dynamic.tryFree(aligned + length, suffix))) {
163bool admit(
void* context) {
164 auto& admission = *
static_cast<Admission*
>(context);
165 admission.called =
true;
166 return admission.process->commitUserReservations(admission.snapshot->generation,
167 *admission.snapshot);
172 explicit RemapFixture(
Process& process)
179 sourceReserved(false),
180 destinationReserved(false),
185 for (
size_t i = 0; i < plan.get()->detachedPageCount(); ++i) {
186 const auto& page = plan.get()->detachedPages()[i];
193 for (
size_t i = 0; i < 5; ++i) {
194 const uintptr_t address = i < 3 ? source + i * PageSize : destination + (i - 3) * PageSize;
195 physical_uintptr_t physical = 0;
198 space.get()->detachMapping(
reinterpret_cast<void*
>(address), physical, flags)) {
209 process.freeUserRange(Process::UserRegion::Dynamic, extra, PageSize);
211 if (sourceReserved) {
212 process.freeUserRange(Process::UserRegion::Dynamic, source, 3 * PageSize);
214 if (destinationReserved) {
215 process.freeUserRange(Process::UserRegion::Dynamic, destination, 2 * PageSize);
220 if (!reserveAligned(process, 3 * PageSize, source)) {
223 sourceReserved =
true;
224 if (!reserveAligned(process, 2 * PageSize, destination)) {
227 destinationReserved =
true;
232 for (
size_t i = 0; i < 5; ++i) {
237 const uintptr_t address = i < 3 ? source + i * PageSize : destination + (i - 3) * PageSize;
244 ByteSet(
physicalAddress(
reinterpret_cast<void*
>(physical)), 0x31 + i, PageSize);
245 if (!space.get()->map(physical,
reinterpret_cast<void*
>(address), flags)) {
249 physicalPages[i] = physical;
250 space.get()->getMapping(
reinterpret_cast<void*
>(address), physicalPages[i], pageFlags[i]);
255 originalCow = physical;
262 for (
size_t i = 0; i < 5; ++i) {
263 const uintptr_t address = i < 3 ? source + i * PageSize : destination + (i - 3) * PageSize;
264 if (!space.get()->isMapped(
reinterpret_cast<void*
>(address))) {
267 physical_uintptr_t physical = 0;
269 space.get()->getMapping(
reinterpret_cast<void*
>(address), physical, flags);
270 if (physical != physicalPages[i] || flags != pageFlags[i]) {
273 const auto* bytes =
physicalAddress(
reinterpret_cast<unsigned char*
>(physical));
274 for (
size_t offset = 0; offset < PageSize; ++offset) {
275 if (bytes[offset] != (offset ? 0x31 + i : firstBytes[i])) {
281 return process.snapshotUserReservations(snapshot) &&
282 !snapshot.dynamic.allocateSpecific(source, 3 * PageSize) &&
283 !snapshot.dynamic.allocateSpecific(destination, 2 * PageSize);
288 uintptr_t source, destination, extra;
289 physical_uintptr_t originalCow;
290 physical_uintptr_t physicalPages[5] = {};
291 size_t pageFlags[5] = {};
292 unsigned char firstBytes[5] = {0x31, 0x32, 0x33, 0x34, 0x35};
293 bool sourceReserved, destinationReserved, committed;
298extern "C" EXPORTED_PUBLIC
bool x64RemapCoreRegression() {
303 if (!retainedTablesRegression()) {
304 ERROR(
"VM-REMAP-CORE: FAIL retained-tables");
307 NOTICE(
"VM-REMAP-CORE: PASS retained-tables");
308 if (!hugePageTableReplacementRegression()) {
309 ERROR(
"VM-REMAP-CORE: FAIL huge-table-replacement");
312 NOTICE(
"VM-REMAP-CORE: PASS huge-table-replacement");
319 fixture.source,
fixture.destination, 3 * PageSize, 2 * PageSize,
true, &victim, 1};
320 size_t failedStages = 0;
321 bool prepared =
false;
322 for (ssize_t stage = 0; stage < 32; ++stage) {
323 fixture.space.get()->setRemapPreparationFailureForTest(stage);
324 const auto status =
fixture.space.get()->prepareRemap(request,
fixture.plan);
328 if (status == Status::Success) {
332 if (status != Status::NoMemory ||
fixture.plan) {
337 if (!prepared || failedStages < 5) {
342 if (!
fixture.process.snapshotUserReservations(reservations) ||
343 !reservations.dynamic.tryFree(
fixture.source, 3 * PageSize) ||
347 Admission stale{&
fixture.process, &reservations};
348 if (
fixture.plan.get()->commit(admit, &stale) != Status::Retry || !stale.called ||
349 fixture.plan.get()->detachedPageCount() || !
fixture.unchanged()) {
354 if (
fixture.space.get()->prepareRemap(request,
fixture.plan) != Status::Success ||
355 !
fixture.space.get()->handleCopyOnWriteFault(
reinterpret_cast<void*
>(
fixture.source),
true)) {
358 physical_uintptr_t currentCow = 0;
359 size_t currentFlags = 0;
360 fixture.space.get()->getMapping(
reinterpret_cast<void*
>(
fixture.source), currentCow,
362 if (currentCow ==
fixture.originalCow) {
366 fixture.physicalPages[0] = currentCow;
367 fixture.pageFlags[0] = currentFlags;
369 for (
size_t attempt = 0; attempt < 32; ++attempt) {
370 if (!
fixture.process.snapshotUserReservations(reservations) ||
371 !reservations.dynamic.tryFree(
fixture.source, 3 * PageSize)) {
374 Admission fresh{&
fixture.process, &reservations};
375 const auto status =
fixture.plan.get()->commit(admit, &fresh);
376 if (status == Status::Success) {
378 fixture.sourceReserved =
false;
384 if (status != Status::Retry || !fresh.called ||
fixture.plan.get()->detachedPageCount() ||
389 if (!
fixture.committed ||
fixture.plan.get()->detachedPageCount() != 3) {
392 for (
size_t i = 0; i < 3; ++i) {
393 const auto& retired =
fixture.plan.get()->detachedPages()[i];
394 const size_t original = i ? i + 2 : 2;
395 if (!retired.mapped || retired.physical !=
fixture.physicalPages[original] ||
396 retired.flags !=
fixture.pageFlags[original] ||
397 fixture.space.get()->isMapped(
reinterpret_cast<void*
>(
fixture.source + i * PageSize))) {
401 physical_uintptr_t moved = 0;
402 size_t movedFlags = 0;
403 fixture.space.get()->getMapping(
reinterpret_cast<void*
>(
fixture.destination), moved, movedFlags);
404 if (moved != currentCow || movedFlags != currentFlags ||
408 fixture.space.get()->getMapping(
reinterpret_cast<void*
>(
fixture.destination + PageSize), moved,
410 if (moved !=
fixture.physicalPages[1] || movedFlags !=
fixture.pageFlags[1]) {
414 return fixture.process.snapshotUserReservations(after) &&
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual void pin(physical_uintptr_t page)=0
bool commitUserReservations(uint64_t expectedGeneration, UserReservationSnapshot &replacement)
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
bool allocateSpecific(T address, T length)
Process * getParent() const
static UniquePointer< T > adopt(T *pointer)
static const size_t CopyOnWrite
static VirtualAddressSpace * create()
static const size_t Borrowed
static const size_t NoAccess
static const size_t Write
static const size_t WriteProtected
static const size_t Execute
uintptr_t physicalAddress(physical_uintptr_t address) PURE