reactos/ntoskrnl/kd/wrappers/gdbstub.c

1591 lines
44 KiB
C
Raw Normal View History

/****************************************************************************
THIS SOFTWARE IS NOT COPYRIGHTED
HP offers the following for use in the public domain. HP makes no
warranty with regard to the software or it's performance and the
user accepts the software "AS IS" with all faults.
HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
****************************************************************************/
/****************************************************************************
* Contributor: Lake Stevens Instrument Division$
*
* Description: low level support for gdb debugger. $
*
* Written by: Glenn Engel $
* ModuleState: Experimental $
*
* Modified for 386 by Jim Kingdon, Cygnus Support.
* Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
*
****************************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/************************************************************************/
static BOOLEAN GspInitialized;
static BOOLEAN GspRemoteDebug;
static CONST CHAR HexChars[] = "0123456789abcdef";
static PETHREAD GspRunThread; /* NULL means run all threads */
static PETHREAD GspDbgThread;
static PETHREAD GspEnumThread;
static FAST_MUTEX GspLock;
extern LIST_ENTRY PsActiveProcessHead;
/* FIXME hardcoded for COM2, 115200 baud */
ULONG GdbPortNumber = DEFAULT_DEBUG_PORT;
CPPORT GdbPortInfo = {0, DEFAULT_DEBUG_BAUD_RATE, 0};
static CHAR GspInBuffer[1000];
static CHAR GspOutBuffer[1000];
/* Number of Registers. */
#define NUMREGS 16
enum REGISTER_NAMES
{
EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
PC /* also known as eip */ ,
PS /* also known as eflags */ ,
CS, SS, DS, ES, FS, GS
};
typedef struct _CPU_REGISTER
{
ULONG Size;
ULONG OffsetInTF;
ULONG OffsetInContext;
BOOLEAN SetInContext;
} CPU_REGISTER, *PCPU_REGISTER;
static CPU_REGISTER GspRegisters[NUMREGS] =
{
{ 4, FIELD_OFFSET(KTRAP_FRAME, Eax), FIELD_OFFSET(CONTEXT, Eax), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, Ecx), FIELD_OFFSET(CONTEXT, Ecx), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, Edx), FIELD_OFFSET(CONTEXT, Edx), FALSE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, Ebx), FIELD_OFFSET(CONTEXT, Ebx), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, HardwareEsp), FIELD_OFFSET(CONTEXT, Esp), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, DbgEbp), FIELD_OFFSET(CONTEXT, Ebp), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, Esi), FIELD_OFFSET(CONTEXT, Esi), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, Edi), FIELD_OFFSET(CONTEXT, Edi), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, DbgEip), FIELD_OFFSET(CONTEXT, Eip), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, EFlags), FIELD_OFFSET(CONTEXT, EFlags), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, SegCs), FIELD_OFFSET(CONTEXT, SegCs), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, HardwareSegSs), FIELD_OFFSET(CONTEXT, SegSs), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, SegDs), FIELD_OFFSET(CONTEXT, SegDs), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, SegEs), FIELD_OFFSET(CONTEXT, SegEs), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, SegFs), FIELD_OFFSET(CONTEXT, SegFs), TRUE },
{ 4, FIELD_OFFSET(KTRAP_FRAME, SegGs), FIELD_OFFSET(CONTEXT, SegGs), TRUE }
};
static PCHAR GspThreadStates[DeferredReady + 1] =
{
"Initialized",
"Ready",
"Running",
"Standby",
"Terminated",
"Waiting",
"Transition",
"DeferredReady"
};
LONG
HexValue(CHAR ch)
{
if ((ch >= '0') && (ch <= '9'))
return (ch - '0');
if ((ch >= 'a') && (ch <= 'f'))
return (ch - 'a' + 10);
if ((ch >= 'A') && (ch <= 'F'))
return (ch - 'A' + 10);
return -1;
}
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
VOID
GdbPutChar(UCHAR Value)
{
KdPortPutByteEx(&GdbPortInfo, Value);
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
}
UCHAR
GdbGetChar(VOID)
{
UCHAR Value;
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
while (!KdPortGetByteEx(&GdbPortInfo, &Value)) ;
return Value;
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
}
/* scan for the sequence $<data>#<Checksum> */
PCHAR
GspGetPacket(VOID)
{
PCHAR Buffer = &GspInBuffer[0];
CHAR Checksum;
CHAR XmitChecksum;
ULONG Count;
CHAR ch;
while (TRUE)
{
/* wait around for the start character, ignore all other characters */
while ((ch = GdbGetChar()) != '$') ;
retry:
Checksum = 0;
XmitChecksum = -1;
Count = 0;
/* now, read until a # or end of Buffer is found */
while (Count < sizeof(GspInBuffer) - 1)
{
ch = GdbGetChar();
if (ch == '$')
goto retry;
if (ch == '#')
break;
Checksum = Checksum + ch;
Buffer[Count] = ch;
Count = Count + 1;
}
Buffer[Count] = 0;
if (ch == '#')
{
ch = GdbGetChar();
XmitChecksum = (CHAR)(HexValue(ch) << 4);
ch = GdbGetChar();
XmitChecksum += (CHAR)(HexValue(ch));
if (Checksum != XmitChecksum)
{
GdbPutChar('-'); /* failed checksum */
}
else
{
GdbPutChar('+'); /* successful transfer */
return &Buffer[0];
}
}
}
}
/* send the packet in Buffer. */
VOID
GspPutPacket(PCHAR Buffer)
{
CHAR Checksum;
LONG Count;
CHAR ch;
/* $<packet info>#<Checksum>. */
do
{
GdbPutChar('$');
Checksum = 0;
Count = 0;
while ((ch = Buffer[Count]))
{
GdbPutChar(ch);
Checksum += ch;
Count += 1;
}
GdbPutChar('#');
GdbPutChar(HexChars[(Checksum >> 4) & 0xf]);
GdbPutChar(HexChars[Checksum & 0xf]);
}
while (GdbGetChar() != '+');
}
VOID
GspPutPacketNoWait(PCHAR Buffer)
{
CHAR Checksum;
LONG Count;
CHAR ch;
/* $<packet info>#<Checksum>. */
GdbPutChar('$');
Checksum = 0;
Count = 0;
while ((ch = Buffer[Count]))
{
GdbPutChar(ch);
Checksum += ch;
Count += 1;
}
GdbPutChar('#');
GdbPutChar(HexChars[(Checksum >> 4) & 0xf]);
GdbPutChar(HexChars[Checksum & 0xf]);
}
/* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an error. */
static volatile BOOLEAN GspMemoryError = FALSE;
static CHAR
GspReadMemSafe(PCHAR Address)
{
CHAR ch = 0;
if (!KdpSafeReadMemory((ULONG_PTR)Address, 1, &ch))
GspMemoryError = TRUE;
return ch;
}
static void
GspWriteMemSafe(PCHAR Address, CHAR Ch)
{
if (!KdpSafeWriteMemory((ULONG_PTR)Address, 1, Ch))
GspMemoryError = TRUE;
}
/* Convert the memory pointed to by Address into hex, placing result in Buffer
* Return a pointer to the last char put in Buffer (null)
* If MayFault is TRUE, then we should set GspMemoryError in response to
* a fault; if FALSE treat a fault like any other fault in the stub.
*/
static PCHAR
GspMem2Hex(PCHAR Address, PCHAR Buffer, LONG Count, BOOLEAN MayFault)
{
ULONG i;
CHAR ch;
for (i = 0; i < (ULONG)Count; i++)
{
if (MayFault)
{
ch = GspReadMemSafe(Address);
if (GspMemoryError)
return Buffer;
}
else
{
ch = *Address;
}
*Buffer++ = HexChars[(ch >> 4) & 0xf];
*Buffer++ = HexChars[ch & 0xf];
Address++;
}
*Buffer = 0;
return Buffer;
}
static ULONG
GspWriteMem(PCHAR Address, ULONG Count, BOOLEAN MayFault,
CHAR (*GetContent)(PVOID Context, ULONG Offset), PVOID Context)
{
PCHAR Current;
PCHAR Page;
ULONG CountInPage;
ULONG i;
CHAR ch;
Current = Address;
while (Current < Address + Count)
{
Page = (PCHAR)PAGE_ROUND_DOWN(Current);
if (Address + Count <= Page + PAGE_SIZE)
{
/* Fits in this page */
CountInPage = Count;
}
else
{
/* Flows into next page, handle only current page in this iteration */
CountInPage = PAGE_SIZE - (Address - Page);
}
for (i = 0; i < CountInPage && !GspMemoryError; i++)
{
ch = (*GetContent)(Context, Current - Address);
if (MayFault)
GspWriteMemSafe(Current, ch);
else
*Current = ch;
Current++;
}
if (MayFault)
{
if (GspMemoryError)
return Current - Address;
}
}
return Current - Address;
}
static CHAR
GspHex2MemGetContent(PVOID Context, ULONG Offset)
{
return (CHAR)((HexValue(*((PCHAR)Context + 2 * Offset)) << 4) +
HexValue(*((PCHAR)Context + 2 * Offset + 1)));
}
/* Convert the hex array pointed to by Buffer into binary to be placed at Address
* Return a pointer to the character AFTER the last byte read from Buffer */
static PCHAR
GspHex2Mem(PCHAR Buffer, PCHAR Address, ULONG Count, BOOLEAN MayFault)
{
Count = GspWriteMem(Address, Count, MayFault, GspHex2MemGetContent, Buffer);
return Buffer + 2 * Count;
}
/**********************************************/
/* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
/* RETURN NUMBER OF CHARS PROCESSED */
/**********************************************/
LONG
GspHex2Long(PCHAR *Address, PLONG Value)
{
LONG NumChars = 0;
LONG Hex;
*Value = 0;
while (**Address)
{
Hex = HexValue(**Address);
if (Hex >= 0)
{
*Value = (*Value << 4) | Hex;
NumChars++;
}
else
{
break;
}
(*Address)++;
}
return NumChars;
}
VOID
GspLong2Hex(PCHAR *Address, LONG Value)
{
LONG Save;
Save = (((Value >> 0) & 0xff) << 24) | (((Value >> 8) & 0xff) << 16) |
(((Value >> 16) & 0xff) << 8) | (((Value >> 24) & 0xff) << 0);
*Address = GspMem2Hex((PCHAR)&Save, *Address, 4, FALSE);
}
/*
* When coming from kernel mode, Esp is not stored in the trap frame.
* Instead, it was pointing to the location of the TrapFrame Esp member
* when the exception occured. When coming from user mode, Esp is just
* stored in the TrapFrame Esp member.
*/
static LONG
GspGetEspFromTrapFrame(PKTRAP_FRAME TrapFrame)
{
return KeGetPreviousMode() == KernelMode ?
(LONG)&TrapFrame->HardwareEsp : (LONG)TrapFrame->HardwareEsp;
}
static VOID
GspGetRegisters(PCHAR Address, PKTRAP_FRAME TrapFrame)
{
ULONG_PTR Value;
PULONG p;
ULONG i;
PETHREAD Thread;
ULONG_PTR *KernelStack;
if (NULL == GspDbgThread)
{
Thread = PsGetCurrentThread();
}
else
{
TrapFrame = GspDbgThread->Tcb.TrapFrame;
Thread = GspDbgThread;
}
if (Waiting == Thread->Tcb.State)
{
KernelStack = Thread->Tcb.KernelStack;
for (i = 0; i < sizeof(GspRegisters) / sizeof(GspRegisters[0]); i++)
{
switch (i)
{
case EBP:
Value = KernelStack[3];
break;
case EDI:
Value = KernelStack[4];
break;
case ESI:
Value = KernelStack[5];
break;
case EBX:
Value = KernelStack[6];
break;
case PC:
Value = KernelStack[7];
break;
case ESP:
Value = (ULONG_PTR)(KernelStack + 8);
break;
case CS:
Value = KGDT_R0_CODE;
break;
case DS:
Value = KGDT_R0_DATA;
break;
default:
Value = 0;
break;
}
Address = GspMem2Hex((PCHAR)&Value, Address, GspRegisters[i].Size, FALSE);
}
}
else
{
for (i = 0; i < sizeof(GspRegisters) / sizeof(GspRegisters[0]); i++)
{
if (TrapFrame)
{
if (ESP == i)
{
Value = GspGetEspFromTrapFrame(TrapFrame);
}
else
{
p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[i].OffsetInTF);
Value = *p;
}
}
else if (i == PC)
{
/*
* This thread has not been sheduled yet so assume it
* is still in PsBeginThreadWithContextInternal().
*/
Value = (ULONG)KiThreadStartup;
}
else
{
Value = 0;
}
Address = GspMem2Hex((PCHAR)&Value, Address, GspRegisters[i].Size, FALSE);
}
}
}
VOID
GspSetRegistersInTrapFrame(PCHAR Address, PCONTEXT Context, PKTRAP_FRAME TrapFrame)
{
ULONG Value;
PCHAR Buffer;
PULONG p;
ULONG i;
if (!TrapFrame)
return;
Buffer = Address;
for (i = 0; i < NUMREGS; i++)
{
if (GspRegisters[i].SetInContext)
{
p = (PULONG)((ULONG_PTR)Context + GspRegisters[i].OffsetInContext);
}
else
{
p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[i].OffsetInTF);
}
Value = 0;
Buffer = GspHex2Mem(Buffer, (PCHAR)&Value, GspRegisters[i].Size, FALSE);
*p = Value;
}
}
VOID
GspSetSingleRegisterInTrapFrame(PCHAR Address, LONG Number, PCONTEXT Context, PKTRAP_FRAME TrapFrame)
{
ULONG Value;
PULONG p;
if (!TrapFrame)
return;
if (GspRegisters[Number].SetInContext)
{
p = (PULONG)((ULONG_PTR)Context + GspRegisters[Number].OffsetInContext);
}
else
{
p = (PULONG)((ULONG_PTR)TrapFrame + GspRegisters[Number].OffsetInTF);
}
Value = 0;
GspHex2Mem(Address, (PCHAR)&Value, GspRegisters[Number].Size, FALSE);
*p = Value;
}
BOOLEAN
GspFindThread(PCHAR Data, PETHREAD *Thread)
{
PETHREAD ThreadInfo = NULL;
if (strcmp(Data, "-1") == 0)
{
/* All threads */
ThreadInfo = NULL;
}
else
{
ULONG uThreadId;
HANDLE ThreadId;
PCHAR ptr = &Data[0];
GspHex2Long(&ptr, (PLONG)&uThreadId);
ThreadId = (HANDLE)uThreadId;
if (!NT_SUCCESS(PsLookupThreadByThreadId(ThreadId, &ThreadInfo)))
{
*Thread = NULL;
return FALSE;
}
}
*Thread = ThreadInfo;
return TRUE;
}
VOID
GspSetThread(PCHAR Request)
{
PETHREAD ThreadInfo;
PCHAR ptr = &Request[1];
switch (Request[0])
{
case 'c': /* Run thread */
if (GspFindThread(ptr, &ThreadInfo))
{
GspOutBuffer[0] = 'O';
GspOutBuffer[1] = 'K';
if (NULL != GspRunThread)
ObDereferenceObject(GspRunThread);
GspRunThread = ThreadInfo;
if (NULL != GspRunThread)
ObReferenceObject(GspRunThread);
}
else
{
GspOutBuffer[0] = 'E';
}
break;
case 'g': /* Debug thread */
if (GspFindThread(ptr, &ThreadInfo))
{
GspOutBuffer[0] = 'O';
GspOutBuffer[1] = 'K';
if (NULL != GspDbgThread)
ObDereferenceObject(GspDbgThread);
if (ThreadInfo == PsGetCurrentThread())
{
GspDbgThread = NULL;
ObDereferenceObject(ThreadInfo);
}
else
{
GspDbgThread = ThreadInfo;
}
}
else
{
GspOutBuffer[0] = 'E';
}
break;
default:
break;
}
}
VOID
GspQuery(PCHAR Request)
{
ULONG Value;
if (strncmp(Request, "C", 1) == 0)
{
PCHAR ptr = &GspOutBuffer[2];
/* Get current thread id */
GspOutBuffer[0] = 'Q';
GspOutBuffer[1] = 'C';
if (NULL != GspDbgThread)
Value = (ULONG)GspDbgThread->Cid.UniqueThread;
else
Value = (ULONG)PsGetCurrentThread()->Cid.UniqueThread;
GspLong2Hex(&ptr, Value);
}
else if (strncmp(Request, "fThreadInfo", 11) == 0)
{
PEPROCESS Process;
PLIST_ENTRY AThread, AProcess;
PCHAR ptr = &GspOutBuffer[1];
/* Get first thread id */
GspEnumThread = NULL;
AProcess = PsActiveProcessHead.Flink;
while (AProcess != &PsActiveProcessHead)
{
Process = CONTAINING_RECORD(AProcess, EPROCESS, ActiveProcessLinks);
AThread = Process->ThreadListHead.Flink;
if (AThread != &Process->ThreadListHead)
{
GspEnumThread = CONTAINING_RECORD(Process->ThreadListHead.Flink,
ETHREAD, ThreadListEntry);
break;
}
AProcess = AProcess->Flink;
}
if (GspEnumThread != NULL)
{
GspOutBuffer[0] = 'm';
Value = (ULONG)GspEnumThread->Cid.UniqueThread;
GspLong2Hex(&ptr, Value);
}
else
{
/* FIXME - what to do here? This case should never happen though, there
should always be at least one thread on the system... */
/* GspOutBuffer[0] = 'l'; */
}
}
else if (strncmp(Request, "sThreadInfo", 11) == 0)
{
PEPROCESS Process;
PLIST_ENTRY AThread, AProcess;
PCHAR ptr = &GspOutBuffer[1];
/* Get next thread id */
if (GspEnumThread != NULL)
{
/* find the next thread */
Process = GspEnumThread->ThreadsProcess;
if (GspEnumThread->ThreadListEntry.Flink != &Process->ThreadListHead)
{
GspEnumThread = CONTAINING_RECORD(GspEnumThread->ThreadListEntry.Flink,
ETHREAD, ThreadListEntry);
}
else
{
PETHREAD Thread = NULL;
AProcess = Process->ActiveProcessLinks.Flink;
while (AProcess != &PsActiveProcessHead)
{
Process = CONTAINING_RECORD(AProcess, EPROCESS, ActiveProcessLinks);
AThread = Process->ThreadListHead.Flink;
if (AThread != &Process->ThreadListHead)
{
Thread = CONTAINING_RECORD(Process->ThreadListHead.Flink,
ETHREAD, ThreadListEntry);
break;
}
AProcess = AProcess->Flink;
}
GspEnumThread = Thread;
}
if (GspEnumThread != NULL)
{
/* return the ID */
GspOutBuffer[0] = 'm';
Value = (ULONG)GspEnumThread->Cid.UniqueThread;
GspLong2Hex(&ptr, Value);
}
else
{
GspOutBuffer[0] = 'l';
}
}
else
{
GspOutBuffer[0] = 'l';
}
}
else if (strncmp(Request, "ThreadExtraInfo", 15) == 0)
{
PETHREAD ThreadInfo;
/* Get thread information */
if (GspFindThread(Request + 16, &ThreadInfo))
{
char Buffer[64];
PEPROCESS Proc;
Proc = (PEPROCESS)ThreadInfo->ThreadsProcess;
Buffer[0] = '\0';
if (NULL != Proc)
sprintf(Buffer, "%s [%d:0x%x], ",
Proc->ImageFileName,
(int)Proc->UniqueProcessId,
(int)ThreadInfo->Cid.UniqueThread);
strcpy(Buffer + strlen(Buffer), GspThreadStates[ThreadInfo->Tcb.State]);
ObDereferenceObject(ThreadInfo);
GspMem2Hex(Buffer, &GspOutBuffer[0], strlen(Buffer), FALSE);
}
}
else if (strncmp(Request, "Supported", 9) == 0)
{
/* tell maximum incoming packet size */
sprintf(GspOutBuffer, "PacketSize=%u", sizeof(GspInBuffer) - 1);
}
else if (strncmp(Request, "Rcmd,", 5) == 0)
{
}
}
VOID
GspQueryThreadStatus(PCHAR Request)
{
PETHREAD ThreadInfo;
PCHAR ptr = &Request[0];
if (GspFindThread(ptr, &ThreadInfo))
{
ObDereferenceObject(ThreadInfo);
GspOutBuffer[0] = 'O';
GspOutBuffer[1] = 'K';
GspOutBuffer[2] = '\0';
}
else
{
GspOutBuffer[0] = 'E';
GspOutBuffer[1] = '\0';
}
}
#define DR6_BS 0x00004000 /* Single step */
#define DR7_L0 0x00000001 /* Local breakpoint 0 enable */
#define DR7_G0 0x00000002 /* Global breakpoint 0 enable */
#define DR7_L1 0x00000004 /* Local breakpoint 1 enable */
#define DR7_G1 0x00000008 /* Global breakpoint 1 enable */
#define DR7_L2 0x00000010 /* Local breakpoint 2 enable */
#define DR7_G2 0x00000020 /* Global breakpoint 2 enable */
#define DR7_L3 0x00000040 /* Local breakpoint 3 enable */
#define DR7_G3 0x00000080 /* Global breakpoint 3 enable */
#define DR7_LE 0x00000100 /* Local exact breakpoint enable (old) */
#define DR7_GE 0x00000200 /* Global exact breakpoint enable (old) */
#define DR7_GD 0x00002000 /* General detect enable */
#define DR7_TYPE0_MASK 0x00030000 /* Breakpoint 0 condition */
#define DR7_LEN0_MASK 0x000c0000 /* Breakpoint 0 length */
#define DR7_TYPE1_MASK 0x00300000 /* Breakpoint 1 condition */
#define DR7_LEN1_MASK 0x00c00000 /* Breakpoint 1 length */
#define DR7_TYPE2_MASK 0x03000000 /* Breakpoint 2 condition */
#define DR7_LEN2_MASK 0x0c000000 /* Breakpoint 2 length */
#define DR7_TYPE3_MASK 0x30000000 /* Breakpoint 3 condition */
#define DR7_LEN3_MASK 0xc0000000 /* Breakpoint 3 length */
#define DR7_GLOBAL_ENABLE(Bp) (2 << (2 * (Bp)))
#define DR7_TYPE(Bp, Type) ((Type) << (16 + 4 * (Bp)))
#define DR7_LEN(Bp, Len) ((Len) << (18 + 4 * (Bp)))
#define I386_BP_TYPE_EXECUTE 0
#define I386_BP_TYPE_DATA_WRITE 1
#define I386_BP_TYPE_DATA_READWRITE 3
#define I386_OPCODE_INT3 0xcc
#define GDB_ZTYPE_MEMORY_BREAKPOINT 0
#define GDB_ZTYPE_HARDWARE_BREAKPOINT 1
#define GDB_ZTYPE_WRITE_WATCHPOINT 2
#define GDB_ZTYPE_READ_WATCHPOINT 3
#define GDB_ZTYPE_ACCESS_WATCHPOINT 4
typedef struct _GSPHWBREAKPOINT
{
ULONG Type;
ULONG_PTR Address;
ULONG Length;
} GSPHWBREAKPOINT;
#define MAX_HW_BREAKPOINTS 4
static unsigned GspHwBreakpointCount = 0;
static GSPHWBREAKPOINT GspHwBreakpoints[MAX_HW_BREAKPOINTS];
typedef struct _GSPSWBREAKPOINT
{
ULONG_PTR Address;
CHAR PrevContent;
BOOLEAN Active;
} GSPSWBREAKPOINT;
#define MAX_SW_BREAKPOINTS 64
static unsigned GspSwBreakpointCount = 0;
static GSPSWBREAKPOINT GspSwBreakpoints[MAX_SW_BREAKPOINTS];
static void
GspSetHwBreakpoint(ULONG Type, ULONG_PTR Address, ULONG Length)
{
DPRINT("GspSetHwBreakpoint(%lu, 0x%p, %lu)\n", Type, Address, Length);
if (GDB_ZTYPE_READ_WATCHPOINT == Type)
{
DPRINT1("Read watchpoint not supported\n");
strcpy(GspOutBuffer, "E22");
}
else if (GDB_ZTYPE_HARDWARE_BREAKPOINT == Type && 1 != Length)
{
DPRINT1("Invalid length %lu for hardware breakpoint\n", Length);
strcpy(GspOutBuffer, "E22");
}
else if (1 != Length && 2 != Length && 4 != Length)
{
DPRINT1("Invalid length %lu for GDB Z type %lu\n", Length, Type);
strcpy(GspOutBuffer, "E22");
}
else if (0 != (Address & (Length - 1)))
{
DPRINT1("Invalid alignment for address 0x%p and length %d\n", Address, Length);
strcpy(GspOutBuffer, "E22");
}
else if (MAX_HW_BREAKPOINTS == GspHwBreakpointCount)
{
DPRINT1("Trying to set too many hardware breakpoints\n");
strcpy(GspOutBuffer, "E22");
}
else
{
DPRINT("Stored at index %u\n", GspHwBreakpointCount);
GspHwBreakpoints[GspHwBreakpointCount].Type = Type;
GspHwBreakpoints[GspHwBreakpointCount].Address = Address;
GspHwBreakpoints[GspHwBreakpointCount].Length = Length;
GspHwBreakpointCount++;
strcpy(GspOutBuffer, "OK");
}
}
static void
GspRemoveHwBreakpoint(ULONG Type, ULONG_PTR Address, ULONG Length)
{
unsigned Index;
DPRINT("GspRemoveHwBreakpoint(%lu, 0x%p, %lu)\n", Type, Address, Length);
for (Index = 0; Index < GspHwBreakpointCount; Index++)
{
if (GspHwBreakpoints[Index].Type == Type &&
GspHwBreakpoints[Index].Address == Address &&
GspHwBreakpoints[Index].Length == Length)
{
DPRINT("Found match at index %u\n", Index);
if (Index + 1 < GspHwBreakpointCount)
memmove(GspHwBreakpoints + Index,
GspHwBreakpoints + (Index + 1),
(GspHwBreakpointCount - Index - 1) * sizeof(GSPHWBREAKPOINT));
GspHwBreakpointCount--;
strcpy(GspOutBuffer, "OK");
return;
}
}
DPRINT1("Not found\n");
strcpy(GspOutBuffer, "E22");
}
static BOOLEAN
GspFindSwBreakpoint(ULONG_PTR Address, PULONG PIndex)
{
ULONG Index;
for (Index = 0; Index < GspSwBreakpointCount; Index++)
{
if (GspSwBreakpoints[Index].Address == Address)
{
if (PIndex)
*PIndex = Index;
return TRUE;
}
}
return FALSE;
}
static void
GspSetSwBreakpoint(ULONG_PTR Address)
{
DPRINT("GspSetSwBreakpoint(0x%p)\n", Address);
if (MAX_SW_BREAKPOINTS == GspSwBreakpointCount)
{
DPRINT1("Trying to set too many software breakpoints\n");
strcpy(GspOutBuffer, "E22");
return;
}
if (GspFindSwBreakpoint(Address, NULL))
{
strcpy(GspOutBuffer, "E22");
return;
}
DPRINT("Stored at index %u\n", GspSwBreakpointCount);
GspSwBreakpoints[GspSwBreakpointCount].Address = Address;
GspSwBreakpoints[GspSwBreakpointCount].Active = FALSE;
GspSwBreakpointCount++;
strcpy(GspOutBuffer, "OK");
}
static void
GspRemoveSwBreakpoint(ULONG_PTR Address)
{
ULONG Index;
DPRINT("GspRemoveSwBreakpoint(0x%p)\n", Address);
if (GspFindSwBreakpoint(Address, &Index))
{
DPRINT("Found match at index %u\n", Index);
ASSERT(!GspSwBreakpoints[Index].Active);
if (Index + 1 < GspSwBreakpointCount)
memmove(GspSwBreakpoints + Index,
GspSwBreakpoints + (Index + 1),
(GspSwBreakpointCount - Index - 1) * sizeof(GSPSWBREAKPOINT));
GspSwBreakpointCount--;
strcpy(GspOutBuffer, "OK");
return;
}
DPRINT1("Not found\n");
strcpy(GspOutBuffer, "E22");
}
static void
GspLoadHwBreakpoint(PKTRAP_FRAME TrapFrame, unsigned BpIndex,
ULONG_PTR Address, ULONG Length, ULONG Type)
{
DPRINT("GspLoadHwBreakpoint(0x%p, %d, 0x%p, %d)\n",
TrapFrame, BpIndex, Address, Type);
/* Set the DR7_Gx bit to globally enable the breakpoint */
TrapFrame->Dr7 |= DR7_GLOBAL_ENABLE(BpIndex) |
DR7_LEN(BpIndex, Length) |
DR7_TYPE(BpIndex, Type);
switch (BpIndex)
{
case 0:
DPRINT("Setting DR0 to 0x%p\n", Address);
TrapFrame->Dr0 = Address;
break;
case 1:
DPRINT("Setting DR1 to 0x%p\n", Address);
TrapFrame->Dr1 = Address;
break;
case 2:
DPRINT("Setting DR2 to 0x%p\n", Address);
TrapFrame->Dr2 = Address;
break;
case 3:
DPRINT("Setting DR3 to 0x%p\n", Address);
TrapFrame->Dr3 = Address;
break;
}
}
static void
GspLoadSwBreakpoint(ULONG Index)
{
GspMemoryError = FALSE;
GspSwBreakpoints[Index].PrevContent = GspReadMemSafe((PCHAR)GspSwBreakpoints[Index].Address);
if (!GspMemoryError)
GspWriteMemSafe((PCHAR)GspSwBreakpoints[Index].Address, I386_OPCODE_INT3);
GspSwBreakpoints[Index].Active = !GspMemoryError;
if (GspMemoryError)
DPRINT1("Failed to set software breakpoint at 0x%p\n", GspSwBreakpoints[Index].Address);
else
DPRINT("Successfully set software breakpoint at 0x%p\n", GspSwBreakpoints[Index].Address);
}
static void
GspLoadBreakpoints(PKTRAP_FRAME TrapFrame)
{
unsigned Index;
ULONG i386Type;
DPRINT("GspLoadBreakpoints\n");
DPRINT("DR7 on entry: 0x%08x\n", TrapFrame->Dr7);
/* Remove all breakpoints */
TrapFrame->Dr7 &= ~(DR7_L0 | DR7_L1 | DR7_L2 | DR7_L3 |
DR7_G0 | DR7_G1 | DR7_G2 | DR7_G3 |
DR7_TYPE0_MASK | DR7_LEN0_MASK |
DR7_TYPE1_MASK | DR7_LEN1_MASK |
DR7_TYPE2_MASK | DR7_LEN2_MASK |
DR7_TYPE3_MASK | DR7_LEN3_MASK);
for (Index = 0; Index < GspHwBreakpointCount; Index++)
{
switch (GspHwBreakpoints[Index].Type)
{
case GDB_ZTYPE_HARDWARE_BREAKPOINT:
i386Type = I386_BP_TYPE_EXECUTE;
break;
case GDB_ZTYPE_WRITE_WATCHPOINT:
i386Type = I386_BP_TYPE_DATA_WRITE;
break;
case GDB_ZTYPE_ACCESS_WATCHPOINT:
i386Type = I386_BP_TYPE_DATA_READWRITE;
break;
default:
ASSERT(FALSE);
i386Type = I386_BP_TYPE_EXECUTE;
break;
}
GspLoadHwBreakpoint(TrapFrame, Index, GspHwBreakpoints[Index].Address,
GspHwBreakpoints[Index].Length - 1, i386Type);
}
for (Index = 0; Index < GspSwBreakpointCount; Index++)
{
DPRINT("Using real software breakpoint\n");
GspLoadSwBreakpoint(Index);
}
DPRINT("Final DR7 value 0x%08x\n", TrapFrame->Dr7);
}
static void
GspUnloadBreakpoints(void)
{
unsigned Index;
DPRINT("GspUnloadBreakpoints\n");
/* Disable hardware debugging while we are inside the stub */
__writedr(7, 0);
for (Index = 0; Index < GspSwBreakpointCount; Index++)
{
if (GspSwBreakpoints[Index].Active)
{
GspMemoryError = FALSE;
GspWriteMemSafe((PCHAR)GspSwBreakpoints[Index].Address,
GspSwBreakpoints[Index].PrevContent);
GspSwBreakpoints[Index].Active = FALSE;
if (GspMemoryError)
{
DPRINT1("Failed to remove software breakpoint from 0x%p\n",
GspSwBreakpoints[Index].Address);
}
else
{
DPRINT("Successfully removed software breakpoint from 0x%p\n",
GspSwBreakpoints[Index].Address);
}
}
}
}
static void
GspStopReply(NTSTATUS ExceptionCode, PKTRAP_FRAME TrapFrame)
{
PCHAR ptr = GspOutBuffer;
ULONG SigVal;
LONG Esp;
switch (ExceptionCode)
{
case STATUS_INTEGER_DIVIDE_BY_ZERO:
SigVal = 8; /* divide by zero */
break;
case STATUS_SINGLE_STEP:
case STATUS_BREAKPOINT:
SigVal = 5; /* breakpoint */
break;
case STATUS_INTEGER_OVERFLOW:
case STATUS_ARRAY_BOUNDS_EXCEEDED:
SigVal = 16; /* bound instruction */
break;
case STATUS_ILLEGAL_INSTRUCTION:
SigVal = 4; /* Invalid opcode */
break;
case STATUS_STACK_OVERFLOW:
case STATUS_DATATYPE_MISALIGNMENT:
case STATUS_ACCESS_VIOLATION:
SigVal = 11; /* access violation */
break;
default:
SigVal = 7; /* "software generated" */
}
ptr = GspOutBuffer;
*ptr++ = 'T'; /* notify gdb with signo, PC, FP and SP */
*ptr++ = HexChars[(SigVal >> 4) & 0xf];
*ptr++ = HexChars[SigVal & 0xf];
*ptr++ = HexChars[ESP];
*ptr++ = ':';
Esp = GspGetEspFromTrapFrame(TrapFrame); /* SP */
ptr = GspMem2Hex((PCHAR)&Esp, ptr, 4, 0);
*ptr++ = ';';
*ptr++ = HexChars[EBP];
*ptr++ = ':';
ptr = GspMem2Hex((PCHAR)&TrapFrame->Ebp, ptr, 4, 0); /* FP */
*ptr++ = ';';
*ptr++ = HexChars[PC];
*ptr++ = ':';
ptr = GspMem2Hex((PCHAR)&TrapFrame->Eip, ptr, 4, 0); /* PC */
*ptr++ = ';';
*ptr = '\0';
}
/*
* This function does all command procesing for interfacing to GDB.
*/
KD_CONTINUE_TYPE
NTAPI
KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
PCONTEXT Context,
PKTRAP_FRAME TrapFrame)
{
static BOOLEAN GdbAttached = FALSE;
BOOLEAN Stepping = FALSE;
NTSTATUS ExceptionCode;
LONG Address;
LONG Length;
PCHAR ptr;
/* FIXME: Stop on other CPUs too */
DPRINT("Thread %p entering stub\n", PsGetCurrentThread());
ExceptionCode = (NTSTATUS)ExceptionRecord->ExceptionCode;
/* Can only debug 1 thread at a time... */
ExAcquireFastMutex(&GspLock);
DPRINT("Thread %p acquired mutex\n", PsGetCurrentThread());
GspUnloadBreakpoints();
/* Make sure we're debugging the current thread. */
if (NULL != GspDbgThread)
{
DPRINT1("Internal error: entering stub with non-NULL GspDbgThread\n");
ObDereferenceObject(GspDbgThread);
GspDbgThread = NULL;
}
if (GdbAttached)
{
GspStopReply(ExceptionCode, TrapFrame);
GspPutPacket(GspOutBuffer);
// DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
}
else
{
GdbAttached = TRUE;
}
while (TRUE)
{
/* Zero the buffer now so we don't have to worry about the terminating zero character */
memset(GspOutBuffer, 0, sizeof(GspOutBuffer));
ptr = GspGetPacket();
// DbgPrint("<<< (%s) <<<\n", ptr);
switch (*ptr++)
{
case '?':
/* a little hack to send more complete status information */
GspStopReply(ExceptionCode, TrapFrame);
break;
case 'd':
GspRemoteDebug = !GspRemoteDebug; /* toggle debug flag */
break;
case 'g': /* return the value of the CPU Registers */
GspGetRegisters(GspOutBuffer, TrapFrame);
break;
case 'G': /* set the value of the CPU Registers - return OK */
if (NULL != GspDbgThread)
GspSetRegistersInTrapFrame(ptr, Context, GspDbgThread->Tcb.TrapFrame);
else
GspSetRegistersInTrapFrame(ptr, Context, TrapFrame);
strcpy(GspOutBuffer, "OK");
break;
case 'P': /* set the value of a single CPU register - return OK */
{
LONG Register;
if ((GspHex2Long(&ptr, &Register)) && (*ptr++ == '='))
{
if ((Register >= 0) && (Register < NUMREGS))
{
if (GspDbgThread)
{
GspSetSingleRegisterInTrapFrame(ptr, Register, Context,
GspDbgThread->Tcb.TrapFrame);
}
else
{
GspSetSingleRegisterInTrapFrame(ptr, Register, Context, TrapFrame);
}
strcpy(GspOutBuffer, "OK");
break;
}
}
strcpy(GspOutBuffer, "E01");
break;
}
/* mAA..AA,LLLL Read LLLL bytes at address AA..AA */
case 'm':
{
/* TRY TO READ %x,%x. IF SUCCEED, SET PTR = 0 */
if (GspHex2Long(&ptr, &Address) && *(ptr++) == ',' && GspHex2Long(&ptr, &Length))
{
PEPROCESS DbgProcess = NULL;
ptr = NULL;
if (NULL != GspDbgThread &&
PsGetCurrentProcess() != GspDbgThread->ThreadsProcess)
{
DbgProcess = GspDbgThread->ThreadsProcess;
KeAttachProcess(&DbgProcess->Pcb);
}
GspMemoryError = FALSE;
GspMem2Hex((PCHAR)Address, GspOutBuffer, Length, 1);
if (NULL != DbgProcess)
KeDetachProcess();
if (GspMemoryError)
{
strcpy(GspOutBuffer, "E03");
DPRINT1("Fault during memory read\n");
}
ptr = NULL;
}
if (NULL != ptr)
strcpy(GspOutBuffer, "E01");
break;
}
/* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
case 'M':
{
/* TRY TO READ '%x,%x:'. IF SUCCEED, SET PTR = 0 */
if (GspHex2Long(&ptr, &Address))
{
if (*(ptr++) == ',' && GspHex2Long(&ptr, &Length) && *(ptr++) == ':')
{
PEPROCESS DbgProcess = NULL;
if (NULL != GspDbgThread &&
PsGetCurrentProcess() != GspDbgThread->ThreadsProcess)
{
DbgProcess = GspDbgThread->ThreadsProcess;
KeAttachProcess(&DbgProcess->Pcb);
}
GspMemoryError = FALSE;
GspHex2Mem(ptr, (PCHAR)Address, Length, TRUE);
if (NULL != DbgProcess)
KeDetachProcess();
if (GspMemoryError)
{
strcpy(GspOutBuffer, "E03");
DPRINT1("Fault during memory write\n");
}
else
{
strcpy(GspOutBuffer, "OK");
}
ptr = NULL;
}
}
if (NULL != ptr)
strcpy(GspOutBuffer, "E02");
break;
}
/* cAA..AA Continue at address AA..AA */
/* sAA..AA Step one instruction from AA..AA */
case 's':
Stepping = TRUE;
case 'c':
{
ULONG BreakpointNumber;
ULONG Dr6;
/* try to read optional parameter, pc changed if param */
if (GspHex2Long(&ptr, &Address))
{
Context->Eip = Address;
}
else if (ExceptionCode == STATUS_BREAKPOINT)
{
if (GspReadMemSafe((PCHAR)Context->Eip) == (CHAR)I386_OPCODE_INT3)
Context->Eip++;
}
/* clear the trace bit */
Context->EFlags &= ~EFLAGS_TF;
/* set the trace bit if we're Stepping */
if (Stepping)
Context->EFlags |= EFLAGS_TF;
Dr6 = __readdr(6);
if (!(Dr6 & DR6_BS))
{
for (BreakpointNumber = 0;
BreakpointNumber < MAX_HW_BREAKPOINTS;
BreakpointNumber++)
{
if (Dr6 & (1 << BreakpointNumber))
{
if (GspHwBreakpoints[BreakpointNumber].Type == I386_BP_TYPE_EXECUTE)
{
/* Set restore flag */
Context->EFlags |= EFLAGS_RF;
break;
}
}
}
}
GspLoadBreakpoints(TrapFrame);
__writedr(6, 0);
if (NULL != GspDbgThread)
{
ObDereferenceObject(GspDbgThread);
GspDbgThread = NULL;
}
DPRINT("Thread %p releasing mutex\n", PsGetCurrentThread());
ExReleaseFastMutex(&GspLock);
DPRINT("Thread %p leaving stub\n", PsGetCurrentThread());
if (ExceptionCode == STATUS_BREAKPOINT ||
ExceptionCode == STATUS_SINGLE_STEP)
return kdContinue;
return kdHandleException;
}
case 'k': /* kill the program */
strcpy(GspOutBuffer, "OK");
break;
case 'H': /* Set thread */
GspSetThread(ptr);
break;
case 'q': /* Query */
GspQuery(ptr);
break;
case 'T': /* Query thread status */
GspQueryThreadStatus(ptr);
break;
case 'Z':
{
My biggest commit so far (everything compiles and apparently runs fine): - replaced DWORD with ULONG in a couple of places - replaced some ULONGs with LONGs in the KD GDB stub - replaced INITIAL_TEB with USER_STACK, as per Nebbet's book, to support both fixed size and expandable stacks - added InterlockedExchangePointer - added the ASM_BREAKPOINT macro as the architecture-dependent assembler code to raise a breakpoint exception - corrected definitions of INT, LONG, DWORD, UINT, ULONG and ULONG32 - corrected IoSetCancelRoutine to use InterlockedExchangePointer - corrected definition of NtCurrentTeb and NtCurrentPeb - corrected DbgBreakPoint and DbgUserBreakPoint not to set up a stack frame (temporary fix with inline assembler - why doesn't GCC understand __declspec(naked)?) - corrected various calls to Interlocked* functions to cast OUT operands to LONG * - corrected various printf format strings - corrected DbgUiIssueRemoteBreakin to use the smallest possible stack (this is what started everything) - removed a DPRINT that accessed pageable memory at non-PASSIVE_LEVEL IRQL - beautified CreateProcessA (another temporary fix - all the new functions will be isolated in the upcoming stand-alone RTL) - prefixed LdrInitializeThunk with a nop that can be overwritten with a breakpoint for debugging purposes (temporary debugging aid until we have user-mode debugger support). Will add support for this to the breakin utility soon - thread creation code rewritten from scratch (some glitches documented inline, but works fine) - thread creation code now duplicated just twice, as opposed to five times (temporary fix - three new, non standard functions have been exported from NTDLL.DLL, will fix later) svn path=/trunk/; revision=4595
2003-04-26 23:13:33 +00:00
LONG Type;
LONG Address;
LONG Length;
GspHex2Long(&ptr, &Type);
ptr++;
GspHex2Long(&ptr, &Address);
ptr++;
GspHex2Long(&ptr, &Length);
if (0 == Type)
GspSetSwBreakpoint((ULONG_PTR)Address);
else
GspSetHwBreakpoint(Type, (ULONG_PTR)Address, Length);
break;
}
case 'z':
{
LONG Type;
LONG Address;
LONG Length;
GspHex2Long(&ptr, &Type);
ptr++;
GspHex2Long(&ptr, &Address);
ptr++;
GspHex2Long(&ptr, &Length);
if (0 == Type)
GspRemoveSwBreakpoint((ULONG_PTR)Address);
else
GspRemoveHwBreakpoint(Type, (ULONG_PTR)Address, Length);
break;
}
default:
break;
}
/* reply to the request */
GspPutPacket(GspOutBuffer);
// DbgPrint(">>> (%s) >>>\n", GspOutBuffer);
}
}
BOOLEAN
NTAPI
GspBreakIn(PKINTERRUPT Interrupt, PVOID ServiceContext)
{
PKTRAP_FRAME TrapFrame;
BOOLEAN DoBreakIn;
CONTEXT Context;
KIRQL OldIrql;
UCHAR Value;
DPRINT("Break In\n");
DoBreakIn = FALSE;
while (KdPortGetByteEx(&GdbPortInfo, &Value))
{
if (Value == 0x03)
DoBreakIn = TRUE;
}
if (!DoBreakIn)
return TRUE;
KeRaiseIrql(HIGH_LEVEL, &OldIrql);
TrapFrame = PsGetCurrentThread()->Tcb.TrapFrame;
KeTrapFrameToContext(TrapFrame, NULL, &Context);
KdpGdbEnterDebuggerException(NULL, &Context, TrapFrame);
KeContextToTrapFrame(&Context, NULL, TrapFrame, Context.ContextFlags, KernelMode);
KeLowerIrql(OldIrql);
return TRUE;
}
VOID
NTAPI
KdpGdbDebugPrint(PCH Message, ULONG Length)
{
}
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
/* Initialize the GDB stub */
VOID
NTAPI
KdpGdbStubInit(PKD_DISPATCH_TABLE WrapperTable, ULONG BootPhase)
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
{
if (!KdDebuggerEnabled || !KdpDebugMode.Gdb)
return;
if (BootPhase == 0)
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
{
ExInitializeFastMutex(&GspLock);
/* Write out the functions that we support for now */
WrapperTable->KdpInitRoutine = KdpGdbStubInit;
WrapperTable->KdpPrintRoutine = KdpGdbDebugPrint;
WrapperTable->KdpExceptionRoutine = KdpGdbEnterDebuggerException;
/* Initialize the Port */
KdPortInitializeEx(&GdbPortInfo, GdbPortNumber);
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
}
else if (BootPhase == 1)
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
{
GspInitialized = TRUE;
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
GspRunThread = NULL;
GspDbgThread = NULL;
GspEnumThread = NULL;
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
HalDisplayString("Waiting for GDB to attach\r\n");
DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
}
else if (BootPhase == 2)
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
{
HalDisplayString("\r\n GDB debugging enabled\r\n\r\n");
KD System Rewrite: - Totally dynamic based on the principle of Native Providers built-in the Kernel (like Screen, FileLog and Serial) and a pluggable Wrapper which is optionally compiled (Bochs, GDB) - Nothing changed in KDBG, except for that its settings (KDSERIAL/KDNOECHO) are now stored in KdbDebugState instead. - Wrappers are currently built uncondtionally. With rbuild, I'll make them easily removable. - Debug Log code simplified greatly, sped up and now supports printing even the first boot messages, which wasn't supported before. - Removed most of KDBG compile-time settings, ones which are needed are in include/dbg as macros now. - Left in some kdbg init code and break code, but it could be made to be used as a 'wrapper' for those functions. I will do it later. - Made a hack for KdpEnterDebuggerException..it seems to be called differently and at different times for GDB vs KDBG and I couldn't unite them. - KdpServiceDispatcher now does both the documented and ros-internal debug functions and will eventually be called through INT2D from keyboard.sys instead of as an API. All in all, this patch makes KD separated from KDBG and creates a pluggable architecture for creating future wrappers that don't require changing tons of code in the future. It improves the debug log by printing even the earliest debug messages to it and it removes many of the manual ifdef(KDBG) but making them automatic though a single macro file. It makes extra debugging functionality optional and it allows removal of a private API from our exports. svn path=/trunk/; revision=14799
2005-04-25 14:44:48 +00:00
}
}
/* EOF */