[KDGDB]
authorJérôme Gardou <jerome.gardou@reactos.org>
Sun, 14 Sep 2014 20:45:30 +0000 (20:45 +0000)
committerJérôme Gardou <jerome.gardou@reactos.org>
Sun, 14 Sep 2014 20:45:30 +0000 (20:45 +0000)
 - Always pass down the result of gdb_receive_packet up to KD, so that it knows when a breakin packet was received. (CTRL-C) now works!
 - Generalize the use of the Send <-> ManipulateState callbacks for a better code reading.
 - Get the exception context as soon as it is thrown (instead of playing with the PRCB)
 - Improve the way we attach to GDB: on the first KD call, we set KdContext->ControlCPending so that KD throws an exception. That way we can first initialize our KD stuff, and then quietly attach to GDB
 - Implement the 'p' (get one register) GDB request.
GDB is now much more reliable.

svn path=/trunk/; revision=64154

reactos/drivers/base/kdgdb/gdb_input.c
reactos/drivers/base/kdgdb/gdb_send.c
reactos/drivers/base/kdgdb/i386_sup.c
reactos/drivers/base/kdgdb/kdgdb.h
reactos/drivers/base/kdgdb/kdpacket.c

index 8c5b3cb..f58e66b 100644 (file)
 
 /* LOCALS *********************************************************************/
 static HANDLE gdb_run_thread;
-static HANDLE gdb_dbg_process;
-HANDLE gdb_dbg_thread;
-CONTEXT CurrentContext;
 /* Keep track of where we are for qfThreadInfo/qsThreadInfo */
 static LIST_ENTRY* CurrentProcessEntry;
 static LIST_ENTRY* CurrentThreadEntry;
 
+/* GLOBALS ********************************************************************/
+HANDLE gdb_dbg_process;
+HANDLE gdb_dbg_thread;
+
 /* PRIVATE FUNCTIONS **********************************************************/
 static
 HANDLE
@@ -68,6 +69,7 @@ handle_gdb_set_thread(void)
         send_gdb_packet("OK");
         break;
     case 'g':
+        KDDBGPRINT("Setting debug thread: %s.\n", gdb_input);
         if (strncmp(&gdb_input[2], "p-1", 3) == 0)
         {
             gdb_dbg_process = (HANDLE)-1;
@@ -90,6 +92,20 @@ handle_gdb_set_thread(void)
     }
 }
 
+KDSTATUS
+gdb_receive_and_interpret_packet(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDSTATUS Status = gdb_receive_packet(KdContext);
+
+    if (Status != KdPacketReceived)
+        return Status;
+    return gdb_interpret_input(State, MessageData, MessageLength, KdContext);
+}
+
 static
 void
 handle_gdb_thread_alive(void)
@@ -118,7 +134,7 @@ handle_gdb_thread_alive(void)
 
 /* q* packets */
 static
