4#if PEDIGREE_PTRACE_TESTS && THREADS
5#include "pedigree/kernel/Log.h"
6#include "pedigree/kernel/process/Thread.h"
7#include "pedigree/kernel/processor/Processor.h"
8#include "pedigree/kernel/processor/UserReturnFrame.h"
9#include "pedigree/kernel/processor/state.h"
10#include "pedigree/kernel/utilities/utility.h"
13bool check(
bool condition,
const char* detail) {
15 ERROR(
"PTRACE-FRAME-CORE: FAIL " << detail);
19bool equalBytes(
const void* left,
const void* right,
size_t count) {
20 const auto* a =
static_cast<const unsigned char*
>(left);
21 const auto* b =
static_cast<const unsigned char*
>(right);
22 for (
size_t i = 0; i < count; ++i)
28bool scopes(
Thread& thread) {
30 ByteSet(&state, 0,
sizeof(state));
32 bool passed = check(!thread.currentUserReturnFrame(),
"initial frame slot");
35 passed &= check(thread.currentUserReturnFrame() == &outer,
"outer frame publication");
39 passed &= check(thread.currentUserReturnFrame() == &inner,
"same-level frame override");
41 passed &= check(thread.currentUserReturnFrame() == &outer,
"same-level frame restoration");
45 passed &= check(!thread.currentUserReturnFrame(),
"nested kernel state excludes outer frame");
48 passed &= check(thread.currentUserReturnFrame() == &inner,
"nested frame publication");
50 passed &= check(thread.
getStateLevel() == level && thread.currentUserReturnFrame() == &outer,
51 "abandoned nested frame restored before storage retirement");
53 passed &= check(thread.currentUserReturnFrame() == &outer,
"discarded scope destructor inert");
55 passed &= check(!thread.currentUserReturnFrame(),
"final frame slot cleared");
57 NOTICE(
"PTRACE-FRAME-CORE: PASS scope lifetime");
61bool legacyAdmission(
Thread& thread) {
63 bool passed = check(pin,
"legacy callback pin admission");
66 const bool unexpectedRequirement = thread.tryRequireSignalFrames();
67 passed &= check(!unexpectedRequirement,
"legacy callback excludes tracing frame requirement");
68 if (unexpectedRequirement)
69 thread.clearSignalFrameRequirement();
70 thread.unpinLegacyUserCallbacks();
71 const bool required = thread.tryRequireSignalFrames();
72 passed &= check(required,
"frame requirement after last callback pin");
75 passed &= check(!unexpectedPin && thread.requiresSignalFrames(),
76 "frame requirement excludes callback");
78 thread.unpinLegacyUserCallbacks();
79 thread.clearSignalFrameRequirement();
82 NOTICE(
"PTRACE-FRAME-CORE: PASS legacy admission exclusion");
90bool metadataRoundTrip() {
92 bool preservedKernelBase;
96 uint32_t beforeLow, beforeHigh, afterLow, afterHigh;
97 asm volatile(
"rdmsr" :
"=a"(beforeLow),
"=d"(beforeHigh) :
"c"(0xc0000102));
98 pedigree_capture_user_entry(&saved);
99 requested.ds = requested.es = 0x23;
100 requested.fsBase = 0x12345000;
101 requested.gsBase = 0x23456000;
102 pedigree_restore_user_entry(&requested);
103 pedigree_capture_user_entry(&observed);
105 pedigree_restore_user_entry(&saved);
106 asm volatile(
"rdmsr" :
"=a"(afterLow),
"=d"(afterHigh) :
"c"(0xc0000102));
107 preservedKernelBase = beforeLow == afterLow && beforeHigh == afterHigh;
111 check(equalBytes(&requested, &observed,
sizeof(requested)) && preservedKernelBase,
112 "actual selectors/bases restore without changing kernel GS stack");
114 NOTICE(
"PTRACE-FRAME-CORE: PASS machine metadata round trip");
118bool payload(
Thread& thread) {
120 ByteSet(&state, 0,
sizeof(state));
121 for (
size_t i = 0; i < 13; ++i)
122 state.setRegister(i, 0x1000 + i);
123 state.setInstructionPointer(0x401234);
124 state.setStackPointer(0x800000);
125 state.setFlags(0x202);
127 state.setUserEntryMetadata(metadata);
130 ByteSet(&output, 0xa5,
sizeof(output));
132 bool passed = check(!frame.snapshot(output) && equalBytes(&output, &canary,
sizeof(output)),
133 "unscoped snapshot leaves output unchanged");
136 passed &= check(frame.snapshot(output),
"complete syscall snapshot");
138 const uint64_t expected[27] = {
139 0x100c, 0x100b, 0x100a, 0x1009, 0x1005, 0x1001, 0x202, 0x1008, 0x1007,
140 0x1006, 0x1000, 0x401234, 0x1002, 0x1004, 0x1003, 234, 0x401234, 0x2b,
141 0x202, 0x800000, 0x23, 0x11110000, 0x22220000, 0x20, 0x21, 0x22, 0x23};
142 passed &= check(output.architecture == UserRegisterSnapshot::Architecture::Amd64 &&
143 equalBytes(&output.amd64, expected,
sizeof(expected)),
144 "all 27 syscall ABI slots");
145 const uint64_t syscallSlots[16] = {0x100c, 0x100b, 0x100a, 0x1009, 0x1008, 0x1007,
146 0x1006, 0x1005, 0x1004, 0x1003, 0x1002, 0x1001,
147 0x1000, 0x202, 0x401234, 0x800000};
148 passed &= check(equalBytes(&state, &metadata,
sizeof(metadata)) &&
149 equalBytes(
reinterpret_cast<const unsigned char*
>(&state) + 32, syscallSlots,
150 sizeof(syscallSlots)),
151 "syscall storage matches assembly slots after metadata prefix");
153 state.setRegister(0, 0xdead);
155 check(equalBytes(&output, &saved,
sizeof(output)),
"snapshot independent of live frame");
156 UserReturnFrame invalid(thread, state, UserReturnFrame::Origin::Interrupt);
159 passed &= check(!invalid.snapshot(output) && equalBytes(&output, &saved,
sizeof(output)),
160 "incompatible frame origin preserves output");
163 ProcessorState registers;
164 registers.rax = 0x2000;
165 registers.rbx = 0x2001;
166 registers.rcx = 0x2002;
167 registers.rdx = 0x2003;
168 registers.rdi = 0x2004;
169 registers.rsi = 0x2005;
170 registers.rbp = 0x2006;
171 registers.r8 = 0x2007;
172 registers.r9 = 0x2008;
173 registers.r10 = 0x2009;
174 registers.r11 = 0x200a;
175 registers.r12 = 0x200b;
176 registers.r13 = 0x200c;
177 registers.r14 = 0x200d;
178 registers.r15 = 0x200e;
179 registers.rip = 0x402345;
180 alignas(16)
unsigned char storage[
sizeof(InterruptState)];
181 registers.rsp =
reinterpret_cast<uintptr_t
>(storage +
sizeof(storage));
182 metadata.origRax = ~uint64_t(0);
183 InterruptState* irq = InterruptState::construct(registers,
true, metadata);
184 irq->setStackPointer(0x900000);
185 irq->setFlags(0x246);
189 passed &= check(irqFrame.snapshot(output),
"complete interrupt snapshot");
191 const uint64_t irqExpected[27] = {
192 0x200e, 0x200d, 0x200c, 0x200b, 0x2006, 0x2001, 0x200a, 0x2009, 0x2008,
193 0x2007, 0x2000, 0x2002, 0x2003, 0x2005, 0x2004, ~uint64_t(0), 0x402345, 0x1b,
194 0x246, 0x900000, 0x23, 0x11110000, 0x22220000, 0x20, 0x21, 0x22, 0x23};
195 passed &= check(equalBytes(&output.amd64, irqExpected,
sizeof(irqExpected)),
196 "all 27 interrupt ABI slots");
197 const uint64_t irqSlots[22] = {0x200e, 0x200d, 0x200c, 0x200b, 0x200a, 0x2009, 0x2008, 0x2007,
198 0x2006, 0x2005, 0x2004, 0x2003, 0x2002, 0x2001, 0x2000, 0,
199 0, 0x402345, 0x1b, 0x246, 0x900000, 0x23};
201 equalBytes(irq, &metadata,
sizeof(metadata)) &&
202 equalBytes(
reinterpret_cast<const unsigned char*
>(irq) + 32, irqSlots,
sizeof(irqSlots)),
203 "interrupt storage matches assembly slots after metadata prefix");
205 irq = InterruptState::construct(registers,
false, metadata);
209 passed &= check(!kernel.snapshot(output) && equalBytes(&output, &irqSaved,
sizeof(output)),
210 "kernel frame rejected without partial snapshot");
213 NOTICE(
"PTRACE-FRAME-CORE: PASS complete register snapshots");
219EXPORTED_PUBLIC
bool runPtraceFrameRegressions() {
220 NOTICE(
"PTRACE-FRAME-CORE: BEGIN");
224 bool passed = scopes(*current) && legacyAdmission(*current);
226 passed = passed && metadataRoundTrip() && payload(*current);
231 const auto before = output;
233 passed &= check(!frame.snapshot(output) && equalBytes(&before, &output,
sizeof(output)),
234 "unsupported architecture snapshot unchanged");
235 NOTICE(
"PTRACE-FRAME-CORE: SKIP amd64 machine metadata (hosted backend)");
238 NOTICE(
"PTRACE-FRAME-CORE: END PASS");
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
bool tryPinLegacyUserCallbacks()
SchedulerState * pushState()
size_t getStateLevel() const
void abandonCurrentState(bool clean=false)