The Pedigree Project 0.1
x64/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/ActivityDiagnostics.h"
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/Metrics.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/processor/InterruptHandler.h"
27#include "pedigree/kernel/processor/Processor.h"
28#include "pedigree/kernel/processor/ProcessorInformation.h"
29#include "pedigree/kernel/processor/state.h"
30#include "pedigree/kernel/utilities/StaticString.h"
31
32#if DEBUGGER
33#include "pedigree/kernel/debugger/Debugger.h"
34#endif
35
36#if THREADS
37#include "pedigree/kernel/Subsystem.h"
38#include "pedigree/kernel/process/InterruptTimeAccounting.h"
39#include "pedigree/kernel/process/PerProcessorScheduler.h"
40#include "pedigree/kernel/process/Process.h"
41#endif
42
43static_assert(sizeof(X64InterruptState) == 208, "IST frame copy must match the saved frame");
44
45#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
46struct alignas(64) NmiEntryDiagnostic {
47 uint64_t count;
48 uint64_t rip;
49 uint64_t cs;
50 uint64_t rsp;
51 uint64_t information;
52 uint64_t index;
53 uint64_t activeGs;
54 uint64_t frame;
55};
56static_assert(sizeof(NmiEntryDiagnostic) == 64);
57extern "C" {
58SYMBOL_HIDDEN volatile NmiEntryDiagnostic pedigree_nmi_entry_diagnostics[256] = {};
59}
60#endif
61
62static const char* g_ExceptionNames[] = {"Divide Error",
63 "Debug",
64 "NMI Interrupt",
65 "Breakpoint",
66 "Overflow",
67 "BOUND Range Exceeded",
68 "Invalid Opcode",
69 "Device Not Available",
70 "Double Fault",
71 "Coprocessor Segment Overrun", /* recent IA-32 processors
72 don't generate this */
73 "Invalid TSS",
74 "Segment Not Present",
75 "Stack Fault",
76 "General Protection Fault",
77 "Page Fault",
78 "FPU Floating-Point Error",
79 "Alignment Check",
80 "Machine-Check",
81 "SIMD Floating-Point Exception",
82 "Reserved: Interrupt 19",
83 "Reserved: Interrupt 20",
84 "Reserved: Interrupt 21",
85 "Reserved: Interrupt 22",
86 "Reserved: Interrupt 23",
87 "Reserved: Interrupt 24",
88 "Reserved: Interrupt 25",
89 "Reserved: Interrupt 26",
90 "Reserved: Interrupt 27",
91 "Reserved: Interrupt 28",
92 "Reserved: Interrupt 29",
93 "Reserved: Interrupt 30",
94 "Reserved: Interrupt 31"};
95
97
100}
101
103 InterruptHandler* pHandler) {
104 // Lock the class until the end of the function
106
107 // Sanity checks
108 if (UNLIKELY(nInterruptNumber >= 256))
109 return false;
110 InterruptHandler* current = __atomic_load_n(&m_pHandler[nInterruptNumber], __ATOMIC_ACQUIRE);
111 if (UNLIKELY(pHandler != 0 && current != 0))
112 return false;
113 if (UNLIKELY(pHandler == 0 && current == 0))
114 return false;
115
116 // Exceptions and IRQs can re-enter on this CPU while this lock is held.
117 // Publish the complete old or new pointer without involving that lock.
118 return __atomic_compare_exchange_n(&m_pHandler[nInterruptNumber], &current, pHandler, false,
119 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
120}
121
122#if DEBUGGER
123
125 InterruptHandler* pHandler) {
126 // Lock the class until the end of the function
128
129 // Sanity checks
130 if (UNLIKELY(nInterruptNumber >= 256))
131 return false;
132 InterruptHandler* current = __atomic_load_n(&m_pDbgHandler[nInterruptNumber], __ATOMIC_ACQUIRE);
133 if (UNLIKELY(pHandler != 0 && current != 0))
134 return false;
135 if (UNLIKELY(pHandler == 0 && current == 0))
136 return false;
137
138 return __atomic_compare_exchange_n(&m_pDbgHandler[nInterruptNumber], &current, pHandler, false,
139 __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
140}
147
148#endif
149
150void X64InterruptManager::interrupt(InterruptState& interruptState) {
151#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
152 if (interruptState.getInterruptNumber() == 2) {
153 uintptr_t information;
154 size_t index;
155 asm volatile("movq %%gs:16, %0; movq %%gs:24, %1"
156 : "=r"(information), "=r"(index)
157 :
158 : "memory");
159 uint32_t low, high;
160 asm volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(0xc0000101));
161 if (index < 256) {
162 auto& record = pedigree_nmi_entry_diagnostics[index];
163 record.rip = interruptState.m_Rip;
164 record.cs = interruptState.m_Cs;
165 record.rsp = interruptState.m_Rsp;
166 record.information = information;
167 record.index = index;
168 record.activeGs = (static_cast<uint64_t>(high) << 32) | low;
169 record.frame = reinterpret_cast<uintptr_t>(&interruptState);
170 __atomic_store_n(&record.count, record.count + 1, __ATOMIC_RELEASE);
171 }
172 return;
173 }
174#endif
175 InterruptTimeAccounting accounting(!interruptState.kernelMode());
176 size_t nIntNumber = interruptState.getInterruptNumber();
177 Metrics::increment(nIntNumber < 32 ? Metrics::Counter::Exception : Metrics::Counter::Interrupt);
178 ActivityDiagnostics::InterruptScope activityScope(nIntNumber);
179
180#if DEBUGGER
181 {
182 InterruptHandler* pHandler =
183 __atomic_load_n(&m_Instance.m_pDbgHandler[nIntNumber], __ATOMIC_ACQUIRE);
184
185 // Call the kernel debugger's handler, if any
186 if (pHandler != 0) {
187 ExecutionContextGuard debuggerContext(ExecutionContext::DebuggerTrap);
188 pHandler->interrupt(nIntNumber, interruptState);
189 }
190 }
191#endif
192
193 InterruptHandler* pHandler =
194 __atomic_load_n(&m_Instance.m_pHandler[nIntNumber], __ATOMIC_ACQUIRE);
195
196 // Call the normal interrupt handler, if any
197 if (LIKELY(pHandler != 0)) {
198 pHandler->interrupt(nIntNumber, interruptState);
199 return;
200 }
201
202// Were we running in the kernel, or user space?
203// User space processes have a subsystem, kernel ones do not.
204#if THREADS
205 Thread* pThread = Processor::information().getCurrentThread();
206 if (pThread) {
207 Process* pProcess = pThread->getParent();
208 if (pProcess) {
209 Subsystem* pSubsystem = pProcess->getSubsystem();
210 if (pSubsystem && !interruptState.kernelMode()) {
211 size_t exceptionType = static_cast<size_t>(Subsystem::Other);
212 if (UNLIKELY(nIntNumber == 0)) {
213 exceptionType = static_cast<size_t>(Subsystem::DivideByZero);
214 } else if (UNLIKELY(nIntNumber == 6)) {
215 exceptionType = static_cast<size_t>(Subsystem::InvalidOpcode);
216 } else if (UNLIKELY(nIntNumber == 13)) {
217 exceptionType = static_cast<size_t>(Subsystem::GeneralProtectionFault);
218 } else if (UNLIKELY(nIntNumber == 16)) {
219 exceptionType = static_cast<size_t>(Subsystem::FpuError);
220 } else if (UNLIKELY(nIntNumber == 19)) {
221 exceptionType = static_cast<size_t>(Subsystem::SpecialFpuError);
222 }
223
224 if (exceptionType != static_cast<size_t>(Subsystem::Other)) {
225 pThread->deferSubsystemException(exceptionType, 0, interruptState.getErrorCode());
226 return;
227 }
228 }
229 }
230 }
231#endif
232
233 // unhandled interrupt, check for an exception (interrupts 0-31 inclusive
234 // are reserved, not for use by system programmers)
235 if (LIKELY(nIntNumber < 32 && nIntNumber != 1 && nIntNumber != 3)) {
236 // TODO:: Check for debugger initialisation.
237 // TODO: register dump, maybe a breakpoint so the deubbger can take
238 // over?
239 // TODO: Rework this
240 // for now just print out the exception name and number
241 static LargeStaticString e;
242 e.clear();
243 e.append("Exception #0x");
244 e.append(nIntNumber, 16);
245 e.append(": \"");
246 e.append(g_ExceptionNames[nIntNumber]);
247 e.append("\"");
248
249#if THREADS
250 e.append(" CPU=");
251 e.append(Processor::id());
252 if (pThread) {
253 Process* pParent = pThread->getParent();
254 if (pParent) {
255 e.append(" PID=");
256 e.append(pParent->getId());
257 }
258 e.append(" TID=");
259 e.append(pThread->getId());
260 }
261#endif
262
263 if (nIntNumber == 14) {
264 uint64_t cr2;
265 asm volatile("mov %%cr2, %%rax" : "=a"(cr2));
266 e.append(" at 0x");
267 e.append(cr2, 16, 16, '0');
268 e.append(", errorcode 0x");
269 e.append(interruptState.m_Errorcode, 16, 8, '0');
270 }
271
272 if (nIntNumber == 13) {
273 // GPF
274 if (interruptState.m_Errorcode) {
275 e.append(" errorcode 0x");
276 e.append(interruptState.m_Errorcode, 16, 8, '0');
277 }
278 e.append(" RIP 0x");
279 e.append(interruptState.getInstructionPointer(), 16, 16, '0');
280 }
281
282 if (nIntNumber == 8) {
283 // On amd64, we actually have a functional InterruptState.
284 ERROR_NOLOCK("(double fault, system is very unhappy)");
285
286 uint64_t cr2;
287 asm volatile("mov %%cr2, %%rax" : "=a"(cr2));
288 NOTICE_NOLOCK(" -> #DF possibly caused by #PF at " << Hex << cr2 << ".");
289 }
290
291 // Write the failure into the kernel log before launching the debugger.
292 ERROR(static_cast<const char*>(e));
293
294#if DEBUGGER
295 Debugger::instance().start(interruptState, e);
296#else
297 panic(e);
298#endif
299 }
300}
301
302void X64InterruptManager::returnFromInterrupt(InterruptState& interruptState) {
303 const size_t vector = interruptState.getInterruptNumber();
304#if PEDIGREE_X64_USER_ENTRY_DIAGNOSTICS
305 if (vector == 2)
306 return;
307#endif
308#if THREADS
309 if (vector >= 32 && Processor::information().getCurrentThread()) {
310 Processor::information().getScheduler().servicePendingScheduling();
311 }
312#endif
313 if (interruptState.kernelMode()) {
314 return;
315 }
316
317 Thread* thread = Processor::information().getCurrentThread();
318 if (!thread) {
319 return;
320 }
321
322 if (vector == 2 || vector == 8 || vector == 18) {
323 // NMI blocking remains active until IRET, and #DF uses an IST stack.
324 // #MC likewise is not a boundary on which arbitrary thread work can
325 // safely suspend the architectural exception return.
328 return;
329 }
330
331 PerProcessorScheduler& scheduler = Processor::information().getScheduler();
332 const bool diagnosticSample = scheduler.sampleUserReturnDiagnostics();
333 const uint64_t tailStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
334
335 // interrupt() has returned, so InterruptTimeAccounting and every raw
336 // handler scope are complete. Finish the architecture accounting tail
337 // before a terminal transition consumes this root stack.
339 bool terminal = false;
340 while (true) {
341 terminal = scheduler.serviceUserReturnWork(interruptState, UserReturnFrame::Origin::Interrupt,
342 diagnosticSample);
343 if (terminal)
344 break;
345 bool waited = false;
346 const uint64_t affinityStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
347 terminal = thread->completeAffinityAtSafePoint(&waited) == AffinityResult::Terminal;
348 if (diagnosticSample) {
349 ActivityDiagnostics::recordUserReturnStage(
350 ActivityDiagnostics::UserReturnStage::InterruptAffinity,
351 ActivityDiagnostics::timestamp() - affinityStart);
352 if (waited)
353 ActivityDiagnostics::recordUserReturnAffinityWait(false);
354 }
355 if (terminal || !waited)
356 break;
358 }
360 const uint64_t accountingStart = diagnosticSample ? ActivityDiagnostics::timestamp() : 0;
362 if (diagnosticSample) {
363 ActivityDiagnostics::recordUserReturnStage(
364 ActivityDiagnostics::UserReturnStage::InterruptAccounting,
365 ActivityDiagnostics::timestamp() - accountingStart);
366 ActivityDiagnostics::recordUserReturnStage(ActivityDiagnostics::UserReturnStage::InterruptTail,
367 ActivityDiagnostics::timestamp() - tailStart);
368 }
369 if (terminal) {
371 Processor::information().getScheduler().commitUserReturnTerminalState();
372 FATAL_NOLOCK("Terminal user-return commit unexpectedly returned");
373 }
374}
375
376//
377// Functions only usable in the kernel initialisation phase
378//
379
381 // BSP/AP bootstrap precedes the permanent GDT and LTR.
382 struct {
383 uint16_t size;
384 uint64_t idt;
385 } PACKED idtr = {4095, reinterpret_cast<uintptr_t>(&m_Instance.m_BootstrapIDT)};
386
387 asm volatile("lidt %0" ::"m"(idtr) : "memory");
388}
389
391 struct {
392 uint16_t size;
393 uint64_t idt;
394 } PACKED idtr = {4095, reinterpret_cast<uintptr_t>(&m_Instance.m_IDT)};
395
396 asm volatile("lidt %0" ::"m"(idtr) : "memory");
397}
398
399void X64InterruptManager::setInterruptGate(size_t nInterruptNumber, uintptr_t interruptHandler) {
400 m_IDT[nInterruptNumber].offset0 = interruptHandler & 0xFFFF;
401 m_IDT[nInterruptNumber].selector = 0x08;
402 m_IDT[nInterruptNumber].ist = 0;
403 m_IDT[nInterruptNumber].flags = 0xEE /*0x8E*/;
404 m_IDT[nInterruptNumber].offset1 = (interruptHandler >> 16) & 0xFFFF;
405 m_IDT[nInterruptNumber].offset2 = (interruptHandler >> 32) & 0xFFFFFFFF;
406 m_IDT[nInterruptNumber].res = 0;
407}
408
409void X64InterruptManager::setIst(size_t nInterruptNumber, size_t ist) {
410 if (ist > 7)
411 return;
412 m_IDT[nInterruptNumber].ist = ist;
413}
414
416 // Initialise the pointers to the pHandler
417 for (size_t i = 0; i < 256; i++) {
418 m_pHandler[i] = 0;
419#if DEBUGGER
420 m_pDbgHandler[i] = 0;
421#endif
422 }
423
424 // Initialise the IDT
425 extern uintptr_t interrupt_handler_array[];
426 for (size_t i = 0; i < 256; i++)
427 setInterruptGate(i, interrupt_handler_array[i]);
428
429 for (size_t i = 0; i < 256; i++)
430 m_BootstrapIDT[i] = m_IDT[i];
431
432 // Separate stacks keep asynchronous entry and faults in user-return windows
433 // away from an interrupted user RSP. CPL3 #DB/#SS/#GP frames move to rsp0.
434 setIst(8, 1);
435 setIst(2, 2);
436 setIst(1, 3);
437 setIst(18, 4);
438 setIst(13, 5);
439 setIst(12, 6);
440}
void start(InterruptState &state, LargeStaticString &description)
Definition Debugger.cc:118
static Debugger & instance()
Definition Debugger.h:47
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)
MUST_USE_RESULT bool serviceUserReturnWork(InterruptState &state, UserReturnFrame::Origin origin=UserReturnFrame::Origin::Interrupt, bool diagnosticSample=false)
size_t getId()
Definition Process.h:462
static ProcessorId id()
static ProcessorInformation & information()
static void setInterrupts(bool bEnable)
bool deferSubsystemException(size_t type, uintptr_t faultAddress, uintptr_t errorCode)
Definition Thread.cc:3632
Process * getParent() const
Definition Thread.h:325
size_t getId()
Definition Thread.h:450
AffinityResult completeAffinityAtSafePoint(bool *waited=nullptr)
static void initialiseProcessorIst() INITIALISATION_ONLY
virtual size_t getBreakpointInterruptNumber() PURE
static void interrupt(InterruptState &interruptState) USED
void setIst(size_t nInterruptNumber, size_t ist)
static void initialiseProcessor() INITIALISATION_ONLY
virtual bool registerInterruptHandlerDebugger(size_t nInterruptNumber, InterruptHandler *pHandler)
void setInterruptGate(size_t nInterruptNumber, uintptr_t interruptHandler) INITIALISATION_ONLY
virtual size_t getDebugInterruptNumber() PURE
X64InterruptManager() INITIALISATION_ONLY
static void returnFromInterrupt(InterruptState &interruptState) USED
virtual bool registerInterruptHandler(size_t nInterruptNumber, InterruptHandler *pHandler)
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:117
@ Hex
Definition Log.h:124