20#include "InterruptManager.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/utilities/StaticString.h"
25#include "HostedPlatform.h"
27#include "pedigree/kernel/debugger/Debugger.h"
29#include "pedigree/kernel/processor/InterruptHandler.h"
30#include "pedigree/kernel/processor/Processor.h"
31#include "pedigree/kernel/processor/state.h"
34#include "pedigree/kernel/Subsystem.h"
35#include "pedigree/kernel/process/AtomicStateCleanup.h"
36#include "pedigree/kernel/process/InterruptTimeAccounting.h"
37#include "pedigree/kernel/process/PerProcessorScheduler.h"
38#include "pedigree/kernel/process/Process.h"
39#include "pedigree/kernel/process/Thread.h"
40#include "pedigree/kernel/processor/ProcessorInformation.h"
43namespace __pedigree_hosted {
50using namespace __pedigree_hosted;
52namespace __pedigree_interrupt_manager_cc {
57bool isHostedIrqSignal(
int which) {
58 bool irqSignal = which == SIGUSR1 || which == SIGUSR2;
59#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
60 irqSignal |= which == SIGURG;
65void setHostedIrqSignals(sigset_t& set,
bool blocked) {
67 sigaddset(&set, SIGUSR1);
68 sigaddset(&set, SIGUSR2);
69#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
70 sigaddset(&set, SIGURG);
73 sigdelset(&set, SIGUSR1);
74 sigdelset(&set, SIGUSR2);
75#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
76 sigdelset(&set, SIGURG);
85bool HostedInterruptManager::m_ActionInstalled[MAX_SIGNAL] = {};
86bool HostedInterruptManager::m_bQuiesced =
false;
90struct HostedSignalFrameCleanup {
91 explicit HostedSignalFrameCleanup(
Thread* signalThread)
92 : thread(signalThread),
94 previousInterruptState(false),
95 restoreInterruptState(false),
100 bool previousInterruptState;
101 bool restoreInterruptState;
105void restoreHostedSignalInterruptState(HostedSignalFrameCleanup& frame) {
106 if (!frame.restoreInterruptState) {
110 const bool previousInterruptState = frame.previousInterruptState;
111 frame.restoreInterruptState =
false;
115void abandonHostedSignalFrame(
void* context) {
116 HostedSignalFrameCleanup* frame =
reinterpret_cast<HostedSignalFrameCleanup*
>(context);
120 restoreHostedSignalInterruptState(*frame);
122 frame->thread->leaveHostedSignalHandler(frame->stateLevel);
124 Processor::leaveHostedSignalFrame();
127struct HostedUserReturnContext {
128 InterruptState* state;
131int serviceHostedUserReturnWork(uintptr_t rawContext) {
132 HostedUserReturnContext* context =
reinterpret_cast<HostedUserReturnContext*
>(rawContext);
136bool runHostedUserReturnTail(
Thread* thread, InterruptState& state) {
139 FATAL_NOLOCK(
"Hosted user-return work exhausted Thread state stacks");
142 HostedUserReturnContext context = {&state};
144 const bool terminal = callOnStack(
reinterpret_cast<uintptr_t
>(thread->
getKernelStack()),
145 reinterpret_cast<uintptr_t
>(&serviceHostedUserReturnWork),
146 reinterpret_cast<uintptr_t
>(&context)) != 0;
167 if (
UNLIKELY(nInterruptNumber >= MAX_SIGNAL))
170 if (
UNLIKELY(pHandler != 0 && current != 0))
172 if (
UNLIKELY(pHandler == 0 && current == 0))
177 return __atomic_compare_exchange_n(&
m_pHandler[nInterruptNumber], ¤t, pHandler,
false,
178 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
189 if (
UNLIKELY(nInterruptNumber >= MAX_SIGNAL))
192 if (
UNLIKELY(pHandler != 0 && current != 0))
194 if (
UNLIKELY(pHandler == 0 && current == 0))
197 return __atomic_compare_exchange_n(&
m_pDbgHandler[nInterruptNumber], ¤t, pHandler,
false,
198 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
210 size_t nIntNumber = interruptState.getInterruptNumber();
220 pHandler->
interrupt(nIntNumber, interruptState);
229 if (
LIKELY(pHandler != 0)) {
230 pHandler->
interrupt(nIntNumber, interruptState);
234 if (
UNLIKELY(nIntNumber == SIGINT || nIntNumber == SIGTERM)) {
238 panic(
"shutdown failed");
244 Subsystem* pSubsystem = pProcess ? pProcess->getSubsystem() :
nullptr;
245 if (pSubsystem && !interruptState.kernelMode()) {
246 if (
UNLIKELY(nIntNumber == SIGILL)) {
247 siginfo_t* info =
reinterpret_cast<siginfo_t*
>(interruptState.getRegister(1));
249 interruptState.getInstructionPointer(),
250 info ?
static_cast<uintptr_t
>(info->si_code) : 0);
253 if (
UNLIKELY(nIntNumber == SIGFPE)) {
254 siginfo_t* info =
reinterpret_cast<siginfo_t*
>(interruptState.getRegister(1));
256 interruptState.getInstructionPointer(),
257 info ?
static_cast<uintptr_t
>(info->si_code) : 0);
263#if HAS_ADDRESS_SANITIZER
265 siginfo_t* info =
reinterpret_cast<siginfo_t*
>(interruptState.getRegister(1));
266 uintptr_t ucontext_loc = interruptState.getRegister(2);
267 ucontext_t* ctx =
reinterpret_cast<ucontext_t*
>(ucontext_loc);
273 if (oact.sa_handler == SIG_IGN) {
275 }
else if (oact.sa_handler == SIG_DFL) {
276 sigaction(info->si_signo, &oact,
nullptr);
277 raise(info->si_signo);
278 }
else if (oact.sa_flags & SA_SIGINFO) {
279 oact.sa_sigaction(info->si_signo, info, ctx);
281 oact.sa_handler(info->si_signo);
290 if (
LIKELY(nIntNumber != SIGTRAP)) {
298 e.append(
"Signal #0x");
299 e.append(nIntNumber, 16);
312extern "C" void hostedSignalTrampoline(
int which, siginfo_t* info,
void* ptr);
314extern "C" void hostedSignalHandler(
int which, siginfo_t* info,
void* ptr,
bool fromUserspace) {
318#if defined(__APPLE__)
319extern "C" void hostedSignalTrampoline(
int which, siginfo_t* info,
void* ptr) {
322 hostedSignalHandler(which, info, ptr,
false);
327 bool terminalUserReturn =
false;
328 const bool hostedIrq = isHostedIrqSignal(which);
329 if (hostedIrq && !Processor::onHostedExecutionThread()) {
330 FATAL_NOLOCK(
"Hosted IRQ delivered on a non-processor host thread");
334 Processor::enterHostedSignalFrame();
338 HostedSignalFrameCleanup frameCleanup(pSignalThread);
340 frameCleanup.stateLevel = pSignalThread->enterHostedSignalHandler();
341 if (fromUserspace && frameCleanup.stateLevel) {
342 FATAL_NOLOCK(
"Hosted userspace resumed with nested kernel state");
344 pSignalThread->armAtomicStateCleanup(frameCleanup.cleanup, abandonHostedSignalFrame,
349 InterruptState state;
350 ucontext_t* interruptedContext =
reinterpret_cast<ucontext_t*
>(meta);
353 : ExecutionContext::HostedSyntheticIrq);
356 if (!previousInterruptState) {
358 FATAL_NOLOCK(
"interrupts disabled but interrupts are firing");
367 frameCleanup.previousInterruptState = previousInterruptState;
368 frameCleanup.restoreInterruptState =
true;
373 siginfo_t* info =
reinterpret_cast<siginfo_t*
>(siginfo);
376 state.fromUserspace = fromUserspace ? 1 : 0;
377 state.extra =
reinterpret_cast<uint64_t
>(info);
378 state.state =
reinterpret_cast<uint64_t
>(info->si_value.sival_ptr);
379 state.meta =
reinterpret_cast<uint64_t
>(meta);
380 state.setInstructionPointer(HostedPlatform::instructionPointer(interruptedContext));
381 state.setStackPointer(HostedPlatform::stackPointer(interruptedContext));
382 state.setBasePointer(HostedPlatform::basePointer(interruptedContext));
398 restoreHostedSignalInterruptState(frameCleanup);
407 if (fromUserspace && pSignalThread &&
409 terminalUserReturn = runHostedUserReturnTail(pSignalThread, state);
414 ucontext_t* ctx = interruptedContext;
415 pthread_sigmask(0,
nullptr, &ctx->uc_sigmask);
419 Processor::maskInterruptsForSignalReturn();
421 pSignalThread->disarmAtomicStateCleanup(frameCleanup.cleanup);
422 pSignalThread->leaveHostedSignalHandler(frameCleanup.stateLevel);
424 Processor::leaveHostedSignalFrame();
425 if (fromUserspace && pSignalThread) {
429 Processor::leaveHostedSignalFrame();
433 if (terminalUserReturn && pSignalThread &&
436 FATAL_NOLOCK(
"Hosted terminal user-return commit unexpectedly returned");
442 return m_OriginalActions[which];
447 ByteSet(m_ActionInstalled, 0,
sizeof(m_ActionInstalled));
450 for (
int i = 1; i < MAX_SIGNAL; ++i) {
451 struct sigaction act, oact;
452 ByteSet(&act, 0,
sizeof(act));
453 act.sa_sigaction = hostedSignalTrampoline;
454 sigemptyset(&act.sa_mask);
457 setHostedIrqSignals(act.sa_mask,
true);
458 act.sa_flags = SA_SIGINFO;
459#if !defined(PEDIGREE_HOSTED_DARWIN) || !PEDIGREE_HOSTED_DARWIN
460 act.sa_flags |= SA_ONSTACK;
462 if (!isHostedIrqSignal(i)) {
465 act.sa_flags |= SA_NODEFER;
468 if (sigaction(i, &act, &oact) == 0) {
470 m_ActionInstalled[i] =
true;
481 sigemptyset(&irqSignals);
482 setHostedIrqSignals(irqSignals,
true);
483 pthread_sigmask(SIG_BLOCK, &irqSignals,
nullptr);
489 if (sigpending(&pending) != 0) {
492 const int irqSignalCandidates[] = {SIGUSR1, SIGUSR2
493#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
499 for (
size_t i = 0; i <
sizeof(irqSignalCandidates) /
sizeof(irqSignalCandidates[0]); ++i) {
500 const int signal = irqSignalCandidates[i];
501 if (sigismember(&pending, signal) == 1) {
511 if (sigwait(&irqSignals, &consumed) != 0) {
516 for (
size_t i = 1; i < MAX_SIGNAL; ++i) {
517 if (m_ActionInstalled[i]) {
519 m_ActionInstalled[i] =
false;
525#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
526void HostedInterruptManager::withMutationLockForTest(MutationLockHook hook) {
536 for (
size_t i = 0; i < MAX_SIGNAL; i++) {
void start(InterruptState &state, LargeStaticString &description)
static Debugger & instance()
static void initialiseProcessor() INITIALISATION_ONLY
static void interrupt(InterruptState &interruptState)
InterruptHandler * m_pDbgHandler[MAX_SIGNAL]
void signalShim(int which, void *siginfo, void *meta, bool fromUserspace)
virtual size_t getDebugInterruptNumber() PURE
virtual bool registerInterruptHandlerDebugger(size_t nInterruptNumber, InterruptHandler *pHandler)
static HostedInterruptManager m_Instance
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)
static HostedInterruptManager & instance()
InterruptHandler * m_pHandler[MAX_SIGNAL]
virtual size_t getBreakpointInterruptNumber() PURE
struct sigaction getOriginalSigaction(int which) const
virtual ~HostedInterruptManager()
static void quiesceProcessor()
HostedInterruptManager() INITIALISATION_ONLY
static struct sigaction m_OriginalActions[MAX_SIGNAL]
Abstract base class for interrupt-handlers.
virtual void interrupt(size_t nInterruptNumber, InterruptState &state)=0
Handles interrupts and interrupt registrations from kernel components.
static EXPORTED_PUBLIC InterruptManager & instance()
static void finishUserReturn(Thread *thread)
static bool getInterrupts()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
bool deferSubsystemException(size_t type, uintptr_t faultAddress, uintptr_t errorCode)
Process * getParent() const
void popState(bool clean=true)
SchedulerState * pushState()
void EXPORTED_PUBLIC panic(const char *msg) NORETURN