8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
10#include "pedigree/kernel/processor/Processor.h"
11#include "pedigree/kernel/processor/VirtualAddressSpace.h"
14constexpr size_t PageCount = 128;
16bool check(
bool condition,
const char* detail) {
18 ERROR(
"HOSTED-WAIT-TEST: FAIL hosted-vas-mapping-index: " << detail);
23bool mappingMatches(
VirtualAddressSpace& space, uintptr_t address, physical_uintptr_t expected,
24 size_t expectedFlags) {
25 if (!check(space.
isMapped(
reinterpret_cast<void*
>(address)),
"a mapping disappeared")) {
28 physical_uintptr_t physical = 0;
30 space.
getMapping(
reinterpret_cast<void*
>(address +
sizeof(uint32_t)), physical, flags);
31 return check(physical == expected && flags == expectedFlags,
32 "mapping lookup returned different backing or permissions");
35uint32_t contents(
size_t owner,
size_t page) {
36 return 0x13570000U +
static_cast<uint32_t
>((owner * PageCount) + page);
43 for (
size_t i = 0; i < PageCount; ++i) {
44 const uintptr_t address = base + (i * pageSize);
46 if (owner == 1 && i == 0) {
48 }
else if (owner == 1 && i == 1) {
50 }
else if (owner == 1 && i == 2) {
53 const bool mapped = mappingMatches(space, address, pages[i], flags);
56 auto* first =
reinterpret_cast<volatile uint32_t*
>(address);
57 auto* last =
reinterpret_cast<volatile uint32_t*
>(address + pageSize -
sizeof(uint32_t));
58 passed &= check(*first == contents(owner, i) && *last == ~contents(owner, i),
59 "an address-space switch exposed another space's page contents");
65bool checkKernelPages(
VirtualAddressSpace& space, uintptr_t* addresses, physical_uintptr_t* pages) {
67 for (
size_t i = 0; i < 2; ++i) {
69 mappingMatches(space, addresses[i], pages[i],
73 passed &= check(*
reinterpret_cast<volatile uint32_t*
>(addresses[i]) == contents(2, i),
74 "an address-space switch changed a global kernel mapping");
84 for (
size_t i = 0; i < PageCount; ++i) {
86 physical_uintptr_t physical = 0;
91 if (!check(detached && physical == pages[i],
"cleanup could not detach the owned page")) {
97 if (!check(PhysicalMemoryManager::pageReferenceCountForTest(pages[i]) != 0,
98 "cleanup found a physical page that was already released")) {
108bool runHostedVasRegressions() {
114 physical_uintptr_t pages[2][PageCount];
115 bool mapped[2][PageCount] = {};
117 bool kernelMapped[2] = {};
118 for (
size_t owner = 0; owner < 2; ++owner) {
119 for (
size_t i = 0; i < PageCount; ++i) {
130 bool available =
false;
133 for (
size_t i = 0; i < PageCount + 2; ++i) {
134 if (original.
isMapped(
reinterpret_cast<void*
>(base + (i * pageSize)))) {
135 base += (PageCount + 2) * pageSize;
141 uintptr_t kernelAddresses[2] = {base + ((PageCount + 1) * pageSize),
143 while (kernel.
isMapped(
reinterpret_cast<void*
>(kernelAddresses[1]))) {
144 kernelAddresses[1] += pageSize;
148 for (
size_t owner = 0; owner < 2; ++owner) {
150 for (
size_t i = 0; i < PageCount; ++i) {
151 const uintptr_t address = base + (i * pageSize);
152 mapped[owner][i] = spaces[owner]->
map(pages[owner][i],
reinterpret_cast<void*
>(address),
154 passed &= check(mapped[owner][i],
"could not create the userspace fixture");
155 if (mapped[owner][i]) {
156 *
reinterpret_cast<volatile uint32_t*
>(address) = contents(owner, i);
157 *
reinterpret_cast<volatile uint32_t*
>(address + pageSize -
sizeof(uint32_t)) =
164 spaces[1]->
setFlags(
reinterpret_cast<void*
>(base +
sizeof(uint32_t)),
166 passed &= check(spaces[1]->trySetFlags(
reinterpret_cast<void*
>(base + pageSize),
168 "trySetFlags could not protect an existing page");
169 spaces[1]->
setFlags(
reinterpret_cast<void*
>(base + (2 * pageSize)), 0);
171 uint32_t expected = contents(1, 0);
172 bool exchanged =
false;
173 passed &= check(spaces[1]->tryReadUser32(base, value) && value == contents(1, 0) &&
174 !spaces[1]->tryWriteUser32(base, 0) &&
175 !spaces[1]->tryCompareExchangeUser32(base, expected, 0, exchanged),
176 "a write-protected page did not retain read-only access");
177 passed &= check(!spaces[1]->tryReadUser32(base + pageSize, value) &&
178 !spaces[1]->tryWriteUser32(base + pageSize, 0) &&
179 !spaces[1]->tryWriteUser32(base + (2 * pageSize), 0),
180 "NoAccess or read-only permissions allowed a user access");
183 for (
size_t i = 0; i < 2; ++i) {
185 spaces[0]->
map(kernelPages[i],
reinterpret_cast<void*
>(kernelAddresses[i]),
187 passed &= check(kernelMapped[i],
"could not create the kernel mapping fixture");
188 if (kernelMapped[i]) {
189 *
reinterpret_cast<volatile uint32_t*
>(kernelAddresses[i]) = contents(2, i);
198 passed &= checkKernelPages(*space, kernelAddresses, kernelPages);
199 if (space != &kernel) {
200 const size_t owner = space == spaces[0] ? 0 : 1;
201 passed &= checkUserPages(*space, base, pages[owner], owner);
208 *
reinterpret_cast<volatile uint32_t*
>(base) == contents(1, 0),
209 "an inactive duplicate map replaced the current space's backing");
213 "an inactive duplicate map was accepted without a live host mapping");
214 passed &= checkKernelPages(kernel, kernelAddresses, kernelPages);
216 passed &= checkUserPages(*spaces[0], base, pages[0], 0);
218 uint32_t expected = contents(0, 0) + 1;
219 bool exchanged =
false;
221 check(spaces[0]->tryWriteUser32(base, expected) &&
222 *
reinterpret_cast<volatile uint32_t*
>(base) == expected &&
223 spaces[0]->tryCompareExchangeUser32(base, expected, contents(0, 0), exchanged) &&
224 exchanged && *
reinterpret_cast<volatile uint32_t*
>(base) == contents(0, 0),
225 "indexed writes or compare-exchange did not reach the mapped page");
226 const uintptr_t pointerAddress = base + (2 *
sizeof(uintptr_t));
227 *
reinterpret_cast<volatile uintptr_t*
>(pointerAddress) = base + pageSize;
228 uintptr_t pointer = 0;
230 check(spaces[0]->tryReadUserPointer(pointerAddress, pointer) && pointer == base + pageSize,
231 "indexed pointer read did not reach the mapped page");
233 const uintptr_t absent = base + (PageCount * pageSize);
235 !spaces[0]->isMapped(
reinterpret_cast<void*
>(absent)) &&
237 "an absent page was found in the mapping index");
238 size_t tablePages = 99;
239 passed &= check(!spaces[0]->tryMapUserPage(pages[1][0],
reinterpret_cast<void*
>(base),
243 "duplicate insertion changed an existing mapping");
245 for (
size_t i = 0; i < PageCount; i += 2) {
246 void* address =
reinterpret_cast<void*
>(base + (i * pageSize));
247 spaces[0]->
unmap(address);
248 mapped[0][i] =
false;
249 passed &= check(!spaces[0]->isMapped(address),
"unmap left a stale mapping index entry");
251 for (
size_t remaining = PageCount; remaining; remaining -= 2) {
252 const size_t i = remaining - 2;
253 mapped[0][i] = spaces[0]->
map(pages[0][i],
reinterpret_cast<void*
>(base + (i * pageSize)),
255 passed &= check(mapped[0][i],
"a deleted mapping index entry could not be reused");
260 void* address =
reinterpret_cast<void*
>(base + (5 * pageSize));
261 physical_uintptr_t detachedPhysical = 0;
262 size_t detachedFlags = 0;
263 passed &= check(!spaces[0]->detachMapping(address, detachedPhysical, detachedFlags,
265 mappingMatches(*spaces[0],
reinterpret_cast<uintptr_t
>(address),
267 "a rejected detach changed the mapping");
268 const bool detached = spaces[0]->
detachMapping(address, detachedPhysical, detachedFlags,
271 mapped[0][5] =
false;
274 check(detached && detachedPhysical == pages[0][5] &&
276 "detach did not return and remove the original mapping");
279 passed &= check(mapped[0][5],
"a detached mapping could not be reinserted");
282 address =
reinterpret_cast<void*
>(base + ((PageCount - 1) * pageSize));
284 passed &= check(!spaces[0]->tryDetachUserPage(address, pages[1][PageCount - 1]),
285 "tryDetachUserPage accepted the wrong physical page");
286 const bool userDetached = spaces[0]->tryDetachUserPage(address, pages[0][PageCount - 1]);
288 mapped[0][PageCount - 1] =
false;
290 passed &= check(userDetached && !spaces[0]->isMapped(address),
291 "tryDetachUserPage left an inaccessible mapping indexed");
293 mapped[0][PageCount - 1] =
295 passed &= check(mapped[0][PageCount - 1],
"tryMapUserPage could not reuse a detached entry");
301 passed &= checkUserPages(*spaces[0], base, pages[0], 0);
302 clone = spaces[0]->
clone(
false);
303 passed &= check(clone !=
nullptr,
"could not clone the populated mapping index");
306 passed &= checkUserPages(*clone, base, pages[0], 0);
307 passed &= checkKernelPages(*clone, kernelAddresses, kernelPages);
308 for (
size_t i = 0; i < PageCount; ++i) {
309 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(pages[0][i]) == 2,
310 "clone did not retain exactly two owners for a private page");
313 *
reinterpret_cast<volatile uint32_t*
>(base) = 0x87654321U;
316 passed &= check(*
reinterpret_cast<volatile uint32_t*
>(base) == 0x87654321U,
317 "clone(false) did not preserve writable aliasing");
318 *
reinterpret_cast<volatile uint32_t*
>(base) = contents(0, 0);
320 bool cloneMapped[PageCount];
321 for (
size_t i = 0; i < PageCount; ++i) {
322 cloneMapped[i] =
true;
324 passed &= releasePages(*clone, base, pages[0], cloneMapped);
325 for (
size_t i = 0; i < PageCount; ++i) {
326 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(pages[0][i]) == 1,
327 "clone cleanup changed the source's physical page ownership");
338 passed &= releasePages(*spaces[0], base, pages[0], mapped[0]);
340 for (
size_t i = 0; i < PageCount; ++i) {
343 for (
size_t i = 0; i < PageCount; ++i) {
344 const uintptr_t address = base + (i * pageSize);
345 mapped[0][i] = spaces[0]->
map(pages[0][i],
reinterpret_cast<void*
>(address),
347 passed &= check(mapped[0][i],
"could not recreate fresh pages for copy-on-write");
349 *
reinterpret_cast<volatile uint32_t*
>(address) = contents(0, i);
350 *
reinterpret_cast<volatile uint32_t*
>(address + pageSize -
sizeof(uint32_t)) =
358 spaces[0]->
setFlags(
reinterpret_cast<void*
>(base + pageSize), 0);
359 clone = spaces[0]->
clone(
true);
360 passed &= check(clone !=
nullptr,
"could not clone the mapping index with copy-on-write");
363 const bool writableCow =
365 passed &= writableCow;
368 mappingMatches(*clone, base + pageSize, pages[0][1],
373 "copy-on-write bypassed a pending split or read-only protection");
375 physical_uintptr_t replacement = 0;
376 size_t replacementFlags = 0;
377 const bool resolved =
379 const bool replacementMapped = resolved && clone->
isMapped(
reinterpret_cast<void*
>(base));
380 passed &= check(replacementMapped,
381 "indexed copy-on-write lookup could not resolve a writable page");
382 if (replacementMapped) {
383 clone->
getMapping(
reinterpret_cast<void*
>(base), replacement, replacementFlags);
384 const bool privatePage =
386 passed &= check(privatePage,
"copy-on-write did not publish an independent writable page");
388 auto* bytes =
reinterpret_cast<volatile uint32_t*
>(base);
390 check(*bytes == contents(0, 0),
"copy-on-write lost the original page contents");
391 *bytes = 0xABCDEF01U;
394 passed &= check(*bytes == contents(0, 0),
"the child write changed the source page");
401 check(*bytes == 0xABCDEF01U,
"the source switch lost the child's private page");
410 passed &= check(!clone->
tryReadUser32(base, value),
"revert left an accessible index entry");
411 for (
size_t i = 0; i < PageCount; ++i) {
412 passed &= check(!clone->
isMapped(
reinterpret_cast<void*
>(base + (i * pageSize))) &&
413 PhysicalMemoryManager::pageReferenceCountForTest(pages[0][i]) == 1,
414 "revert retained a userspace mapping or changed source ownership");
416 if (replacement && replacement != pages[0][0]) {
417 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(replacement) == 0,
418 "revert leaked the private copy-on-write page");
420 passed &= checkKernelPages(*clone, kernelAddresses, kernelPages);
422 const physical_uintptr_t reused = memory.
allocatePage();
423 const bool remapped =
425 passed &= check(remapped,
"revert left an index entry that blocked remapping");
429 clone->
tryReadUser32(base, value) && value == contents(3, 0),
430 "a remapped page retained stale copy-on-write permissions or backing");
435 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(reused) == 0,
436 "the remapped fixture leaked its physical page");
440 for (
size_t owner = 0; owner < 2; ++owner) {
441 passed &= releasePages(*spaces[owner], base, pages[owner], mapped[owner]);
442 for (
size_t i = 0; i < PageCount; ++i) {
443 passed &= check(PhysicalMemoryManager::pageReferenceCountForTest(pages[owner][i]) == 0,
444 "userspace fixture leaked a physical page");
448 for (
size_t i = 0; i < 2; ++i) {
449 if (kernelMapped[i]) {
450 kernel.
unmap(
reinterpret_cast<void*
>(kernelAddresses[i]));
461 NOTICE(
"HOSTED-WAIT-TEST: PASS hosted-vas-mapping-index");
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
virtual void freePage(physical_uintptr_t page)=0
static bool getInterrupts()
static ProcessorInformation & information()
static void switchAddressSpace(VirtualAddressSpace &AddressSpace)
static void setInterrupts(bool bEnable)
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
static const size_t CopyOnWrite
static VirtualAddressSpace * create()
virtual uintptr_t getDynamicStart() const
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool tryWriteUser32(uintptr_t address, uint32_t value)
virtual bool isMapped(void *virtualAddress)=0
virtual void revertToKernelAddressSpace()=0
virtual bool handleCopyOnWriteFault(void *virtualAddress, bool userMode)=0
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t NoAccess
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual bool detachMapping(void *virtualAddress, physical_uintptr_t &physical, size_t &flags, size_t requiredFlags=0)
static const size_t WriteProtected
virtual uintptr_t getKernelHeapEnd() const =0
virtual void unmap(void *virtualAddress)=0
virtual VirtualAddressSpace * clone(bool copyOnWrite=true)=0
virtual bool tryReadUser32(uintptr_t address, uint32_t &value)