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
;
105 PCONTEXT UserContext
;
106 PEXCEPTION_RECORD UserExceptionRecord
;
108 /* Make sure we have a valid SS */
109 if (TrapFrame
->SegSs
!= (KGDT64_R3_DATA
| RPL_MASK
))
111 /* Raise an access violation instead */
112 LocalExceptRecord
.ExceptionCode
= STATUS_ACCESS_VIOLATION
;
113 LocalExceptRecord
.ExceptionFlags
= 0;
114 LocalExceptRecord
.NumberParameters
= 0;
115 ExceptionRecord
= &LocalExceptRecord
;
118 /* Calculate the size of the exception record */
119 Size
= FIELD_OFFSET(EXCEPTION_RECORD
, ExceptionInformation
) +
120 ExceptionRecord
->NumberParameters
* sizeof(ULONG64
);
122 /* Get new stack pointer and align it to 16 bytes */
123 UserRsp
= (Context
->Rsp
- Size
- sizeof(CONTEXT
)) & ~15;
125 /* Get pointers to the usermode context and exception record */
126 UserContext
= (PVOID
)UserRsp
;
127 UserExceptionRecord
= (PVOID
)(UserRsp
+ sizeof(CONTEXT
));
129 /* Set up the user-stack */
132 /* Probe stack and copy Context */
133 ProbeForWrite(UserContext
, sizeof(CONTEXT
), sizeof(ULONG64
));
134 *UserContext
= *Context
;
136 /* Probe stack and copy exception record */
137 ProbeForWrite(UserExceptionRecord
, Size
, sizeof(ULONG64
));
138 *UserExceptionRecord
= *ExceptionRecord
;
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
)UserContext
;
152 TrapFrame
->Rdx
= (ULONG64
)UserExceptionRecord
;
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 /* Set the context flags */
249 Context
.ContextFlags
= CONTEXT_ALL
;
252 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
254 /* Look at our exception code */
255 switch (ExceptionRecord
->ExceptionCode
)
258 case STATUS_BREAKPOINT
:
260 /* Decrement RIP by one */
264 /* Internal exception */
265 case KI_EXCEPTION_ACCESS_VIOLATION
:
267 /* Set correct code */
268 ExceptionRecord
->ExceptionCode
= STATUS_ACCESS_VIOLATION
;
269 if (PreviousMode
== UserMode
)
271 /* FIXME: Handle no execute */
276 /* Handle kernel-mode first, it's simpler */
277 if (PreviousMode
== KernelMode
)
279 /* Check if this is a first-chance exception */
282 /* Break into the debugger for the first time */
283 if (KiDebugRoutine(TrapFrame
,
290 /* Exception was handled */
294 /* If the Debugger couldn't handle it, dispatch the exception */
295 if (RtlDispatchException(ExceptionRecord
, &Context
)) goto Handled
;
298 /* This is a second-chance exception, only for the debugger */
299 if (KiDebugRoutine(TrapFrame
,
306 /* Exception was handled */
310 /* Third strike; you're out */
311 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED
,
312 ExceptionRecord
->ExceptionCode
,
313 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
,
314 (ULONG_PTR
)TrapFrame
,
319 /* User mode exception, was it first-chance? */
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
327 if ((!(PsGetCurrentProcess()->DebugPort
) &&
328 !(KdIgnoreUmExceptions
)) ||
329 (KdIsThisAKdTrap(ExceptionRecord
, &Context
, PreviousMode
)))
331 /* Make sure the debugger can access debug directories */
332 KiPrepareUserDebugData();
334 /* Call the kernel debugger */
335 if (KiDebugRoutine(TrapFrame
,
342 /* Exception was handled */
347 /* Forward exception to user mode debugger */
348 if (DbgkForwardException(ExceptionRecord
, TRUE
, FALSE
)) return;
350 //KiDispatchExceptionToUser()
354 /* Try second chance */
355 if (DbgkForwardException(ExceptionRecord
, TRUE
, TRUE
))
357 /* Handled, get out */
360 else if (DbgkForwardException(ExceptionRecord
, FALSE
, TRUE
))
362 /* Handled, get out */
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
);
373 ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord
->ExceptionCode
);
374 KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED
,
375 ExceptionRecord
->ExceptionCode
,
376 (ULONG_PTR
)ExceptionRecord
->ExceptionAddress
,
377 (ULONG_PTR
)TrapFrame
,
382 /* Convert the context back into Trap/Exception Frames */
383 KeContextToTrapFrame(&Context
,
386 Context
.ContextFlags
,
393 KeRaiseUserException(IN NTSTATUS ExceptionCode
)
396 return STATUS_UNSUCCESSFUL
;
402 KiSystemFatalException(IN ULONG ExceptionCode
,
403 IN PKTRAP_FRAME TrapFrame
)
405 /* Bugcheck the system */
406 KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP
,
416 KiNpxNotAvailableFaultHandler(
417 IN PKTRAP_FRAME TrapFrame
)
420 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);
427 KiGeneralProtectionFaultHandler(
428 IN PKTRAP_FRAME TrapFrame
)
432 /* Check for user-mode GPF */
433 if (TrapFrame
->SegCs
& 3)
439 /* Check for lazy segment load */
440 if (TrapFrame
->SegDs
!= (KGDT64_R3_DATA
| RPL_MASK
))
443 TrapFrame
->SegDs
= (KGDT64_R3_DATA
| RPL_MASK
);
444 return STATUS_SUCCESS
;
446 else if (TrapFrame
->SegEs
!= (KGDT64_R3_DATA
| RPL_MASK
))
449 TrapFrame
->SegEs
= (KGDT64_R3_DATA
| RPL_MASK
);
450 return STATUS_SUCCESS
;
453 /* Check for nested exception */
454 if ((TrapFrame
->Rip
>= (ULONG64
)KiGeneralProtectionFaultHandler
) &&
455 (TrapFrame
->Rip
< (ULONG64
)KiGeneralProtectionFaultHandler
))
457 /* Not implemented */
462 /* Get Instruction Pointer */
463 Instructions
= (PUCHAR
)TrapFrame
->Rip
;
466 if (Instructions
[0] == 0x48 && Instructions
[1] == 0xCF)
468 /* Not implemented */
473 /* Check for RDMSR/WRMSR */
474 if ((Instructions
[0] == 0xF) && // 2-byte opcode
475 ((Instructions
[1] == 0x30) || // RDMSR
476 (Instructions
[1] == 0x32))) // WRMSR
478 /* Unknown CPU MSR, so raise an access violation */
479 return STATUS_ACCESS_VIOLATION
;
483 return STATUS_UNSUCCESSFUL
;
488 KiXmmExceptionHandler(
489 IN PKTRAP_FRAME TrapFrame
)
492 KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN
, 13, 0, 0, 1, TrapFrame
);