[REACTOS] Cleanup INIT and some PAGE section allocations
[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 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 }