* Sync up to trunk head (r64829).
[reactos.git] / ntoskrnl / ke / amd64 / except.c
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 // FIXNE: 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 == TRUE)
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 }