534 lines
13 KiB
C
534 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
Copyright (c) 1992-1993 Digital Equipment Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
kdtrap.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains code to implement the target side of the portable
|
|||
|
kernel debugger.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler 27-July-1990
|
|||
|
Joe Notarangelo 24-June-1992 (ALPHA version)
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "kdp.h"
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// globals
|
|||
|
//
|
|||
|
ULONG KdpPageInAddress;
|
|||
|
WORK_QUEUE_ITEM KdpPageInWorkItem;
|
|||
|
|
|||
|
//
|
|||
|
// externs
|
|||
|
//
|
|||
|
extern BOOLEAN KdpControlCPressed;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
#pragma optimize( "", off )
|
|||
|
VOID
|
|||
|
KdpPageInData (
|
|||
|
IN PUCHAR volatile DataAddress
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called to page in data at the supplied address.
|
|||
|
It is called either directly from KdpTrap() or from a worker
|
|||
|
thread that is queued by KdpTrap().
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DataAddress - Supplies a pointer to the data to be paged in.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (MmIsSystemAddressAccessable(DataAddress)) {
|
|||
|
UCHAR c = *DataAddress;
|
|||
|
DataAddress = &c;
|
|||
|
}
|
|||
|
KdpControlCPending = TRUE;
|
|||
|
}
|
|||
|
#pragma optimize( "", on )
|
|||
|
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KdpTrap (
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN PKEXCEPTION_FRAME ExceptionFrame,
|
|||
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
IN PCONTEXT ContextRecord,
|
|||
|
IN KPROCESSOR_MODE PreviousMode,
|
|||
|
IN BOOLEAN SecondChance
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called whenever a exception is dispatched and the kernel
|
|||
|
debugger is active.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame that describes the
|
|||
|
trap.
|
|||
|
|
|||
|
ExceptionFrame - Supplies a pointer to a exception frame that describes
|
|||
|
the trap.
|
|||
|
|
|||
|
ExceptionRecord - Supplies a pointer to an exception record that
|
|||
|
describes the exception.
|
|||
|
|
|||
|
ContextRecord - Supplies the context at the time of the exception.
|
|||
|
|
|||
|
PreviousMode - Supplies the previous processor mode.
|
|||
|
|
|||
|
SecondChance - Supplies a boolean value that determines whether this is
|
|||
|
the second chance (TRUE) that the exception has been raised.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A value of TRUE is returned if the exception is handled. Otherwise a
|
|||
|
value of FALSE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
BOOLEAN Completion;
|
|||
|
BOOLEAN Enable;
|
|||
|
BOOLEAN UnloadSymbols = FALSE;
|
|||
|
STRING Input;
|
|||
|
STRING Output;
|
|||
|
PKPRCB Prcb;
|
|||
|
|
|||
|
//
|
|||
|
// Enter debugger and synchronize processor execution.
|
|||
|
//
|
|||
|
|
|||
|
re_enter_debugger:
|
|||
|
Enable = KdEnterDebugger(TrapFrame, ExceptionFrame);
|
|||
|
Prcb = KeGetCurrentPrcb();
|
|||
|
|
|||
|
//
|
|||
|
// If this is a breakpoint instruction, then check to determine if is
|
|||
|
// an internal command.
|
|||
|
//
|
|||
|
|
|||
|
if ((ExceptionRecord->ExceptionCode == STATUS_BREAKPOINT) &&
|
|||
|
(ExceptionRecord->ExceptionInformation[0] >= DEBUG_PRINT_BREAKPOINT)){
|
|||
|
|
|||
|
//
|
|||
|
// Switch on the breakpoint code.
|
|||
|
//
|
|||
|
|
|||
|
switch (ExceptionRecord->ExceptionInformation[0]) {
|
|||
|
|
|||
|
//
|
|||
|
// Print a debug string.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
//
|
|||
|
// a0 - Supplies a pointer to an output string buffer.
|
|||
|
// a1 - Supplies the length of the output string buffer.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_PRINT_BREAKPOINT:
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
Output.Buffer = (PCHAR)ContextRecord->IntA0;
|
|||
|
Output.Length = (USHORT)ContextRecord->IntA1;
|
|||
|
if (KdDebuggerNotPresent == FALSE) {
|
|||
|
if (KdpPrintString(&Output)) {
|
|||
|
ContextRecord->IntV0 = (ULONG)STATUS_BREAKPOINT;
|
|||
|
} else {
|
|||
|
ContextRecord->IntV0 = (ULONG)STATUS_SUCCESS;
|
|||
|
}
|
|||
|
} else {
|
|||
|
ContextRecord->IntV0 = (ULONG)STATUS_DEVICE_NOT_CONNECTED;
|
|||
|
}
|
|||
|
|
|||
|
KdExitDebugger(Enable);
|
|||
|
return TRUE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Stop in the debugger
|
|||
|
//
|
|||
|
// As this is not a normal breakpoint we must increment the
|
|||
|
// context past the breakpoint instruction
|
|||
|
//
|
|||
|
|
|||
|
case BREAKIN_BREAKPOINT:
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Print a debug prompt string, then input a string.
|
|||
|
//
|
|||
|
// a0 - Supplies a pointer to an output string buffer.
|
|||
|
// a1 - Supplies the length of the output string buffer..
|
|||
|
// a2 - supplies a pointer to an input string buffer.
|
|||
|
// a3 - Supplies the length of the input string bufffer.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_PROMPT_BREAKPOINT:
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
Output.Buffer = (PCHAR)ContextRecord->IntA0;
|
|||
|
Output.Length = (USHORT)ContextRecord->IntA1;
|
|||
|
Input.Buffer = (PCHAR)ContextRecord->IntA2;
|
|||
|
Input.MaximumLength = (USHORT)ContextRecord->IntA3;
|
|||
|
KdpPromptString(&Output, &Input);
|
|||
|
ContextRecord->IntV0 = Input.Length;
|
|||
|
KdExitDebugger(Enable);
|
|||
|
return TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Load the symbolic information for an image.
|
|||
|
//
|
|||
|
// Arguments:
|
|||
|
//
|
|||
|
// a0 - Supplies a pointer to an output string descriptor.
|
|||
|
// a1 - Supplies a the base address of the image.
|
|||
|
// a2 - Supplies the current process id.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_UNLOAD_SYMBOLS_BREAKPOINT:
|
|||
|
UnloadSymbols = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Fall through
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_LOAD_SYMBOLS_BREAKPOINT:
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
if (KdDebuggerNotPresent == FALSE) {
|
|||
|
Completion =
|
|||
|
KdpReportLoadSymbolsStateChange((PSTRING)ContextRecord->IntA0,
|
|||
|
(PKD_SYMBOLS_INFO) ContextRecord->IntA1,
|
|||
|
UnloadSymbols,
|
|||
|
ContextRecord);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
KdExitDebugger(Enable);
|
|||
|
|
|||
|
//
|
|||
|
// Always return TRUE if this is the first chance to handle the
|
|||
|
// exception. The return status for the first chance indicates
|
|||
|
// handling of a breakpoint instruction which will always be
|
|||
|
// successful.
|
|||
|
//
|
|||
|
|
|||
|
if( SecondChance ){
|
|||
|
return Completion;
|
|||
|
} else {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unknown internal command.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
default:
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Report state change to kernel debugger on host machine.
|
|||
|
//
|
|||
|
|
|||
|
RtlCopyMemory(&Prcb->ProcessorState.ContextFrame,
|
|||
|
ContextRecord,
|
|||
|
sizeof (CONTEXT));
|
|||
|
|
|||
|
Completion = KdpReportExceptionStateChange(
|
|||
|
ExceptionRecord,
|
|||
|
&Prcb->ProcessorState.ContextFrame,
|
|||
|
SecondChance
|
|||
|
);
|
|||
|
|
|||
|
RtlCopyMemory(ContextRecord,
|
|||
|
&Prcb->ProcessorState.ContextFrame,
|
|||
|
sizeof (CONTEXT) );
|
|||
|
|
|||
|
KdExitDebugger(Enable);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// check to see if the user of the remote debugger
|
|||
|
// requested memory to be paged in
|
|||
|
//
|
|||
|
if (KdpPageInAddress) {
|
|||
|
|
|||
|
if (KeGetCurrentIrql() <= APC_LEVEL) {
|
|||
|
|
|||
|
//
|
|||
|
// if the IQRL is below DPC level then cause
|
|||
|
// the page fault to occur and then re-enter
|
|||
|
// the debugger. this whole process is transparent
|
|||
|
// to the user.
|
|||
|
//
|
|||
|
KdpPageInData( (PUCHAR)KdpPageInAddress );
|
|||
|
KdpPageInAddress = 0;
|
|||
|
KdpControlCPending = FALSE;
|
|||
|
goto re_enter_debugger;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// we cannot take a page fault
|
|||
|
// here so a worker item is queued to take the
|
|||
|
// page fault. after the worker item takes the
|
|||
|
// page fault it sets the contol-c flag so that
|
|||
|
// the user re-enters the debugger just as if
|
|||
|
// control-c was pressed.
|
|||
|
//
|
|||
|
if (KdpControlCPressed) {
|
|||
|
ExInitializeWorkItem(
|
|||
|
&KdpPageInWorkItem,
|
|||
|
(PWORKER_THREAD_ROUTINE) KdpPageInData,
|
|||
|
(PVOID) KdpPageInAddress
|
|||
|
);
|
|||
|
ExQueueWorkItem( &KdpPageInWorkItem, DelayedWorkQueue );
|
|||
|
KdpPageInAddress = 0;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KdpControlCPressed = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Always return TRUE if this is the first chance to handle the
|
|||
|
// exception. Otherwise, return the completion status of the
|
|||
|
// state change reporting.
|
|||
|
//
|
|||
|
|
|||
|
if( SecondChance ){
|
|||
|
return Completion;
|
|||
|
} else {
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KdIsThisAKdTrap (
|
|||
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
IN PCONTEXT ContextRecord,
|
|||
|
IN KPROCESSOR_MODE PreviousMode
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called whenever a user-mode exception occurs and
|
|||
|
it might be a kernel debugger exception (Like DbgPrint/DbgPrompt ).
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ExceptionRecord - Supplies a pointer to an exception record that
|
|||
|
describes the exception.
|
|||
|
|
|||
|
ContextRecord - Supplies the context at the time of the exception.
|
|||
|
|
|||
|
PreviousMode - Supplies the previous processor mode.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A value of TRUE is returned if this is for the kernel debugger.
|
|||
|
Otherwise, a value of FALSE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Isolate the breakpoint code from the breakpoint instruction which
|
|||
|
// is stored by the exception dispatch code in the information field
|
|||
|
// of the exception record.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Switch on the breakpoint code.
|
|||
|
//
|
|||
|
|
|||
|
switch (ExceptionRecord->ExceptionInformation[0]) {
|
|||
|
|
|||
|
//
|
|||
|
// Kernel breakpoint code.
|
|||
|
//
|
|||
|
|
|||
|
case KERNEL_BREAKPOINT:
|
|||
|
case BREAKIN_BREAKPOINT:
|
|||
|
#if DEVL
|
|||
|
return TRUE;
|
|||
|
#else
|
|||
|
if (PreviousMode == KernelMode) {
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Debug print code.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_PRINT_BREAKPOINT:
|
|||
|
return TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Debug prompt code.
|
|||
|
//
|
|||
|
case DEBUG_PROMPT_BREAKPOINT:
|
|||
|
return TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Debug stop code.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_STOP_BREAKPOINT:
|
|||
|
#if DEVL
|
|||
|
return TRUE;
|
|||
|
#else
|
|||
|
if (PreviousMode == KernelMode) {
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Debug load symbols code.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_LOAD_SYMBOLS_BREAKPOINT:
|
|||
|
if (PreviousMode == KernelMode) {
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Debug unload symbols code.
|
|||
|
//
|
|||
|
|
|||
|
case DEBUG_UNLOAD_SYMBOLS_BREAKPOINT:
|
|||
|
if (PreviousMode == KernelMode) {
|
|||
|
return TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
//
|
|||
|
// All other codes.
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KdpStub (
|
|||
|
IN PKTRAP_FRAME TrapFrame,
|
|||
|
IN PKEXCEPTION_FRAME ExceptionFrame,
|
|||
|
IN PEXCEPTION_RECORD ExceptionRecord,
|
|||
|
IN PCONTEXT ContextRecord,
|
|||
|
IN KPROCESSOR_MODE PreviousMode,
|
|||
|
IN BOOLEAN SecondChance
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine provides a kernel debugger stub routine that catchs debug
|
|||
|
prints in checked systems when the kernel debugger is not active.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
TrapFrame - Supplies a pointer to a trap frame that describes the
|
|||
|
trap.
|
|||
|
|
|||
|
ExceptionFrame - Supplies a pointer to a exception frame that describes
|
|||
|
the trap.
|
|||
|
|
|||
|
ExceptionRecord - Supplies a pointer to an exception record that
|
|||
|
describes the exception.
|
|||
|
|
|||
|
ContextRecord - Supplies the context at the time of the exception.
|
|||
|
|
|||
|
PreviousMode - Supplies the previous processor mode.
|
|||
|
|
|||
|
SecondChance - Supplies a boolean value that determines whether this is
|
|||
|
the second chance (TRUE) that the exception has been raised.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A value of TRUE is returned if the exception is handled. Otherwise a
|
|||
|
value of FALSE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG BreakpointCode;
|
|||
|
|
|||
|
//
|
|||
|
// Isolate the breakpoint code from the breakpoint instruction which
|
|||
|
// is stored by the exception dispatch code in the information field
|
|||
|
// of the exception record.
|
|||
|
//
|
|||
|
|
|||
|
BreakpointCode = ExceptionRecord->ExceptionInformation[0];
|
|||
|
|
|||
|
//
|
|||
|
// If the breakpoint is a debug print, debug load symbols, or debug
|
|||
|
// unload symbols, then return TRUE. Otherwise, return FALSE;
|
|||
|
//
|
|||
|
|
|||
|
if ((BreakpointCode == DEBUG_PRINT_BREAKPOINT) ||
|
|||
|
(BreakpointCode == DEBUG_LOAD_SYMBOLS_BREAKPOINT) ||
|
|||
|
(BreakpointCode == DEBUG_UNLOAD_SYMBOLS_BREAKPOINT) ||
|
|||
|
(BreakpointCode == KERNEL_BREAKPOINT)) {
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
return TRUE;
|
|||
|
} else {
|
|||
|
if ( (BreakpointCode == DEBUG_STOP_BREAKPOINT) &&
|
|||
|
(PreviousMode == KernelMode) ){
|
|||
|
ContextRecord->Fir += 4;
|
|||
|
return TRUE;
|
|||
|
} else {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|