page ,132
subttl "Debug Exception"
;++
;
; Routine Description:
;
; Handle debug exception.
;
; The processor triggers this exception for any of the following
; conditions:
;
; 1. Instruction breakpoint fault.
; 2. Data address breakpoint trap.
; 3. General detect fault.
; 4. Single-step trap.
; 5. Task-switch breakpoint trap.
;
;
; Arguments:
;
; At entry, the values of saved CS and EIP depend on whether the
; exception is a fault or a trap.
; No error code is provided with the divide error.
;
; Return value:
;
; None
;
;--
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
align dword
;
; We branch here to handle a trap01 fromt he fast system call entry
; we need to propagate the trap bit to the return eflags etc and
; continue.
;
Kt0100: mov [ebp].TsEip, _KiFastCallEntry2
and dword ptr [ebp].TsEflags, NOT EFLAGS_TF
jmp _KiExceptionExit ; join common code
ENTER_DR_ASSIST kit1_a, kit1_t, NoAbiosAssist
align dword
public _KiTrap01
_KiTrap01 proc
;
; Inspect old EIP in case this is a single stepped
; sysenter instruction.
;
mov ecx, [ebp]+TsEip
cmp ecx, _KiFastCallEntry
je Kt0100
;
; See if were doing the fast bop optimization of touching user memory
; with ints disabled. If we are then ignore data breakpoints.
;
;
if FAST_BOP
cmp dword ptr PCR[PcVdmAlert], 0
jne Kt01VdmAlert
endif
;
; If caller is user mode, we want interrupts back on.
; . all relevant state has already been saved
; . user mode code always runs with ints on
;
; If caller is kernel mode, we want them off!
; . some state still in registers, must prevent races
; . kernel mode code can run with ints off
;
;
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
jnz kit01_30 ; fault occured in V86 mode => Usermode
.errnz (MODE_MASK AND 0FFFFFF00h)
test byte ptr [ebp]+TsSegCs,MODE_MASK
jz kit01_10
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
jne kit01_30
kit01_05:
sti
kit01_10:
;
; Set up exception record for raising single step exception
; and call _KiDispatchException
;
kit01_20:
and dword ptr [ebp]+TsEflags, not EFLAGS_TF
mov ebx, [ebp]+TsEip ; (ebx)-> faulting instruction
mov eax, STATUS_SINGLE_STEP
jmp CommonDispatchException0Args ; Never return
kit01_30:
; Check to see if this process is a vdm
mov ebx,PCR[PcPrcbData+PbCurrentThread]
mov ebx,[ebx]+ThApcState+AsProcess
cmp dword ptr [ebx]+PrVdmObjects,0 ; is this a vdm process?
je kit01_05
stdCall _Ki386VdmReflectException_A, <01h>
test ax,0FFFFh
jz Kit01_20
jmp _KiExceptionExit
if FAST_BOP
Kt01VdmAlert:
;
; If a DEBUG trap occured while we are in VDM alert mode (processing
; v86 trap without building trap frame), we will restore all the
; registers and return to its recovery routine.
;
page ,132
subttl "Single Byte INT3 Breakpoint"
;++
;
; Routine Description:
;
; Handle INT 3 breakpoint.
;
; The trap is caused by a single byte INT 3 instruction. A
; BREAKPOINT exception with additional parameter indicating
; READ access is raised for this trap if previous mode is user.
;
; Arguments:
;
; At entry, the saved CS:EIP point to the instruction immediately
; following the INT 3 instruction.
; No error code is provided with the error.
;
; Return value:
;
; None
;
;--
ASSUME DS:NOTHING, SS:NOTHING, ES:NOTHING
KiTrap03DebugService:
;
; If caller is user mode, we want interrupts back on.
; . all relevant state has already been saved
; . user mode code always runs with ints on
;
; If caller is kernel mode, we want them off!
; . some state still in registers, must prevent races
; . kernel mode code can run with ints off
;
;
; Arguments:
; eax - ServiceClass - which call is to be performed
; ecx - Arg1 - generic first argument
; edx - Arg2 - generic second argument
;
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
jnz kit03_30 ; fault occured in V86 mode => Usermode
.errnz (MODE_MASK AND 0FFFFFF00h)
test byte ptr [ebp]+TsSegCs,MODE_MASK
jz kit03_10
cmp word ptr [ebp]+TsSegCs,KGDT_R3_CODE OR RPL_MASK
jne kit03_30
kit03_05:
sti
kit03_10:
;
; Set up exception record and arguments for raising breakpoint exception
;
;++
;
; CommonDispatchException
;
; Routine Description:
;
; This routine allocates exception record on stack, sets up exception
; record using specified parameters and finally sets up arguments
; and calls _KiDispatchException.
;
; NOTE:
;
; The purpose of this routine is to save code space. Use this routine
; only if:
; 1. ExceptionRecord is NULL
; 2. ExceptionFlags is 0
; 3. Number of parameters is less or equal than 3.
;
; Otherwise, you should use DISPATCH_EXCEPTION macro to set up your special
; exception record.
;
; Arguments:
;
; (eax) = ExcepCode - Exception code to put into exception record
; (ebx) = ExceptAddress - Addr of instruction which the hardware exception occurs
; (ecx) = NumParms - Number of additional parameters
; (edx) = Parameter1
; (esi) = Parameter2
; (edi) = Parameter3
;
; Return Value:
;
; None.
;
;--
CommonDispatchException0Args:
xor ecx, ecx ; zero arguments
call CommonDispatchException
CommonDispatchException1Arg0d:
xor edx, edx ; zero edx
CommonDispatchException1Arg:
mov ecx, 1 ; one argument
call CommonDispatchException ; there is no return
CommonDispatchException2Args0d:
xor edx, edx ; zero edx
CommonDispatchException2Args:
mov ecx, 2 ; two arguments
call CommonDispatchException ; there is no return
public CommonDispatchException
align dword
CommonDispatchException proc
cPublicFpo 0, ExceptionRecordLength/4
;
; Set up exception record for raising exception
;
sub esp, ExceptionRecordLength
; allocate exception record
mov dword ptr [esp]+ErExceptionCode, eax
; set up exception code
xor eax, eax
mov dword ptr [esp]+ErExceptionFlags, eax
; set exception flags
mov dword ptr [esp]+ErExceptionRecord, eax
; set associated exception record
mov dword ptr [esp]+ErExceptionAddress, ebx
mov dword ptr [esp]+ErNumberParameters, ecx
; set number of parameters
cmp ecx, 0
je short de00
lea ebx, [esp + ErExceptionInformation]
mov [ebx], edx
mov [ebx+4], esi
mov [ebx+8], edi
de00:
;
; set up arguments and call _KiDispatchException
;
mov ecx, esp ; (ecx)->exception record
.errnz (EFLAGS_V86_MASK AND 0FF00FFFFh)
test byte ptr [ebp]+TsEFlags+2,EFLAGS_V86_MASK/010000h
jz short de10
mov eax,0FFFFh
jmp short de20
de10: mov eax,[ebp]+TsSegCs
de20: and eax,MODE_MASK
VOID
KiDispatchException (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance
)
/*++
Routine Description:
This function is called to dispatch an exception to the proper mode and
to cause the exception dispatcher to be called. If the previous mode is
kernel, then the exception dispatcher is called directly to process the
exception. Otherwise the exception record, exception frame, and trap
frame contents are copied to the user mode stack. The contents of the
exception frame and trap are then modified such that when control is
returned, execution will commense in user mode in a routine which will
call the exception dispatcher.
Arguments:
ExceptionRecord - Supplies a pointer to an exception record.
ExceptionFrame - Supplies a pointer to an exception frame. For NT386,
this should be NULL.
TrapFrame - Supplies a pointer to a trap frame.
PreviousMode - Supplies the previous processor mode.
FirstChance - Supplies a boolean value that specifies whether this is
the first (TRUE) or second (FALSE) chance for the exception.
if ((PreviousMode == UserMode) || KdDebuggerEnabled) {
//
// For usermode exceptions always try to dispatch the floating
// point state. This allows exception handlers & debuggers to
// examine/edit the npx context if required. Plus it allows
// exception handlers to use fp instructions without destroying
// the npx state at the time of the exception.
//
// Note: If there's no 80387, ContextTo/FromKFrames will use the
// emulator's current state. If the emulator can not give the
// current state, then the context_floating_point bit will be
// turned off by ContextFromKFrames.
//
//
// if it is BREAK_POINT exception, we subtract 1 from EIP and report
// the updated EIP to user. This is because Cruiser requires EIP
// points to the int 3 instruction (not the instruction following int 3).
// In this case, BreakPoint exception is fatal. Otherwise we will step
// on the int 3 over and over again, if user does not handle it
//
// if the BREAK_POINT occured in V86 mode, the debugger running in the
// VDM will expect CS:EIP to point after the exception (the way the
// processor left it. this is also true for protected mode dos
// app debuggers. We will need a way to detect this.
//
//
switch (ExceptionRecord->ExceptionCode) {
case STATUS_BREAKPOINT:
ContextFrame.Eip--;
break;
case KI_EXCEPTION_ACCESS_VIOLATION:
ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
if (PreviousMode == UserMode) {
if (KiCheckForAtlThunk(ExceptionRecord,&ContextFrame) != FALSE) {
goto Handled1;
}
if ((SharedUserData->ProcessorFeatures[PF_NX_ENABLED] == TRUE) &&
(ExceptionRecord->ExceptionInformation [0] == EXCEPTION_EXECUTE_FAULT)) {
//
// Previous mode was kernel.
//
// If the kernel debugger is active, then give the kernel debugger the
// first chance to handle the exception. If the kernel debugger handles
// the exception, then continue execution. Else attempt to dispatch the
// exception to a frame based handler. If a frame based handler handles
// the exception, then continue execution.
//
// If a frame based handler does not handle the exception,
// give the kernel debugger a second chance, if it's present.
//
// If the exception is still unhandled, call KeBugCheck().
//
//
// Previous mode was user.
//
// If this is the first chance and the current process has a debugger
// port, then send a message to the debugger port and wait for a reply.
// If the debugger handles the exception, then continue execution. Else
// transfer the exception information to the user stack, transition to
// user mode, and attempt to dispatch the exception to a frame based
// handler. If a frame based handler handles the exception, then continue
// execution with the continue system service. Else execute the
// NtRaiseException system service with FirstChance == FALSE, which
// will call this routine a second time to process the exception.
//
// If this is the second chance and the current process has a debugger
// port, then send a message to the debugger port and wait for a reply.
// If the debugger handles the exception, then continue execution. Else
// if the current process has a subsystem port, then send a message to
// the subsystem port and wait for a reply. If the subsystem handles the
// exception, then continue execution. Else terminate the process.
//
if (FirstChance == TRUE) {
//
// This is the first chance to handle the exception.
//
if ((KiDebugRoutine != NULL) &&
((PsGetCurrentProcess()->DebugPort == NULL &&
!KdIgnoreUmExceptions) ||
(KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) {
//
// Now dispatch the fault to the kernel debugger.
//
//
// Probe user stack area for writeability and then transfer the
// context record to the user stack area.
// N.B. The probing length is Length+8 because there are two
// arguments need to be pushed to user stack later.
//
//
// Set the address of the exception routine that will call the
// exception dispatcher and then return to the trap handler.
// The trap handler will restore the exception and trap frame
// context and continue execution in the routine that will
// call the exception dispatcher.
//
//
// If the exception is a stack overflow, then attempt
// to raise the stack overflow exception. Otherwise,
// the user's stack is not accessible, or is misaligned,
// and second chance processing is performed.
//
//
// Exception was handled by the debugger or the associated subsystem
// and state was modified, if necessary, using the get state and set
// state capabilities. Therefore the context frame does not need to
// be transferred to the trap and exception frames.
//