[UNIATA] Fix 64 bit issues
[reactos.git] / drivers / base / kdgdb / gdb_input.c
index 115d680..9331dbd 100644 (file)
@@ -9,14 +9,24 @@
 
 /* LOCALS *********************************************************************/
 static ULONG_PTR gdb_run_tid;
-/* Keep track of where we are for qfThreadInfo/qsThreadInfo */
-static LIST_ENTRY* CurrentProcessEntry;
-static LIST_ENTRY* CurrentThreadEntry;
+static struct
+{
+    ULONG_PTR Address;
+    ULONG Handle;
+} BreakPointHandles[32];
+
 
 /* GLOBALS ********************************************************************/
 UINT_PTR gdb_dbg_pid;
 UINT_PTR gdb_dbg_tid;
 
+static inline
+KDSTATUS
+LOOP_IF_SUCCESS(x)
+{
+    return (x == KdPacketReceived) ? (KDSTATUS)-1 : x;
+}
+
 /* PRIVATE FUNCTIONS **********************************************************/
 static
 UINT_PTR
@@ -55,9 +65,11 @@ hex_to_address(char* buffer)
 
 /* H* packets */
 static
-void
+KDSTATUS
 handle_gdb_set_thread(void)
 {
+    KDSTATUS Status;
+
     switch (gdb_input[1])
     {
     case 'c':
@@ -65,10 +77,21 @@ handle_gdb_set_thread(void)
             gdb_run_tid = (ULONG_PTR)-1;
         else
             gdb_run_tid = hex_to_tid(&gdb_input[2]);
-        send_gdb_packet("OK");
+        Status = send_gdb_packet("OK");
         break;
     case 'g':
         KDDBGPRINT("Setting debug thread: %s.\n", gdb_input);
+#if MONOPROCESS
+        gdb_dbg_pid = 0;
+        if (strncmp(&gdb_input[2], "-1", 2) == 0)
+        {
+            gdb_dbg_tid = (UINT_PTR)-1;
+        }
+        else
+        {
+            gdb_dbg_tid = hex_to_tid(&gdb_input[2]);
+        }
+#else
         if (strncmp(&gdb_input[2], "p-1", 3) == 0)
         {
             gdb_dbg_pid = (UINT_PTR)-1;
@@ -83,173 +106,309 @@ handle_gdb_set_thread(void)
             else
                 gdb_dbg_tid = hex_to_tid(ptr);
         }
-        send_gdb_packet("OK");
+#endif
+        Status = send_gdb_packet("OK");
         break;
     default:
         KDDBGPRINT("KDGBD: Unknown 'H' command: %s\n", gdb_input);
-        send_gdb_packet("");
+        Status = send_gdb_packet("");
     }
-}
 
-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);
+    return Status;
 }
 
 static
-void
+KDSTATUS
 handle_gdb_thread_alive(void)
 {
     ULONG_PTR Pid, Tid;
     PETHREAD Thread;
+    KDSTATUS Status;
+
+#if MONOPROCESS
+    Pid = 0;
+    Tid = hex_to_tid(&gdb_input[1]);
+
+    KDDBGPRINT("Checking if %p is alive.\n", Tid);
 
+#else
     Pid = hex_to_pid(&gdb_input[2]);
     Tid = hex_to_tid(strstr(gdb_input, ".") + 1);
 
     /* We cannot use PsLookupProcessThreadByCid as we could be running at any IRQL.
      * So loop. */
     KDDBGPRINT("Checking if p%p.%p is alive.\n", Pid, Tid);
+#endif
 
     Thread = find_thread(Pid, Tid);
 
     if (Thread != NULL)
-        send_gdb_packet("OK");
+        Status = send_gdb_packet("OK");
     else
-        send_gdb_packet("E03");
+        Status = send_gdb_packet("E03");
+
+    return Status;
 }
 
 /* q* packets */
 static
 KDSTATUS
