[KDGDB]
authorJérôme Gardou <jerome.gardou@reactos.org>
Thu, 11 Sep 2014 20:55:42 +0000 (20:55 +0000)
committerJérôme Gardou <jerome.gardou@reactos.org>
Thu, 11 Sep 2014 20:55:42 +0000 (20:55 +0000)
 - introduce KDGDB, a KDCOM-like DLL, wrapping the KD protocol and the GDB remote protocol together.
It is not fully functional, but for now it permits source-level debugging in some modules. More will be added as I feel the need and find the time to work a bit more on it. (That is, unless an angel comes and resume the work)
To use it, set GDB and _WINKD_ to TRUE in your CMakeCache.txt. Using separate debug symbols is also a good idea.

svn path=/trunk/; revision=64121

12 files changed:
reactos/cmake/gcc.cmake
reactos/drivers/base/CMakeLists.txt
reactos/drivers/base/kdgdb/CMakeLists.txt [new file with mode: 0644]
reactos/drivers/base/kdgdb/gdb_input.c [new file with mode: 0644]
reactos/drivers/base/kdgdb/gdb_receive.c [new file with mode: 0644]
reactos/drivers/base/kdgdb/gdb_send.c [new file with mode: 0644]
reactos/drivers/base/kdgdb/i386_sup.c [new file with mode: 0644]
reactos/drivers/base/kdgdb/kdcom.c [new file with mode: 0644]
reactos/drivers/base/kdgdb/kdgdb.h [new file with mode: 0644]
reactos/drivers/base/kdgdb/kdgdb.rc [new file with mode: 0644]
reactos/drivers/base/kdgdb/kdgdb.spec [new file with mode: 0644]
reactos/drivers/base/kdgdb/kdpacket.c [new file with mode: 0644]

index 7cf984e..f27d80a 100644 (file)
@@ -62,13 +62,13 @@ set(REACTOS_SOURCE_DIR_NATIVE ${REACTOS_SOURCE_DIR})
 string(REPLACE "/" "\\" REACTOS_SOURCE_DIR_NATIVE ${REACTOS_SOURCE_DIR})
 endif()
 
