The Pedigree Project 0.1
payload-page-regressions.cc
1/*
2 * Copyright (c) 2026, Pedigree Developers
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted.
6 */
7
8#include "pedigree/kernel/Log.h"
9#include "pedigree/kernel/process/Event.h"
10#include "pedigree/kernel/process/Ipc.h"
11#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
12#include "pedigree/kernel/processor/Processor.h"
13#include "pedigree/kernel/processor/ProcessorInformation.h"
14#include "pedigree/kernel/processor/VirtualAddressSpace.h"
15#include "pedigree/kernel/utilities/MemoryPool.h"
16
17static_assert(Event::getHostedHandlerBufferSize(1024) == EVENT_LIMIT);
18static_assert(Event::getHostedHandlerBufferSize(4096) == EVENT_LIMIT);
19static_assert(Event::getHostedHandlerBufferSize(16384) == 16384);
20static_assert(Ipc::IpcMessage::getHostedInlinePageCount(1024) == 4);
21static_assert(Ipc::IpcMessage::getHostedInlinePageCount(4096) == 1);
22static_assert(Ipc::IpcMessage::getHostedInlinePageCount(16384) == 1);
23static_assert(Ipc::IpcMessage::getHostedInlineSlotSize(1024) == 4096);
24static_assert(Ipc::IpcMessage::getHostedInlineSlotSize(4096) == 4096);
25static_assert(Ipc::IpcMessage::getHostedInlineSlotSize(16384) == 16384);
26static_assert(Ipc::IpcMessage::getHostedInlinePoolPageCount(1024) == 4096);
27static_assert(Ipc::IpcMessage::getHostedInlinePoolPageCount(4096) == 1024);
28static_assert(Ipc::IpcMessage::getHostedInlinePoolPageCount(16384) == 1024);
29
30namespace {
31bool check(bool condition, const char* test, const char* detail) {
32 if (condition) {
33 return true;
34 }
35 ERROR("HOSTED-WAIT-TEST: FAIL " << test << ": " << detail);
36 return false;
37}
38
39bool eventPayloadGeometry() {
40 constexpr const char* Test = "event-payload-page-span";
41 const size_t pageSize = PhysicalMemoryManager::getPageSize();
42 const size_t pageCount = (EVENT_LIMIT / pageSize) + ((EVENT_LIMIT % pageSize) ? 1 : 0);
43 const size_t expectedSpan = pageCount * pageSize;
44
45 bool passed = true;
46 passed &= check(Event::getHostedHandlerBufferSize(1024) == EVENT_LIMIT, Test,
47 "a 4 KiB event did not span four 1 KiB pages");
48 passed &= check(Event::getHostedHandlerBufferSize(4096) == EVENT_LIMIT, Test,
49 "the 4 KiB target changed its event slot size");
50 passed &= check(Event::getHostedHandlerBufferSize(16384) == 16384, Test,
51 "a sub-page event slot was not rounded to one target page");
52 passed &= check(Event::getHandlerBufferSize() == expectedSpan, Test,
53 "the live event slot does not match target-page geometry");
55 (EVENT_TID_MAX * MAX_NESTED_EVENTS * expectedSpan),
56 Test, "the reserved handler-buffer range does not use the rounded slot span");
57 if (passed) {
58 NOTICE("HOSTED-WAIT-TEST: PASS event-payload-page-span");
59 }
60 return passed;
61}
62
63bool multiPagePoolMapping() {
64 constexpr const char* Test = "memory-pool-page-span";
65 constexpr size_t PageCount = 4;
66 const size_t pageSize = PhysicalMemoryManager::getPageSize();
67 if (pageSize > (~size_t{0} / PageCount)) {
68 return check(false, Test, "the target page size overflows the regression buffer");
69 }
70
71 MemoryPool pool("hosted-payload-page-regression");
72 if (!pool.initialise(PageCount, PageCount * pageSize)) {
73 return check(false, Test, "the multi-page pool could not be initialised");
74 }
75
76 const uintptr_t buffer = pool.allocateNow();
77 bool passed = check(buffer != 0, Test, "the multi-page buffer could not be allocated");
79 if (buffer) {
80 for (size_t page = 0; page < PageCount; ++page) {
81 passed &= check(va.isMapped(reinterpret_cast<void*>(buffer + (page * pageSize))), Test,
82 "allocation left part of the buffer unmapped");
83 }
84
85 pool.free(buffer);
86 passed &= check(pool.trim(), Test, "the released multi-page buffer was not reclaimed");
87 for (size_t page = 0; page < PageCount; ++page) {
88 passed &= check(!va.isMapped(reinterpret_cast<void*>(buffer + (page * pageSize))), Test,
89 "trim left part of the buffer mapped");
90 }
91 }
92
93 if (passed) {
94 NOTICE("HOSTED-WAIT-TEST: PASS memory-pool-page-span");
95 }
96 return passed;
97}
98
99bool ipcPayloadGeometry() {
100 constexpr const char* Test = "ipc-payload-page-span";
101 bool passed = true;
102 passed &= check(Ipc::IpcMessage::InlineCapacity == 4096, Test,
103 "the conventional inline IPC capacity changed");
104 passed &= check(Ipc::IpcMessage::getHostedInlinePageCount(1024) == 4, Test,
105 "the 4 KiB IPC buffer did not span four 1 KiB pages");
106 passed &= check(Ipc::IpcMessage::getHostedInlinePageCount(4096) == 1, Test,
107 "the 4 KiB target changed its IPC buffer span");
108 passed &= check(Ipc::IpcMessage::getHostedInlinePageCount(16384) == 1, Test,
109 "the IPC buffer used more than one 16 KiB page");
110 passed &= check(Ipc::IpcMessage::getHostedInlineSlotSize(1024) == 4096 &&
111 Ipc::IpcMessage::getHostedInlineSlotSize(4096) == 4096 &&
112 Ipc::IpcMessage::getHostedInlineSlotSize(16384) == 16384,
113 Test, "an inline IPC slot shared a target page with another message");
114 passed &= check(Ipc::IpcMessage::getHostedInlinePoolPageCount(1024) == 4096 &&
115 Ipc::IpcMessage::getHostedInlinePoolPageCount(4096) == 1024 &&
116 Ipc::IpcMessage::getHostedInlinePoolPageCount(16384) == 1024,
117 Test, "the IPC pool did not retain its logical buffer capacity");
118
120 uint8_t* buffer = reinterpret_cast<uint8_t*>(inlineMessage.getBuffer());
121 passed &= check(buffer != nullptr && inlineMessage.getHandle() == nullptr, Test,
122 "an inline IPC message did not use the message pool");
123 if (buffer) {
124 VirtualAddressSpace& va = Processor::information().getVirtualAddressSpace();
125 const size_t pageSize = PhysicalMemoryManager::getPageSize();
126 const size_t pageCount = Ipc::IpcMessage::getHostedInlinePageCount(pageSize);
127 passed &= check(!(reinterpret_cast<uintptr_t>(buffer) & (pageSize - 1)), Test,
128 "the inline IPC slot was not target-page aligned");
129 for (size_t page = 0; page < pageCount; ++page) {
130 void* address = buffer + (page * pageSize);
131 const bool mapped = va.isMapped(address);
132 passed &= check(mapped, Test, "part of the inline IPC buffer is unmapped");
133 if (mapped) {
134 physical_uintptr_t physicalAddress = 0;
135 size_t flags = 0;
136 va.getMapping(address, physicalAddress, flags);
137 passed &= check(
139 Test, "part of the inline IPC buffer is not user-writable");
140 }
141 }
142 buffer[0] = 0xA5;
143 buffer[Ipc::IpcMessage::InlineCapacity - 1] = 0x5A;
144 passed &= check(buffer[0] == 0xA5 && buffer[Ipc::IpcMessage::InlineCapacity - 1] == 0x5A, Test,
145 "the inline IPC payload was not writable end-to-end");
146 }
147
149 passed &= check(sharedMessage.getBuffer() != nullptr && sharedMessage.getHandle() != nullptr,
150 Test, "the exact 4 KiB threshold did not use a shared region");
151 if (passed) {
152 NOTICE("HOSTED-WAIT-TEST: PASS ipc-payload-page-span");
153 }
154 return passed;
155}
156} // namespace
157
158bool runHostedPayloadPageRegressions() {
159 return eventPayloadGeometry() && multiPagePoolMapping() && ipcPayloadGeometry();
160}
static size_t getHandlerBufferSize()
Definition Event.cc:217
static uintptr_t getLastHandlerBuffer()
Definition Event.cc:221
static uintptr_t getHandlerBuffer()
Definition Event.cc:213
static ProcessorInformation & information()
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39