[KDGDB]
[reactos.git] / reactos / drivers / base / kdgdb / gdb_input.c
index 4e85422..f58e66b 100644 (file)
@@ -7,11 +7,17 @@
 
 #include "kdgdb.h"
 
+#include <pstypes.h>
+
 /* LOCALS *********************************************************************/
 static HANDLE gdb_run_thread;
-static HANDLE gdb_dbg_process;
+/* 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;
-CONTEXT CurrentContext;
 
 /* PRIVATE FUNCTIONS **********************************************************/
 static
@@ -63,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;
@@ -85,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)
@@ -113,26 +134,30 @@ handle_gdb_thread_alive(void)
 
 /* q* packets */
 static
-void
-handle_gdb_query(void)
+KDSTATUS
+handle_gdb_query(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
 {
     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)
@@ -142,18 +167,101 @@ handle_gdb_query(void)
             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, "qTStatus", 8) == 0)
+    if ((strncmp(gdb_input, "qfThreadInfo", 12) == 0)
+            || (strncmp(gdb_input, "qsThreadInfo", 12) == 0))
     {
-        /* We don't support tracepoints. */
-        send_gdb_packet("T0");
-        return;
+        LIST_ENTRY* ProcessListHead = (LIST_ENTRY*)KdDebuggerDataBlock->PsActiveProcessHead.Pointer;
+        BOOLEAN FirstThread = TRUE;
+        PEPROCESS Process;
+        PETHREAD Thread;
+        char gdb_out[1024];
+        char* ptr;
+        BOOLEAN Resuming = strncmp(gdb_input, "qsThreadInfo", 12) == 0;
+
+        /* Maybe this was not initialized yet */
+        if (!ProcessListHead->Flink)
+        {
+            char gdb_out[64];
+
+            if (Resuming)
+            {
+                /* there is only one thread to tell about */
+                send_gdb_packet("l");
+                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 */
+            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+        }
+
+        if (Resuming)
+        {
+            if (CurrentThreadEntry == NULL)
+                CurrentProcessEntry = CurrentProcessEntry->Flink;
+        }
+        else
+            CurrentProcessEntry = ProcessListHead->Flink;
+
+        if (CurrentProcessEntry == ProcessListHead)
+        {
+            /* We're done */
+            send_gdb_packet("l");
+            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+        }
+
+        Process = CONTAINING_RECORD(CurrentProcessEntry, EPROCESS, ActiveProcessLinks);
+
+        if (Resuming && CurrentThreadEntry != NULL)
+            CurrentThreadEntry = CurrentThreadEntry->Flink;
+        else
+            CurrentThreadEntry = Process->ThreadListHead.Flink;
+
+        ptr = gdb_out;
+
+        *ptr++ = 'm';
+        /* List threads from this process */
+        for ( ;
+             CurrentThreadEntry != &Process->ThreadListHead;
+             CurrentThreadEntry = CurrentThreadEntry->Flink)
+        {
+            Thread = CONTAINING_RECORD(CurrentThreadEntry, ETHREAD, ThreadListEntry);
+
+            /* See if we should add a comma */
+            if (FirstThread)
+            {
+                FirstThread = FALSE;
+            }
+            else
+            {
+                *ptr++ = ',';
+            }
+
+            ptr += _snprintf(ptr, 1024 - (ptr - gdb_out),
+                "p%p.%p", PsGetProcessId(Process), PsGetThreadId(Thread));
+            if (ptr > (gdb_out + 1024))
+            {
+                /* send what we got */
+                send_gdb_packet(gdb_out);
+                /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
+                return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+            }
+        }
+
+        /* send the list for this process */
+        send_gdb_packet(gdb_out);
+        CurrentThreadEntry = NULL;
+        /* GDB can ask anything at this point, it isn't necessarily a qsThreadInfo packet */
+        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
@@ -181,163 +289,56 @@ handle_gdb_registers(
 #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
-VOID
-GetCurrentContextSendHandler(
+void
+ReadMemorySendHandler(
     _In_ ULONG PacketType,
     _In_ PSTRING MessageHeader,
-    _In_ PSTRING MessageData
-)
+    _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)))
+    if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
     {
-        /* Should we bugcheck ? */
+        // KdAssert
+        KDDBGPRINT("Wrong packet type (%lu) received after DbgKdReadVirtualMemoryApi request.\n", PacketType);
         while (1);
     }
 
-    /* Just copy it */
-    RtlCopyMemory(&CurrentContext, Context, sizeof(*Context));
-}
-
-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))
+    if (State->ApiNumber != DbgKdReadVirtualMemoryApi)
     {
-        /* Should we bugcheck ? */
-        while (1);
+        KDDBGPRINT("Wrong API number (%lu) after DbgKdReadVirtualMemoryApi request.\n", State->ApiNumber);
     }
-}
-
-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;
+    /* Check status */
+    if (!NT_SUCCESS(State->ReturnStatus))
+        send_gdb_ntstatus(State->ReturnStatus);
+    else
+        send_gdb_memory(MessageData->Buffer, MessageData->Length);
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
 }
 
 static
 KDSTATUS
-SendContinue(
+handle_gdb_read_mem(
     _Out_ DBGKD_MANIPULATE_STATE64* State,
     _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext
-)
+    _Out_ PULONG MessageLength)
 {
-    /* Let's go on */
-    State->ApiNumber = DbgKdContinueApi;
+    State->ApiNumber = DbgKdReadVirtualMemoryApi;
     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;
+    State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
+    State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
 
-    /* So we must get past the breakpoint instruction */
-    ProgramCounter = KdpGetContextPc(&CurrentContext);
-    KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
+    /* KD will reply with KdSendPacket. Catch it */
+    KdpSendPacketHandler = ReadMemorySendHandler;
 
-    /* Set the context and continue */
-    SetContext(State, MessageData, MessageLength, KdContext, SendContinue);
     return KdPacketReceived;
 }
 
@@ -366,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;
 
@@ -373,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;
 }
 
@@ -393,7 +404,6 @@ gdb_interpret_input(
     _Out_ PULONG MessageLength,
     _Inout_ PKD_CONTEXT KdContext)
 {
-    KDSTATUS Status;
     switch (gdb_input[0])
     {
     case '?':
@@ -401,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();
-        break;
+        return handle_gdb_query(State, MessageData, MessageLength, KdContext);
     case 'T':
         handle_gdb_thread_alive();
         break;
@@ -419,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);
 }