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");
65bool metadataRoundTrip() {
67 bool preservedKernelBase;
71 uint32_t beforeLow, beforeHigh, afterLow, afterHigh;
72 asm volatile(
"rdmsr" :
"=a"(beforeLow),
"=d"(beforeHigh) :
"c"(0xc0000102));
73 pedigree_capture_user_entry(&saved);
74 requested.ds = requested.es = 0x23;
75 requested.fsBase = 0x12345000;
76 requested.gsBase = 0x23456000;
77 pedigree_restore_user_entry(&requested);
78 pedigree_capture_user_entry(&observed);
80 pedigree_restore_user_entry(&saved);
81 asm volatile(
"rdmsr" :
"=a"(afterLow),
"=d"(afterHigh) :
"c"(0xc0000102));
82 preservedKernelBase = beforeLow == afterLow && beforeHigh == afterHigh;
86 check(equalBytes(&requested, &observed,
sizeof(requested)) && preservedKernelBase,
87 "actual selectors/bases restore without changing kernel GS stack");
89 NOTICE(
"PTRACE-FRAME-CORE: PASS machine metadata round trip");
93bool payload(
Thread& thread) {
95 ByteSet(&state, 0,
sizeof(state));
96 for (
size_t i = 0; i < 13; ++i)
97 state.setRegister(i, 0x1000 + i);
98 state.setInstructionPointer(0x401234);
99 state.setStackPointer(0x800000);
100 state.setFlags(0x202);
102 state.setUserEntryMetadata(metadata);
105 ByteSet(&output, 0xa5,
sizeof(output));
107 bool passed = check(!frame.snapshot(output) && equalBytes(&output, &canary,
sizeof(output)),
108 "unscoped snapshot leaves output unchanged");
111 passed &= check(frame.snapshot(output),
"complete syscall snapshot");
113 const uint64_t expected[27] = {
114 0x100c, 0x100b, 0x100a, 0x1009, 0x1005, 0x1001, 0x202, 0x1008, 0x1007,
115 0x1006, 0x1000, 0x401234, 0x1002, 0x1004, 0x1003, 234, 0x401234, 0x2b,
116 0x202, 0x800000, 0x23, 0x11110000, 0x22220000, 0x20, 0x21, 0x22, 0x23};
117 passed &= check(output.architecture == UserRegisterSnapshot::Architecture::Amd64 &&
118 equalBytes(&output.amd64, expected,
sizeof(expected)),
119 "all 27 syscall ABI slots");
120 const uint64_t syscallSlots[16] = {0x100c, 0x100b, 0x100a, 0x1009, 0x1008, 0x1007,
121 0x1006, 0x1005, 0x1004, 0x1003, 0x1002, 0x1001,
122 0x1000, 0x202, 0x401234, 0x800000};
123 passed &= check(equalBytes(&state, &metadata,
sizeof(metadata)) &&
124 equalBytes(
reinterpret_cast<const unsigned char*
>(&state) + 32, syscallSlots,
125 sizeof(syscallSlots)),
126 "syscall storage matches assembly slots after metadata prefix");
128 state.setRegister(0, 0xdead);
130 check(equalBytes(&output, &saved,
sizeof(output)),
"snapshot independent of live frame");
131 UserReturnFrame invalid(thread, state, UserReturnFrame::Origin::Interrupt);
134 passed &= check(!invalid.snapshot(output) && equalBytes(&output, &saved,
sizeof(output)),
135 "incompatible frame origin preserves output");
138 ProcessorState registers;
139 registers.rax = 0x2000;
140 registers.rbx = 0x2001;
141 registers.rcx = 0x2002;
142 registers.rdx = 0x2003;
143 registers.rdi = 0x2004;
144 registers.rsi = 0x2005;
145 registers.rbp = 0x2006;
146 registers.r8 = 0x2007;
147 registers.r9 = 0x2008;
148 registers.r10 = 0x2009;
149 registers.r11 = 0x200a;
150 registers.r12 = 0x200b;
151 registers.r13 = 0x200c;
152 registers.r14 = 0x200d;
153 registers.r15 = 0x200e;
154 registers.rip = 0x402345;
155 alignas(16)
unsigned char storage[
sizeof(InterruptState)];
156 registers.rsp =
reinterpret_cast<uintptr_t
>(storage +
sizeof(storage));
157 metadata.origRax = ~uint64_t(0);
158 InterruptState* irq = InterruptState::construct(registers,
true, metadata);
159 irq->setStackPointer(0x900000);
160 irq->setFlags(0x246);
164 passed &= check(irqFrame.snapshot(output),
"complete interrupt snapshot");
166 const uint64_t irqExpected[27] = {
167 0x200e, 0x200d, 0x200c, 0x200b, 0x2006, 0x2001, 0x200a, 0x2009, 0x2008,
168 0x2007, 0x2000, 0x2002, 0x2003, 0x2005, 0x2004, ~uint64_t(0), 0x402345, 0x1b,
169 0x246, 0x900000, 0x23, 0x11110000, 0x22220000, 0x20, 0x21, 0x22, 0x23};
170 passed &= check(equalBytes(&output.amd64, irqExpected,
sizeof(irqExpected)),
171 "all 27 interrupt ABI slots");
172 const uint64_t irqSlots[22] = {0x200e, 0x200d, 0x200c, 0x200b, 0x200a, 0x2009, 0x2008, 0x2007,
173 0x2006, 0x2005, 0x2004, 0x2003, 0x2002, 0x2001, 0x2000, 0,
174 0, 0x402345, 0x1b, 0x246, 0x900000, 0x23};
176 equalBytes(irq, &metadata,
sizeof(metadata)) &&
177 equalBytes(
reinterpret_cast<const unsigned char*
>(irq) + 32, irqSlots,
sizeof(irqSlots)),
178 "interrupt storage matches assembly slots after metadata prefix");
180 irq = InterruptState::construct(registers,
false, metadata);
184 passed &= check(!kernel.snapshot(output) && equalBytes(&output, &irqSaved,
sizeof(output)),
185 "kernel frame rejected without partial snapshot");
188 NOTICE(
"PTRACE-FRAME-CORE: PASS complete register snapshots");
194EXPORTED_PUBLIC
bool runPtraceFrameRegressions() {
195 NOTICE(
"PTRACE-FRAME-CORE: BEGIN");
199 bool passed = scopes(*current);
201 passed = passed && metadataRoundTrip() && payload(*current);
206 const auto before = output;
208 passed &= check(!frame.snapshot(output) && equalBytes(&before, &output,
sizeof(output)),
209 "unsupported architecture snapshot unchanged");
210 NOTICE(
"PTRACE-FRAME-CORE: SKIP amd64 machine metadata (hosted backend)");
213 NOTICE(
"PTRACE-FRAME-CORE: END PASS");
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
SchedulerState * pushState()
size_t getStateLevel() const
void abandonCurrentState(bool clean=false)