8#include "pedigree/kernel/Atomic.h"
9#include "pedigree/kernel/Log.h"
10#include "pedigree/kernel/process/PerProcessorScheduler.h"
11#include "pedigree/kernel/process/Scheduler.h"
12#include "pedigree/kernel/process/TerminationDeferral.h"
13#include "pedigree/kernel/process/Thread.h"
14#include "pedigree/kernel/process/Uninterruptible.h"
15#include "pedigree/kernel/processor/Processor.h"
16#include "pedigree/kernel/processor/SyscallHandler.h"
17#include "pedigree/kernel/processor/SyscallManager.h"
18#include "pedigree/kernel/processor/state.h"
19#include "pedigree/kernel/time/Time.h"
20#include "pedigree/kernel/utilities/utility.h"
23bool check(
bool condition,
const char* test,
const char* detail) {
28 ERROR(
"HOSTED-WAIT-TEST: FAIL " << test <<
": " << detail);
32struct HandlerLifetimeContext;
33HandlerLifetimeContext* g_HandlerLifetimeContext =
nullptr;
37 explicit LifetimeHandler(HandlerLifetimeContext& context) : m_Context(context) {}
39 uintptr_t
syscall(SyscallState&)
override;
42 HandlerLifetimeContext& m_Context;
45struct HandlerLifetimeContext {
46 HandlerLifetimeContext()
53 callbacksAfterReturn(0),
54 unregisterReturned(0),
55 unregisterSucceeded(0),
58 LifetimeHandler handler;
71uintptr_t LifetimeHandler::syscall(SyscallState&) {
72 m_Context.handlerCalls += 1;
73 if (m_Context.unregisterReturned) {
74 m_Context.callbacksAfterReturn += 1;
82 : m_Registration(registration), calls(0), rejectionSeen(0) {}
84 uintptr_t
syscall(SyscallState&)
override {
86 if (!m_Registration.reset() && m_Registration) {
97struct ReciprocalUnregisterContext;
101 ReciprocalUnregisterHandler(ReciprocalUnregisterContext& context,
bool first)
102 : m_Context(context), m_First(first) {}
104 uintptr_t
syscall(SyscallState&)
override;
107 ReciprocalUnregisterContext& m_Context;
111struct ReciprocalUnregisterContext {
112 ReciprocalUnregisterContext()
113 : first(*this, true),
114 second(*this, false),
121 ReciprocalUnregisterHandler first;
122 ReciprocalUnregisterHandler second;
132uintptr_t ReciprocalUnregisterHandler::syscall(SyscallState&) {
133 m_Context.entered += 1;
134 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
135 while (m_Context.entered !=
static_cast<size_t>(2) && Time::getTicks() < deadline) {
139 if (m_Context.entered !=
static_cast<size_t>(2)) {
140 m_Context.failures += 1;
144 m_Context.attempts += 1;
146 m_First ? m_Context.secondRegistration : m_Context.firstRegistration;
147 if (!peer.reset() && peer) {
148 m_Context.rejections += 1;
150 m_Context.failures += 1;
152 m_Context.resetReturns += 1;
153 const Time::Timestamp returnDeadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
154 while (m_Context.resetReturns !=
static_cast<size_t>(2) && Time::getTicks() < returnDeadline) {
157 if (m_Context.resetReturns !=
static_cast<size_t>(2)) {
158 m_Context.failures += 1;
160 return m_First ? 0x53 : 0x54;
163struct ReciprocalSyscallInvocation {
164 ReciprocalUnregisterContext* context;
169int invokeReciprocalSyscall(
void* parameter) {
170 ReciprocalSyscallInvocation* invocation =
171 reinterpret_cast<ReciprocalSyscallInvocation*
>(parameter);
176bool reciprocalUnregisterRejected() {
178 ReciprocalUnregisterContext context;
179 const bool registered =
183 if (context.firstRegistration) {
184 context.firstRegistration.reset();
186 return check(
false,
"syscall-reciprocal-unregister",
187 "the reciprocal handlers could not register");
190 ReciprocalSyscallInvocation first = {&context, TUI, 0};
191 ReciprocalSyscallInvocation second = {&context, native, 0};
193 invokeReciprocalSyscall, &first,
nullptr,
false,
true);
195 invokeReciprocalSyscall, &second,
nullptr,
false,
true);
196 firstThread->setName(
"hosted syscall reciprocal first");
197 secondThread->setName(
"hosted syscall reciprocal second");
199 const bool firstJoined = firstThread->
join();
200 const bool secondJoined = secondThread->
join();
201 const bool registrationsPreserved = context.firstRegistration && context.secondRegistration;
202 const bool firstRetired = context.firstRegistration.reset();
203 const bool secondRetired = context.secondRegistration.reset();
205 check(firstJoined && secondJoined && first.result == 0x53 && second.result == 0x54 &&
206 context.entered == 2 && context.attempts == 2 && context.rejections == 2 &&
207 context.resetReturns == 2 && context.failures == 0,
208 "syscall-reciprocal-unregister",
"reciprocal callbacks did not finish cleanly") &&
209 check(registrationsPreserved && firstRetired && secondRetired,
210 "syscall-reciprocal-unregister",
211 "callback-context rejection lost external cleanup ownership");
213 NOTICE(
"HOSTED-WAIT-TEST: PASS syscall-reciprocal-unregister");
219 HandlerLifetimeContext* context = g_HandlerLifetimeContext;
220 if (!context || service != TUI || handler != &context->handler ||
221 !context->phase.compareAndSwap(0, 1)) {
225 context->hookCalls += 1;
226 for (
size_t attempt = 0; attempt < 10000; ++attempt) {
227 if (context->phase ==
static_cast<size_t>(2) && !context->unregisterReturned &&
229 context->hookObservedDrain += 1;
236 context->failures += 1;
240int unregisterPinnedHandler(
void* parameter) {
241 HandlerLifetimeContext* context =
reinterpret_cast<HandlerLifetimeContext*
>(parameter);
242 const Time::Timestamp deadline = Time::getTicks() + (500 * Time::Multiplier::Millisecond);
243 while (context->phase !=
static_cast<size_t>(1) && Time::getTicks() < deadline) {
247 if (context->phase !=
static_cast<size_t>(1)) {
248 context->failures += 1;
252 if (!context->registration.closeAdmission()) {
253 context->failures += 1;
258 if (context->registration.reset()) {
259 context->unregisterSucceeded += 1;
261 context->unregisterReturned += 1;
265bool handlerLifetimeBarrier() {
267 HandlerLifetimeContext context;
269 &context,
nullptr,
false,
true);
270 context.remover->setName(
"hosted syscall-handler remover");
272 g_HandlerLifetimeContext = &context;
273 manager.setHandlerPinHook(handlerPinHook);
274 const bool registered =
277 const bool duplicateRejected =
279 const uintptr_t result = manager.
syscall(TUI, 0);
280 const bool joined = context.remover->join();
281 manager.setHandlerPinHook(
nullptr);
282 g_HandlerLifetimeContext =
nullptr;
284 const size_t callsAtUnregisterReturn = context.handlerCalls;
285 const uintptr_t lateResult = manager.
syscall(TUI, 0);
286 const bool lateDispatchRejected = lateResult == 0 &&
287 context.handlerCalls == callsAtUnregisterReturn &&
288 context.callbacksAfterReturn == 0;
292 const uintptr_t redispatchResult = reregistered ? manager.
syscall(TUI, 0) : 0;
294 const bool movePreservedOwnership = !reusable && moved && moved.reset();
297 SelfUnregisteringHandler selfUnregistering(selfRegistration);
298 const bool selfRegistered =
300 const uintptr_t selfResult = selfRegistered ? manager.
syscall(TUI, 0) : 0;
301 const bool selfCleanup = selfRegistered && selfRegistration.reset();
304 check(registered && duplicateRejected && result == 0x51,
"syscall-handler-lifetime",
305 "registration, token ownership, or dispatch failed") &&
306 check(joined && context.failures == 0,
"syscall-handler-lifetime",
307 "the unregister worker did not finish cleanly") &&
308 check(context.hookCalls == 1 && context.hookObservedDrain == 1 &&
309 context.unregisterSucceeded == 1 && context.unregisterReturned == 1,
310 "syscall-handler-lifetime",
"unregister returned before the admitted handler") &&
311 check(lateDispatchRejected,
"syscall-handler-lifetime",
312 "a handler ran after token reset returned") &&
313 check(reregistered && redispatchResult == 0x51 && movePreservedOwnership,
314 "syscall-handler-lifetime",
"generation-bearing token move or slot reuse failed") &&
315 check(selfRegistered && selfResult == 0x52 && selfUnregistering.calls == 1 &&
316 selfUnregistering.rejectionSeen == 1 && selfCleanup,
317 "syscall-handler-lifetime",
"self-reset changed ownership or reported safe teardown");
320 NOTICE(
"HOSTED-WAIT-TEST: PASS syscall-handler-lifetime");
327 PostActionHandler() : requested(
SyscallManager::NoPostSyscallAction), calls(0), failures(0) {}
329 uintptr_t
syscall(SyscallState& syscallState)
override {
331 if (syscallState.getSyscallNumber() == 1) {
336 void* dispatchContext = thread->getSyscallDispatchContext();
339 case SyscallManager::TerminateCurrentThread:
342 case SyscallManager::ExitCurrentProcess:
345 case SyscallManager::ReturnFromEvent:
348 case SyscallManager::PopEventState:
351 case SyscallManager::RestoreProcessorState: {
352 ProcessorState state;
354 state.state = 0x12345678;
356 volatile uint64_t* source = &state.state;
357 *source = 0x87654321;
360 case SyscallManager::JumpToUserspace:
363 case SyscallManager::RebootSystem:
366 case SyscallManager::NoPostSyscallAction:
374 if (requested != SyscallManager::NoPostSyscallAction) {
375 ProcessorState rejectedState;
376 rejectedState.state = 0xabcdef;
384 thread->getSyscallDispatchContext() != dispatchContext) {
390 SyscallManager::PostSyscallActionKind requested;
395struct PostActionContext {
396 SyscallManager::PostSyscallActionKind expected = SyscallManager::NoPostSyscallAction;
397 intptr_t expectedValue = 0;
398 void* previousContext =
nullptr;
405PostActionContext* g_PostActionContext =
nullptr;
407bool postActionHook(SyscallManager::PostSyscallActionKind kind, intptr_t value,
408 const ProcessorState* state) {
409 PostActionContext* context = g_PostActionContext;
416 if (kind != context->expected || value != context->expectedValue || !thread ||
418 thread->getSyscallDispatchContext() != context->previousContext) {
419 context->failures += 1;
421 const bool needsState =
422 kind == SyscallManager::RestoreProcessorState || kind == SyscallManager::JumpToUserspace;
423 if (needsState != (state !=
nullptr) ||
424 (state && state->state != (kind == SyscallManager::RestoreProcessorState ? 0x12345678 : 0))) {
425 context->failures += 1;
427 if (context->registration && context->registration->reset()) {
428 context->retired += 1;
430 context->failures += 1;
435bool postSyscallActions() {
436 static const SyscallManager::PostSyscallActionKind actions[] = {
437 SyscallManager::TerminateCurrentThread, SyscallManager::ExitCurrentProcess,
438 SyscallManager::ReturnFromEvent, SyscallManager::PopEventState,
439 SyscallManager::RestoreProcessorState, SyscallManager::NoPostSyscallAction,
440 SyscallManager::JumpToUserspace, SyscallManager::RebootSystem};
441 constexpr size_t ActionCount =
sizeof(actions) /
sizeof(actions[0]);
444 PostActionHandler handler;
445 PostActionContext context;
447 context.previousContext = thread->getSyscallDispatchContext();
448 g_PostActionContext = &context;
449 manager.setPostSyscallHook(postActionHook);
451 bool loopPassed =
true;
452 for (
size_t i = 0; i < ActionCount; ++i) {
453 const bool needsEventState = actions[i] == SyscallManager::ReturnFromEvent ||
454 actions[i] == SyscallManager::PopEventState;
455 const bool stateReady = !needsEventState || (thread && thread->
pushState() !=
nullptr);
457 context.expected = actions[i];
458 context.expectedValue = actions[i] == SyscallManager::ExitCurrentProcess ? 37 : 0;
459 context.registration = ®istration;
460 handler.requested = actions[i];
461 const bool noAction = actions[i] == SyscallManager::NoPostSyscallAction;
462 const size_t hookCalls = context.calls;
463 const bool dispatched = stateReady &&
465 manager.
syscall(TUI, 0) == 0x61;
466 if (!dispatched || thread->getSyscallDispatchContext() != context.previousContext ||
467 (noAction ? !registration || context.calls != hookCalls || !registration.reset()
468 :
static_cast<bool>(registration))) {
471 registration.reset();
474 if (needsEventState && stateReady) {
479 manager.setPostSyscallHook(
nullptr);
480 g_PostActionContext =
nullptr;
482 check(loopPassed && handler.calls == 2 * ActionCount && handler.failures == 0 &&
483 context.calls == ActionCount - 1 && context.retired == ActionCount - 1 &&
484 context.failures == 0,
485 "syscall-post-actions",
"post-action state, nesting, rejection, or retirement failed");
487 NOTICE(
"HOSTED-WAIT-TEST: PASS syscall-post-actions");
494 InvalidEventActionHandler()
495 : requested(
SyscallManager::NoPostSyscallAction), calls(0), rejections(0) {}
497 uintptr_t
syscall(SyscallState&)
override {
498 bool accepted =
false;
499 if (requested == SyscallManager::ReturnFromEvent) {
501 }
else if (requested == SyscallManager::PopEventState) {
512 SyscallManager::PostSyscallActionKind requested;
517bool baseStateEventActionsRejected() {
520 InvalidEventActionHandler handler;
522 const bool registered = thread && !thread->
getStateLevel() &&
525 handler.requested = SyscallManager::ReturnFromEvent;
526 const uintptr_t returnResult = registered ? manager.
syscall(TUI, 0) : 0;
527 handler.requested = SyscallManager::PopEventState;
528 const uintptr_t popResult = registered ? manager.
syscall(TUI, 0) : 0;
529 const bool retired = registered && registration.reset();
532 check(registered && returnResult == 0x62 && popResult == 0x62 && handler.calls == 2 &&
533 handler.rejections == 2 && retired && !thread->
getStateLevel(),
534 "syscall-event-action-boundary",
"a base-state event return or pop was accepted");
537 "HOSTED-WAIT-TEST: PASS "
538 "syscall-event-action-boundary");
543struct AbandonedStackContext;
547 explicit AbandoningHandler(AbandonedStackContext& context) : m_Context(context) {}
549 uintptr_t
syscall(SyscallState&)
override;
552 AbandonedStackContext& m_Context;
555struct AbandonedStackContext {
556 AbandonedStackContext() : handler(*this), entered(0), returned(0), destructed(0) {}
558 AbandoningHandler handler;
564class AbandonedStackCanary {
566 explicit AbandonedStackCanary(
Atomic<size_t>* destructed) : m_Destructed(destructed) {}
568 ~AbandonedStackCanary() {
576uintptr_t AbandoningHandler::syscall(SyscallState&) {
584 AbandonedStackCanary stackCanary(&m_Context.destructed);
585 m_Context.entered += 1;
587 PerProcessorScheduler::StackDiscardReason::HostedRegression);
590int invokeAbandoningSyscall(
void* parameter) {
591 AbandonedStackContext* context =
reinterpret_cast<AbandonedStackContext*
>(parameter);
593 context->returned += 1;
597bool abandonedSyscallStack() {
599 AbandonedStackContext context;
602 return check(
false,
"syscall-abandoned-stack",
"the abandoning handler could not register");
606 PerProcessorScheduler::StackDiscardReason::HostedRegression);
608 &context,
nullptr,
false,
true);
609 thread->setName(
"hosted abandoning syscall");
610 const bool joined = thread->
join();
611 const bool retired = registration.reset();
613 const bool passed = check(
614 joined && retired && context.entered == 1 && context.returned == 0 &&
615 context.destructed == 0 &&
617 PerProcessorScheduler::StackDiscardReason::HostedRegression) == discardsBefore + 1,
618 "syscall-abandoned-stack",
619 "forced teardown did not count exactly one intentionally leaked stack");
621 NOTICE(
"HOSTED-WAIT-TEST: PASS syscall-abandoned-stack");
627bool runHostedPostSyscallRegressions() {
628 return postSyscallActions() && baseStateEventActionsRejected();
631bool runHostedSyscallRegressions() {
632 return handlerLifetimeBarrier() && reciprocalUnregisterRejected() && postSyscallActions() &&
633 baseStateEventActionsRejected() && abandonedSyscallStack();
void abandonCurrentThreadStack(StackDiscardReason reason, Spinlock *pLock=0) NORETURN
static size_t stackDiscardCount()
static ProcessorInformation & information()
static Scheduler & instance()
virtual uintptr_t syscall(SyscallState &State)=0
virtual uintptr_t syscall(Service_t service, uintptr_t function, uintptr_t p1=0, uintptr_t p2=0, uintptr_t p3=0, uintptr_t p4=0, uintptr_t p5=0)=0
virtual bool registerSyscallHandler(Service_t Service, SyscallHandler *pHandler, Registration ®istration, FastEntry entry=nullptr)=0
static EXPORTED_PUBLIC SyscallManager & instance()
EXPORTED_PUBLIC bool requestThreadExit()
void popState(bool clean=true)
class PerProcessorScheduler * getScheduler() const
bool isTerminationDeferred() const
SchedulerState * pushState()
size_t getStateLevel() const