-handle_gdb_query(
-    _Out_ DBGKD_MANIPULATE_STATE64* State,
-    _Out_ PSTRING MessageData,
-    _Out_ PULONG MessageLength,
-    _Inout_ PKD_CONTEXT KdContext)
+handle_gdb_query(void)
 {
     if (strncmp(gdb_input, "qSupported:", 11) == 0)
     {
-        send_gdb_packet("PacketSize=4096;multiprocess+;");
-        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+#if MONOPROCESS
+        return send_gdb_packet("PacketSize=1000;qXfer:libraries:read+;");
+#else
+        return send_gdb_packet("PacketSize=1000;multiprocess+;qXfer:libraries:read+;");
+#endif
     }
 
     if (strncmp(gdb_input, "qAttached", 9) == 0)
     {
-        /* Say no: We didn't attach, we create the process! */
-        send_gdb_packet("0");
-        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+#if MONOPROCESS
+        return send_gdb_packet("1");
+#else
+        UINT_PTR queried_pid = hex_to_pid(&gdb_input[10]);
+        /* Let's say we created system process */
+        if (gdb_pid_to_handle(queried_pid) == NULL)
+            return send_gdb_packet("0");
+        else
+            return send_gdb_packet("1");
+#endif
     }
 
     if (strncmp(gdb_input, "qRcmd,", 6) == 0)
     {
-        send_gdb_packet("OK");
-        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+        return send_gdb_packet("OK");
     }
 
     if (strcmp(gdb_input, "qC") == 0)
     {
         char gdb_out[64];
+#if MONOPROCESS
+        sprintf(gdb_out, "QC:%"PRIxPTR";",
+            handle_to_gdb_tid(PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)));
+#else
         sprintf(gdb_out, "QC:p%"PRIxPTR".%"PRIxPTR";",
             handle_to_gdb_pid(PsGetThreadProcessId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)),
             handle_to_gdb_tid(PsGetThreadId((PETHREAD)(ULONG_PTR)CurrentStateChange.Thread)));
-        send_gdb_packet(gdb_out);
-        return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+#endif
+        return send_gdb_packet(gdb_out);
     }
 