-void
+KDSTATUS
 handle_gdb_query(
     _Out_ DBGKD_MANIPULATE_STATE64* State,
     _Out_ PSTRING MessageData,
@@ -128,20 +144,20 @@ handle_gdb_query(
     if (strncmp(gdb_input, "qSupported:", 11) == 0)
     {
         send_gdb_packet("PacketSize=4096;multiprocess+;");
-        return;
+        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
     }
 
     if (strncmp(gdb_input, "qAttached", 9) == 0)
     {
-        /* Say yes: the remote server didn't create the process, ReactOS did! */
+        /* Say no: We didn't attach, we create the process! */
         send_gdb_packet("0");
-        return;
+        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
     }
 
     if (strncmp(gdb_input, "qRcmd,", 6) == 0)
     {
         send_gdb_packet("OK");
-        return;
+        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
     }
 
     if (strcmp(gdb_input, "qC") == 0)
@@ -151,7 +167,7 @@ handle_gdb_query(
             PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
             PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
         send_gdb_packet(gdb_out);
-        return;
+        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
     }
 
     if ((strncmp(gdb_input, "qfThreadInfo", 12) == 0)
@@ -174,15 +190,13 @@ handle_gdb_query(
             {
                 /* there is only one thread to tell about */
                 send_gdb_packet("l");
-                return;
+                return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
             }
             /* Just tell GDB about the current thread */
             sprintf(gdb_out, "mp%p.%p", PsGetCurrentProcessId(), PsGetCurrentThreadId());
             send_gdb_packet(gdb_out);
             /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
-            gdb_receive_packet(KdContext);
-            gdb_interpret_input(State, MessageData, MessageLength, KdContext);
-            return;
+            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
         }
 
         if (Resuming)
@@ -197,7 +211,7 @@ handle_gdb_query(
         {
             /* We're done */
             send_gdb_packet("l");
-            return;
+            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
         }
 
         Process = CONTAINING_RECORD(CurrentProcessEntry, EPROCESS, ActiveProcessLinks);
@@ -234,9 +248,7 @@ handle_gdb_query(
                 /* send what we got */
                 send_gdb_packet(gdb_out);
                 /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
-                gdb_receive_packet(KdContext);
-                gdb_interpret_input(State, MessageData, MessageLength, KdContext);
-                return;
+                return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
             }
         }
 
@@ -244,13 +256,12 @@ handle_gdb_query(
         send_gdb_packet(gdb_out);
         CurrentThreadEntry = NULL;
         /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
-        gdb_receive_packet(KdContext);
-        gdb_interpret_input(State, MessageData, MessageLength, KdContext);
-        return;
+        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
     }
 
     KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
     send_gdb_packet("");
+    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
 }
 
 #if 0
@@ -304,6 +315,7 @@ ReadMemorySendHandler(
     else
         send_gdb_memory(MessageData->Buffer, MessageData->Length);
     KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
 }
 
 static
@@ -330,150 +342,6 @@ handle_gdb_read_mem(
     return KdPacketReceived;
 }
 
-static
-VOID
-GetCurrentContextSendHandler(
-    _In_ ULONG PacketType,
-    _In_ PSTRING MessageHeader,
-    _In_ PSTRING MessageData
-)
-{
-    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
-    const CONTEXT* Context = (const CONTEXT*)MessageData->Buffer;
-
-    if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
-            || (State->ApiNumber != DbgKdGetContextApi)
-            || (MessageData->Length < sizeof(*Context)))
-    {
-        /* Should we bugcheck ? */
-        while (1);
-    }
-
-    /* Just copy it */
-    RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
-    KdpSendPacketHandler = NULL;
-}
-
-static
-VOID
-GetCurrentContext(
-    _Out_ DBGKD_MANIPULATE_STATE64* State,
-    _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext,
-    _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
-)
-{
-    State->ApiNumber = DbgKdGetContextApi;
-    State->Processor = CurrentStateChange.Processor;
-    State->ReturnStatus = STATUS_SUCCESS;
-    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
-    MessageData->Length = 0;
-
-    /* Update the send <-> receive loop handler */
-    KdpSendPacketHandler = GetCurrentContextSendHandler;
-    KdpManipulateStateHandler = ManipulateStateHandler;
-}
-
-static
-VOID
-SetContextSendHandler(
-    _In_ ULONG PacketType,
-    _In_ PSTRING MessageHeader,
-    _In_ PSTRING MessageData
-)
-{
-    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
-
-    /* We just confirm that all went well */
-    if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
-            || (State->ApiNumber != DbgKdSetContextApi)
-            || (State->ReturnStatus != STATUS_SUCCESS))
-    {
-        /* Should we bugcheck ? */
-        while (1);
-    }
-
-    KdpSendPacketHandler = NULL;
-}
-
-static
-KDSTATUS
-SetContext(
-    _Out_ DBGKD_MANIPULATE_STATE64* State,
-    _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext,
-    _In_opt_ KDP_MANIPULATESTATE_HANDLER ManipulateStateHandler
-)
-{
-    State->ApiNumber = DbgKdSetContextApi;
-    State->Processor = CurrentStateChange.Processor;
-    State->ReturnStatus = STATUS_SUCCESS;
-    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
-    MessageData->Length = sizeof(CurrentContext);
-
-    if (MessageData->MaximumLength < sizeof(CurrentContext))
-    {
-        while (1);
-    }
-
-    RtlCopyMemory(MessageData->Buffer, &CurrentContext, sizeof(CurrentContext));
-
-    /* Update the send <-> receive loop handlers */
-    KdpSendPacketHandler = SetContextSendHandler;
-    KdpManipulateStateHandler = ManipulateStateHandler;
-
-    return KdPacketReceived;
-}
-
-static
-KDSTATUS
-SendContinue(
-    _Out_ DBGKD_MANIPULATE_STATE64* State,
-    _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext
-)
-{
-    /* 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;
-
-    /* We definitely are at the end of the send <-> receive loop, if any */
-    KdpSendPacketHandler = NULL;
-    KdpManipulateStateHandler = NULL;
-
-    /* Tell GDB we are fine */
-    send_gdb_packet("OK");
-    return KdPacketReceived;
-}
-
-static
-KDSTATUS
-UpdateProgramCounterSendContinue(
-    _Out_ DBGKD_MANIPULATE_STATE64* State,
-    _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext)
-{
-    ULONG_PTR ProgramCounter;
-
-    /* So we must get past the breakpoint instruction */
-    ProgramCounter = KdpGetContextPc(&CurrentContext);
-    KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
-
-    /* Set the context and continue */
-    SetContext(State, MessageData, MessageLength, KdContext, SendContinue);
-    return KdPacketReceived;
-}
-
 static
 KDSTATUS
 handle_gdb_v(
@@ -499,6 +367,9 @@ handle_gdb_v(
         {
             DBGKM_EXCEPTION64* Exception = NULL;
 
+            /* Tell GDB everything is fine, we will handle it */
+            send_gdb_packet("OK");
+
             if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
                 Exception = &CurrentStateChange.u.Exception;
 
@@ -506,15 +377,22 @@ handle_gdb_v(
             if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
                     && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
             {
-                /* So we get the context, update it and send it back */
-                GetCurrentContext(State, MessageData, MessageLength, KdContext, UpdateProgramCounterSendContinue);
+                ULONG_PTR ProgramCounter;
+
+                /* So we must get past the breakpoint instruction */
+                ProgramCounter = KdpGetContextPc(&CurrentContext);
+                KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
+
+                SetContextManipulateHandler(State, MessageData, MessageLength, KdContext);
+                KdpManipulateStateHandler = ContinueManipulateStateHandler;
                 return KdPacketReceived;
             }
 
-            return SendContinue(State, MessageData, MessageLength, KdContext);
+            return ContinueManipulateStateHandler(State, MessageData, MessageLength, KdContext);
         }
     }
 
+    KDDBGPRINT("Unhandled 'v' packet: %s\n", gdb_input);
     return KdPacketReceived;
 }
 
@@ -526,7 +404,6 @@ gdb_interpret_input(
     _Out_ PULONG MessageLength,
     _Inout_ PKD_CONTEXT KdContext)
 {
-    KDSTATUS Status;
     switch (gdb_input[0])
     {
     case '?':
@@ -534,16 +411,16 @@ gdb_interpret_input(
         gdb_send_exception();
         break;
     case 'g':
-        gdb_send_registers();
-        break;
+        return gdb_send_registers(State, MessageData, MessageLength, KdContext);
     case 'H':
         handle_gdb_set_thread();
         break;
     case 'm':
         return handle_gdb_read_mem(State, MessageData, MessageLength);
+    case 'p':
+        return gdb_send_register(State, MessageData, MessageLength, KdContext);
     case 'q':
-        handle_gdb_query(State, MessageData, MessageLength, KdContext);
-        break;
+        return handle_gdb_query(State, MessageData, MessageLength, KdContext);
     case 'T':
         handle_gdb_thread_alive();
         break;
@@ -552,12 +429,8 @@ gdb_interpret_input(
     default:
         /* We don't know how to handle this request. Maybe this is something for KD */
         State->ReturnStatus = STATUS_NOT_SUPPORTED;
+        KDDBGPRINT("Unsupported GDB command: %s.\n", gdb_input);
         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);
+    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
 }
index b31faeb..87aeca1 100644 (file)
@@ -168,17 +168,12 @@ gdb_send_exception(void)
 {
     char gdb_out[1024];
     char* ptr = gdb_out;
-    DBGKM_EXCEPTION64* Exception = NULL;
-
-    if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
-        Exception = &CurrentStateChange.u.Exception;
+    DBGKM_EXCEPTION64* 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 = exception_code_to_gdb(Exception->ExceptionRecord.ExceptionCode, ptr);
     ptr += sprintf(ptr, "thread:p%p.%p;",
         PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread),
         PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread));
index 86ddabc..16cceb1 100644 (file)
@@ -79,21 +79,48 @@ ctx_to_reg(CONTEXT* ctx, enum reg_name name, unsigned short* size)
     return 0;
 }
 
-void
-gdb_send_registers(void)
+KDSTATUS
+gdb_send_registers(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
 {
-    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);
+        Registers[i] = *(ULONG32*)ctx_to_reg(&CurrentContext, i, &size);
     }
     send_gdb_memory(Registers, sizeof(Registers));
+    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+}
+
+KDSTATUS
+gdb_send_register(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    enum reg_name reg_name;
+    void *ptr;
+    unsigned short size;
+
+    /* Get the GDB register name (gdb_input = "pXX") */
+    reg_name = (hex_value(gdb_input[1]) << 4) | hex_value(gdb_input[2]);
+
+    ptr = ctx_to_reg(&CurrentContext, reg_name, &size);
+    if (!ptr)
+    {
+        /* Undefined. Let's assume 32 bit register */
+        send_gdb_packet("xxxxxxxx");
+    }
+    else
+    {
+        send_gdb_memory(ptr, size);
+    }
+    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
 }
