--- /dev/null
+/****************************************************************************
+
+ 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.
+
+****************************************************************************/
+
+/****************************************************************************
+ * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
+ *
+ * Module name: remcom.c $
+ * Revision: 1.34 $
+ * Date: 91/03/09 12:29:49 $
+ * Contributor: Lake Stevens Instrument Division$
+ *
+ * Description: low level support for gdb debugger. $
+ *
+ * Considerations: only works on target hardware $
+ *
+ * Written by: Glenn Engel $
+ * ModuleState: Experimental $
+ *
+ * NOTES: See Below $
+ *
+ * Modified for 386 by Jim Kingdon, Cygnus Support.
+ * Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * To enable debugger support, two things need to happen. One, setting
+ * up a routine so that it is in the exception path, is necessary in order
+ * to allow any breakpoints or error conditions to be properly intercepted
+ * and reported to gdb.
+ * Two, a breakpoint needs to be generated to begin communication.
+ *
+ * Because gdb will sometimes write to the stack area to execute function
+ * calls, this program cannot rely on using the supervisor stack so it
+ * uses it's own stack area.
+ *
+ *************
+ *
+ * The following gdb commands are supported:
+ *
+ * command function Return value
+ *
+ * g return the value of the CPU Registers hex data or ENN
+ * G set the value of the CPU Registers OK or ENN
+ *
+ * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN
+ * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN
+ *
+ * c Resume at current address SNN ( signal NN)
+ * cAA..AA Continue at address AA..AA SNN
+ *
+ * s Step one instruction SNN
+ * sAA..AA Step one instruction from AA..AA SNN
+ *
+ * k kill
+ *
+ * ? What was the last sigval ? SNN (signal NN)
+ *
+ * All commands and responses are sent with a packet which includes a
+ * Checksum. A packet consists of
+ *
+ * $<packet info>#<Checksum>.
+ *
+ * where
+ * <packet info> :: <characters representing the command or response>
+ * <Checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>>
+ *
+ * When a packet is received, it is first acknowledged with either '+' or '-'.
+ * '+' indicates a successful transfer. '-' indicates a failed transfer.
+ *
+ * Example:
+ *
+ * Host: Reply:
+ * $m0,10#2a +$00010203040506070809101112131415#42
+ *
+ ****************************************************************************/
+
+#include <ntoskrnl.h>
+#define NDEBUG
+#include <internal/debug.h>
+
+/************************************************************************/
+/* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
+/* at least NUMREGBYTES*2 are needed for register packets */
+#define BUFMAX 1000
+
+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;
+KD_PORT_INFORMATION GdbPortInfo = { 2, 115200, 0 }; /* FIXME hardcoded for COM2, 115200 baud */
+
+/* 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] =
+{
+};
+
+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;
+}
+
+static CHAR GspInBuffer[BUFMAX];
+static CHAR GspOutBuffer[BUFMAX];
+
+VOID
+GdbPutChar(UCHAR Value)
+{
+ KdPortPutByteEx(&GdbPortInfo, Value);
+}
+
+UCHAR
+GdbGetChar(VOID)
+{
+ UCHAR Value;
+
+ while (!KdPortGetByteEx(&GdbPortInfo, &Value))
+ ;
+
+ return Value;
+}
+
+/* scan for the sequence $<data>#<Checksum> */
+
+PCHAR
+GspGetPacket()
+{
+ 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 < BUFMAX)
+ {
+ 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 volatile void *GspAccessLocation = NULL;
+
+static CHAR
+GspReadMemSafe(PCHAR Address)
+{
+ CHAR ch;
+
+ if (NULL == Address)
+ {
+ GspMemoryError = TRUE;
+ return '\0';
+ }
+
+ GspAccessLocation = Address;
+ ch = *Address;
+ GspAccessLocation = NULL;
+
+ return ch;
+}
+
+/* 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;
+ ULONG OldProt = 0;
+
+ 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);
+ }
+ if (MayFault)
+ {
+ OldProt = MmGetPageProtect(NULL, Address);
+ MmSetPageProtect(NULL, Address, PAGE_EXECUTE_READWRITE);
+ }
+
+ for (i = 0; i < CountInPage && ! GspMemoryError; i++)
+ {
+ ch = (*GetContent)(Context, Current - Address);
+
+ if (MayFault)
+ {
+ GspAccessLocation = Current;
+ }
+ *Current = ch;
+ if (MayFault)
+ {
+ GspAccessLocation = NULL;
+ }
+ Current++;
+ }
+ if (MayFault)
+ {
+ MmSetPageProtect(NULL, Page, OldProt);
+ 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;
+}
+
+static CHAR
+GspWriteMemSafeGetContent(PVOID Context, ULONG Offset)
+{
+ ASSERT(0 == Offset);
+
+ return *((PCHAR) Context);
+}
+
+static void
+GspWriteMemSafe(PCHAR Address,
+ CHAR Ch)
+{
+ GspWriteMem(Address, 1, TRUE, GspWriteMemSafeGetContent, &Ch);
+}
+
+
+/* This function takes the 386 exception vector and attempts to
+ translate this number into a unix compatible signal value */
+ULONG
+GspComputeSignal(NTSTATUS ExceptionCode)
+{
+ ULONG SigVal;
+
+ 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" */
+ }
+ return SigVal;
+}
+
+
+/**********************************************/
+/* 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->Gpr1 : (LONG)TrapFrame->Gpr1;
+}
+
+
+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)
+ {
+ }
+ 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);
+ }
+ }
+}
+
+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 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 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");
+ }
+ else
+ {
+ 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)
+{
+ unsigned Index;
+
+ DPRINT("GspRemoveSwBreakpoint(0x%p)\n", Address);
+ for (Index = 0; Index < GspSwBreakpointCount; Index++)
+ {
+ if (GspSwBreakpoints[Index].Address == Address)
+ {
+ 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
+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++)
+ {
+ if (GspHwBreakpointCount + Index < MAX_HW_BREAKPOINTS)
+ {
+ DPRINT("Implementing software interrupt using hardware register\n");
+ GspLoadHwBreakpoint(TrapFrame, GspHwBreakpointCount + Index,
+ GspSwBreakpoints[Index].Address, 0,
+ I386_BP_TYPE_EXECUTE);
+ GspSwBreakpoints[Index].Active = FALSE;
+ }
+ else
+ {
+ DPRINT("Using real software breakpoint\n");
+ 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);
+ DPRINT1("Successfully set software breakpoint at 0x%p\n", GspSwBreakpoints[Index].Address);
+ }
+ }
+ }
+
+ DPRINT("Final DR7 value 0x%08x\n", TrapFrame->Dr7);
+}
+
+static void
+GspUnloadBreakpoints(PKTRAP_FRAME TrapFrame)
+{
+ unsigned Index;
+
+ DPRINT("GspUnloadHwBreakpoints\n");
+
+ 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 BOOLEAN gdb_attached_yet = FALSE;
+/*
+ * This function does all command procesing for interfacing to gdb.
+ */
+KD_CONTINUE_TYPE
+STDCALL
+KdpGdbEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
+ PCONTEXT Context,
+ PKTRAP_FRAME TrapFrame)
+{
+ BOOLEAN Stepping;
+ LONG Address;
+ LONG Length;
+ LONG SigVal = 0;
+ //LONG NewPC;
+ PCHAR ptr;
+
+ /* FIXME: Stop on other CPUs too */
+
+ if (STATUS_ACCESS_VIOLATION == (NTSTATUS) ExceptionRecord->ExceptionCode &&
+ NULL != GspAccessLocation &&
+ (ULONG_PTR) GspAccessLocation ==
+ (ULONG_PTR) ExceptionRecord->ExceptionInformation[1])
+ {
+ GspAccessLocation = NULL;
+ GspMemoryError = TRUE;
+ //Context->Eip += 3;
+ }
+ else
+ {
+ DPRINT("Thread %p entering stub\n", PsGetCurrentThread());
+ /* Can only debug 1 thread at a time... */
+ ExAcquireFastMutex(&GspLock);
+ DPRINT("Thread %p acquired mutex\n", PsGetCurrentThread());
+
+ /* Disable hardware debugging while we are inside the stub */
+ //Ke386SetDr7(0);
+ GspUnloadBreakpoints(TrapFrame);
+
+ /* 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;
+ }
+
+ /* ugly hack to avoid attempting to send status at the very
+ * beginning, right when GDB is trying to query the stub */
+ if (gdb_attached_yet)
+ {
+ LONG Esp;
+
+ stop_reply:
+ /* reply to host that an exception has occurred */
+ SigVal = GspComputeSignal(ExceptionRecord->ExceptionCode);
+
+ 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';
+
+ GspPutPacket(&GspOutBuffer[0]);
+ }
+ else
+ {
+ gdb_attached_yet = 1;
+ }
+
+ Stepping = FALSE;
+
+ while (TRUE)
+ {
+ /* Zero the buffer now so we don't have to worry about the terminating zero character */
+ memset(GspOutBuffer, 0, sizeof(GspInBuffer));
+ ptr = GspGetPacket();
+
+ switch(*ptr++)
+ {
+ case '?':
+ /* a little hack to send more complete status information */
+ goto stop_reply;
+ GspOutBuffer[0] = 'S';
+ GspOutBuffer[1] = HexChars[SigVal >> 4];
+ GspOutBuffer[2] = HexChars[SigVal % 16];
+ GspOutBuffer[3] = 0;
+ 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");
+ DPRINT("Fault during memory read\n");
+ }
+ }
+
+ 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");
+ DPRINT("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(optional) */
+ /* sAA..AA Step one instruction from AA..AA(optional) */
+ case 's':
+ Stepping = TRUE;
+ case 'c':
+ {
+ ULONG BreakpointNumber;
+ ULONG dr6_ = 0;
+
+ /* try to read optional parameter, pc unchanged if no parm */
+ if (GspHex2Long (&ptr, &Address))
+ {
+ //Context->Eip = Address;
+ }
+
+ //NewPC = Context->Eip;
+
+ /* clear the trace bit */
+ //Context->EFlags &= 0xfffffeff;
+
+ /* set the trace bit if we're Stepping */
+ if (Stepping)
+ {
+ //Context->EFlags |= 0x100;
+ }
+
+ // XXX arty load dr6
+ if (!(dr6_ & 0x4000))
+ {
+ for (BreakpointNumber = 0; BreakpointNumber < 4; ++BreakpointNumber)
+ {
+ if (dr6_ & (1 << BreakpointNumber))
+ {
+ if (GspHwBreakpoints[BreakpointNumber].Type == 0)
+ {
+ /* Set restore flag */
+ //Context->EFlags |= 0x10000;
+ break;
+ }
+ }
+ }
+ }
+ GspLoadBreakpoints(TrapFrame);
+ // XXX load dr6
+ if (NULL != GspDbgThread)
+ {
+ ObDereferenceObject(GspDbgThread);
+ GspDbgThread = NULL;
+ }
+
+ DPRINT("Thread %p releasing mutex\n", PsGetCurrentThread());
+ ExReleaseFastMutex(&GspLock);
+ DPRINT("Thread %p leaving stub\n", PsGetCurrentThread());
+ return kdContinue;
+ break;
+ }
+
+ case 'k': /* kill the program */
+ strcpy(GspOutBuffer, "OK");
+ break;
+ /* kill the program */
+
+ case 'H': /* Set thread */
+ GspSetThread(ptr);
+ break;
+
+ case 'q': /* Query */
+ GspQuery(ptr);
+ break;
+
+ case 'T': /* Query thread status */
+ GspQueryThreadStatus(ptr);
+ break;
+
+ case 'Z':
+ {
+ 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);
+ }
+
+ /* not reached */
+ ASSERT(0);
+ }
+
+ if (NULL != GspDbgThread)
+ {
+ ObDereferenceObject(GspDbgThread);
+ GspDbgThread = NULL;
+ }
+
+ return kdContinue;
+}
+
+
+BOOLEAN
+STDCALL
+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
+STDCALL
+KdpGdbDebugPrint(PCH Message, ULONG Length)
+{
+}
+
+/* Initialize the GDB stub */
+VOID
+STDCALL
+KdpGdbStubInit(PKD_DISPATCH_TABLE WrapperTable,
+ ULONG BootPhase)
+{
+ if (!KdDebuggerEnabled || !KdpDebugMode.Gdb)
+ {
+ return;
+ }
+
+ if (BootPhase == 0)
+ {
+ 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, 0, 0);
+
+ KdpPort = GdbPortInfo.ComPort;
+ }
+ else if (BootPhase == 1)
+ {
+ GspInitialized = TRUE;
+
+ GspRunThread = NULL;
+ GspDbgThread = NULL;
+ GspEnumThread = NULL;
+
+ HalDisplayString("Waiting for GDB to attach\n");
+ DbgBreakPointWithStatus(DBG_STATUS_CONTROL_C);
+ }
+ else if (BootPhase == 2)
+ {
+ HalDisplayString("\n GDB debugging enabled\n\n");
+ }
+}
+
+/* EOF */