-    if ((strncmp(gdb_input, "qfThreadInfo", 12) == 0)
-            || (strncmp(gdb_input, "qsThreadInfo", 12) == 0))
+    if (strncmp(gdb_input, "qfThreadInfo", 12) == 0)
     {
-        BOOLEAN FirstThread = TRUE;
         PEPROCESS Process;
-        PETHREAD Thread;
-        char gdb_out[1024];
-        char* ptr = gdb_out;
-        BOOLEAN Resuming = strncmp(gdb_input, "qsThreadInfo", 12) == 0;
+        char gdb_out[40];
+        LIST_ENTRY* CurrentProcessEntry;
 
-        if (Resuming)
+        CurrentProcessEntry = ProcessListHead->Flink;
+        if (CurrentProcessEntry == NULL) /* Ps is not initialized */
         {
-            if (CurrentProcessEntry == (LIST_ENTRY*)1)
+#if MONOPROCESS
+            return send_gdb_packet("m1");
+#else
+            return send_gdb_packet("mp1.1");
+#endif
+        }
+
+        /* We will push threads as we find them */
+        start_gdb_packet();
+
+        /* Start with the system thread */
+#if MONOPROCESS
+        send_gdb_partial_packet("m1");
+#else
+        send_gdb_partial_packet("mp1.1");
+#endif
+
+        /* List all the processes */
+        for ( ;
+            CurrentProcessEntry != ProcessListHead;
+            CurrentProcessEntry = CurrentProcessEntry->Flink)
+        {
+            LIST_ENTRY* CurrentThreadEntry;
+
+            Process = CONTAINING_RECORD(CurrentProcessEntry, EPROCESS, ActiveProcessLinks);
+
+            /* List threads from this process */
+            for ( CurrentThreadEntry = Process->ThreadListHead.Flink;
+                 CurrentThreadEntry != &Process->ThreadListHead;
+                 CurrentThreadEntry = CurrentThreadEntry->Flink)
             {
-                /* We're done */
-                send_gdb_packet("l");
-                CurrentProcessEntry = NULL;
-                return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+                PETHREAD Thread = CONTAINING_RECORD(CurrentThreadEntry, ETHREAD, ThreadListEntry);
+
+#if MONOPROCESS
+                _snprintf(gdb_out, 40, ",%p", handle_to_gdb_tid(Thread->Cid.UniqueThread));
+#else
+                _snprintf(gdb_out, 40, ",p%p.%p",
+                    handle_to_gdb_pid(Process->UniqueProcessId),
+                    handle_to_gdb_tid(Thread->Cid.UniqueThread));
+#endif
+                send_gdb_partial_packet(gdb_out);
             }
+        }
+
+        return finish_gdb_packet();
+    }
+
+    if (strncmp(gdb_input, "qsThreadInfo", 12) == 0)
+    {
+        /* We sent the whole thread list on first qfThreadInfo call */
+        return send_gdb_packet("l");
+    }
+
+    if (strncmp(gdb_input, "qThreadExtraInfo,", 17) == 0)
+    {
+        ULONG_PTR Pid, Tid;
+        PETHREAD Thread;
+        PEPROCESS Process;
+        char out_string[64];
+        STRING String = {0, 64, out_string};
+
+        KDDBGPRINT("Giving extra info for");
 
-            if (CurrentThreadEntry == NULL)
-                CurrentProcessEntry = CurrentProcessEntry->Flink;
+#if MONOPROCESS
+        Pid = 0;
+        Tid = hex_to_tid(&gdb_input[17]);
+
+        KDDBGPRINT(" %p.\n", Tid);
+
+        Thread = find_thread(Pid, Tid);
+        Process = CONTAINING_RECORD(Thread->Tcb.Process, EPROCESS, Pcb);
+#else
+        Pid = hex_to_pid(&gdb_input[18]);
+        Tid = hex_to_tid(strstr(&gdb_input[18], ".") + 1);
+
+        /* We cannot use PsLookupProcessThreadByCid as we could be running at any IRQL.
+         * So loop. */
+        KDDBGPRINT(" p%p.%p.\n", Pid, Tid);
+
+        Process = find_process(Pid);
+        Thread = find_thread(Pid, Tid);
+#endif
+
+        if (PsGetThreadProcessId(Thread) == 0)
+        {
+            String.Length = sprintf(out_string, "SYSTEM");
         }
         else
-            CurrentProcessEntry = ProcessListHead->Flink;
+        {
+            String.Length = sprintf(out_string, "%.*s", 16, Process->ImageFileName);
+        }
+
+        return gdb_send_debug_io(&String, FALSE);
+    }
+
+    if (strncmp(gdb_input, "qOffsets", 8) == 0)
+    {
+        /* We load ntoskrnl at 0x80800000 while compiling it at 0x00800000 base address */
+        return send_gdb_packet("TextSeg=80000000");
+    }
+
+    if (strcmp(gdb_input, "qTStatus") == 0)
+    {
+        /* No tracepoint support */
+        return send_gdb_packet("T0");
+    }
+
+    if (strcmp(gdb_input, "qSymbol::") == 0)
+    {
+        /* No need */
+        return send_gdb_packet("OK");
+    }
+
+    if (strncmp(gdb_input, "qXfer:libraries:read::", 22) == 0)
+    {
+        static LIST_ENTRY* CurrentEntry = NULL;
+        char str_helper[256];
+        char name_helper[64];        
+        ULONG_PTR Offset = hex_to_address(&gdb_input[22]);
+        ULONG_PTR ToSend = hex_to_address(strstr(&gdb_input[22], ",") + 1);
+        ULONG Sent = 0;
+        static BOOLEAN allDone = FALSE;
+
+        KDDBGPRINT("KDGDB: qXfer:libraries:read !\n");
 
-        if ((CurrentProcessEntry == ProcessListHead) ||
-                (CurrentProcessEntry == NULL)) /* Ps is not initialized */
+        /* Start the packet */
+        start_gdb_packet();
+
+        if (allDone)
         {
-            /* We're almost done. Tell GDB about the idle thread */
-            send_gdb_packet("mp1.1");
-            CurrentProcessEntry = (LIST_ENTRY*)1;
-            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+            send_gdb_partial_packet("l");
+            allDone = FALSE;
+            return finish_gdb_packet();
         }
 
-        Process = CONTAINING_RECORD(CurrentProcessEntry, EPROCESS, ActiveProcessLinks);
+        send_gdb_partial_packet("m");
+        Sent++;
 
-        if (Resuming && CurrentThreadEntry != NULL)
-            CurrentThreadEntry = CurrentThreadEntry->Flink;
-        else
-            CurrentThreadEntry = Process->ThreadListHead.Flink;
+        /* Are we starting ? */
+        if (Offset == 0)
+        {
+            Sent += send_gdb_partial_binary("<?xml version=\"1.0\"?>", 21);
+            Sent += send_gdb_partial_binary("<library-list>", 14);
 
-        ptr = gdb_out;
+            CurrentEntry = ModuleListHead->Flink;
+
+            if (!CurrentEntry)
+            {
+                /* Ps is not initialized. Send end of XML data or mark that we are finished. */
+                Sent += send_gdb_partial_binary("</library-list>", 15);
+                allDone = TRUE;
+                return finish_gdb_packet();
+            }
+        }
 
-        *ptr++ = 'm';
-        /* List threads from this process */
         for ( ;
-             CurrentThreadEntry != &Process->ThreadListHead;
-             CurrentThreadEntry = CurrentThreadEntry->Flink)
+            CurrentEntry != ModuleListHead;
+            CurrentEntry = CurrentEntry->Flink)
         {
-            Thread = CONTAINING_RECORD(CurrentThreadEntry, ETHREAD, ThreadListEntry);
+            PLDR_DATA_TABLE_ENTRY TableEntry = CONTAINING_RECORD(CurrentEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
+            PVOID DllBase = (PVOID)((ULONG_PTR)TableEntry->DllBase + 0x1000);
+            LONG mem_length;
+            char* ptr;
 
-            /* See if we should add a comma */
-            if (FirstThread)
+            /* Convert names to lower case. Yes this _is_ ugly */
+            _snprintf(name_helper, 64, "%wZ", &TableEntry->BaseDllName);
+            for (ptr = name_helper; *ptr; ptr++)
             {
-                FirstThread = FALSE;
+                if (*ptr >= 'A' && *ptr <= 'Z')
+                    *ptr += 'a' - 'A';
             }
-            else
+
+            /* GDB doesn't load the file if you don't prefix it with a drive letter... */
+            mem_length = _snprintf(str_helper, 256, "<library name=\"C:\\%s\"><segment address=\"0x%p\"/></library>", &name_helper, DllBase);
+            
+            /* DLL name must be too long. */
+            if (mem_length < 0)
             {
-                *ptr++ = ',';
+                KDDBGPRINT("Failed to report %wZ\n", &TableEntry->BaseDllName);
+                continue;
             }
 
-            ptr += _snprintf(ptr, 1024 - (ptr - gdb_out),
-                "p%p.%p",
-                handle_to_gdb_pid(Process->UniqueProcessId),
-                handle_to_gdb_tid(Thread->Cid.UniqueThread));
-            if (ptr > (gdb_out + 1024))
+            if ((Sent + mem_length) > ToSend)
             {
-                /* 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);
+                /* We're done for this pass */
+                return finish_gdb_packet();
             }
+                
+            Sent += send_gdb_partial_binary(str_helper, mem_length);
+        }
+
+        if ((ToSend - Sent) > 15)
+        {
+            Sent += send_gdb_partial_binary("</library-list>", 15);
+            allDone = TRUE;
         }
 
-        /* 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);
+        return finish_gdb_packet();
+   
     }
 
     KDDBGPRINT("KDGDB: Unknown query: %s\n", gdb_input);
-    send_gdb_packet("");
-    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+    return send_gdb_packet("");
 }
 
 #if 0
@@ -297,18 +456,24 @@ ReadMemorySendHandler(
         KDDBGPRINT("Wrong API number (%lu) after DbgKdReadVirtualMemoryApi request.\n", State->ApiNumber);
     }
 
-    /* Check status */
-    if (!NT_SUCCESS(State->ReturnStatus))
+    /* Check status. Allow to send partial data. */
+    if (!MessageData->Length && !NT_SUCCESS(State->ReturnStatus))
         send_gdb_ntstatus(State->ReturnStatus);
     else
         send_gdb_memory(MessageData->Buffer, MessageData->Length);
     KdpSendPacketHandler = NULL;
     KdpManipulateStateHandler = NULL;
 
+#if MONOPROCESS
+    if (gdb_dbg_tid != 0)
     /* Reset the TLB */
+#else
     if ((gdb_dbg_pid != 0) && gdb_pid_to_handle(gdb_dbg_pid) != PsGetCurrentProcessId())
+#endif
     {
-        __writecr3(PsGetCurrentProcess()->Pcb.DirectoryTableBase[0]);
+        /* Only do this if Ps is initialized */
+        if (ProcessListHead->Flink)
+            __writecr3(PsGetCurrentProcess()->Pcb.DirectoryTableBase[0]);
     }
 }
 