index 07a0eab..bc18e5b 100644 (file)
@@ -25,6 +25,18 @@ extern ULONG KdpDbgPrint(const char* Format, ...);
 #define KDDBGPRINT KdpDbgPrint
 #endif
 
+FORCEINLINE
+VOID
+InitManipulateFromStateChange(
+    _In_ ULONG ApiNumber,
+    _In_ const DBGKD_ANY_WAIT_STATE_CHANGE* StateChange,
+    _Out_ DBGKD_MANIPULATE_STATE64* Manipulate)
+{
+    Manipulate->ApiNumber = ApiNumber;
+    Manipulate->Processor = StateChange->Processor;
+    Manipulate->ProcessorLevel = StateChange->ProcessorLevel;
+}
+
 /* Callbacks to simulate a KdReceive <-> KdSend loop without GDB being aware of it */
 typedef VOID (*KDP_SEND_HANDLER)(
     _In_ ULONG PacketType,
@@ -40,7 +52,9 @@ typedef KDSTATUS (*KDP_MANIPULATESTATE_HANDLER)(
 
 /* 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);
+extern HANDLE gdb_dbg_process;
+extern KDSTATUS gdb_interpret_input(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
+extern KDSTATUS gdb_receive_and_interpret_packet(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
 
 /* gdb_receive.c */
 extern CHAR gdb_input[];
@@ -61,14 +75,18 @@ KDSTATUS NTAPI KdpReceiveByte(_Out_ PUCHAR OutByte);
 
 /* kdpacket.c */
 extern DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange;
+extern CONTEXT CurrentContext;
 extern DBGKD_GET_VERSION64 KdVersion;
 extern KDDEBUGGER_DATA64* KdDebuggerDataBlock;
 extern KDP_SEND_HANDLER KdpSendPacketHandler;
 extern KDP_MANIPULATESTATE_HANDLER KdpManipulateStateHandler;
-
+/* Commone ManipulateState handlers */
+extern KDSTATUS ContinueManipulateStateHandler(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
+extern KDSTATUS SetContextManipulateHandler(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
 
 /* arch_sup.c */
-void gdb_send_registers(void);
+extern KDSTATUS gdb_send_register(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
+extern KDSTATUS gdb_send_registers(_Out_ DBGKD_MANIPULATE_STATE64* State, _Out_ PSTRING MessageData, _Out_ PULONG MessageLength, _Inout_ PKD_CONTEXT KdContext);
 
 /* Architecture specific defines. See ntoskrnl/include/internal/arch/ke.h */
 #ifdef _M_IX86
index 9d29826..7f86b37 100644 (file)
 
 #include "kdgdb.h"
 
-/* GLOBALS ********************************************************************/
+/* LOCALS *********************************************************************/
+static
+VOID
+FirstSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData);
 
-DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange;
+/* GLOBALS ********************************************************************/
 DBGKD_GET_VERSION64 KdVersion;
 KDDEBUGGER_DATA64* KdDebuggerDataBlock;
+BOOLEAN InException = FALSE;
 /* Callbacks used to communicate with KD aside from GDB */
-KDP_SEND_HANDLER KdpSendPacketHandler = NULL;
+KDP_SEND_HANDLER KdpSendPacketHandler = FirstSendHandler;
 KDP_MANIPULATESTATE_HANDLER KdpManipulateStateHandler = NULL;
-
-/* LOCALS *********************************************************************/
-static BOOLEAN FakeNextManipulatePacket = FALSE;
-static DBGKD_MANIPULATE_STATE64 FakeManipulateState = {0};
+/* Data describing the current exception */
+DBGKD_ANY_WAIT_STATE_CHANGE CurrentStateChange;
+CONTEXT CurrentContext;
 
 /* PRIVATE FUNCTIONS **********************************************************/
+
 static
-void
-send_kd_state_change(DBGKD_ANY_WAIT_STATE_CHANGE* StateChange)
+VOID
+GetContextSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData
+)
 {
-    static BOOLEAN first = TRUE;
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+    const CONTEXT* Context = (const CONTEXT*)MessageData->Buffer;
 
-    /* Save current state for later GDB queries */
-    CurrentStateChange = *StateChange;
+    if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+            || (State->ApiNumber != DbgKdGetContextApi)
+            || (MessageData->Length < sizeof(*Context)))
+    {
+        /* Should we bugcheck ? */
+        KDDBGPRINT("ERROR: Received wrong packet from KD.\n");
+        while (1);
+    }
+
+    /* Just copy it */
+    RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
+    KdpSendPacketHandler = NULL;
+}
+
+static
+KDSTATUS
+GetContextManipulateHandler(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext
+)
+{
+    State->ApiNumber = DbgKdGetContextApi;
+    State->Processor = CurrentStateChange.Processor;
+    State->ReturnStatus = STATUS_SUCCESS;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    MessageData->Length = 0;
+
+    /* Update the send <-> receive loop handler */
+    KdpSendPacketHandler = GetContextSendHandler;
+    KdpManipulateStateHandler = NULL;
 
-    if (first)
+    return KdPacketReceived;
+}
+
+static
+VOID
+SetContextSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData
+)
+{
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+
+    /* We just confirm that all went well */
+    if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+            || (State->ApiNumber != DbgKdSetContextApi)
+            || (State->ReturnStatus != STATUS_SUCCESS))
     {
-        /*
-         * 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;
+        /* Should we bugcheck ? */
+        while (1);
+    }
+
+    KdpSendPacketHandler = NULL;
+}
+
+KDSTATUS
+SetContextManipulateHandler(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext
+)
+{
+    State->ApiNumber = DbgKdSetContextApi;
+    State->Processor = CurrentStateChange.Processor;
+    State->ReturnStatus = STATUS_SUCCESS;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    MessageData->Length = sizeof(CurrentContext);
+
+    if (MessageData->MaximumLength < sizeof(CurrentContext))
+    {
+        while (1);
     }
 
+    RtlCopyMemory(MessageData->Buffer, &CurrentContext, sizeof(CurrentContext));
+
+    /* Update the send <-> receive loop handlers */
+    KdpSendPacketHandler = SetContextSendHandler;
+    KdpManipulateStateHandler = NULL;
+
+    return KdPacketReceived;
+}
+
+static
+void
+send_kd_state_change(DBGKD_ANY_WAIT_STATE_CHANGE* StateChange)
+{
     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;
+        KdpManipulateStateHandler = ContinueManipulateStateHandler;
         break;
     }
     case DbgKdExceptionStateChange:
