xref: /reactos/ntoskrnl/ke/amd64/except.c (revision 81f8bcea)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/amd64/except.c
5  * PURPOSE:         Exception Dispatching for amd64
6  * PROGRAMMER:      Timo Kreuzer (timo.kreuzer@reactos.org)
7  *                  Alex Ionescu (alex.ionescu@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange[256];
17 
18 /* GLOBALS *******************************************************************/
19 
20 KIDT_INIT KiInterruptInitTable[] =
21 {
22   /* Id,   Dpl,  IST,  ServiceRoutine */
23     {0x00, 0x00, 0x00, KiDivideErrorFault},
24     {0x01, 0x00, 0x00, KiDebugTrapOrFault},
25     {0x02, 0x00, 0x03, KiNmiInterrupt},
26     {0x03, 0x03, 0x00, KiBreakpointTrap},
27     {0x04, 0x03, 0x00, KiOverflowTrap},
28     {0x05, 0x00, 0x00, KiBoundFault},
29     {0x06, 0x00, 0x00, KiInvalidOpcodeFault},
30     {0x07, 0x00, 0x00, KiNpxNotAvailableFault},
31     {0x08, 0x00, 0x01, KiDoubleFaultAbort},
32     {0x09, 0x00, 0x00, KiNpxSegmentOverrunAbort},
33     {0x0A, 0x00, 0x00, KiInvalidTssFault},
34     {0x0B, 0x00, 0x00, KiSegmentNotPresentFault},
35     {0x0C, 0x00, 0x00, KiStackFault},
36     {0x0D, 0x00, 0x00, KiGeneralProtectionFault},
37     {0x0E, 0x00, 0x00, KiPageFault},
38     {0x10, 0x00, 0x00, KiFloatingErrorFault},
39     {0x11, 0x00, 0x00, KiAlignmentFault},
40     {0x12, 0x00, 0x02, KiMcheckAbort},
41     {0x13, 0x00, 0x00, KiXmmException},
42     {0x1F, 0x00, 0x00, KiApcInterrupt},
43     {0x2C, 0x03, 0x00, KiRaiseAssertion},
44     {0x2D, 0x03, 0x00, KiDebugServiceTrap},
45     {0x2F, 0x00, 0x00, KiDpcInterrupt},
46     {0xE1, 0x00, 0x00, KiIpiInterrupt},
47     {0, 0, 0, 0}
48 };
49 
50 KIDTENTRY64 KiIdt[256];
51 KDESCRIPTOR KiIdtDescriptor = {{0}, sizeof(KiIdt) - 1, KiIdt};
52 
53 
54 /* FUNCTIONS *****************************************************************/
55 
56 CODE_SEG("INIT")
57 VOID
58 NTAPI
59 KeInitExceptions(VOID)
60 {
61     int i, j;
62 
63     /* Initialize the Idt */
64     for (j = i = 0; i < 256; i++)
65     {
66         ULONG64 Offset;
67 
68         if (KiInterruptInitTable[j].InterruptId == i)
69         {
70             Offset = (ULONG64)KiInterruptInitTable[j].ServiceRoutine;
71             KiIdt[i].Dpl = KiInterruptInitTable[j].Dpl;
72             KiIdt[i].IstIndex = KiInterruptInitTable[j].IstIndex;
73             j++;
74         }
75         else
76         {
77             Offset = (ULONG64)&KiUnexpectedRange[i]._Op_push;
78             KiIdt[i].Dpl = 0;
79             KiIdt[i].IstIndex = 0;
80         }
81         KiIdt[i].OffsetLow = Offset & 0xffff;
82         KiIdt[i].Selector = KGDT64_R0_CODE;
83         KiIdt[i].Type = 0x0e;
84         KiIdt[i].Reserved0 = 0;
85         KiIdt[i].Present = 1;
86         KiIdt[i].OffsetMiddle = (Offset >> 16) & 0xffff;
87         KiIdt[i].OffsetHigh = (Offset >> 32);
88         KiIdt[i].Reserved1 = 0;
89     }
90 
91     KeGetPcr()->IdtBase = KiIdt;
92     __lidt(&KiIdtDescriptor.Limit);
93 }
94 
95 static
96 VOID
97 KiDispatchExceptionToUser(
98     IN PKTRAP_FRAME TrapFrame,
99     IN PCONTEXT Context,
100     IN PEXCEPTION_RECORD ExceptionRecord)
101 {
102     EXCEPTION_RECORD LocalExceptRecord;
103     ULONG64 UserRsp;
104     PKUSER_EXCEPTION_STACK UserStack;
105 
106     /* Make sure we have a valid SS */
107     if (TrapFrame->SegSs != (KGDT64_R3_DATA | RPL_MASK))
108     {
109         /* Raise an access violation instead */
110         LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
111         LocalExceptRecord.ExceptionFlags = 0;
112         LocalExceptRecord.NumberParameters = 0;
113         ExceptionRecord = &LocalExceptRecord;
114     }
115 
116     /* Get new stack pointer and align it to 16 bytes */
117     UserRsp = (Context->Rsp - sizeof(KUSER_EXCEPTION_STACK)) & ~15;
118 
119     /* Get pointer to the usermode context, exception record and machine frame */
120     UserStack = (PKUSER_EXCEPTION_STACK)UserRsp;
121 
122     /* Set up the user-stack */
123     _SEH2_TRY
124     {
125         /* Probe the user stack frame and zero it out */
126         ProbeForWrite(UserStack, sizeof(*UserStack), TYPE_ALIGNMENT(KUSER_EXCEPTION_STACK));
127         RtlZeroMemory(UserStack, sizeof(*UserStack));
128 
129         /* Copy Context and ExceptionFrame */
130         UserStack->Context = *Context;
131         UserStack->ExceptionRecord = *ExceptionRecord;
132 
133         /* Setup the machine frame */
134         UserStack->MachineFrame.Rip = Context->Rip;
135         UserStack->MachineFrame.SegCs = Context->SegCs;
136         UserStack->MachineFrame.EFlags = Context->EFlags;
137         UserStack->MachineFrame.Rsp = Context->Rsp;
138         UserStack->MachineFrame.SegSs = Context->SegSs;
139     }
140     _SEH2_EXCEPT((LocalExceptRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord),
141                  EXCEPTION_EXECUTE_HANDLER)
142     {
143         // FIXME: handle stack overflow
144 
145         /* Nothing we can do here */
146         _SEH2_YIELD(return);
147     }
148     _SEH2_END;
149 
150     /* Now set the two params for the user-mode dispatcher */
151     TrapFrame->Rcx = (ULONG64)&UserStack->ExceptionRecord;
152     TrapFrame->Rdx = (ULONG64)&UserStack->Context;
153 
154     /* Set new Stack Pointer */
155     TrapFrame->Rsp = UserRsp;
156 
157     /* Force correct segments */
158     TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
159     TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK;
160     TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK;
161     TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
162     TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
163     TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
164 
165     /* Set RIP to the User-mode Dispatcher */
166     TrapFrame->Rip = (ULONG64)KeUserExceptionDispatcher;
167 
168     /* Exit to usermode */
169     KiServiceExit2(TrapFrame);
170 }
171 
172 static
173 VOID
174 KiPageInDirectory(PVOID ImageBase, USHORT Directory)
175 {
176     volatile CHAR *Pointer;
177     ULONG Size;
178 
179    /* Get a pointer to the debug directory */
180     Pointer = RtlImageDirectoryEntryToData(ImageBase, 1, Directory, &Size);
181     if (!Pointer) return;
182 
183     /* Loop all pages */
184     while ((LONG)Size > 0)
185     {
186         /* Touch it, to page it in */
187         (void)*Pointer;
188         Pointer += PAGE_SIZE;
189         Size -= PAGE_SIZE;
190     }
191 }
192 
193 VOID
194 KiPrepareUserDebugData(void)
195 {
196     PLDR_DATA_TABLE_ENTRY LdrEntry;
197     PPEB_LDR_DATA PebLdr;
198     PLIST_ENTRY ListEntry;
199     PTEB Teb;
200 
201     /* Get the Teb for this process */
202     Teb = KeGetCurrentThread()->Teb;
203     if (!Teb) return;
204 
205     _SEH2_TRY
206     {
207         /* Get a pointer to the loader data */
208         PebLdr = Teb->ProcessEnvironmentBlock->Ldr;
209         if (!PebLdr) _SEH2_YIELD(return);
210 
211         /* Now loop all entries in the module list */
212         for (ListEntry = PebLdr->InLoadOrderModuleList.Flink;
213              ListEntry != &PebLdr->InLoadOrderModuleList;
214              ListEntry = ListEntry->Flink)
215         {
216             /* Get the loader entry */
217             LdrEntry = CONTAINING_RECORD(ListEntry,
218                                          LDR_DATA_TABLE_ENTRY,
219                                          InLoadOrderLinks);
220 
221             KiPageInDirectory((PVOID)LdrEntry->DllBase,
222                               IMAGE_DIRECTORY_ENTRY_DEBUG);
223 
224             KiPageInDirectory((PVOID)LdrEntry->DllBase,
225                               IMAGE_DIRECTORY_ENTRY_EXCEPTION);
226         }
227 
228     }
229     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
230     {
231     }
232     _SEH2_END;
233 }
234 
235 VOID
236 NTAPI
237 KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
238                     IN PKEXCEPTION_FRAME ExceptionFrame,
239                     IN PKTRAP_FRAME TrapFrame,
240                     IN KPROCESSOR_MODE PreviousMode,
241                     IN BOOLEAN FirstChance)
242 {
243     CONTEXT Context;
244 
245     /* Increase number of Exception Dispatches */
246     KeGetCurrentPrcb()->KeExceptionDispatchCount++;
247 
248     /* Zero out the context to avoid leaking kernel stack memor to user mode */
249     RtlZeroMemory(&Context, sizeof(Context));
250 
251     /* Set the context flags */
252     Context.ContextFlags = CONTEXT_ALL;
253 
254     /* Get the Context from the trap and exception frame */
255     KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
256 
257     /* Look at our exception code */
258     switch (ExceptionRecord->ExceptionCode)
259     {
260         /* Breakpoint */
261         case STATUS_BREAKPOINT:
262 
263             /* Decrement RIP by one */
264             Context.Rip--;
265             break;
266 
267         /* Internal exception */
268         case KI_EXCEPTION_ACCESS_VIOLATION:
269 
270             /* Set correct code */
271             ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
272             if (PreviousMode == UserMode)
273             {
274                 /* FIXME: Handle no execute */
275             }
276             break;
277     }
278 
279     /* Handle kernel-mode first, it's simpler */
280     if (PreviousMode == KernelMode)
281     {
282         /* Check if this is a first-chance exception */
283         if (FirstChance)
284         {
285             /* Break into the debugger for the first time */
286             if (KiDebugRoutine(TrapFrame,
287                                ExceptionFrame,
288                                ExceptionRecord,
289                                &Context,
290                                PreviousMode,
291                                FALSE))
292             {
293                 /* Exception was handled */
294                 goto Handled;
295             }
296 
297             /* If the Debugger couldn't handle it, dispatch the exception */
298             if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled;
299         }
300 
301         /* This is a second-chance exception, only for the debugger */
302         if (KiDebugRoutine(TrapFrame,
303                            ExceptionFrame,
304                            ExceptionRecord,
305                            &Context,
306                            PreviousMode,
307                            TRUE))
308         {
309             /* Exception was handled */
310             goto Handled;
311         }
312 
313         /* Third strike; you're out */
314         KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
315                      ExceptionRecord->ExceptionCode,
316                      (ULONG_PTR)ExceptionRecord->ExceptionAddress,
317                      (ULONG_PTR)TrapFrame,
318                      0);
319     }
320     else
321     {
322         /* User mode exception, was it first-chance? */
323         if (FirstChance)
324         {
325             /*
326              * Break into the kernel debugger unless a user mode debugger
327              * is present or user mode exceptions are ignored, except if this
328              * is a debug service which we must always pass to KD
329              */
330             if ((!(PsGetCurrentProcess()->DebugPort) &&
331                  !(KdIgnoreUmExceptions)) ||
332                  (KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode)))
333             {
334                 /* Make sure the debugger can access debug directories */
335                 KiPrepareUserDebugData();
336 
337                 /* Call the kernel debugger */
338                 if (KiDebugRoutine(TrapFrame,
339                                    ExceptionFrame,
340                                    ExceptionRecord,
341                                    &Context,
342                                    PreviousMode,
343                                    FALSE))
344                 {
345                     /* Exception was handled */
346                     goto Handled;
347                 }
348             }
349 
350             /* Forward exception to user mode debugger */
351             if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return;
352 
353             /* Forward exception to user mode (does not return) */
354             KiDispatchExceptionToUser(TrapFrame, &Context, ExceptionRecord);
355             NT_ASSERT(FALSE);
356         }
357 
358         /* Try second chance */
359         if (DbgkForwardException(ExceptionRecord, TRUE, TRUE))
360         {
361             /* Handled, get out */
362             return;
363         }
364         else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE))
365         {
366             /* Handled, get out */
367             return;
368         }
369 
370         /* 3rd strike, kill the process */
371         DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n",
372                 PsGetCurrentProcess()->ImageFileName,
373                 ExceptionRecord->ExceptionCode,
374                 ExceptionRecord->ExceptionAddress,
375                 PsGetCurrentProcess()->SectionBaseAddress);
376 
377         ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
378         KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
379                      ExceptionRecord->ExceptionCode,
380                      (ULONG_PTR)ExceptionRecord->ExceptionAddress,
381                      (ULONG_PTR)TrapFrame,
382                      0);
383     }
384 
385 Handled:
386     /* Convert the context back into Trap/Exception Frames */
387     KeContextToTrapFrame(&Context,
388                          ExceptionFrame,
389                          TrapFrame,
390                          Context.ContextFlags,
391                          PreviousMode);
392     return;
393 }
394 
395 NTSTATUS
396 NTAPI
397 KeRaiseUserException(IN NTSTATUS ExceptionCode)
398 {
399     UNIMPLEMENTED;
400     return STATUS_UNSUCCESSFUL;
401 }
402 
403 
404 VOID
405 DECLSPEC_NORETURN
406 KiSystemFatalException(IN ULONG ExceptionCode,
407                        IN PKTRAP_FRAME TrapFrame)
408 {
409     /* Bugcheck the system */
410     KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP,
411                      ExceptionCode,
412                      0,
413                      0,
414                      0,
415                      TrapFrame);
416 }
417 
418 NTSTATUS
419 NTAPI
420 KiNpxNotAvailableFaultHandler(
421     IN PKTRAP_FRAME TrapFrame)
422 {
423     UNIMPLEMENTED;
424     KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
425     return -1;
426 }
427 
428 
429 NTSTATUS
430 NTAPI
431 KiGeneralProtectionFaultHandler(
432     IN PKTRAP_FRAME TrapFrame)
433 {
434     PUCHAR Instructions;
435 
436     /* Check for user-mode GPF */
437     if (TrapFrame->SegCs & 3)
438     {
439         UNIMPLEMENTED;
440         ASSERT(FALSE);
441     }
442 
443     /* Check for lazy segment load */
444     if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK))
445     {
446         /* Fix it */
447         TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK);
448         return STATUS_SUCCESS;
449     }
450     else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK))
451     {
452         /* Fix it */
453         TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK);
454         return STATUS_SUCCESS;
455     }
456 
457     /* Check for nested exception */
458     if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) &&
459         (TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler))
460     {
461         /* Not implemented */
462         UNIMPLEMENTED;
463         ASSERT(FALSE);
464     }
465 
466     /* Get Instruction Pointer */
467     Instructions = (PUCHAR)TrapFrame->Rip;
468 
469     /* Check for IRET */
470     if (Instructions[0] == 0x48 && Instructions[1] == 0xCF)
471     {
472         /* Not implemented */
473         UNIMPLEMENTED;
474         ASSERT(FALSE);
475     }
476 
477     /* Check for RDMSR/WRMSR */
478     if ((Instructions[0] == 0xF) &&            // 2-byte opcode
479         ((Instructions[1] == 0x30) ||        // RDMSR
480          (Instructions[1] == 0x32)))         // WRMSR
481     {
482         /* Unknown CPU MSR, so raise an access violation */
483         return STATUS_ACCESS_VIOLATION;
484     }
485 
486     ASSERT(FALSE);
487     return STATUS_UNSUCCESSFUL;
488 }
489 
490 NTSTATUS
491 NTAPI
492 KiXmmExceptionHandler(
493     IN PKTRAP_FRAME TrapFrame)
494 {
495     UNIMPLEMENTED;
496     KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
497     return -1;
498 }
499