@@ -329,116 +494,534 @@ handle_gdb_read_mem(
     *MessageLength = 0;
 
     /* Set the TLB according to the process being read. Pid 0 means any process. */
+#if MONOPROCESS
+    if ((gdb_dbg_tid != 0) && gdb_tid_to_handle(gdb_dbg_tid) != PsGetCurrentThreadId())
+    {
+        PETHREAD AttachedThread = find_thread(0, gdb_dbg_tid);
+        PKPROCESS AttachedProcess;
+        if (AttachedThread == NULL)
+        {
+            KDDBGPRINT("The current GDB debug thread is invalid!");
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
+        }
+
+        AttachedProcess = AttachedThread->Tcb.Process;
+        if (AttachedProcess == NULL)
+        {
+            KDDBGPRINT("The current GDB debug thread is invalid!");
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
+        }
+        __writecr3(AttachedProcess->DirectoryTableBase[0]);
+    }
+#else
     if ((gdb_dbg_pid != 0) && gdb_pid_to_handle(gdb_dbg_pid) != PsGetCurrentProcessId())
     {
         PEPROCESS AttachedProcess = find_process(gdb_dbg_pid);
         if (AttachedProcess == NULL)
         {
             KDDBGPRINT("The current GDB debug thread is invalid!");
-            send_gdb_packet("E03");
-            return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
         }
-        __writecr3(AttachedProcess->Pcb.DirectoryTableBase[0]);
+        /* Only do this if Ps is initialized */
+        if (ProcessListHead->Flink)
+            __writecr3(AttachedProcess->Pcb.DirectoryTableBase[0]);
     }
+#endif
 
     State->u.ReadMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
     State->u.ReadMemory.TransferCount = hex_to_address(strstr(&gdb_input[1], ",") + 1);
 
     /* KD will reply with KdSendPacket. Catch it */
     KdpSendPacketHandler = ReadMemorySendHandler;
-
     return KdPacketReceived;
 }
 
