The Pedigree Project 0.1
hosted/InterruptManager.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "InterruptManager.h"
21#include "pedigree/kernel/LockGuard.h"
22#include "pedigree/kernel/panic.h"
23#include "pedigree/kernel/utilities/StaticString.h"
24
25#include "HostedPlatform.h"
26#if DEBUGGER
27#include "pedigree/kernel/debugger/Debugger.h"
28#endif
29#include "pedigree/kernel/processor/InterruptHandler.h"
30#include "pedigree/kernel/processor/Processor.h"
31#include "pedigree/kernel/processor/state.h"
32
33#if THREADS
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"
41#endif
42
43namespace __pedigree_hosted {
44#include <errno.h>
45#include <pthread.h>
46#include <signal.h>
47#include <stdio.h>
48#include <time.h>
49}; // namespace __pedigree_hosted
50using namespace __pedigree_hosted;
51
52namespace __pedigree_interrupt_manager_cc {
53#include <string.h>
54}
55
56namespace {
57bool isHostedIrqSignal(int which) {
58 bool irqSignal = which == SIGUSR1 || which == SIGUSR2;
59#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
60 irqSignal |= which == SIGURG;
61#endif
62 return irqSignal;
63}
64
65void setHostedIrqSignals(sigset_t& set, bool blocked) {
66 if (blocked) {
67 sigaddset(&set, SIGUSR1);
68 sigaddset(&set, SIGUSR2);
69#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
70 sigaddset(&set, SIGURG);
71#endif
72 } else {
73 sigdelset(&set, SIGUSR1);
74 sigdelset(&set, SIGUSR2);
75#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
76 sigdelset(&set, SIGURG);
77#endif
78 }
79}
80} // namespace
81
83
84struct sigaction HostedInterruptManager::m_OriginalActions[MAX_SIGNAL];
85bool HostedInterruptManager::m_ActionInstalled[MAX_SIGNAL] = {};
86bool HostedInterruptManager::m_bQuiesced = false;
87
88#if THREADS
89namespace {
90struct HostedSignalFrameCleanup {
91 explicit HostedSignalFrameCleanup(Thread* signalThread)
92 : thread(signalThread),
93 stateLevel(0),
94 previousInterruptState(false),
95 restoreInterruptState(false),
96 cleanup() {}
97
98 Thread* thread;
99 size_t stateLevel;
100 bool previousInterruptState;
101 bool restoreInterruptState;
103};
104
105void restoreHostedSignalInterruptState(HostedSignalFrameCleanup& frame) {
106 if (!frame.restoreInterruptState) {
107 return;
108 }
109
110 const bool previousInterruptState = frame.previousInterruptState;
111 frame.restoreInterruptState = false;
112 Processor::setInterrupts(previousInterruptState);
113}
114
115void abandonHostedSignalFrame(void* context) {
116 HostedSignalFrameCleanup* frame = reinterpret_cast<HostedSignalFrameCleanup*>(context);
117 // Keep the host IRQ signals physically masked until the interrupted
118 // logical IF state has been restored. The resumed context owns the final
119 // signal mask after this abandoned stack is retired.
120 restoreHostedSignalInterruptState(*frame);
121 if (frame->thread) {
122 frame->thread->leaveHostedSignalHandler(frame->stateLevel);
123 }
124 Processor::leaveHostedSignalFrame();
125}
126
127struct HostedUserReturnContext {
128 InterruptState* state;
129};
130
131int serviceHostedUserReturnWork(uintptr_t rawContext) {
132 HostedUserReturnContext* context = reinterpret_cast<HostedUserReturnContext*>(rawContext);
133 return Processor::information().getScheduler().serviceUserReturnWork(*context->state) ? 1 : 0;
134}
135
136bool runHostedUserReturnTail(Thread* thread, InterruptState& state) {
138 if (!thread->pushState()) {
139 FATAL_NOLOCK("Hosted user-return work exhausted Thread state stacks");
140 }
141
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;
147
148 const bool returnInterrupts = Processor::getInterrupts();
150 thread->popState(false);
151 Processor::setInterrupts(returnInterrupts);
152 return terminal;
153}
154} // namespace
155#endif
156
159}
160
162 InterruptHandler* pHandler) {
163 // Lock the class until the end of the function
165
166 // Sanity checks
167 if (UNLIKELY(nInterruptNumber >= MAX_SIGNAL))
168 return false;
169 InterruptHandler* current = __atomic_load_n(&m_pHandler[nInterruptNumber], __ATOMIC_ACQUIRE);
170 if (UNLIKELY(pHandler != 0 && current != 0))
171 return false;
172 if (UNLIKELY(pHandler == 0 && current == 0))
173 return false;
174
175 // Dispatch can re-enter on this CPU while the mutation lock is held.
176 // Publish the complete old or new pointer without involving that lock.
177 return __atomic_compare_exchange_n(&m_pHandler[nInterruptNumber], &current, pHandler, false,
178 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
179}
180
181#if DEBUGGER
182
184 InterruptHandler* pHandler) {
185 // Lock the class until the end of the function
187
188 // Sanity checks
189 if (UNLIKELY(nInterruptNumber >= MAX_SIGNAL))
190 return false;
191 InterruptHandler* current = __atomic_load_n(&m_pDbgHandler[nInterruptNumber], __ATOMIC_ACQUIRE);
192 if (UNLIKELY(pHandler != 0 && current != 0))
193 return false;
194 if (UNLIKELY(pHandler == 0 && current == 0))
195 return false;
196
197 return __atomic_compare_exchange_n(&m_pDbgHandler[nInterruptNumber], &current, pHandler, false,
198 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
199}
204 return SIGTRAP;
205}
206
207#endif
208
209void HostedInterruptManager::interrupt(InterruptState& interruptState) {
210 size_t nIntNumber = interruptState.getInterruptNumber();
211
212#if DEBUGGER
213 {
214 InterruptHandler* pHandler =
215 __atomic_load_n(&m_Instance.m_pDbgHandler[nIntNumber], __ATOMIC_ACQUIRE);
216
217 // Call the kernel debugger's handler, if any
218 if (pHandler != 0) {
219 ExecutionContextGuard debuggerContext(ExecutionContext::DebuggerTrap);
220 pHandler->interrupt(nIntNumber, interruptState);
221 }
222 }
223#endif
224
225 InterruptHandler* pHandler =
226 __atomic_load_n(&m_Instance.m_pHandler[nIntNumber], __ATOMIC_ACQUIRE);
227
228 // Call the normal interrupt handler, if any
229 if (LIKELY(pHandler != 0)) {
230 pHandler->interrupt(nIntNumber, interruptState);
231 return;
232 }
233
234 if (UNLIKELY(nIntNumber == SIGINT || nIntNumber == SIGTERM)) {
235 // Shut down (uncleanly for now).
238 panic("shutdown failed");
239 }
240
241#if THREADS
242 Thread* pThread = Processor::information().getCurrentThread();
243 Process* pProcess = pThread ? pThread->getParent() : nullptr;
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));
248 pThread->deferSubsystemException(static_cast<size_t>(Subsystem::InvalidOpcode),
249 interruptState.getInstructionPointer(),
250 info ? static_cast<uintptr_t>(info->si_code) : 0);
251 return;
252 }
253 if (UNLIKELY(nIntNumber == SIGFPE)) {
254 siginfo_t* info = reinterpret_cast<siginfo_t*>(interruptState.getRegister(1));
255 pThread->deferSubsystemException(static_cast<size_t>(Subsystem::FpuError),
256 interruptState.getInstructionPointer(),
257 info ? static_cast<uintptr_t>(info->si_code) : 0);
258 return;
259 }
260 }
261#endif
262
263#if HAS_ADDRESS_SANITIZER
264 // If we're running with sanitizers, just raise the signal to them.
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);
268
269 // Escalate to the original signal handler - this is a real error, and in
270 // asan we get asan-based analysis in the asan segv handler.
271 struct sigaction oact = static_cast<HostedInterruptManager&>(InterruptManager::instance())
272 .getOriginalSigaction(info->si_signo);
273 if (oact.sa_handler == SIG_IGN) {
274 return;
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);
280 } else {
281 oact.sa_handler(info->si_signo);
282 }
283
284 return;
285#endif
286
287 // Were we running in the kernel, or user space?
288 // User space processes have a subsystem, kernel ones do not.
289 // unhandled interrupt, check for an exception
290 if (LIKELY(nIntNumber != SIGTRAP)) {
291 // TODO:: Check for debugger initialisation.
292 // TODO: register dump, maybe a breakpoint so the deubbger can take
293 // over?
294 // TODO: Rework this
295 // for now just print out the exception name and number
296 static LargeStaticString e;
297 e.clear();
298 e.append("Signal #0x");
299 e.append(nIntNumber, 16);
300#if DEBUGGER
301 Debugger::instance().start(interruptState, e);
302#else
303 panic(e);
304#endif
305 }
306}
307
308//
309// Functions only usable in the kernel initialisation phase
310//
311
312extern "C" void hostedSignalTrampoline(int which, siginfo_t* info, void* ptr);
313
314extern "C" void hostedSignalHandler(int which, siginfo_t* info, void* ptr, bool fromUserspace) {
315 HostedInterruptManager::instance().signalShim(which, info, ptr, fromUserspace);
316}
317
318#if defined(__APPLE__)
319extern "C" void hostedSignalTrampoline(int which, siginfo_t* info, void* ptr) {
320 // This host target has no emulated userspace/TLS transition. Signals
321 // therefore always interrupt the kernel-side hosted context.
322 hostedSignalHandler(which, info, ptr, false);
323}
324#endif
325
326void HostedInterruptManager::signalShim(int which, void* siginfo, void* meta, bool fromUserspace) {
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");
331 return;
332 }
333
334 Processor::enterHostedSignalFrame();
335
336#if THREADS
337 Thread* pSignalThread = Processor::information().getCurrentThread();
338 HostedSignalFrameCleanup frameCleanup(pSignalThread);
339 if (pSignalThread) {
340 frameCleanup.stateLevel = pSignalThread->enterHostedSignalHandler();
341 if (fromUserspace && frameCleanup.stateLevel) {
342 FATAL_NOLOCK("Hosted userspace resumed with nested kernel state");
343 }
344 pSignalThread->armAtomicStateCleanup(frameCleanup.cleanup, abandonHostedSignalFrame,
345 &frameCleanup);
346 }
347#endif
348
349 InterruptState state;
350 ucontext_t* interruptedContext = reinterpret_cast<ucontext_t*>(meta);
351 {
352 ExecutionContextGuard signalContext(which == SIGTRAP ? ExecutionContext::DebuggerTrap
353 : ExecutionContext::HostedSyntheticIrq);
354
355 const bool previousInterruptState = Processor::getInterrupts();
356 if (!previousInterruptState) {
357 if (hostedIrq) {
358 FATAL_NOLOCK("interrupts disabled but interrupts are firing");
359 }
360 }
361
362 if (hostedIrq) {
363#if THREADS
364 // Hosted signals already mask IRQ delivery physically. Publish the
365 // matching logical IF boundary as well, and make its restoration
366 // survive a scheduler callback which abandons this signal stack.
367 frameCleanup.previousInterruptState = previousInterruptState;
368 frameCleanup.restoreInterruptState = true;
369#endif
371 }
372
373 siginfo_t* info = reinterpret_cast<siginfo_t*>(siginfo);
374
375 state.which = which;
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));
383#if THREADS
384 {
385 // Match native interrupt accounting. User-origin frames remain in
386 // Kernel accounting mode until the return tail has completed.
387 InterruptTimeAccounting accounting(fromUserspace);
388 interrupt(state);
389 }
390#else
391 interrupt(state);
392#endif
393
394 // Raw IRQ dispatch is complete. Restore logical IF while hosted-signal
395 // depth still keeps IRQ signals physically masked, then let the ordinary
396 // return-to-user tail run at its IRQ-enabled thread boundary.
397#if THREADS
398 restoreHostedSignalInterruptState(frameCleanup);
399#else
400 if (hostedIrq) {
401 Processor::setInterrupts(previousInterruptState);
402 }
403#endif
404 }
405
406#if THREADS
407 if (fromUserspace && pSignalThread &&
408 Processor::information().getCurrentThread() == pSignalThread) {
409 terminalUserReturn = runHostedUserReturnTail(pSignalThread, state);
410 }
411#endif
412
413 // sigreturn restores this mask atomically with the interrupted context.
414 ucontext_t* ctx = interruptedContext;
415 pthread_sigmask(0, nullptr, &ctx->uc_sigmask);
416 setHostedIrqSignals(ctx->uc_sigmask, !Processor::getInterrupts());
417
418#if THREADS
419 Processor::maskInterruptsForSignalReturn();
420 if (pSignalThread) {
421 pSignalThread->disarmAtomicStateCleanup(frameCleanup.cleanup);
422 pSignalThread->leaveHostedSignalHandler(frameCleanup.stateLevel);
423 }
424 Processor::leaveHostedSignalFrame();
425 if (fromUserspace && pSignalThread) {
427 }
428#else
429 Processor::leaveHostedSignalFrame();
430#endif
431
432#if THREADS
433 if (terminalUserReturn && pSignalThread &&
434 Processor::information().getCurrentThread() == pSignalThread) {
435 Processor::information().getScheduler().commitUserReturnTerminalState();
436 FATAL_NOLOCK("Hosted terminal user-return commit unexpectedly returned");
437 }
438#endif
439}
440
441struct sigaction HostedInterruptManager::getOriginalSigaction(int which) const {
442 return m_OriginalActions[which];
443}
444
446 m_bQuiesced = false;
447 ByteSet(m_ActionInstalled, 0, sizeof(m_ActionInstalled));
448
449 // Set up our handler for every signal we want to trap.
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);
455 // A synchronous exception may publish deferred return work, but an
456 // IRQ must not suspend that raw publication half-complete.
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;
461#endif
462 if (!isHostedIrqSignal(i)) {
463 // Keep synchronous signals re-entrant. IRQ signals remain masked
464 // until raw fault publication reaches the return tail.
465 act.sa_flags |= SA_NODEFER;
466 }
467
468 if (sigaction(i, &act, &oact) == 0) {
469 m_OriginalActions[i] = oact;
470 m_ActionInstalled[i] = true;
471 }
472 }
473}
474
476 if (m_bQuiesced) {
477 return;
478 }
479
480 sigset_t irqSignals;
481 sigemptyset(&irqSignals);
482 setHostedIrqSignals(irqSignals, true);
483 pthread_sigmask(SIG_BLOCK, &irqSignals, nullptr);
484
485 // The timers have already been deleted, so no new IRQ signals can be
486 // queued. Drain signals that were pending while interrupts were masked.
487 while (true) {
488 sigset_t pending;
489 if (sigpending(&pending) != 0) {
490 break;
491 }
492 const int irqSignalCandidates[] = {SIGUSR1, SIGUSR2
493#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
494 ,
495 SIGURG
496#endif
497 };
498 int pendingIrq = 0;
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) {
502 pendingIrq = signal;
503 break;
504 }
505 }
506 if (!pendingIrq) {
507 break;
508 }
509
510 int consumed = 0;
511 if (sigwait(&irqSignals, &consumed) != 0) {
512 break;
513 }
514 }
515
516 for (size_t i = 1; i < MAX_SIGNAL; ++i) {
517 if (m_ActionInstalled[i]) {
518 sigaction(i, &m_OriginalActions[i], nullptr);
519 m_ActionInstalled[i] = false;
520 }
521 }
522 m_bQuiesced = true;
523}
524
525#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
526void HostedInterruptManager::withMutationLockForTest(MutationLockHook hook) {
528 if (hook) {
529 hook();
530 }
531}
532#endif
533
535 // Initialise the pointers to the pHandler
536 for (size_t i = 0; i < MAX_SIGNAL; i++) {
537 m_pHandler[i] = 0;
538#if DEBUGGER
539 m_pDbgHandler[i] = 0;
540#endif
541 }
542}
543
void start(InterruptState &state, LargeStaticString &description)
Definition Debugger.cc:118
static Debugger & instance()
Definition Debugger.h:47
static void initialiseProcessor() INITIALISATION_ONLY
static void interrupt(InterruptState &interruptState)
void signalShim(int which, void *siginfo, void *meta, bool fromUserspace)
virtual size_t getDebugInterruptNumber() PURE
virtual bool registerInterruptHandlerDebugger(size_t nInterruptNumber, InterruptHandler *pHandler)
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)
virtual size_t getBreakpointInterruptNumber() PURE
struct sigaction getOriginalSigaction(int which) const
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 void reset()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
bool deferSubsystemException(size_t type, uintptr_t faultAddress, uintptr_t errorCode)
Definition Thread.cc:3627
void * getKernelStack()
Definition Thread.cc:1070
Process * getParent() const
Definition Thread.h:340
void popState(bool clean=true)
Definition Thread.cc:913
SchedulerState * pushState()
Definition Thread.cc:841
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118