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