+        /* Save current state for later GDB queries */
+        CurrentStateChange = *StateChange;
+        /* Unless GDB tells us otherwise, those are what we should have */
+        gdb_dbg_thread = PsGetThreadId((PETHREAD)StateChange->Thread);
+        gdb_dbg_process = PsGetThreadProcessId((PETHREAD)StateChange->Thread);
         gdb_send_exception();
+        /* Next receive call will ask for the context */
+        KdpManipulateStateHandler = GetContextManipulateHandler;
         break;
     default:
         /* FIXME */
@@ -100,21 +184,119 @@ send_kd_state_manipulate(
         gdb_send_registers((CONTEXT*)MessageData->Buffer);
         return;
 #endif
-    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);
     }
 }
 
+KDSTATUS
+ContinueManipulateStateHandler(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext
+)
+{
+    /* 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;
+
+    /* We definitely are at the end of the send <-> receive loop, if any */
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
+    return KdPacketReceived;
+}
+
+static
+VOID
+GetVersionSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData)
+{
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+    LIST_ENTRY* DebuggerDataList;
+
+    /* Confirm that all went well */
+    if ((PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+            || (State->ApiNumber != DbgKdGetVersionApi)
+            || !NT_SUCCESS(State->ReturnStatus))
+    {
+        /* FIXME: should detach from KD and go along without debugging */
+        KDDBGPRINT("Wrong packet received after asking for data.\n");
+        while(1);
+    }
+
+    /* Copy the relevant data */
+    RtlCopyMemory(&KdVersion, &State->u.GetVersion64, sizeof(KdVersion));
+    DebuggerDataList = (LIST_ENTRY*)(ULONG_PTR)KdVersion.DebuggerDataList;
+    KdDebuggerDataBlock = CONTAINING_RECORD(DebuggerDataList->Flink, KDDEBUGGER_DATA64, Header.List);
+
+    /* We can tell KD to continue */
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = ContinueManipulateStateHandler;
+}
+
+static
+KDSTATUS
+GetVersionManipulateStateHandler(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    /* Ask for the version data */
+    State->ApiNumber = DbgKdGetVersionApi;
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+
+    /* The next send call will serve this query */
+    KdpSendPacketHandler = GetVersionSendHandler;
+    KdpManipulateStateHandler = NULL;
+
+    /* This will make KD breakin and we will be able to properly attach to GDB */
+    KdContext->KdpControlCPending = TRUE;
+
+    return KdPacketReceived;
+}
+
+static
+VOID
+FirstSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData)
+{
+    DBGKD_ANY_WAIT_STATE_CHANGE* StateChange = (DBGKD_ANY_WAIT_STATE_CHANGE*)MessageHeader->Buffer;
+
+    if (PacketType == PACKET_TYPE_KD_DEBUG_IO)
+    {
+        /* This is not the packet we are waiting for */
+        send_kd_debug_io((DBGKD_DEBUG_IO*)MessageHeader->Buffer, MessageData);
+        return;
+    }
+
+    if (PacketType != PACKET_TYPE_KD_STATE_CHANGE64)
+    {
+        KDDBGPRINT("First KD packet is not a state change!\n");
+        /* FIXME: What should we send back to KD ? */
+        while(1);
+    }
+
+    CurrentStateChange = *StateChange;
+
+    /* The next receive call will be asking for the version data */
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = GetVersionManipulateStateHandler;
+}
+
 /* PUBLIC FUNCTIONS ***********************************************************/
 
 /******************************************************************************
@@ -162,13 +344,6 @@ KdReceivePacket(
     if (KdpManipulateStateHandler != NULL)
         return KdpManipulateStateHandler(State, MessageData, DataLength, KdContext);
 
-    if (FakeNextManipulatePacket)
-    {
-        FakeNextManipulatePacket = FALSE;
-        *State = FakeManipulateState;
-        return KdPacketReceived;
-    }
-
     /* Receive data from GDB */
     Status = gdb_receive_packet(KdContext);
     if (Status != KdPacketReceived)