-if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
+if((NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") AND (NOT SEPARATE_DBG))
     add_compile_flags("-fdebug-prefix-map=\"${REACTOS_SOURCE_DIR_NATIVE}\"=ReactOS")
 endif()
 
 # Debugging
 if(SEPARATE_DBG)
-    add_compile_flags("-gdwarf-2 -g2")
+    add_compile_flags("-gdwarf-4 -fvar-tracking-assignments")
 else()
     add_compile_flags("-gdwarf-2 -gstrict-dwarf")
     if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
index 2a539a8..7d84024 100644 (file)
@@ -4,9 +4,13 @@ add_subdirectory(bootvid)
 add_subdirectory(condrv)
 
 if(_WINKD_)
-add_subdirectory(kdcom)
+       if (GDB)
+               add_subdirectory(kdgdb)
+       else()
+               add_subdirectory(kdcom)
+       endif()
 else()
-add_subdirectory(kdrosdbg)
+       add_subdirectory(kdrosdbg)
 endif()
 
 add_subdirectory(nmidebug)
diff --git a/reactos/drivers/base/kdgdb/CMakeLists.txt b/reactos/drivers/base/kdgdb/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cf180c1
--- /dev/null
@@ -0,0 +1,29 @@
+
+spec2def(kdcom.dll kdgdb.spec ADD_IMPORTLIB)
+
+list(APPEND SOURCE
+       gdb_input.c
+       gdb_receive.c
+       gdb_send.c
+    kdcom.c
+    kdpacket.c
+    kdgdb.h)
+
+# TODO: AMD64, ARM...
+if(ARCH STREQUAL "i386")
+       list(APPEND SOURCE i386_sup.c)
+endif()
+
+add_library(kdcom SHARED
+    ${SOURCE}
+    kdgdb.rc
+    ${CMAKE_CURRENT_BINARY_DIR}/kdcom.def)
+
+set_entrypoint(kdcom 0)
+set_subsystem(kdcom native)
+set_image_base(kdcom 0x00010000)
+
+add_importlibs(kdcom ntoskrnl hal)
+target_link_libraries(kdcom cportlib)
+add_pch(kdcom kdgdb.h SOURCE)
+add_cd_file(TARGET kdcom DESTINATION reactos/system32 NO_CAB FOR all)
diff --git a/reactos/drivers/base/kdgdb/gdb_input.c b/reactos/drivers/base/kdgdb/gdb_input.c
new file mode 100644 (file)
index 0000000..2305055
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/gdb_input.c
+ * PURPOSE:         Base functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+/* LOCALS *********************************************************************/
+static HANDLE gdb_run_thread;
+static HANDLE gdb_dbg_process;
+HANDLE gdb_dbg_thread;
+
+/* PRIVATE FUNCTIONS **********************************************************/
+static
+HANDLE
+hex_to_thread(char* buffer)
+{
+    ULONG_PTR ret = 0;
+    char hex;
+    while (*buffer)
+    {
+        hex = hex_value(*buffer++);
+        if (hex < 0)
+            return (HANDLE)ret;
+        ret <<= 4;
+        ret += hex;
+    }
+    return (HANDLE)ret;
+}
+
+static
+ULONG64
+hex_to_address(char* buffer)
+{
+    ULONG64 ret = 0;
+    char hex;
+    while (*buffer)
+    {
+        hex = hex_value(*buffer++);
+        if (hex < 0)
+            return ret;
+        ret <<= 4;
+        ret += hex;
+    }
+    return ret;
+}
+
+/* H* packets */
+static
+void
+handle_gdb_set_thread(void)
+{
+    switch (gdb_input[1])
+    {
+    case 'c':
+        if (strcmp(&gdb_input[2], "-1") == 0)
+            gdb_run_thread = (HANDLE)-1;
+        else
+            gdb_run_thread = hex_to_thread(&gdb_input[2]);
+        send_gdb_packet("OK");
+        break;
+    case 'g':
+        if (strncmp(&gdb_input[2], "p-1", 3) == 0)
+        {
+            gdb_dbg_process = (HANDLE)-1;
+            gdb_dbg_thread = (HANDLE)-1;
+        }
+        else
+        {
+            char* ptr = strstr(gdb_input, ".") + 1;
+            gdb_dbg_process = hex_to_thread(&gdb_input[3]);
+            if (strncmp(ptr, "-1", 2) == 0)
+                gdb_dbg_thread = (HANDLE)-1;
+            else
+                gdb_dbg_thread = hex_to_thread(ptr);
+        }
+        send_gdb_packet("OK");
+        break;
+    default:
+        KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input);
+        send_gdb_packet("");
+    }
+}
+
+static
+void
+handle_gdb_thread_alive(void)
+{
+    char* ptr = strstr(gdb_input, ".") + 1;
+    CLIENT_ID ClientId;
+    PETHREAD Thread;
+    NTSTATUS Status;
+
+    ClientId.UniqueProcess = hex_to_thread(&gdb_input[2]);
+    ClientId.UniqueThread = hex_to_thread(ptr);
+
+    Status = PsLookupProcessThreadByCid(&ClientId, NULL, &Thread);
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* Thread doesn't exist */
+        send_gdb_packet("E03");
+        return;
+    }
+
+    /* It's OK */
+    ObDereferenceObject(Thread);
+    send_gdb_packet("OK");
+}
+
+/* q* packets */
+static
+void
+handle_gdb_query(void)
+{
+    if (strncmp(gdb_input, "qSupported:", 11) == 0)
+    {
+        send_gdb_packet("PacketSize=4096;multiprocess+;");
+        return;
+    }
+
+    if (strncmp(gdb_input, "qAttached", 9) == 0)
+    {
+        /* Say yes: the remote server didn't create the process, ReactOS did! */
+        send_gdb_packet("0");
+        return;
+    }
+
+    if (strncmp(gdb_input, "qRcmd,", 6) == 0)
+    {
+        send_gdb_packet("OK");
+        return;
+    }
+
+    if (strcmp(gdb_input, "qC") == 0)
+    {
+        char gdb_out[64];
+        sprintf(gdb_out, "QC:p%p.%p;",
+            PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
+            PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
+        send_gdb_packet(gdb_out);
+        return;
+    }
+
+    if (strncmp(gdb_input, "qTStatus", 8) == 0)
+    {
+        /* We don't support tracepoints. */
+        send_gdb_packet("T0");
+        return;
+    }
+
+    KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
+    send_gdb_packet("");
+}
+
+#if 0
+static
+KDSTATUS
+handle_gdb_registers(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength)
+{
+    /*
+    if (gdb_dbg_thread)
+        KDDBGPRINT("Should get registers from other thread!\n");
+    */
+
+    State->ApiNumber = DbgKdGetContextApi;
+    State->ReturnStatus = STATUS_SUCCESS; /* ? */
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    if (MessageData)
+        MessageData->Length = 0;
+    *MessageLength = 0;
+    return KdPacketReceived;
+}
+#endif
+
+static
+KDSTATUS
+handle_gdb_read_mem(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength)
+{
+    State->ApiNumber = DbgKdReadVirtualMemoryApi;
+    State->ReturnStatus = STATUS_SUCCESS; /* ? */
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    if (MessageData)
+        MessageData->Length = 0;
+    *MessageLength = 0;
+
+    State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
+    State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
+    return KdPacketReceived;
+}
+
+static
+KDSTATUS
+handle_gdb_v(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    if (strncmp(gdb_input, "vCont", 5) == 0)
+    {
+        if (gdb_input[5] == '?')
+        {
+            KDSTATUS Status;
+            /* Report what we support */
+            send_gdb_packet("vCont;c;C;s;S");
+            Status = gdb_receive_packet(KdContext);
+            if (Status != KdPacketReceived)
+                return Status;
+            return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
+        }
+
+        if (strcmp(gdb_input, "vCont;c") == 0)
+        {
+            /* Let's go on */
+            State->ApiNumber = DbgKdContinueApi;
+            State->ReturnStatus = STATUS_SUCCESS; /* ? */
+            State->Processor = CurrentStateChange.Processor;
+            State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+            if (MessageData)
+                MessageData->Length = 0;
+            *MessageLength = 0;
+            State->u.Continue.ContinueStatus = STATUS_SUCCESS;
+            /* Tell GDB we are fine */
+            send_gdb_packet("OK");
+            return KdPacketReceived;
+        }
+    }
+
+    return KdPacketReceived;
+}
+
+/* GLOBAL FUNCTIONS ***********************************************************/
+KDSTATUS
+gdb_interpret_input(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDSTATUS Status;
+    switch (gdb_input[0])
+    {
+    case '?':
+        /* Send the Status */
+        gdb_send_exception();
+        break;
+    case 'g':
+        gdb_send_registers();
+        break;
+    case 'H':
+        handle_gdb_set_thread();
+        break;
+    case 'm':
+        return handle_gdb_read_mem(State, MessageData, MessageLength);
+    case 'q':
+        handle_gdb_query();
+        break;
+    case 'T':
+        handle_gdb_thread_alive();
+        break;
+    case 'v':
+        return handle_gdb_v(State, MessageData, MessageLength, KdContext);
+    default:
+        /* We don't know how to handle this request. Maybe this is something for KD */
+        State->ReturnStatus = STATUS_NOT_SUPPORTED;
+        return KdPacketReceived;
+    }
+    /* Get the answer from GDB */
+    Status = gdb_receive_packet(KdContext);
+    if (Status != KdPacketReceived)
+        return Status;
+    /* Try interpreting this new packet */
+    return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
+}
diff --git a/reactos/drivers/base/kdgdb/gdb_receive.c b/reactos/drivers/base/kdgdb/gdb_receive.c
new file mode 100644 (file)
index 0000000..7494039
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/gdb_receive.c
+ * PURPOSE:         Base functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+/* GLOBALS ********************************************************************/
+CHAR gdb_input[0x1000];
+
+/* GLOBAL FUNCTIONS ***********************************************************/
+char
+hex_value(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;
+}
+
+KDSTATUS
+NTAPI
+gdb_receive_packet(_Inout_ PKD_CONTEXT KdContext)
+{
+    char* ByteBuffer = gdb_input;
+    UCHAR Byte;
+    KDSTATUS Status;
+    CHAR CheckSum = 0, ReceivedCheckSum;
+
+    do
+    {
+        Status = KdpReceiveByte(&Byte);
+        if (Status != KdPacketReceived)
+            return Status;
+        if (Byte == 0x03)
+        {
+            KdContext->KdpControlCPending = TRUE;
+            return KdPacketNeedsResend;
+        }
+    } while (Byte != '$');
+
+    while (TRUE)
+    {
+        /* Try to get a byte from the port */
+        Status = KdpReceiveByte(&Byte);
+        if (Status != KdPacketReceived)
+            return Status;
+
+        if (Byte == '#')
+        {
+            *ByteBuffer = '\0';
+            break;
+        }
+
+        *ByteBuffer++ = Byte;
+        CheckSum += (CHAR)Byte;
+    }
+
+    /* Get Check sum (two bytes) */
+    Status = KdpReceiveByte(&Byte);
+    if (Status != KdPacketReceived)
+        goto end;
+    ReceivedCheckSum = hex_value(Byte) << 4;;
+
+    Status = KdpReceiveByte(&Byte);
+    if (Status != KdPacketReceived)
+        goto end;
+    ReceivedCheckSum += hex_value(Byte);
+
+end:
+    if (ReceivedCheckSum != CheckSum)
+    {
+        /* Do not acknowledge to GDB */
+        KdpSendByte('-');
+        return KdPacketNeedsResend;
+    }
+
+    /* Acknowledge */
+    KdpSendByte('+');
+
+    return KdPacketReceived;
+}
diff --git a/reactos/drivers/base/kdgdb/gdb_send.c b/reactos/drivers/base/kdgdb/gdb_send.c
new file mode 100644 (file)
index 0000000..4e64495
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/gdb_send.c
+ * PURPOSE:         Base functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+/* LOCALS *********************************************************************/
+const char hex_chars[] = "0123456789abcdef";
+
+/* PRIVATE FUNCTIONS **********************************************************/
+static
+char*
+exception_code_to_gdb(NTSTATUS code, char* out)
+{
+    unsigned char SigVal;
+
+    switch (code)
+    {
+    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" */
+    }
+    *out++ = hex_chars[(SigVal >> 4) & 0xf];
+    *out++ = hex_chars[SigVal & 0xf];
+    return out;
+}
+
+/* GLOBAL FUNCTIONS ***********************************************************/
+void
+send_gdb_packet(_In_ CHAR* Buffer)
+{
+    UCHAR ack;
+
+    do {
+        CHAR* ptr = Buffer;
+        CHAR check_sum = 0;
+
+        KdpSendByte('$');
+
+        /* Calculate checksum */
+        check_sum = 0;
+        while (*ptr)
+        {
+            check_sum += *ptr;
+            KdpSendByte(*ptr++);
+        }
+
+        /* append it */
+        KdpSendByte('#');
+        KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]);
+        KdpSendByte(hex_chars[check_sum & 0xf]);
+
+        /* Wait for acknowledgement */
+        if (KdpReceiveByte(&ack) != KdPacketReceived)
+        {
+            KD_DEBUGGER_NOT_PRESENT = TRUE;
+            break;
+        }
+    } while (ack != '+');
+}
+
+void
+send_gdb_memory(
+    _In_ VOID* Buffer,
+    _In_ size_t Length)
+{
+    UCHAR ack;
+
+    do {
+        CHAR* ptr = Buffer;
+        CHAR check_sum = 0;
+        size_t len = Length;
+        CHAR Byte;
+
+        KdpSendByte('$');
+
+        /* Send the data */
+        check_sum = 0;
+        while (len--)
+        {
+            Byte = hex_chars[(*ptr >> 4) & 0xf];
+            KdpSendByte(Byte);
+            check_sum += Byte;
+            Byte = hex_chars[*ptr++ & 0xf];
+            KdpSendByte(Byte);
+            check_sum += Byte;
+        }
+
+        /* append check sum */
+        KdpSendByte('#');
+        KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]);
+        KdpSendByte(hex_chars[check_sum & 0xf]);
+
+        /* Wait for acknowledgement */
+        if (KdpReceiveByte(&ack) != KdPacketReceived)
+        {
+            KD_DEBUGGER_NOT_PRESENT = TRUE;
+            break;
+        }
+    } while (ack != '+');
+}
+
+void
+gdb_send_debug_io(
+    _In_ PSTRING String)
+{
+    UCHAR ack;
+
+    do {
+        CHAR* ptr = String->Buffer;
+        CHAR check_sum;
+        USHORT Length = String->Length;
+        CHAR Byte;
+
+        KdpSendByte('$');
+
+        KdpSendByte('O');
+
+        /* Send the data */
+        check_sum = 'O';
+        while (Length--)
+        {
+            Byte = hex_chars[(*ptr >> 4) & 0xf];
+            KdpSendByte(Byte);
+            check_sum += Byte;
+            Byte = hex_chars[*ptr++ & 0xf];
+            KdpSendByte(Byte);
+            check_sum += Byte;
+        }
+
+        /* append check sum */
+        KdpSendByte('#');
+        KdpSendByte(hex_chars[(check_sum >> 4) & 0xf]);
+        KdpSendByte(hex_chars[check_sum & 0xf]);
+
+        /* Wait for acknowledgement */
+        if (KdpReceiveByte(&ack) != KdPacketReceived)
+        {
+            KD_DEBUGGER_NOT_PRESENT = TRUE;
+            break;
+        }
+    } while (ack != '+');
+}
+
+void
+gdb_send_exception(void)
+{
+    char gdb_out[1024];
+    char* ptr = gdb_out;
+    DBGKM_EXCEPTION64* Exception = NULL;
+
+    if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
+        Exception = &CurrentStateChange.u.Exception;
+
+    /* Report to GDB */
+    *ptr++ = 'T';
+    if (Exception)
+        ptr = exception_code_to_gdb(Exception->ExceptionRecord.ExceptionCode, ptr);
+    else
+        ptr += sprintf(ptr, "05");
+    ptr += sprintf(ptr, "thread:p%p.%p;",
+        PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
+        PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
+    ptr += sprintf(ptr, "core:%x;", CurrentStateChange.Processor);
+    send_gdb_packet(gdb_out);
+}
+
+#ifdef KDDEBUG
+ULONG KdpDbgPrint(const char* Format, ...)
+{
+    va_list ap;
+    CHAR Buffer[512];
+    struct _STRING Str;
+    int Length;
+
+    va_start(ap, Format);
+    Length = _vsnprintf(Buffer, sizeof(Buffer), Format, ap);
+    va_end(ap);
+
+    /* Check if we went past the buffer */
+    if (Length == -1)
+    {
+        /* Terminate it if we went over-board */
+        Buffer[sizeof(Buffer) - 1] = '\n';
+
+        /* Put maximum */
+        Length = sizeof(Buffer);
+    }
+
+    Str.Buffer = Buffer;
+    Str.Length = Length;
+    Str.MaximumLength = sizeof(Buffer);
+
+    gdb_send_debug_io(&Str);
+
+    return 0;
+}
+#endif
+
diff --git a/reactos/drivers/base/kdgdb/i386_sup.c b/reactos/drivers/base/kdgdb/i386_sup.c
new file mode 100644 (file)
index 0000000..86ddabc
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/gdb_input.c
+ * PURPOSE:         Base functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+enum reg_name
+{
+    EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
+    EIP,
+    EFLAGS,
+    CS, SS, DS, ES, FS, GS,
+    ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7,
+    FCTRL, FSTAT, FTAG, FISEG, FIOFF, FOSEG, FOOFF, FOP,
+    XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7,
+    MXCSR
+};
+
+static
+void*
+ctx_to_reg(CONTEXT* ctx, enum reg_name name, unsigned short* size)
+{
+    /* For general registers: 32bits */
+    *size = 4;
+    switch (name)
+    {
+    case EAX: return &ctx->Eax;
+    case EBX: return &ctx->Ebx;
+    case ECX: return &ctx->Ecx;
+    case EDX: return &ctx->Edx;
+    case ESP: return &ctx->Esp;
+    case EBP: return &ctx->Ebp;
+    case ESI: return &ctx->Esi;
+    case EDI: return &ctx->Edi;
+    case EIP: return &ctx->Eip;
+    case EFLAGS: return &ctx->EFlags;
+    case CS: return &ctx->SegCs;
+    case DS: return &ctx->SegDs;
+    case ES: return &ctx->SegEs;
+    case FS: return &ctx->SegFs;
+    case GS: return &ctx->SegGs;
+    case SS: return &ctx->SegSs;
+    /* 80 bits */
+    case ST0:
+    case ST1:
+    case ST2:
+    case ST3:
+    case ST4:
+    case ST5:
+    case ST6:
+    case ST7:
+        *size = 10;
+        return &ctx->FloatSave.RegisterArea[10 * (name - ST0)];
+    /* X87 registers */
+    case FCTRL: return &ctx->FloatSave.ControlWord;
+    case FSTAT: return &ctx->FloatSave.StatusWord;
+    case FTAG: return &ctx->FloatSave.TagWord;
+    case FISEG: return &ctx->FloatSave.DataSelector;
+    case FIOFF: return &ctx->FloatSave.DataOffset;
+    case FOSEG: return &ctx->FloatSave.ErrorSelector;
+    case FOOFF: return &ctx->FloatSave.ErrorOffset;
+    case FOP: return &ctx->FloatSave.Cr0NpxState;
+    /* SSE */
+    case XMM0:
+    case XMM1:
+    case XMM2:
+    case XMM3:
+    case XMM4:
+    case XMM5:
+    case XMM6:
+    case XMM7:
+        *size = 16;
+        return &ctx->ExtendedRegisters[160 + (name - XMM0)*16];
+    case MXCSR: return &ctx->ExtendedRegisters[24];
+    }
+    return 0;
+}
+
+void
+gdb_send_registers(void)
+{
+    CONTEXT* ctx;
+    PKPRCB* ProcessorBlockLists;
+    ULONG32 Registers[16];
+    unsigned i;
+    unsigned short size;
+
+    ProcessorBlockLists = (PKPRCB*)KdDebuggerDataBlock->KiProcessorBlock.Pointer;
+    ctx = (CONTEXT*)((char*)ProcessorBlockLists[CurrentStateChange.Processor] + KdDebuggerDataBlock->OffsetPrcbProcStateContext);
+
+    for(i=0; i < 16; i++)
+    {
+        Registers[i] = *(ULONG32*)ctx_to_reg(ctx, i, &size);
+    }
+    send_gdb_memory(Registers, sizeof(Registers));
+}
diff --git a/reactos/drivers/base/kdgdb/kdcom.c b/reactos/drivers/base/kdgdb/kdcom.c
new file mode 100644 (file)
index 0000000..f07a4d8
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kdgdb/kdcom.c
+ * PURPOSE:         COM port functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+#include <cportlib/cportlib.h>
+#include <arc/arc.h>
+#include <stdlib.h>
+#include <ndk/halfuncs.h>
+
+/* Serial debug connection */
+#define DEFAULT_DEBUG_PORT      2 /* COM2 */
+#define DEFAULT_DEBUG_COM1_IRQ  4 /* COM1 IRQ */
+#define DEFAULT_DEBUG_COM2_IRQ  3 /* COM2 IRQ */
+#define DEFAULT_DEBUG_BAUD_RATE 115200 /* 115200 Baud */
+
+#define DEFAULT_BAUD_RATE   19200
+
+#if defined(_M_IX86) || defined(_M_AMD64)
+const ULONG BaseArray[] = {0, 0x3F8, 0x2F8, 0x3E8, 0x2E8};
+#elif defined(_M_PPC)
+const ULONG BaseArray[] = {0, 0x800003F8};
+#elif defined(_M_MIPS)
+const ULONG BaseArray[] = {0, 0x80006000, 0x80007000};
+#elif defined(_M_ARM)
+const ULONG BaseArray[] = {0, 0xF1012000};
+#else
+#error Unknown architecture
+#endif
+
+/* GLOBALS ********************************************************************/
+
+CPPORT KdDebugComPort;
+ULONG  KdDebugComPortIrq = 0; // Not used at the moment.
+
+
+/* FUNCTIONS ******************************************************************/
+
+NTSTATUS
+NTAPI
+KdD0Transition(VOID)
+{
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+KdD3Transition(VOID)
+{
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+KdSave(IN BOOLEAN SleepTransition)
+{
+    /* Nothing to do on COM ports */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+KdRestore(IN BOOLEAN SleepTransition)
+{
+    /* Nothing to do on COM ports */
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+KdpPortInitialize(IN ULONG ComPortNumber,
+                  IN ULONG ComPortBaudRate)
+{
+    NTSTATUS Status;
+
+    Status = CpInitialize(&KdDebugComPort,
+                          UlongToPtr(BaseArray[ComPortNumber]),
+                          ComPortBaudRate);
+    if (!NT_SUCCESS(Status))
+    {
+        return STATUS_INVALID_PARAMETER;
+    }
+    else
+    {
+        KdComPortInUse = KdDebugComPort.Address;
+        return STATUS_SUCCESS;
+    }
+}
+
+/******************************************************************************
+ * \name KdDebuggerInitialize0
+ * \brief Phase 0 initialization.
+ * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL.
+ * \return Status
+ */
+NTSTATUS
+NTAPI
+KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
+{
+    ULONG ComPortNumber   = DEFAULT_DEBUG_PORT;
+    ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE;
+
+    PCHAR CommandLine, PortString, BaudString, IrqString;
+    ULONG Value;
+
+    /* Check if e have a LoaderBlock */
+    if (LoaderBlock)
+    {
+
+        /* Get the Command Line */
+        CommandLine = LoaderBlock->LoadOptions;
+
+        /* Upcase it */
+        _strupr(CommandLine);
+
+        /* Get the port and baud rate */
+        PortString = strstr(CommandLine, "DEBUGPORT");
+        BaudString = strstr(CommandLine, "BAUDRATE");
+        IrqString  = strstr(CommandLine, "IRQ");
+
+        /* Check if we got the /DEBUGPORT parameter */
+        if (PortString)
+        {
+            /* Move past the actual string, to reach the port*/
+            PortString += strlen("DEBUGPORT");
+
+            /* Now get past any spaces and skip the equal sign */
+            while (*PortString == ' ') PortString++;
+            PortString++;
+
+            /* Do we have a serial port? */
+            if (strncmp(PortString, "COM", 3) != 0)
+            {
+                return STATUS_INVALID_PARAMETER;
+            }
+
+            /* Check for a valid Serial Port */
+            PortString += 3;
+            Value = atol(PortString);
+            if (Value >= sizeof(BaseArray) / sizeof(BaseArray[0]))
+            {
+                return STATUS_INVALID_PARAMETER;
+            }
+
+            /* Set the port to use */
+            ComPortNumber = Value;
+       }
+
+        /* Check if we got a baud rate */
+        if (BaudString)
+        {
+            /* Move past the actual string, to reach the rate */
+            BaudString += strlen("BAUDRATE");
+
+            /* Now get past any spaces */
+            while (*BaudString == ' ') BaudString++;
+
+            /* And make sure we have a rate */
+            if (*BaudString)
+            {
+                /* Read and set it */
+                Value = atol(BaudString + 1);
+                if (Value) ComPortBaudRate = Value;
+            }
+        }
+
+        /* Check Serial Port Settings [IRQ] */
+        if (IrqString)
+        {
+            /* Move past the actual string, to reach the rate */
+            IrqString += strlen("IRQ");
+
+            /* Now get past any spaces */
+            while (*IrqString == ' ') IrqString++;
+
+            /* And make sure we have an IRQ */
+            if (*IrqString)
+            {
+                /* Read and set it */
+                Value = atol(IrqString + 1);
+                if (Value) KdDebugComPortIrq = Value;
+            }
+        }
+    }
+
+    /* Initialize the port */
+    return KdpPortInitialize(ComPortNumber, ComPortBaudRate);
+}
+
+/******************************************************************************
+ * \name KdDebuggerInitialize1
+ * \brief Phase 1 initialization.
+ * \param [opt] LoaderBlock Pointer to the Loader parameter block. Can be NULL.
+ * \return Status
+ */
+NTSTATUS
+NTAPI
+KdDebuggerInitialize1(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
+{
+    return STATUS_SUCCESS;
+}
+
+
+VOID
+NTAPI
+KdpSendByte(_In_ UCHAR Byte)
+{
+    /* Send the byte */
+    CpPutByte(&KdDebugComPort, Byte);
+}
+
+KDSTATUS
+NTAPI
+KdpPollByte(OUT PUCHAR OutByte)
+{
+    /* Poll the byte */
+    if (CpGetByte(&KdDebugComPort, OutByte, FALSE, FALSE) == CP_GET_SUCCESS)
+    {
+        return KdPacketReceived;
+    }
+    else
+    {
+        return KdPacketTimedOut;
+    }
+}
+
+KDSTATUS
+NTAPI
+KdpReceiveByte(_Out_ PUCHAR OutByte)
+{
+    /* Get the byte */
+    if (CpGetByte(&KdDebugComPort, OutByte, TRUE, FALSE) == CP_GET_SUCCESS)
+    {
+        return KdPacketReceived;
+    }
+    else
+    {
+        return KdPacketTimedOut;
+    }
+}
+
+KDSTATUS
+NTAPI
+KdpPollBreakIn(VOID)
+{
+    KDSTATUS KdStatus;
+    UCHAR Byte;
+
+    KdStatus = KdpPollByte(&Byte);
+    if (KdStatus == KdPacketReceived)
+    {
+        if (Byte == 0x03)
+        {
+            return KdPacketReceived;
+        }
+        else if (Byte == '$')
+        {
+            /* GDB tried to send a new packet. N-ack it. */
+            KdpSendByte('-');
+        }
+    }
+    return KdPacketTimedOut;
+}
+
+/* EOF */
diff --git a/reactos/drivers/base/kdgdb/kdgdb.h b/reactos/drivers/base/kdgdb/kdgdb.h
new file mode 100644 (file)
index 0000000..37b5510
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/kddll.h
+ * PURPOSE:         Base definitions for the kernel debugger.
+ */
+
+#ifndef _KDGDB_H_
+#define _KDGDB_H_
+
+#define NOEXTAPI
+#include <ntifs.h>
+#include <halfuncs.h>
+#include <stdio.h>
+#include <arc/arc.h>
+#include <windbgkd.h>
+#include <kddll.h>
+
+// #define KDDEBUG /* uncomment to enable debugging this dll */
+
+#ifndef KDDEBUG
+#define KDDBGPRINT(...)
+#else
+extern ULONG KdpDbgPrint(const char* Format, ...);
+#define KDDBGPRINT KdpDbgPrint
+#endif
+
+/* gdb_input.c */
+extern HANDLE gdb_dbg_thread;
+KDSTATUS gdb_interpret_input(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
+
+/* gdb_receive.c */
+extern CHAR gdb_input[];
+KDSTATUS NTAPI gdb_receive_packet(_Inout_ PKD_CONTEXT KdContext);
+char hex_value(char ch);
+
+/* gdb_send.c */
+void send_gdb_packet(_In_ CHAR* Buffer);
+void send_gdb_memory(_In_ VOID* Buffer, size_t Length);
+void gdb_send_debug_io(_In_ PSTRING String);
+void gdb_send_exception(void);
+
+/* kdcom.c */
+KDSTATUS NTAPI KdpPollBreakIn(VOID);
+VOID NTAPI KdpSendByte(_In_ UCHAR Byte);
+KDSTATUS NTAPI KdpReceiveByte(_Out_ PUCHAR OutByte);
+
+/* kdpacket.c */
+extern DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange;
+extern DBGKD_GET_VERSION64 KdVersion;
+extern KDDEBUGGER_DATA64* KdDebuggerDataBlock;
+
+/* arch_sup.c */
+void gdb_send_registers(void);
+
+#endif /* _KDGDB_H_ */
diff --git a/reactos/drivers/base/kdgdb/kdgdb.rc b/reactos/drivers/base/kdgdb/kdgdb.rc
new file mode 100644 (file)
index 0000000..d24a029
--- /dev/null
@@ -0,0 +1,5 @@
+#define REACTOS_VERSION_DLL
+#define REACTOS_STR_FILE_DESCRIPTION  "ReactOS GDB KDCOM wrapper DLL"
+#define REACTOS_STR_INTERNAL_NAME     "kdcom"
+#define REACTOS_STR_ORIGINAL_FILENAME "kdcom.dll"
+#include <reactos/version.rc>
diff --git a/reactos/drivers/base/kdgdb/kdgdb.spec b/reactos/drivers/base/kdgdb/kdgdb.spec
new file mode 100644 (file)
index 0000000..4098dd6
--- /dev/null
@@ -0,0 +1,8 @@
+@ stdcall KdD0Transition()
+@ stdcall KdD3Transition()
+@ stdcall KdDebuggerInitialize0(ptr)
+@ stdcall KdDebuggerInitialize1(ptr)
+@ stdcall KdReceivePacket(long ptr ptr ptr ptr)
+@ stdcall KdRestore(long)
+@ stdcall KdSave(long)
+@ stdcall KdSendPacket(long ptr ptr ptr)
diff --git a/reactos/drivers/base/kdgdb/kdpacket.c b/reactos/drivers/base/kdgdb/kdpacket.c
new file mode 100644 (file)
index 0000000..adcf15b
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * COPYRIGHT:       GPL, see COPYING in the top level directory
+ * PROJECT:         ReactOS kernel
+ * FILE:            drivers/base/kddll/kdpacket.c
+ * PURPOSE:         Base functions for the kernel debugger.
+ */
+
+#include "kdgdb.h"
+
+/* GLOBALS ********************************************************************/
+
+DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange;
+DBGKD_GET_VERSION64 KdVersion;
+KDDEBUGGER_DATA64* KdDebuggerDataBlock;
+
+/* LOCALS *********************************************************************/
+static BOOLEAN FakeNextManipulatePacket = FALSE;
+static DBGKD_MANIPULATE_STATE64 FakeManipulateState = {0};
+
+/* PRIVATE FUNCTIONS **********************************************************/
+static
+void
+send_kd_state_change(DBGKD_ANY_WAIT_STATE_CHANGE* StateChange)
+{
+    static BOOLEAN first = TRUE;
+
+    /* Save current state for later GDB queries */
+    CurrentStateChange = *StateChange;
+
+    if (first)
+    {
+        /*
+         * This is the first packet we receive.
+         * We take this as an opportunity to connect with GDB and to
+         * get the KD version block
+         */
+        FakeNextManipulatePacket = TRUE;
+        FakeManipulateState.ApiNumber = DbgKdGetVersionApi;
+        FakeManipulateState.Processor = StateChange->Processor;
+        FakeManipulateState.ProcessorLevel = StateChange->ProcessorLevel;
+        FakeManipulateState.ReturnStatus = STATUS_SUCCESS;
+
+        first = FALSE;
+        return;
+    }
+
+    switch (StateChange->NewState)
+    {
+    case DbgKdLoadSymbolsStateChange:
+    {
+        /* We don't care about symbols loading */
+        FakeNextManipulatePacket = TRUE;
+        FakeManipulateState.ApiNumber = DbgKdContinueApi;
+        FakeManipulateState.Processor = StateChange->Processor;
+        FakeManipulateState.ProcessorLevel = StateChange->ProcessorLevel;
+        FakeManipulateState.ReturnStatus = STATUS_SUCCESS;
+        FakeManipulateState.u.Continue.ContinueStatus = STATUS_SUCCESS;
+        break;
+    }
+    case DbgKdExceptionStateChange:
+        gdb_send_exception();
+        break;
+    default:
+        /* FIXME */
+        while (1);
+    }
+}
+
+static
+void
+send_kd_debug_io(
+    _In_ DBGKD_DEBUG_IO* DebugIO,
+    _In_ PSTRING String)
+{
+    switch (DebugIO->ApiNumber)
+    {
+    case DbgKdPrintStringApi:
+        gdb_send_debug_io(String);
+        break;
+    default:
+        /* FIXME */
+        while (1);
+    }
+}
+
+static
+void
+send_kd_state_manipulate(
+    _In_ DBGKD_MANIPULATE_STATE64* State,
+    _In_ PSTRING MessageData)
+{
+    switch (State->ApiNumber)
+    {
+#if 0
+    case DbgKdGetContextApi:
+        /* This is an answer to a 'g' GDB request */
+        gdb_send_registers((CONTEXT*)MessageData->Buffer);
+        return;
+#endif
+    case DbgKdReadVirtualMemoryApi:
+        /* Answer to 'm' GDB request */
+        send_gdb_memory(MessageData->Buffer, State->u.ReadMemory.ActualBytesRead);
+        break;
+    case DbgKdGetVersionApi:
+    {
+        LIST_ENTRY* DebuggerDataList;
+        /* Simply get a copy */
+        RtlCopyMemory(&KdVersion, &State->u.GetVersion64, sizeof(KdVersion));
+        DebuggerDataList = (LIST_ENTRY*)(ULONG_PTR)KdVersion.DebuggerDataList;
+        KdDebuggerDataBlock = CONTAINING_RECORD(DebuggerDataList->Flink, KDDEBUGGER_DATA64, Header.List);
+        return;
+    }
+    default:
+        /* FIXME */
+        while (1);
+    }
+}
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+/******************************************************************************
+ * \name KdReceivePacket
+ * \brief Receive a packet from the KD port.
+ * \param [in] PacketType Describes the type of the packet to receive.
+ *        This can be one of the PACKET_TYPE_ constants.
+ * \param [out] MessageHeader Pointer to a STRING structure for the header.
+ * \param [out] MessageData Pointer to a STRING structure for the data.
+ * \return KdPacketReceived if successful, KdPacketTimedOut if the receive
+ *         timed out, KdPacketNeedsResend to signal that the last packet needs
+ *         to be sent again.
+ * \note If PacketType is PACKET_TYPE_KD_POLL_BREAKIN, the function doesn't
+ *       wait for any data, but returns KdPacketTimedOut instantly if no breakin
+ *       packet byte is received.
+ * \sa http://www.nynaeve.net/?p=169
+ */
+KDSTATUS
+NTAPI
+KdReceivePacket(
+    _In_ ULONG PacketType,
+    _Out_ PSTRING MessageHeader,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG DataLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDSTATUS Status;
+    DBGKD_MANIPULATE_STATE64* State;
+
+    /* Special handling for breakin packet */
+    if (PacketType == PACKET_TYPE_KD_POLL_BREAKIN)
+    {
+        return KdpPollBreakIn();
+    }
+
+    if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+    {
+        /* What should we do ? */
+        while (1);
+    }
+
+    State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+
+    if (FakeNextManipulatePacket)
+    {
+        FakeNextManipulatePacket = FALSE;
+        *State = FakeManipulateState;
+        return KdPacketReceived;
+    }
+
+    /* Receive data from GDB */
+    Status = gdb_receive_packet(KdContext);
+    if (Status != KdPacketReceived)
+        return Status;
+
+    /* Interpret it */
+    return gdb_interpret_input(State, MessageData, DataLength, KdContext);
+}
+
+VOID
+NTAPI
+KdSendPacket(
+    IN ULONG PacketType,
+    IN PSTRING MessageHeader,
+    IN PSTRING MessageData,
+    IN OUT PKD_CONTEXT KdContext)
+{
+    switch (PacketType)
+    {
+    case PACKET_TYPE_KD_STATE_CHANGE64:
+        send_kd_state_change((DBGKD_ANY_WAIT_STATE_CHANGE*)MessageHeader->Buffer);
+        return;
+    case PACKET_TYPE_KD_DEBUG_IO:
+        send_kd_debug_io((DBGKD_DEBUG_IO*)MessageHeader->Buffer, MessageData);
+        break;
+    case PACKET_TYPE_KD_STATE_MANIPULATE:
+        send_kd_state_manipulate((DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer, MessageData);
+        break;
+    default:
+        /* FIXME */
+        while (1);
+    }
+}
+
+/* EOF */