+static
+void
+WriteMemorySendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData)
+{
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+
+    if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+    {
+        // KdAssert
+        KDDBGPRINT("Wrong packet type (%lu) received after DbgKdWriteVirtualMemoryApi request.\n", PacketType);
+        while (1);
+    }
+
+    if (State->ApiNumber != DbgKdWriteVirtualMemoryApi)
+    {
+        KDDBGPRINT("Wrong API number (%lu) after DbgKdWriteVirtualMemoryApi request.\n", State->ApiNumber);
+    }
+
+    /* Check status */
+    if (!NT_SUCCESS(State->ReturnStatus))
+        send_gdb_ntstatus(State->ReturnStatus);
+    else
+        send_gdb_packet("OK");
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
+
+#if MONOPROCESS
+    if (gdb_dbg_tid != 0)
+    /* Reset the TLB */
+#else
+    if ((gdb_dbg_pid != 0) && gdb_pid_to_handle(gdb_dbg_pid) != PsGetCurrentProcessId())
+#endif
+    {
+        /* Only do this if Ps is initialized */
+        if (ProcessListHead->Flink)
+            __writecr3(PsGetCurrentProcess()->Pcb.DirectoryTableBase[0]);
+    }
+}
+
 static
 KDSTATUS
-handle_gdb_v(
+handle_gdb_write_mem(
     _Out_ DBGKD_MANIPULATE_STATE64* State,
     _Out_ PSTRING MessageData,
     _Out_ PULONG MessageLength,
     _Inout_ PKD_CONTEXT KdContext)
 {
-    if (strncmp(gdb_input, "vCont", 5) == 0)
+    /* Maximal input buffer is 0x1000. Each byte is encoded on two bytes by GDB */
+    static UCHAR OutBuffer[0x800];
+    ULONG BufferLength;
+    char* blob_ptr;
+    UCHAR* OutPtr;
+
+    State->ApiNumber = DbgKdWriteVirtualMemoryApi;
+    State->ReturnStatus = STATUS_SUCCESS; /* ? */
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+
+    /* Set the TLB according to the process being read. Pid 0 means any process. */
+#if MONOPROCESS
+    if ((gdb_dbg_tid != 0) && gdb_tid_to_handle(gdb_dbg_tid) != PsGetCurrentThreadId())
     {
-        if (gdb_input[5] == '?')
+        PETHREAD AttachedThread = find_thread(0, gdb_dbg_tid);
+        PKPROCESS AttachedProcess;
+        if (AttachedThread == NULL)
         {
-            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);
+            KDDBGPRINT("The current GDB debug thread is invalid!");
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
+        }
+
+        AttachedProcess = AttachedThread->Tcb.Process;
+        if (AttachedProcess == NULL)
+        {
+            KDDBGPRINT("The current GDB debug thread is invalid!");
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
+        }
+        __writecr3(AttachedProcess->DirectoryTableBase[0]);
+    }
+#else
+    if ((gdb_dbg_pid != 0) && gdb_pid_to_handle(gdb_dbg_pid) != PsGetCurrentProcessId())
+    {
+        PEPROCESS AttachedProcess = find_process(gdb_dbg_pid);
+        if (AttachedProcess == NULL)
+        {
+            KDDBGPRINT("The current GDB debug thread is invalid!");
+            return LOOP_IF_SUCCESS(send_gdb_packet("E03"));
+        }
+        /* Only do this if Ps is initialized */
+        if (ProcessListHead->Flink)
+            __writecr3(AttachedProcess->Pcb.DirectoryTableBase[0]);
+    }
+#endif
+
+    State->u.WriteMemory.TargetBaseAddress = hex_to_address(&gdb_input[1]);
+    BufferLength = hex_to_address(strstr(&gdb_input[1], ",") + 1);
+    if (BufferLength == 0)
+    {
+        /* Nothing to do */
+        return LOOP_IF_SUCCESS(send_gdb_packet("OK"));
+    }
+    
+    State->u.WriteMemory.TransferCount = BufferLength;
+    MessageData->Length = BufferLength;
+    MessageData->Buffer = (CHAR*)OutBuffer;
+
+    OutPtr = OutBuffer;
+    blob_ptr = strstr(strstr(&gdb_input[1], ",") + 1, ":") + 1;
+    while (BufferLength)
+    {
+        if (BufferLength >= 4)
+        {
+            *((ULONG*)OutPtr) = *((ULONG*)blob_ptr);
+            OutPtr += 4;
+            blob_ptr += 4;
+            BufferLength -= 4;
+        }
+        else if (BufferLength >= 2)
+        {
+            *((USHORT*)OutPtr) = *((USHORT*)blob_ptr);
+            OutPtr += 2;
+            blob_ptr += 2;
+            BufferLength -= 2;
+        }
+        else
+        {
+            *OutPtr++ = *blob_ptr++;
+            BufferLength--;
         }
+    }
+
+    /* KD will reply with KdSendPacket. Catch it */
+    KdpSendPacketHandler = WriteMemorySendHandler;
+    return KdPacketReceived;
+}
+
+static
+void
+WriteBreakPointSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData)
+{
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+
+    if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+    {
+        // KdAssert
+        KDDBGPRINT("Wrong packet type (%lu) received after DbgKdWriteBreakPointApi request.\n", PacketType);
+        while (1);
+    }
 
-        if (strcmp(gdb_input, "vCont;c") == 0)
+    if (State->ApiNumber != DbgKdWriteBreakPointApi)
+    {
+        KDDBGPRINT("Wrong API number (%lu) after DbgKdWriteBreakPointApi request.\n", State->ApiNumber);
+    }
+
+    /* Check status */
+    if (!NT_SUCCESS(State->ReturnStatus))
+    {
+        KDDBGPRINT("Inserting breakpoint failed!\n");
+        send_gdb_ntstatus(State->ReturnStatus);
+    }
+    else
+    {
+        /* Keep track of the address+handle couple */
+        ULONG i;
+        for (i = 0; i < (sizeof(BreakPointHandles) / sizeof(BreakPointHandles[0])); i++)
         {
-            DBGKM_EXCEPTION64* Exception = NULL;
+            if (BreakPointHandles[i].Address == 0)
+            {
+                BreakPointHandles[i].Address = (ULONG_PTR)State->u.WriteBreakPoint.BreakPointAddress;
+                BreakPointHandles[i].Handle = State->u.WriteBreakPoint.BreakPointHandle;
+                break;
+            }
+        }
+        send_gdb_packet("OK");
+    }
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
+}
 
