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)
10 /* INCLUDES ******************************************************************/
16 extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange
[256];
18 /* GLOBALS *******************************************************************/
20 KIDT_INIT KiInterruptInitTable
[] =
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
},
50 KIDTENTRY64 KiIdt
[256];
51 KDESCRIPTOR KiIdtDescriptor
= {{0}, sizeof(KiIdt
) - 1, KiIdt
};
54 /* FUNCTIONS *****************************************************************/
59 KeInitExceptions(VOID
)
63 /* Initialize the Idt */
64 for (j
= i
= 0; i
< 256; i
++)
68 if (KiInterruptInitTable
[j
].InterruptId
== i
)
70 Offset
= (ULONG64
)KiInterruptInitTable
[j
].ServiceRoutine
;
71 KiIdt
[i
].Dpl
= KiInterruptInitTable
[j
].Dpl
;
72 KiIdt
[i
].IstIndex
= KiInterruptInitTable
[j
].IstIndex
;
77 Offset
= (ULONG64
)&KiUnexpectedRange
[i
]._Op_push
;
79 KiIdt
[i
].IstIndex
= 0;
81 KiIdt
[i
].OffsetLow
= Offset
& 0xffff;
82 KiIdt
[i
].Selector
= KGDT64_R0_CODE
;
84 KiIdt
[i
].Reserved0
= 0;
86 KiIdt
[i
].OffsetMiddle
= (Offset
>> 16) & 0xffff;
87 KiIdt
[i
].OffsetHigh
= (Offset
>> 32);
88 KiIdt
[i
].Reserved1
= 0;
91 KeGetPcr()->IdtBase
= KiIdt
;
92 __lidt(&KiIdtDescriptor
.Limit
);
97 KiDispatchExceptionToUser(
98 IN PKTRAP_FRAME TrapFrame
,
100 IN PEXCEPTION_RECORD ExceptionRecord
)
102 EXCEPTION_RECORD LocalExceptRecord
;
104 PKUSER_EXCEPTION_STACK UserStack
;
106 /* Make sure we have a valid SS */
107 if (TrapFrame
->SegSs
!= (KGDT64_R3_DATA
| RPL_MASK
))
109 /* Raise an access violation instead */
110 LocalExceptRecord
.ExceptionCode
= STATUS_ACCESS_VIOLATION
;
111 LocalExceptRecord
.ExceptionFlags
= 0;
112 LocalExceptRecord
.NumberParameters
= 0;
113 ExceptionRecord
= &LocalExceptRecord
;
116 /* Get new stack pointer and align it to 16 bytes */
117 UserRsp
= (Context
->Rsp
- sizeof(KUSER_EXCEPTION_STACK
)) & ~15;
119 /* Get pointer to the usermode context, exception record and machine frame */
120 UserStack
= (PKUSER_EXCEPTION_STACK
)UserRsp
;
122 /* Set up the user-stack */
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
));
129 /* Copy Context and ExceptionFrame */
130 UserStack
->Context
= *Context
;
131 UserStack
->ExceptionRecord
= *ExceptionRecord
;
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
;
140 _SEH2_EXCEPT((LocalExceptRecord
= *_SEH2_GetExceptionInformation()->ExceptionRecord
),
141 EXCEPTION_EXECUTE_HANDLER
)
143 // FIXME: handle stack overflow
145 /* Nothing we can do here */
150 /* Now set the two params for the user-mode dispatcher */
151 TrapFrame
->Rcx
= (ULONG64
)&UserStack
->ExceptionRecord
;
152 TrapFrame
->Rdx
= (ULONG64
)&UserStack
->Context
;
154 /* Set new Stack Pointer */
155 TrapFrame
->Rsp
= UserRsp
;
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
;
165 /* Set RIP to the User-mode Dispatcher */
166 TrapFrame
->Rip
= (ULONG64
)KeUserExceptionDispatcher
;
168 /* Exit to usermode */
169 KiServiceExit2(TrapFrame
);
174 KiPageInDirectory(PVOID ImageBase
, USHORT Directory
)
176 volatile CHAR
*Pointer
;
179 /* Get a pointer to the debug directory */
180 Pointer
= RtlImageDirectoryEntryToData(ImageBase
, 1, Directory
, &Size
);
181 if (!Pointer
) return;
184 while ((LONG
)Size
> 0)
186 /* Touch it, to page it in */
188 Pointer
+= PAGE_SIZE
;
194 KiPrepareUserDebugData(void)
196 PLDR_DATA_TABLE_ENTRY LdrEntry
;
197 PPEB_LDR_DATA PebLdr
;
198 PLIST_ENTRY ListEntry
;
201 /* Get the Teb for this process */
202 Teb
= KeGetCurrentThread()->Teb
;
207 /* Get a pointer to the loader data */
208 PebLdr
= Teb
->ProcessEnvironmentBlock
->Ldr
;
209 if (!PebLdr
) _SEH2_YIELD(return);
211 /* Now loop all entries in the module list */
212 for (ListEntry
= PebLdr
->InLoadOrderModuleList
.Flink
;
213 ListEntry
!= &PebLdr
->InLoadOrderModuleList
;
214 ListEntry
= ListEntry
->Flink
)
216 /* Get the loader entry */
217 LdrEntry
= CONTAINING_RECORD(ListEntry
,
218 LDR_DATA_TABLE_ENTRY
,
221 KiPageInDirectory((PVOID
)LdrEntry
->DllBase
,
222 IMAGE_DIRECTORY_ENTRY_DEBUG
);
224 KiPageInDirectory((PVOID
)LdrEntry
->DllBase
,
225 IMAGE_DIRECTORY_ENTRY_EXCEPTION
);
229 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
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
)
245 /* Increase number of Exception Dispatches */
246 KeGetCurrentPrcb()->KeExceptionDispatchCount
++;
248 /* Zero out the context to avoid leaking kernel stack memor to user mode */
249 RtlZeroMemory(&Context
, sizeof(Context
));
251 /* Set the context flags */
252 Context
.ContextFlags
= CONTEXT_ALL
;
254 /* Get the Context from the trap and exception frame */
255 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
257 /* Look at our exception code */
258 switch (ExceptionRecord
->ExceptionCode
)
261 case STATUS_BREAKPOINT
:
263 /* Decrement RIP by one */
267 /* Internal exception */
268 case KI_EXCEPTION_ACCESS_VIOLATION
:
270 /* Set correct code */
271 ExceptionRecord
->ExceptionCode
= STATUS_ACCESS_VIOLATION
;
272 if (PreviousMode
== UserMode
)
274 /* FIXME: Handle no execute */
279 /* Handle kernel-mode first, it's simpler */
280 if (PreviousMode
== KernelMode
)
282 /* Check if this is a first-chance exception */
285 /* Break into the debugger for the first time */
286 if (KiDebugRoutine(TrapFrame
,
293 /* Exception was handled */
297 /* If the Debugger couldn't handle it, dispatch the exception */
298 if (RtlDispatchException(ExceptionRecord
, &Context
)) goto Handled
;
301 /* This is a second-chance exception, only for the debugger */
302 if (KiDebugRoutine(TrapFrame
,
309 /* Exception was handled */
313 /* Third strike; you're out */
314 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED
,
315 ExceptionRecord
->ExceptionCode
,
316 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
,
317 (ULONG_PTR
)TrapFrame
,
322 /* User mode exception, was it first-chance? */
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
330 if ((!(PsGetCurrentProcess()->DebugPort
) &&
331 !(KdIgnoreUmExceptions
)) ||
332 (KdIsThisAKdTrap(ExceptionRecord
, &Context
, PreviousMode
)))
334 /* Make sure the debugger can access debug directories */
335 KiPrepareUserDebugData();
337 /* Call the kernel debugger */
338 if (KiDebugRoutine(TrapFrame
,
345 /* Exception was handled */
350 /* Forward exception to user mode debugger */
351 if (DbgkForwardException(ExceptionRecord
, TRUE
, FALSE
)) return;
353 /* Forward exception to user mode (does not return) */
354 KiDispatchExceptionToUser(TrapFrame
, &Context
, ExceptionRecord
);
358 /* Try second chance */
359 if (DbgkForwardException(ExceptionRecord
, TRUE
, TRUE
))
361 /* Handled, get out */
364 else if (DbgkForwardException(ExceptionRecord
, FALSE
, TRUE
))
366 /* Handled, get out */
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
);
377 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord
->ExceptionCode
);
378 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED
,
379 ExceptionRecord
->ExceptionCode
,
380 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
,
381 (ULONG_PTR
)TrapFrame
,
386 /* Convert the context back into Trap/Exception Frames */
387 KeContextToTrapFrame(&Context
,
390 Context
.ContextFlags
,
397 KeRaiseUserException(IN NTSTATUS ExceptionCode
)
400 return STATUS_UNSUCCESSFUL
;
406 KiSystemFatalException(IN ULONG ExceptionCode
,
407 IN PKTRAP_FRAME TrapFrame
)
409 /* Bugcheck the system */
410 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP
,
420 KiNpxNotAvailableFaultHandler(
421 IN PKTRAP_FRAME TrapFrame
)
424 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);
431 KiGeneralProtectionFaultHandler(
432 IN PKTRAP_FRAME TrapFrame
)
436 /* Check for user-mode GPF */
437 if (TrapFrame
->SegCs
& 3)
443 /* Check for lazy segment load */
444 if (TrapFrame
->SegDs
!= (KGDT64_R3_DATA
| RPL_MASK
))
447 TrapFrame
->SegDs
= (KGDT64_R3_DATA
| RPL_MASK
);
448 return STATUS_SUCCESS
;
450 else if (TrapFrame
->SegEs
!= (KGDT64_R3_DATA
| RPL_MASK
))
453 TrapFrame
->SegEs
= (KGDT64_R3_DATA
| RPL_MASK
);
454 return STATUS_SUCCESS
;
457 /* Check for nested exception */
458 if ((TrapFrame
->Rip
>= (ULONG64
)KiGeneralProtectionFaultHandler
) &&
459 (TrapFrame
->Rip
< (ULONG64
)KiGeneralProtectionFaultHandler
))
461 /* Not implemented */
466 /* Get Instruction Pointer */
467 Instructions
= (PUCHAR
)TrapFrame
->Rip
;
470 if (Instructions
[0] == 0x48 && Instructions
[1] == 0xCF)
472 /* Not implemented */
477 /* Check for RDMSR/WRMSR */
478 if ((Instructions
[0] == 0xF) && // 2-byte opcode
479 ((Instructions
[1] == 0x30) || // RDMSR
480 (Instructions
[1] == 0x32))) // WRMSR
482 /* Unknown CPU MSR, so raise an access violation */
483 return STATUS_ACCESS_VIOLATION
;
487 return STATUS_UNSUCCESSFUL
;
492 KiXmmExceptionHandler(
493 IN PKTRAP_FRAME TrapFrame
)
496 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);