xref: /reactos/ntoskrnl/ke/amd64/except.c (revision 50cf16b3)
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 VOID
57 INIT_FUNCTION
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     ULONG Size;
104     ULONG64 UserRsp;
105     PCONTEXT UserContext;
106     PEXCEPTION_RECORD UserExceptionRecord;
107 
108     /* Make sure we have a valid SS */
109     if (TrapFrame->SegSs != (KGDT64_R3_DATA | RPL_MASK))
110     {
111         /* Raise an access violation instead */
112         LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
113         LocalExceptRecord.ExceptionFlags = 0;
114         LocalExceptRecord.NumberParameters = 0;
115         ExceptionRecord = &LocalExceptRecord;
116     }
117 
118     /* Calculate the size of the exception record */
119     Size = FIELD_OFFSET(EXCEPTION_RECORD, ExceptionInformation) +
120            ExceptionRecord->NumberParameters * sizeof(ULONG64);
121 
122     /* Get new stack pointer and align it to 16 bytes */
123     UserRsp = (Context->Rsp - Size - sizeof(CONTEXT)) & ~15;
124 
125     /* Get pointers to the usermode context and exception record */
126     UserContext = (PVOID)UserRsp;
127     UserExceptionRecord = (PVOID)(UserRsp + sizeof(CONTEXT));
128 
129     /* Set up the user-stack */
130     _SEH2_TRY
131     {
132         /* Probe stack and copy Context */
133         ProbeForWrite(UserContext, sizeof(CONTEXT), sizeof(ULONG64));
134         *UserContext = *Context;
135 
136         /* Probe stack and copy exception record */
137         ProbeForWrite(UserExceptionRecord, Size, sizeof(ULONG64));
138         *UserExceptionRecord = *ExceptionRecord;
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)UserContext;
152     TrapFrame->Rdx = (ULONG64)UserExceptionRecord;
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     /* Set the context flags */
249     Context.ContextFlags = CONTEXT_ALL;
250 
251     /* Get a Context */
252     KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
253 
254     /* Look at our exception code */
255     switch (ExceptionRecord->ExceptionCode)
256     {
257         /* Breakpoint */
258         case STATUS_BREAKPOINT:
259 
260             /* Decrement RIP by one */
261             Context.Rip--;
262             break;
263 
264         /* Internal exception */
265         case KI_EXCEPTION_ACCESS_VIOLATION:
266 
267             /* Set correct code */
268             ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
269             if (PreviousMode == UserMode)
270             {
271                 /* FIXME: Handle no execute */
272             }
273             break;
274     }
275 
276     /* Handle kernel-mode first, it's simpler */
277     if (PreviousMode == KernelMode)
278     {
279         /* Check if this is a first-chance exception */
280         if (FirstChance)
281         {
282             /* Break into the debugger for the first time */
283             if (KiDebugRoutine(TrapFrame,
284                                ExceptionFrame,
285                                ExceptionRecord,
286                                &Context,
287                                PreviousMode,
288                                FALSE))
289             {
290                 /* Exception was handled */
291                 goto Handled;
292             }
293 
294             /* If the Debugger couldn't handle it, dispatch the exception */
295             if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled;
296         }
297 
298         /* This is a second-chance exception, only for the debugger */
299         if (KiDebugRoutine(TrapFrame,
300                            ExceptionFrame,
301                            ExceptionRecord,
302                            &Context,
303                            PreviousMode,
304                            TRUE))
305         {
306             /* Exception was handled */
307             goto Handled;
308         }
309 
310         /* Third strike; you're out */
311         KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
312                      ExceptionRecord->ExceptionCode,
313                      (ULONG_PTR)ExceptionRecord->ExceptionAddress,
314                      (ULONG_PTR)TrapFrame,
315                      0);
316     }
317     else
318     {
319         /* User mode exception, was it first-chance? */
320         if (FirstChance)
321         {
322             /*
323              * Break into the kernel debugger unless a user mode debugger
324              * is present or user mode exceptions are ignored, except if this
325              * is a debug service which we must always pass to KD
326              */
327             if ((!(PsGetCurrentProcess()->DebugPort) &&
328                  !(KdIgnoreUmExceptions)) ||
329                  (KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode)))
330             {
331                 /* Make sure the debugger can access debug directories */
332                 KiPrepareUserDebugData();
333 
334                 /* Call the kernel debugger */
335                 if (KiDebugRoutine(TrapFrame,
336                                    ExceptionFrame,
337                                    ExceptionRecord,
338                                    &Context,
339                                    PreviousMode,
340                                    FALSE))
341                 {
342                     /* Exception was handled */
343                     goto Handled;
344                 }
345             }
346 
347             /* Forward exception to user mode debugger */
348             if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return;
349 
350             //KiDispatchExceptionToUser()
351             __debugbreak();
352         }
353 
354         /* Try second chance */
355         if (DbgkForwardException(ExceptionRecord, TRUE, TRUE))
356         {
357             /* Handled, get out */
358             return;
359         }
360         else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE))
361         {
362             /* Handled, get out */
363             return;
364         }
365 
366         /* 3rd strike, kill the process */
367         DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n",
368                 PsGetCurrentProcess()->ImageFileName,
369                 ExceptionRecord->ExceptionCode,
370                 ExceptionRecord->ExceptionAddress,
371                 PsGetCurrentProcess()->SectionBaseAddress);
372 
373         ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
374         KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
375                      ExceptionRecord->ExceptionCode,
376                      (ULONG_PTR)ExceptionRecord->ExceptionAddress,
377                      (ULONG_PTR)TrapFrame,
378                      0);
379     }
380 
381 Handled:
382     /* Convert the context back into Trap/Exception Frames */
383     KeContextToTrapFrame(&Context,
384                          ExceptionFrame,
385                          TrapFrame,
386                          Context.ContextFlags,
387                          PreviousMode);
388     return;
389 }
390 
391 NTSTATUS
392 NTAPI
393 KeRaiseUserException(IN NTSTATUS ExceptionCode)
394 {
395     UNIMPLEMENTED;
396     return STATUS_UNSUCCESSFUL;
397 }
398 
399 
400 VOID
401 DECLSPEC_NORETURN
402 KiSystemFatalException(IN ULONG ExceptionCode,
403                        IN PKTRAP_FRAME TrapFrame)
404 {
405     /* Bugcheck the system */
406     KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP,
407                      ExceptionCode,
408                      0,
409                      0,
410                      0,
411                      TrapFrame);
412 }
413 
414 NTSTATUS
415 NTAPI
416 KiNpxNotAvailableFaultHandler(
417     IN PKTRAP_FRAME TrapFrame)
418 {
419     UNIMPLEMENTED;
420     KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
421     return -1;
422 }
423 
424 
425 NTSTATUS
426 NTAPI
427 KiGeneralProtectionFaultHandler(
428     IN PKTRAP_FRAME TrapFrame)
429 {
430     PUCHAR Instructions;
431 
432     /* Check for user-mode GPF */
433     if (TrapFrame->SegCs & 3)
434     {
435         UNIMPLEMENTED;
436         ASSERT(FALSE);
437     }
438 
439     /* Check for lazy segment load */
440     if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK))
441     {
442         /* Fix it */
443         TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK);
444         return STATUS_SUCCESS;
445     }
446     else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK))
447     {
448         /* Fix it */
449         TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK);
450         return STATUS_SUCCESS;
451     }
452 
453     /* Check for nested exception */
454     if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) &&
455         (TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler))
456     {
457         /* Not implemented */
458         UNIMPLEMENTED;
459         ASSERT(FALSE);
460     }
461 
462     /* Get Instruction Pointer */
463     Instructions = (PUCHAR)TrapFrame->Rip;
464 
465     /* Check for IRET */
466     if (Instructions[0] == 0x48 && Instructions[1] == 0xCF)
467     {
468         /* Not implemented */
469         UNIMPLEMENTED;
470         ASSERT(FALSE);
471     }
472 
473     /* Check for RDMSR/WRMSR */
474     if ((Instructions[0] == 0xF) &&            // 2-byte opcode
475         ((Instructions[1] == 0x30) ||        // RDMSR
476          (Instructions[1] == 0x32)))         // WRMSR
477     {
478         /* Unknown CPU MSR, so raise an access violation */
479         return STATUS_ACCESS_VIOLATION;
480     }
481 
482     ASSERT(FALSE);
483     return STATUS_UNSUCCESSFUL;
484 }
485 
486 NTSTATUS
487 NTAPI
488 KiXmmExceptionHandler(
489     IN PKTRAP_FRAME TrapFrame)
490 {
491     UNIMPLEMENTED;
492     KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
493     return -1;
494 }
495