-            /* Tell GDB everything is fine, we will handle it */
-            send_gdb_packet("OK");
+static
+KDSTATUS
+handle_gdb_insert_breakpoint(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    State->ReturnStatus = STATUS_SUCCESS; /* ? */
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    if (MessageData)
+        MessageData->Length = 0;
+    *MessageLength = 0;
+
+    switch (gdb_input[1])
+    {
+        case '0':
+        {
+            ULONG_PTR Address = hex_to_address(&gdb_input[3]);
+            ULONG i;
+            BOOLEAN HasFreeSlot = FALSE;
 
-            if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
-                Exception = &CurrentStateChange.u.Exception;
+            KDDBGPRINT("Inserting breakpoint at %p.\n", (void*)Address);
 
-            /* See if we should update the program counter (unlike windbg, gdb doesn't do it for us) */
-            if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
-                    && (Exception->ExceptionRecord.ExceptionInformation[0] == 0))
+            for (i = 0; i < (sizeof(BreakPointHandles) / sizeof(BreakPointHandles[0])); i++)
             {
-                ULONG_PTR ProgramCounter;
+                if (BreakPointHandles[i].Address == 0)
+                    HasFreeSlot = TRUE;
+            }
+
+            if (!HasFreeSlot)
+            {
+                /* We don't have a way to keep track of this break point. Fail. */
+                KDDBGPRINT("No breakpoint slot available!\n");
+                return LOOP_IF_SUCCESS(send_gdb_packet("E01"));
+            }
+
+            State->ApiNumber = DbgKdWriteBreakPointApi;
+            State->u.WriteBreakPoint.BreakPointAddress = Address;
+            /* FIXME : ignoring all other Z0 arguments */
+
+            /* KD will reply with KdSendPacket. Catch it */
+            KdpSendPacketHandler = WriteBreakPointSendHandler;
+            return KdPacketReceived;
+        }
+    }
+
+    KDDBGPRINT("Unhandled 'Z' packet: %s\n", gdb_input);
+    return LOOP_IF_SUCCESS(send_gdb_packet("E01"));
+}
+
+static
+void
+RestoreBreakPointSendHandler(
+    _In_ ULONG PacketType,
+    _In_ PSTRING MessageHeader,
+    _In_ PSTRING MessageData)
+{
+    DBGKD_MANIPULATE_STATE64* State = (DBGKD_MANIPULATE_STATE64*)MessageHeader->Buffer;
+    ULONG i;
+
+    if (PacketType != PACKET_TYPE_KD_STATE_MANIPULATE)
+    {
+        // KdAssert
+        KDDBGPRINT("Wrong packet type (%lu) received after DbgKdRestoreBreakPointApi request.\n", PacketType);
+        while (1);
+    }
+
+    if (State->ApiNumber != DbgKdRestoreBreakPointApi)
+    {
+        KDDBGPRINT("Wrong API number (%lu) after DbgKdRestoreBreakPointApi request.\n", State->ApiNumber);
+    }
+
+    /* We ignore failure here. If DbgKdRestoreBreakPointApi fails, 
+     * this means that the breakpoint was already invalid for KD. So clean it up on our side. */
+    for (i = 0; i < (sizeof(BreakPointHandles) / sizeof(BreakPointHandles[0])); i++)
+    {
+        if (BreakPointHandles[i].Handle == State->u.RestoreBreakPoint.BreakPointHandle)
+        {
+            BreakPointHandles[i].Address = 0;
+            BreakPointHandles[i].Handle = 0;
+            break;
+        }
+    }
+
+    send_gdb_packet("OK");
+
+    KdpSendPacketHandler = NULL;
+    KdpManipulateStateHandler = NULL;
+}
+
+static
+KDSTATUS
+handle_gdb_remove_breakpoint(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    State->ReturnStatus = STATUS_SUCCESS; /* ? */
+    State->Processor = CurrentStateChange.Processor;
+    State->ProcessorLevel = CurrentStateChange.ProcessorLevel;
+    if (MessageData)
+        MessageData->Length = 0;
+    *MessageLength = 0;
+
+    switch (gdb_input[1])
+    {
+        case '0':
+        {
+            ULONG_PTR Address = hex_to_address(&gdb_input[3]);
+            ULONG i, Handle = 0;
+
+            KDDBGPRINT("Removing breakpoint on %p.\n", (void*)Address);
 
-                /* So we must get past the breakpoint instruction */
-                ProgramCounter = KdpGetContextPc(&CurrentContext);
-                KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
+            for (i = 0; i < (sizeof(BreakPointHandles) / sizeof(BreakPointHandles[0])); i++)
+            {
+                if (BreakPointHandles[i].Address == Address)
+                {
+                    Handle = BreakPointHandles[i].Handle;
+                    break;
+                }
+            }
 
-                SetContextManipulateHandler(State, MessageData, MessageLength, KdContext);
-                KdpManipulateStateHandler = ContinueManipulateStateHandler;
-                return KdPacketReceived;
+            if (Handle == 0)
+            {
+                KDDBGPRINT("Received %s, but breakpoint was never inserted ?!\n", gdb_input);
+                return LOOP_IF_SUCCESS(send_gdb_packet("E01"));
             }
 
-            return ContinueManipulateStateHandler(State, MessageData, MessageLength, KdContext);
+            State->ApiNumber = DbgKdRestoreBreakPointApi;
+            State->u.RestoreBreakPoint.BreakPointHandle = Handle;
+            /* FIXME : ignoring all other z0 arguments */
+
+            /* KD will reply with KdSendPacket. Catch it */
+            KdpSendPacketHandler = RestoreBreakPointSendHandler;
+            return KdPacketReceived;
         }
     }
 
-    KDDBGPRINT("Unhandled 'v' packet: %s\n", gdb_input);
+    KDDBGPRINT("Unhandled 'Z' packet: %s\n", gdb_input);
+    return LOOP_IF_SUCCESS(send_gdb_packet("E01"));
+}
+
+static
+KDSTATUS
+handle_gdb_c(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDSTATUS Status;
+
+    /* Tell GDB everything is fine, we will handle it */
+    Status = send_gdb_packet("OK");
+    if (Status != KdPacketReceived)
+        return Status;
+        
+
+    if (CurrentStateChange.NewState == DbgKdExceptionStateChange)
+    {
+        DBGKM_EXCEPTION64* Exception = &CurrentStateChange.u.Exception;
+        ULONG_PTR ProgramCounter = KdpGetContextPc(&CurrentContext);
+
+        /* See if we should update the program counter */
+        if (Exception && (Exception->ExceptionRecord.ExceptionCode == STATUS_BREAKPOINT)
+                && ((*(KD_BREAKPOINT_TYPE*)ProgramCounter) == KD_BREAKPOINT_VALUE))
+        {
+            /* We must get past the breakpoint instruction */
+            KdpSetContextPc(&CurrentContext, ProgramCounter + KD_BREAKPOINT_SIZE);
+
+            SetContextManipulateHandler(State, MessageData, MessageLength, KdContext);
+            KdpManipulateStateHandler = ContinueManipulateStateHandler;
+            return KdPacketReceived;
+        }
+    }
+
+    return ContinueManipulateStateHandler(State, MessageData, MessageLength, KdContext);
+}
+
+static
+KDSTATUS
+handle_gdb_s(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDDBGPRINT("Single stepping.\n");
+    /* Set CPU single step mode and continue */
+    KdpSetSingleStep(&CurrentContext);
+    SetContextManipulateHandler(State, MessageData, MessageLength, KdContext);
+    KdpManipulateStateHandler = ContinueManipulateStateHandler;
     return KdPacketReceived;
 }
 
-/* GLOBAL FUNCTIONS ***********************************************************/
+static
 KDSTATUS
-gdb_interpret_input(
+handle_gdb_v(
     _Out_ DBGKD_MANIPULATE_STATE64* State,
     _Out_ PSTRING MessageData,
     _Out_ PULONG MessageLength,
     _Inout_ PKD_CONTEXT KdContext)
 {
-    switch (gdb_input[0])
+    if (strncmp(gdb_input, "vCont", 5) == 0)
     {
-    case '?':
-        /* Send the Status */
-        gdb_send_exception();
-        break;
-    case 'g':
-        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, KdContext);
-    case 'p':
-        return gdb_send_register(State, MessageData, MessageLength, KdContext);
-    case 'q':
-        return handle_gdb_query(State, MessageData, MessageLength, KdContext);
-    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;
-        KDDBGPRINT("Unsupported GDB command: %s.\n", gdb_input);
-        return KdPacketReceived;
+        if (gdb_input[5] == '?')
+        {
+            /* Report what we support */
+            return LOOP_IF_SUCCESS(send_gdb_packet("vCont;c;s"));
+        }
+
+        if (strncmp(gdb_input, "vCont;c", 7) == 0)
+        {
+            return handle_gdb_c(State, MessageData, MessageLength, KdContext);
+        }
+
+        if (strncmp(gdb_input, "vCont;s", 7) == 0)
+        {
+            
+            return handle_gdb_s(State, MessageData, MessageLength, KdContext);
+        }
     }
-    return gdb_receive_and_interpret_packet(State, MessageData, MessageLength, KdContext);
+
+    KDDBGPRINT("Unhandled 'v' packet: %s\n", gdb_input);
+    return LOOP_IF_SUCCESS(send_gdb_packet(""));
 }
+
+KDSTATUS
+gdb_receive_and_interpret_packet(
+    _Out_ DBGKD_MANIPULATE_STATE64* State,
+    _Out_ PSTRING MessageData,
+    _Out_ PULONG MessageLength,
+    _Inout_ PKD_CONTEXT KdContext)
+{
+    KDSTATUS Status;
+
+    do
+    {
+        KDDBGPRINT("KDGBD: Receiving packet.\n");
+        Status = gdb_receive_packet(KdContext);
+        KDDBGPRINT("KDGBD: Packet \"%s\" received with status %u\n", gdb_input, Status);
+
+        if (Status != KdPacketReceived)
+            return Status;
+
+        Status = (KDSTATUS)-1;
+
+        switch (gdb_input[0])
+        {
+        case '?':
+            /* Send the Status */
+            Status = LOOP_IF_SUCCESS(gdb_send_exception());
+            break;
+        case '!':
+            Status = LOOP_IF_SUCCESS(send_gdb_packet("OK"));
+            break;
+        case 'c':
+            Status = handle_gdb_c(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'g':
+            Status = LOOP_IF_SUCCESS(gdb_send_registers());
+            break;
+        case 'H':
+            Status = LOOP_IF_SUCCESS(handle_gdb_set_thread());
+            break;
+        case 'm':
+            Status = handle_gdb_read_mem(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'p':
+            Status = LOOP_IF_SUCCESS(gdb_send_register());
+            break;
+        case 'q':
+            Status = LOOP_IF_SUCCESS(handle_gdb_query());
+            break;
+        case 's':
+            Status = handle_gdb_s(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'T':
+            Status = LOOP_IF_SUCCESS(handle_gdb_thread_alive());
+            break;
+        case 'v':
+            Status = handle_gdb_v(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'X':
+            Status = handle_gdb_write_mem(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'z':
+            Status = handle_gdb_remove_breakpoint(State, MessageData, MessageLength, KdContext);
+            break;
+        case 'Z':
+            Status = handle_gdb_insert_breakpoint(State, MessageData, MessageLength, KdContext);
+            break;
+        default:
+            /* We don't know how to handle this request. */
+            KDDBGPRINT("Unsupported GDB command: %s.\n", gdb_input);
+            Status = LOOP_IF_SUCCESS(send_gdb_packet(""));
+        }
+    } while (Status == (KDSTATUS)-1);
+
+    return Status;
+}
+