update for HEAD-2003091401
[reactos.git] / ntoskrnl / kd / gdbstub.c
1 /****************************************************************************
2
3                 THIS SOFTWARE IS NOT COPYRIGHTED
4
5    HP offers the following for use in the public domain.  HP makes no
6    warranty with regard to the software or it's performance and the
7    user accepts the software "AS IS" with all faults.
8
9    HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD
10    TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES
11    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12
13 ****************************************************************************/
14
15 /****************************************************************************
16  *  Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $
17  *
18  *  Module name: remcom.c $
19  *  Revision: 1.34 $
20  *  Date: 91/03/09 12:29:49 $
21  *  Contributor:     Lake Stevens Instrument Division$
22  *
23  *  Description:     low level support for gdb debugger. $
24  *
25  *  Considerations:  only works on target hardware $
26  *
27  *  Written by:      Glenn Engel $
28  *  ModuleState:     Experimental $
29  *
30  *  NOTES:           See Below $
31  *
32  *  Modified for 386 by Jim Kingdon, Cygnus Support.
33  *  Modified for ReactOS by Casper S. Hornstrup <chorns@users.sourceforge.net>
34  *
35  *  To enable debugger support, two things need to happen.  One, setting
36  *  up a routine so that it is in the exception path, is necessary in order
37  *  to allow any breakpoints or error conditions to be properly intercepted
38  *  and reported to gdb.
39  *  Two, a breakpoint needs to be generated to begin communication.
40  *
41  *  Because gdb will sometimes write to the stack area to execute function
42  *  calls, this program cannot rely on using the supervisor stack so it
43  *  uses it's own stack area.
44  *
45  *************
46  *
47  *    The following gdb commands are supported:
48  *
49  * command          function                               Return value
50  *
51  *    g             return the value of the CPU Registers  hex data or ENN
52  *    G             set the value of the CPU Registers     OK or ENN
53  *
54  *    mAA..AA,LLLL  Read LLLL bytes at address AA..AA      hex data or ENN
55  *    MAA..AA,LLLL: Write LLLL bytes at address AA.AA      OK or ENN
56  *
57  *    c             Resume at current address              SNN   ( signal NN)
58  *    cAA..AA       Continue at address AA..AA             SNN
59  *
60  *    s             Step one instruction                   SNN
61  *    sAA..AA       Step one instruction from AA..AA       SNN
62  *
63  *    k             kill
64  *
65  *    ?             What was the last sigval ?             SNN   (signal NN)
66  *
67  * All commands and responses are sent with a packet which includes a
68  * Checksum.  A packet consists of
69  *
70  * $<packet info>#<Checksum>.
71  *
72  * where
73  * <packet info> :: <characters representing the command or response>
74  * <Checksum>    :: < two hex digits computed as modulo 256 sum of <packetinfo>>
75  *
76  * When a packet is received, it is first acknowledged with either '+' or '-'.
77  * '+' indicates a successful transfer.  '-' indicates a failed transfer.
78  *
79  * Example:
80  *
81  * Host:                  Reply:
82  * $m0,10#2a               +$00010203040506070809101112131415#42
83  *
84  ****************************************************************************/
85
86 #include <ntddk.h>
87 #include <internal/kd.h>
88 #include <internal/ke.h>
89 #include <internal/ps.h>
90 #include <internal/module.h>
91 #include <internal/ldr.h>
92
93 #define NDEBUG
94 #include <internal/debug.h>
95
96 extern LIST_ENTRY PiThreadListHead;
97
98
99 /************************************************************************/
100 /* BUFMAX defines the maximum number of characters in inbound/outbound buffers*/
101 /* at least NUMREGBYTES*2 are needed for register packets */
102 #define BUFMAX 1000
103
104 static BOOLEAN GspInitialized;
105 #if 0
106 static PKINTERRUPT GspInterrupt;
107 #endif
108
109 static BOOLEAN GspRemoteDebug;
110
111 static CONST CHAR HexChars[]="0123456789abcdef";
112
113 static PETHREAD GspRunThread; /* NULL means run all threads */
114 static PETHREAD GspDbgThread;
115 static PETHREAD GspEnumThread;
116
117 /* Number of Registers.  */
118 #define NUMREGS 16
119
120 enum REGISTER_NAMES
121 {
122   EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI,
123         PC /* also known as eip */,
124         PS /* also known as eflags */,
125         CS, SS, DS, ES, FS, GS
126 };
127
128 typedef struct _CPU_REGISTER
129 {
130   DWORD Size;
131   DWORD OffsetInTF;
132   DWORD OffsetInContext;
133   BOOLEAN SetInContext;
134 } CPU_REGISTER, *PCPU_REGISTER;
135
136 #define KTRAP_FRAME_X86 KTRAP_FRAME
137
138 #define EIP_REGNO 8
139
140 static CPU_REGISTER GspRegisters[NUMREGS] =
141 {
142   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eax), FIELD_OFFSET (CONTEXT, Eax), TRUE },
143   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ecx), FIELD_OFFSET (CONTEXT, Ecx), TRUE },
144   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Edx), FIELD_OFFSET (CONTEXT, Edx), FALSE },
145   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ebx), FIELD_OFFSET (CONTEXT, Ebx), TRUE },
146   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Esp), FIELD_OFFSET (CONTEXT, Esp), TRUE },
147   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ebp), FIELD_OFFSET (CONTEXT, Ebp), TRUE },
148   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Esi), FIELD_OFFSET (CONTEXT, Esi), TRUE },
149   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Edi), FIELD_OFFSET (CONTEXT, Edi), TRUE },
150   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eip), FIELD_OFFSET (CONTEXT, Eip), TRUE },
151   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eflags), FIELD_OFFSET (CONTEXT, EFlags), TRUE },
152   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Cs), FIELD_OFFSET (CONTEXT, SegCs), TRUE },
153   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ss), FIELD_OFFSET (CONTEXT, SegSs), TRUE },
154   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ds), FIELD_OFFSET (CONTEXT, SegDs), TRUE },
155   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Es), FIELD_OFFSET (CONTEXT, SegEs), TRUE },
156   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Fs), FIELD_OFFSET (CONTEXT, SegFs), TRUE },
157   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Gs), FIELD_OFFSET (CONTEXT, SegGs), TRUE }
158 };
159
160 static PCHAR GspThreadStates[THREAD_STATE_MAX] =
161 {
162   "Initialized",  /* THREAD_STATE_INITIALIZED */
163   "Ready",        /* THREAD_STATE_READY */
164   "Running",      /* THREAD_STATE_RUNNING */
165   "Suspended",    /* THREAD_STATE_SUSPENDED */
166   "Frozen",       /* THREAD_STATE_FROZEN */
167   "Terminated 1", /* THREAD_STATE_TERMINATED_1 */
168   "Terminated 2", /* THREAD_STATE_TERMINATED_2 */
169   "Blocked"       /* THREAD_STATE_BLOCKED */
170 };
171
172 char *
173 strtok(char *s, const char *delim)
174 {
175   const char *spanp;
176   int c, sc;
177   char *tok;
178   static char *last;
179
180
181   if (s == NULL && (s = last) == NULL)
182     return (NULL);
183
184   /*
185    * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
186    */
187  cont:
188   c = *s++;
189   for (spanp = delim; (sc = *spanp++) != 0;) {
190     if (c == sc)
191       goto cont;
192   }
193
194   if (c == 0) {                 /* no non-delimiter characters */
195     last = NULL;
196     return (NULL);
197   }
198   tok = s - 1;
199
200   /*
201    * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
202    * Note that delim must have one NUL; we stop if we see that, too.
203    */
204   for (;;) {
205     c = *s++;
206     spanp = delim;
207     do {
208       if ((sc = *spanp++) == c) {
209         if (c == 0)
210           s = NULL;
211         else
212           s[-1] = 0;
213         last = s;
214         return (tok);
215       }
216     } while (sc != 0);
217   }
218   /* NOTREACHED */
219 }
220
221
222 LONG
223 HexValue (CHAR ch)
224 {
225   if ((ch >= '0') && (ch <= '9')) return (ch - '0');
226   if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
227   if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
228   return (-1);
229 }
230
231 static CHAR GspInBuffer[BUFMAX];
232 static CHAR GspOutBuffer[BUFMAX];
233
234 /* scan for the sequence $<data>#<Checksum>     */
235
236 PCHAR
237 GspGetPacket()
238 {
239   PCHAR Buffer = &GspInBuffer[0];
240   CHAR Checksum;
241   CHAR XmitChecksum;
242   ULONG Count;
243   CHAR ch;
244
245   while (TRUE)
246     {
247       /* wait around for the start character, ignore all other characters */
248       while ((ch = KdGetChar ()) != '$');
249
250     retry:
251       Checksum = 0;
252       XmitChecksum = -1;
253       Count = 0;
254
255       /* now, read until a # or end of Buffer is found */
256       while (Count < BUFMAX)
257         {
258           ch = KdGetChar ();
259           if (ch == '$')
260             goto retry;
261           if (ch == '#')
262             break;
263           Checksum = Checksum + ch;
264           Buffer[Count] = ch;
265           Count = Count + 1;
266         }
267       Buffer[Count] = 0;
268
269       if (ch == '#')
270         {
271           ch = KdGetChar ();
272           XmitChecksum = HexValue (ch) << 4;
273           ch = KdGetChar ();
274           XmitChecksum += HexValue (ch);
275
276           if (Checksum != XmitChecksum)
277             {
278               KdPutChar ('-');  /* failed checksum */
279             }
280           else
281             {
282               KdPutChar ('+');  /* successful transfer */
283
284               /* if a sequence char is present, reply the sequence ID */
285               if (Buffer[2] == ':')
286                 {
287                   KdPutChar (Buffer[0]);
288                   KdPutChar (Buffer[1]);
289
290                   return &Buffer[3];
291                 }
292
293               return &Buffer[0];
294             }
295         }
296     }
297 }
298
299 /* send the packet in Buffer.  */
300
301 VOID
302 GspPutPacket (PCHAR Buffer)
303 {
304   CHAR Checksum;
305   LONG Count;
306   CHAR ch;
307
308   /*  $<packet info>#<Checksum>. */
309   do
310     {
311       KdPutChar ('$');
312       Checksum = 0;
313       Count = 0;
314
315       while ((ch = Buffer[Count]))
316                                 {
317                                   KdPutChar (ch);
318                                   Checksum += ch;
319                                   Count += 1;
320                                 }
321
322       KdPutChar ('#');
323       KdPutChar (HexChars[(Checksum >> 4) & 0xf]);
324       KdPutChar (HexChars[Checksum & 0xf]);
325     }
326   while (KdGetChar () != '+');
327 }
328
329
330 VOID
331 GspPutPacketNoWait (PCHAR Buffer)
332 {
333   CHAR Checksum;
334   LONG Count;
335   CHAR ch;
336
337   /*  $<packet info>#<Checksum>. */
338   KdPutChar ('$');
339   Checksum = 0;
340   Count = 0;
341
342   while ((ch = Buffer[Count]))
343                 {
344                   KdPutChar (ch);
345                   Checksum += ch;
346                   Count += 1;
347                 }
348
349   KdPutChar ('#');
350   KdPutChar (HexChars[(Checksum >> 4) & 0xf]);
351   KdPutChar (HexChars[Checksum & 0xf]);
352 }
353
354 /* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an
355    error.  */
356 static volatile BOOLEAN GspMemoryError = FALSE;
357 static volatile void *GspAccessLocation = NULL;
358
359
360 /* Convert the memory pointed to by Address into hex, placing result in Buffer */
361 /* Return a pointer to the last char put in Buffer (null) */
362 /* If MayFault is TRUE, then we should set GspMemoryError in response to
363    a fault; if FALSE treat a fault like any other fault in the stub.  */
364 PCHAR
365 GspMem2Hex (PCHAR Address,
366   PCHAR Buffer,
367   LONG Count,
368   BOOLEAN MayFault)
369 {
370   ULONG i;
371   CHAR ch;
372
373   if (NULL == Address && MayFault)
374     {
375       GspMemoryError = TRUE;
376       return Buffer;
377     }
378
379   for (i = 0; i < (ULONG) Count; i++)
380     {
381       if (MayFault)
382         GspAccessLocation = Address;
383       ch = *Address;
384       GspAccessLocation = NULL;
385       if (MayFault && GspMemoryError)
386         return (Buffer);
387       *Buffer++ = HexChars[(ch >> 4) & 0xf];
388       *Buffer++ = HexChars[ch & 0xf];
389       Address++;
390     }
391   *Buffer = 0;
392   return (Buffer);
393 }
394
395
396 /* Convert the hex array pointed to by Buffer into binary to be placed at Address */
397 /* Return a pointer to the character AFTER the last byte read from Buffer */
398 PCHAR
399 GspHex2Mem (PCHAR Buffer,
400   PCHAR Address,
401   ULONG Count,
402   BOOLEAN MayFault)
403 {
404   PCHAR current;
405   PCHAR page;
406   ULONG countinpage;
407   ULONG i;
408   CHAR ch;
409   ULONG oldprot = 0;
410
411   current = Address;
412   while ( current < Address + Count )
413     {
414       page = (PCHAR)PAGE_ROUND_DOWN (current);
415       if (Address + Count <= page + PAGE_SIZE)
416         {
417           /* Fits in this page */
418           countinpage = Count;
419         }
420       else
421         {
422           /* Flows into next page, handle only current page in this iteration */
423           countinpage = PAGE_SIZE - (Address - page);
424         }
425       if (MayFault)
426         {
427           oldprot = MmGetPageProtect (NULL, Address);
428           MmSetPageProtect (NULL, Address, PAGE_EXECUTE_READWRITE);
429         }
430
431       for (i = 0; i < countinpage && ! GspMemoryError; i++)
432         {
433           ch = HexValue (*Buffer++) << 4;
434           ch = ch + HexValue (*Buffer++);
435
436           GspAccessLocation = Address;
437           *current = ch;
438           GspAccessLocation = NULL;
439           current++;
440         }
441       if (MayFault)
442         {
443           MmSetPageProtect (NULL, page, oldprot);
444           if (GspMemoryError)
445             return (Buffer);
446         }
447     }
448
449   return (Buffer);
450 }
451
452
453 /* This function takes the 386 exception vector and attempts to
454    translate this number into a unix compatible signal value */
455 ULONG
456 GspComputeSignal (NTSTATUS ExceptionCode)
457 {
458   ULONG SigVal;
459
460   switch (ExceptionCode)
461     {
462     case STATUS_INTEGER_DIVIDE_BY_ZERO:
463       SigVal = 8;
464       break;                    /* divide by zero */
465     case STATUS_SINGLE_STEP:
466                                 /* debug exception */
467     case STATUS_BREAKPOINT:
468       SigVal = 5;
469       break;                    /* breakpoint */
470     case STATUS_INTEGER_OVERFLOW:
471                                 /* into instruction (overflow) */
472     case STATUS_ARRAY_BOUNDS_EXCEEDED:
473       SigVal = 16;
474       break;                    /* bound instruction */
475     case STATUS_ILLEGAL_INSTRUCTION:
476       SigVal = 4;
477       break;                    /* Invalid opcode */
478 #if 0
479     case STATUS_FLT_INVALID_OPERATION:
480       SigVal = 8;
481       break;                    /* coprocessor not available */
482 #endif
483     case STATUS_STACK_OVERFLOW:
484                                 /* stack exception */
485     case STATUS_DATATYPE_MISALIGNMENT:
486                                 /* page fault */
487     case STATUS_ACCESS_VIOLATION:
488       SigVal = 11;              /* access violation */
489       break;
490     default:
491       SigVal = 7;               /* "software generated" */
492     }
493   return (SigVal);
494 }
495
496
497 /**********************************************/
498 /* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
499 /* RETURN NUMBER OF CHARS PROCESSED           */
500 /**********************************************/
501 LONG
502 GspHex2Long (PCHAR *Address,
503   PLONG Value)
504 {
505   LONG NumChars = 0;
506   LONG Hex;
507
508   *Value = 0;
509
510   while (**Address)
511     {
512       Hex = HexValue (**Address);
513       if (Hex >= 0)
514                                 {
515                                   *Value = (*Value << 4) | Hex;
516                                   NumChars++;
517                                 }
518         else
519                 break;
520
521       (*Address)++;
522     }
523
524   return (NumChars);
525 }
526
527
528 VOID
529 GspLong2Hex (PCHAR *Address,
530   LONG Value)
531 {
532   LONG Save;
533
534   Save = (((Value >> 0) & 0xff) << 24) |
535          (((Value >> 8) & 0xff) << 16) |
536          (((Value >> 16) & 0xff) << 8) |
537          (((Value >> 24) & 0xff) << 0);
538   *Address = GspMem2Hex ((PCHAR) &Save, *Address, 4, FALSE);
539 }
540
541
542 /*
543  * Esp is not stored in the trap frame, although there is a member with it's name.
544  * Instead, it was pointing to the location of the TrapFrame Esp member when the
545  * exception occured.
546  */
547 static LONG
548 GspGetEspFromTrapFrame(PKTRAP_FRAME TrapFrame)
549 {
550   return (LONG) &TrapFrame->Esp;
551 }
552
553
554 VOID
555 GspGetRegistersFromTrapFrame(PCHAR Address,
556   PCONTEXT Context,
557   PKTRAP_FRAME TrapFrame)
558 {
559   ULONG Value;
560   PCHAR Buffer;
561   PULONG p;
562   DWORD i;
563
564   Buffer = Address;
565   for (i = 0; i < sizeof (GspRegisters) / sizeof (GspRegisters[0]); i++)
566   {
567     if (TrapFrame)
568     {
569       if (ESP == i)
570       {
571         Value = GspGetEspFromTrapFrame (TrapFrame);
572       }
573       else
574       {
575         p = (PULONG) ((ULONG_PTR) TrapFrame + GspRegisters[i].OffsetInTF);
576         Value = *p;
577       }
578     }
579     else if (i == EIP_REGNO)
580     {
581       /*
582        * This thread has not been sheduled yet so assume it
583        * is still in PsBeginThreadWithContextInternal().
584        */
585       Value = (ULONG) PsBeginThreadWithContextInternal;
586     }
587     else
588     {
589       Value = 0;
590     }
591     Buffer = GspMem2Hex ((PCHAR) &Value, Buffer, GspRegisters[i].Size, FALSE);
592   }
593 }
594
595
596 VOID
597 GspSetRegistersInTrapFrame(PCHAR Address,
598   PCONTEXT Context,
599   PKTRAP_FRAME TrapFrame)
600 {
601   ULONG Value;
602   PCHAR Buffer;
603   PULONG p;
604   DWORD i;
605
606   if (!TrapFrame)
607     return;
608
609   Buffer = Address;
610   for (i = 0; i < NUMREGS; i++)
611   {
612     if (GspRegisters[i].SetInContext)
613       p = (PULONG) ((ULONG_PTR) Context + GspRegisters[i].OffsetInContext);
614     else
615       p = (PULONG) ((ULONG_PTR) TrapFrame + GspRegisters[i].OffsetInTF);
616     Value = 0;
617     Buffer = GspHex2Mem (Buffer, (PCHAR) &Value, GspRegisters[i].Size, FALSE);
618     *p = Value;
619   }
620 }
621
622
623 VOID
624 GspSetSingleRegisterInTrapFrame(PCHAR Address,
625   LONG Number,
626   PCONTEXT Context,
627   PKTRAP_FRAME TrapFrame)
628 {
629   ULONG Value;
630   PULONG p;
631
632   if (!TrapFrame)
633     return;
634
635   if (GspRegisters[Number].SetInContext)
636     p = (PULONG) ((ULONG_PTR) Context + GspRegisters[Number].OffsetInContext);
637   else
638     p = (PULONG) ((ULONG_PTR) TrapFrame + GspRegisters[Number].OffsetInTF);
639   Value = 0;
640   GspHex2Mem (Address, (PCHAR) &Value, GspRegisters[Number].Size, FALSE);
641   *p = Value;
642 }
643
644
645 BOOLEAN
646 GspFindThread(PCHAR Data,
647   PETHREAD *Thread)
648 {
649   PETHREAD ThreadInfo = NULL;
650
651   if (strcmp (Data, "-1") == 0)
652     {
653       /* All threads */
654       ThreadInfo = NULL;
655     }
656     else if (strcmp (Data, "0") == 0)
657     {
658        /* Pick any thread, pick the first thread,
659         * which is what most people are interested in
660         */
661        ThreadInfo = CONTAINING_RECORD (PiThreadListHead.Flink,
662          ETHREAD, Tcb.ThreadListEntry);
663     }
664     else
665     {
666       ULONG ThreadId;
667       PCHAR ptr = &Data[0];
668
669       GspHex2Long (&ptr, (PLONG) &ThreadId);
670
671       if (!NT_SUCCESS (PsLookupThreadByThreadId ((PVOID) ThreadId, &ThreadInfo)))
672                           {
673           *Thread = NULL;
674           return FALSE;
675         }
676     }
677   *Thread = ThreadInfo;
678   return TRUE;
679 }
680
681
682 VOID
683 GspSetThread(PCHAR Request)
684 {
685   PETHREAD ThreadInfo;
686   PCHAR ptr = &Request[1];
687
688   switch (Request[0])
689   {
690     case 'c': /* Run thread */
691       if (GspFindThread (ptr, &ThreadInfo))
692                           {
693                             GspOutBuffer[0] = 'O';
694                             GspOutBuffer[1] = 'K';
695
696                             if(GspRunThread) ObDereferenceObject(GspRunThread);
697
698                             GspRunThread = ThreadInfo;
699                           }
700                           else
701                           {
702                             GspOutBuffer[0] = 'E';
703                           }
704       break;
705     case 'g': /* Debug thread */
706       if (GspFindThread (ptr, &ThreadInfo))
707                           {
708                             GspOutBuffer[0] = 'O';
709                             GspOutBuffer[1] = 'K';
710
711                             if(GspDbgThread) ObDereferenceObject(GspDbgThread);
712
713                             GspDbgThread = ThreadInfo;
714                           }
715                           else
716                           {
717                             GspOutBuffer[0] = 'E';
718                           }
719       break;
720     default:
721       break;
722   }
723 }
724
725
726 VOID
727 GspQuery(PCHAR Request)
728 {
729   PCHAR Command;
730   ULONG Value;
731
732         Command = strtok (Request, ",");
733         if (strncmp (Command, "C", 1) == 0)
734   {
735     PCHAR ptr = &GspOutBuffer[2];
736
737     /* Get current thread id */
738     GspOutBuffer[0] = 'Q';
739     GspOutBuffer[1] = 'C';
740     if (NULL != GspDbgThread)
741     {
742       Value = (ULONG) GspDbgThread->Cid.UniqueThread;
743     }
744     else
745     {
746       Value = (ULONG) PsGetCurrentThread()->Cid.UniqueThread;
747     }
748     GspLong2Hex (&ptr, Value);
749   }
750   else if (strncmp (Command, "fThreadInfo", 11) == 0)
751   {
752     PCHAR ptr = &GspOutBuffer[1];
753
754     /* Get first thread id */
755     GspOutBuffer[0] = 'm';
756     GspEnumThread = CONTAINING_RECORD (PiThreadListHead.Flink,
757       ETHREAD, Tcb.ThreadListEntry);
758     Value = (ULONG) GspEnumThread->Cid.UniqueThread;
759     GspLong2Hex (&ptr, Value);
760   }
761   else if (strncmp (Command, "sThreadInfo", 11) == 0)
762   {
763     PCHAR ptr = &GspOutBuffer[1];
764
765     /* Get next thread id */
766     if ((GspEnumThread) && (GspEnumThread->Tcb.ThreadListEntry.Flink != PiThreadListHead.Flink))
767     {
768       GspEnumThread = CONTAINING_RECORD (GspEnumThread->Tcb.ThreadListEntry.Flink,
769         ETHREAD, Tcb.ThreadListEntry);
770             GspOutBuffer[0] = 'm';
771             Value = (ULONG) GspEnumThread->Cid.UniqueThread;
772       GspLong2Hex (&ptr, Value);
773     }
774                 else
775                 {
776             GspOutBuffer[0] = 'l';
777                 }
778   }
779   else if (strncmp (Command, "ThreadExtraInfo", 15) == 0)
780   {
781     PETHREAD ThreadInfo;
782     PCHAR ptr = &Command[15];
783
784     /* Get thread information */
785     if (GspFindThread (ptr, &ThreadInfo))
786           {
787              PCHAR String = GspThreadStates[ThreadInfo->Tcb.State];
788            
789              ObDereferenceObject(ThreadInfo);
790            
791              GspMem2Hex (String, &GspOutBuffer[0], strlen (String), FALSE);
792           }
793   }
794 #if 0
795         else if (strncmp (Command, "L", 1) == 0)
796   {
797     PLIST_ENTRY CurrentEntry;
798     PETHREAD Current;
799     ULONG MaxThreads = 0;
800     ULONG ThreadId = 0;
801     ULONG ThreadCount = 0;
802
803     /* List threads */
804     GspHex2Mem (&Request[1], (PCHAR) &MaxThreads, 2, TRUE);
805     GspHex2Mem (&Request[3], (PCHAR) &Value, 4, TRUE);
806     GspHex2Mem (&Request[11], (PCHAR) &ThreadId, 4, TRUE);
807
808     GspOutBuffer[0] = 'q';
809     GspOutBuffer[1] = 'M';
810     Value = 0;
811     GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[5], 4, TRUE);
812     GspMem2Hex ((PCHAR) &ThreadId, &GspOutBuffer[13], 4, TRUE);
813
814     CurrentEntry = PiThreadListHead.Flink;
815     while ((CurrentEntry != &PiThreadListHead) && (ThreadCount < MaxThreads))
816       {
817         Current = CONTAINING_RECORD (CurrentEntry, ETHREAD, Tcb.ThreadListEntry);
818         Value = 0;
819         GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[21+ThreadCount*16], 4, TRUE);
820         Value = (ULONG) Current->Cid.UniqueThread;
821         GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[21+ThreadCount*16+8], 4, TRUE);
822         CurrentEntry = CurrentEntry->Flink;
823         ThreadCount++;
824       }
825
826     if (CurrentEntry != &PiThreadListHead)
827                 {
828       GspOutBuffer[4] = '0';
829                 }
830     else
831     {
832       GspOutBuffer[4] = '1';
833     }
834
835     GspMem2Hex ((PCHAR) &ThreadCount, &GspOutBuffer[2], 1, TRUE);
836   }
837 #endif
838   else if (strncmp (Command, "Offsets", 7) == 0)
839   {
840     strcpy (GspOutBuffer, "Text=0;Data=0;Bss=0");
841   }
842 }
843
844 VOID
845 GspQueryThreadStatus(PCHAR Request)
846 {
847   PETHREAD ThreadInfo;
848   PCHAR ptr = &Request[0];
849
850   if (GspFindThread (ptr, &ThreadInfo))
851   {
852     ObDereferenceObject(ThreadInfo);
853
854     GspOutBuffer[0] = 'O';
855     GspOutBuffer[1] = 'K';
856     GspOutBuffer[2] = '\0';
857   }
858   else
859   {
860     GspOutBuffer[0] = 'E';
861     GspOutBuffer[1] = '\0';
862   }
863 }
864
865
866 typedef struct _GsHwBreakPoint
867 {
868   BOOLEAN Enabled;
869   ULONG Type;
870   ULONG Length;
871   ULONG Address;
872 } GsHwBreakPoint;
873
874 GsHwBreakPoint GspBreakpoints[4] =
875 {
876   { Enabled : FALSE },
877   { Enabled : FALSE },
878   { Enabled : FALSE },
879   { Enabled : FALSE }
880 };
881
882 VOID
883 GspCorrectHwBreakpoint()
884 {
885   ULONG BreakpointNumber;
886   BOOLEAN CorrectIt;
887   BOOLEAN Bit;
888   ULONG dr7;
889
890   asm volatile (
891     "movl %%db7, %0\n" : "=r" (dr7) : );
892   do
893     {
894       ULONG addr0, addr1, addr2, addr3;
895
896       asm volatile (
897         "movl %%db0, %0\n"
898         "movl %%db1, %1\n"
899         "movl %%db2, %2\n"
900         "movl %%db3, %3\n"
901           : "=r" (addr0), "=r" (addr1),
902             "=r" (addr2), "=r" (addr3) : );
903     } while (FALSE);
904     CorrectIt = FALSE;
905     for (BreakpointNumber = 0; BreakpointNumber < 3; BreakpointNumber++)
906     {
907                         Bit = 2 << (BreakpointNumber << 1);
908                         if (!(dr7 & Bit) && GspBreakpoints[BreakpointNumber].Enabled) {
909                   CorrectIt = TRUE;
910                         dr7 |= Bit;
911                         dr7 &= ~(0xf0000 << (BreakpointNumber << 2));
912                         dr7 |= (((GspBreakpoints[BreakpointNumber].Length << 2) |
913         GspBreakpoints[BreakpointNumber].Type) << 16) << (BreakpointNumber << 2);
914     switch (BreakpointNumber) {
915                         case 0:
916                           asm volatile ("movl %0, %%dr0\n"
917                             : : "r" (GspBreakpoints[BreakpointNumber].Address) );
918                           break;
919
920                         case 1:
921                                 asm volatile ("movl %0, %%dr1\n"
922                                   : : "r" (GspBreakpoints[BreakpointNumber].Address) );
923                                 break;
924
925                         case 2:
926                                 asm volatile ("movl %0, %%dr2\n"
927                                 : : "r" (GspBreakpoints[BreakpointNumber].Address) );
928                         break;
929
930       case 3:
931         asm volatile ("movl %0, %%dr3\n"
932           : : "r" (GspBreakpoints[BreakpointNumber].Address) );
933         break;
934       }
935     }
936     else if ((dr7 & Bit) && !GspBreakpoints[BreakpointNumber].Enabled)
937       {
938         CorrectIt = TRUE;
939         dr7 &= ~Bit;
940         dr7 &= ~(0xf0000 << (BreakpointNumber << 2));
941       }
942   }
943   if (CorrectIt)
944     {
945             asm volatile ( "movl %0, %%db7\n" : : "r" (dr7));
946     }
947 }
948
949 ULONG
950 GspRemoveHwBreakpoint(ULONG BreakpointNumber)
951 {
952         if (!GspBreakpoints[BreakpointNumber].Enabled)
953     {
954             return -1;
955         }
956         GspBreakpoints[BreakpointNumber].Enabled = 0;
957         return 0;
958 }
959
960
961 ULONG
962 GspSetHwBreakpoint(ULONG BreakpointNumber,
963   ULONG Type,
964   ULONG Length,
965   ULONG Address)
966 {
967         if (GspBreakpoints[BreakpointNumber].Enabled)
968           {
969                 return -1;
970                 }
971         GspBreakpoints[BreakpointNumber].Enabled = TRUE;
972         GspBreakpoints[BreakpointNumber].Type = Type;
973         GspBreakpoints[BreakpointNumber].Length = Length;
974         GspBreakpoints[BreakpointNumber].Address = Address;
975         return 0;
976 }
977
978
979 /*
980  * This function does all command procesing for interfacing to gdb.
981  */
982 KD_CONTINUE_TYPE
983 KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
984                          PCONTEXT Context,
985                          PKTRAP_FRAME TrapFrame)
986 {
987   BOOLEAN Stepping;
988   LONG Address;
989   LONG Length;
990   LONG SigVal;
991   LONG NewPC;
992   PCHAR ptr;
993   LONG Esp;
994
995   /* FIXME: Stop on other CPUs too */
996   /* Disable hardware debugging while we are inside the stub */
997   __asm__("movl %0,%%db7" : /* no output */ : "r" (0));
998
999   if (STATUS_ACCESS_VIOLATION == (NTSTATUS) ExceptionRecord->ExceptionCode &&
1000       NULL != GspAccessLocation &&
1001       (ULONG_PTR) GspAccessLocation ==
1002       (ULONG_PTR) ExceptionRecord->ExceptionInformation[1])
1003     {
1004       GspAccessLocation = NULL;
1005       GspMemoryError = TRUE;
1006       Context->Eip += 2;
1007     }
1008   else
1009     {
1010       /* reply to host that an exception has occurred */
1011       SigVal = GspComputeSignal (ExceptionRecord->ExceptionCode);
1012
1013       ptr = &GspOutBuffer[0];
1014
1015       *ptr++ = 'T';                     /* notify gdb with signo, PC, FP and SP */
1016       *ptr++ = HexChars[(SigVal >> 4) & 0xf];
1017       *ptr++ = HexChars[SigVal & 0xf];
1018
1019       *ptr++ = HexChars[ESP];
1020       *ptr++ = ':';
1021
1022       Esp = GspGetEspFromTrapFrame (TrapFrame);                 /* SP */
1023       ptr = GspMem2Hex ((PCHAR) &Esp, ptr, 4, 0);
1024       *ptr++ = ';';
1025
1026       *ptr++ = HexChars[EBP];
1027       *ptr++ = ':';
1028       ptr = GspMem2Hex ((PCHAR) &TrapFrame->Ebp, ptr, 4, 0);    /* FP */
1029       *ptr++ = ';';
1030
1031       *ptr++ = HexChars[PC];
1032       *ptr++ = ':';
1033       ptr = GspMem2Hex((PCHAR) &TrapFrame->Eip, ptr, 4, 0);     /* PC */
1034       *ptr++ = ';';
1035
1036       *ptr = '\0';
1037
1038       GspPutPacket (&GspOutBuffer[0]);
1039
1040       Stepping = FALSE;
1041
1042       while (TRUE)
1043         {
1044           /* Zero the buffer now so we don't have to worry about the terminating zero character */
1045           memset (GspOutBuffer, 0, sizeof (GspInBuffer));
1046           ptr = GspGetPacket ();
1047
1048           switch (*ptr++)
1049             {
1050             case '?':
1051               GspOutBuffer[0] = 'S';
1052               GspOutBuffer[1] = HexChars[SigVal >> 4];
1053               GspOutBuffer[2] = HexChars[SigVal % 16];
1054               GspOutBuffer[3] = 0;
1055               break;
1056             case 'd':
1057               GspRemoteDebug = !GspRemoteDebug; /* toggle debug flag */
1058               break;
1059             case 'g':           /* return the value of the CPU Registers */
1060               if (GspDbgThread)
1061                 GspGetRegistersFromTrapFrame (&GspOutBuffer[0], Context, GspDbgThread->Tcb.TrapFrame);
1062               else
1063                 GspGetRegistersFromTrapFrame (&GspOutBuffer[0], Context, TrapFrame);
1064               break;
1065             case 'G':           /* set the value of the CPU Registers - return OK */
1066               if (GspDbgThread)
1067 /*                GspSetRegistersInTrapFrame (ptr, Context, GspDbgThread->Tcb.TrapFrame);*/
1068 GspSetRegistersInTrapFrame (ptr, Context, TrapFrame);
1069               else
1070                 GspSetRegistersInTrapFrame (ptr, Context, TrapFrame);
1071               strcpy (GspOutBuffer, "OK");
1072               break;
1073             case 'P':           /* set the value of a single CPU register - return OK */
1074               {
1075                 LONG Register;
1076
1077                 if ((GspHex2Long (&ptr, &Register)) && (*ptr++ == '='))
1078                   if ((Register >= 0) && (Register < NUMREGS))
1079                     {
1080                       if (GspDbgThread)
1081 /*                        GspSetSingleRegisterInTrapFrame (ptr, Register,
1082                                                          Context, GspDbgThread->Tcb.TrapFrame);*/
1083 GspSetSingleRegisterInTrapFrame (ptr, Register, Context, TrapFrame);
1084                       else
1085                         GspSetSingleRegisterInTrapFrame (ptr, Register, Context, TrapFrame);
1086                       strcpy (GspOutBuffer, "OK");
1087                       break;
1088                     }
1089
1090                 strcpy (GspOutBuffer, "E01");
1091                 break;
1092               }
1093
1094             /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
1095             case 'm':
1096               /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
1097               if (GspHex2Long (&ptr, &Address))
1098                 if (*(ptr++) == ',')
1099                   if (GspHex2Long (&ptr, &Length))
1100                     {
1101                       ptr = 0;
1102                       GspMemoryError = FALSE;
1103                       GspMem2Hex ((PCHAR) Address, GspOutBuffer, Length, 1);
1104                       if (GspMemoryError)
1105                         {
1106                           strcpy (GspOutBuffer, "E03");
1107                           DPRINT ("Fault during memory read\n");
1108                         }
1109                     }
1110
1111               if (ptr)
1112                 strcpy (GspOutBuffer, "E01");
1113               break;
1114
1115             /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1116             case 'M':
1117               /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
1118               if (GspHex2Long (&ptr, &Address))
1119                 if (*(ptr++) == ',')
1120                   if (GspHex2Long (&ptr, &Length))
1121                     if (*(ptr++) == ':')
1122                       {
1123                         GspMemoryError = FALSE;
1124                         GspHex2Mem (ptr, (PCHAR) Address, Length, TRUE);
1125
1126                         if (GspMemoryError)
1127                           {
1128                             strcpy (GspOutBuffer, "E03");
1129                             DPRINT ("Fault during memory write\n");
1130                           }
1131                         else
1132                           {
1133                             strcpy (GspOutBuffer, "OK");
1134                           }
1135
1136                         ptr = NULL;
1137                       }
1138               if (ptr)
1139                 strcpy (GspOutBuffer, "E02");
1140               break;
1141
1142             /* cAA..AA   Continue at address AA..AA(optional) */
1143             /* sAA..AA   Step one instruction from AA..AA(optional) */
1144             case 's':
1145               Stepping = TRUE;
1146             case 'c':
1147               {
1148                 ULONG BreakpointNumber;
1149                 ULONG dr6;
1150
1151                 /* try to read optional parameter, pc unchanged if no parm */
1152                 if (GspHex2Long (&ptr, &Address))
1153                 Context->Eip = Address;
1154
1155                 NewPC = Context->Eip;
1156
1157                 /* clear the trace bit */
1158                 Context->EFlags &= 0xfffffeff;
1159
1160                 /* set the trace bit if we're Stepping */
1161                 if (Stepping)
1162                   Context->EFlags |= 0x100;
1163
1164                 asm volatile ("movl %%db6, %0\n" : "=r" (dr6) : );
1165                 if (!(dr6 & 0x4000))
1166                   {
1167                     for (BreakpointNumber = 0; BreakpointNumber < 4; ++BreakpointNumber)
1168                       {
1169                         if (dr6 & (1 << BreakpointNumber))
1170                           {
1171                             if (GspBreakpoints[BreakpointNumber].Type == 0)
1172                               {
1173                                 /* Set restore flag */
1174                                 Context->EFlags |= 0x10000;
1175                                 break;
1176                               }
1177                           }
1178                       }
1179                   }
1180                 GspCorrectHwBreakpoint();
1181                 asm volatile ("movl %0, %%db6\n" : : "r" (0));
1182
1183                 return kdHandleException;
1184                 break;
1185               }
1186
1187             case 'k':   /* kill the program */
1188               strcpy (GspOutBuffer, "OK");
1189               break;
1190               /* kill the program */
1191
1192             case 'H':           /* Set thread */
1193               GspSetThread (ptr);
1194               break;
1195
1196             case 'q': /* Query */
1197               GspQuery (ptr);
1198               break;
1199
1200             case 'T': /* Query thread status */
1201               GspQueryThreadStatus (ptr);
1202               break;
1203
1204             case 'Y':
1205               {
1206                 LONG Number;
1207                 LONG Length;
1208                 LONG Type;
1209                 LONG Address;
1210
1211                 ptr = &GspOutBuffer[1];
1212                 GspHex2Long (&ptr, &Number);
1213                 ptr++;
1214                 GspHex2Long (&ptr, &Type);
1215                 ptr++;
1216                 GspHex2Long (&ptr, &Length);
1217                 ptr++;
1218                 GspHex2Long (&ptr, &Address);
1219                 if (GspSetHwBreakpoint (Number & 0x3, Type & 0x3 , Length & 0x3, Address) == 0)
1220                   {
1221                     strcpy (GspOutBuffer, "OK");
1222                   }
1223                 else
1224                   {
1225                     strcpy (GspOutBuffer, "E");
1226                   }
1227                 break;
1228               }
1229
1230             /* Remove hardware breakpoint */
1231             case 'y':
1232               {
1233                 LONG Number;
1234
1235                 ptr = &GspOutBuffer[1];
1236                 GspHex2Long(&ptr, &Number);
1237                 if (GspRemoveHwBreakpoint (Number & 0x3) == 0)
1238                   {
1239                     strcpy (GspOutBuffer, "OK");
1240                   }
1241                 else
1242                   {
1243                     strcpy (GspOutBuffer, "E");
1244                   }
1245                 break;
1246               }
1247
1248             default:
1249               break;
1250             }                   /* switch */
1251
1252           /* reply to the request */
1253           GspPutPacket (&GspOutBuffer[0]);
1254         }
1255     }
1256
1257   return kdHandleException;
1258 }
1259
1260
1261 BOOLEAN
1262 STDCALL
1263 GspBreakIn(PKINTERRUPT Interrupt,
1264   PVOID ServiceContext)
1265 {
1266   PKTRAP_FRAME TrapFrame;
1267   BOOLEAN DoBreakIn;
1268   CONTEXT Context;
1269   KIRQL OldIrql;
1270   CHAR Value;
1271
1272   DPRINT ("Break In\n");
1273
1274   DoBreakIn = FALSE;
1275   while (KdPortGetByteEx (&GdbPortInfo, &Value))
1276     {
1277       if (Value == 0x03)
1278         DoBreakIn = TRUE;
1279     }
1280
1281   if (!DoBreakIn)
1282     return TRUE;
1283
1284   KeRaiseIrql (HIGH_LEVEL, &OldIrql);
1285
1286   TrapFrame = PsGetCurrentThread()->Tcb.TrapFrame;
1287
1288   KeTrapFrameToContext (TrapFrame, &Context);
1289
1290   KdEnterDebuggerException (NULL, &Context, TrapFrame);
1291
1292   KeContextToTrapFrame (&Context, TrapFrame);
1293
1294   KeLowerIrql (OldIrql);
1295
1296   return TRUE;
1297 }
1298
1299
1300 extern ULONG KdpPortIrq;
1301
1302 /* Initialize the GDB stub */
1303 VOID
1304 KdGdbStubInit(ULONG Phase)
1305 {
1306 #if 0
1307   KAFFINITY Affinity;
1308   NTSTATUS Status;
1309   ULONG MappedIrq;
1310   KIRQL Dirql;
1311 #endif
1312
1313   if (Phase == 0)
1314     {
1315                   DbgPrint("Module 'hal.dll' loaded at 0x%.08x.\n", LdrHalBase);
1316
1317                   GspInitialized = TRUE;
1318                   GspRunThread = PsGetCurrentThread();
1319      
1320                   ObReferenceObject(GspRunThread);
1321
1322 /*                GspDbgThread = PsGetCurrentThread(); */
1323                   GspDbgThread = NULL;
1324                   GspEnumThread = NULL;
1325
1326       DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1327     }
1328   else if (Phase == 1)
1329     {
1330 #if 0
1331                   /* Hook an interrupt handler to allow the debugger to break into
1332                      the system */
1333                   MappedIrq = HalGetInterruptVector (Internal,
1334                     0,
1335                     0,
1336                     KdpPortIrq,
1337                     &Dirql,
1338                     &Affinity);
1339
1340                   Status = IoConnectInterrupt(&GspInterrupt,
1341                     GspBreakIn,
1342                     NULL,
1343                     NULL,
1344                     MappedIrq,
1345                     Dirql,
1346                     Dirql,
1347                     0,
1348                     FALSE,
1349                     Affinity,
1350                     FALSE);
1351       if (!NT_SUCCESS (Status))
1352       {
1353         DPRINT1("Could not connect to IRQ line %d (0x%x)\n",
1354           KdpPortIrq, Status);
1355         return;
1356       }
1357
1358        KdPortEnableInterrupts();
1359
1360        DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1361 #endif
1362   }
1363 }
1364
1365
1366 VOID
1367 KdGdbDebugPrint(LPSTR Message)
1368 {
1369 #if 0
1370   /* This can be quite annoying! */
1371   if (GspInitialized)
1372           {
1373             ULONG Length;
1374         
1375             GspOutBuffer[0] = 'O';
1376             GspOutBuffer[1] = '\0';
1377             strcat (&GspOutBuffer[0], Message);
1378             Length = strlen (Message);
1379             GspOutBuffer[2 + Length] = '\n';
1380             GspOutBuffer[3 + Length] = '\0';
1381             GspPutPacketNoWait (&GspOutBuffer[0]);
1382           }
1383 #else
1384   HalDisplayString(Message);
1385 #endif
1386 }
1387
1388
1389 extern LIST_ENTRY ModuleListHead;
1390
1391 VOID
1392 KdGdbListModules()
1393 {
1394   PLIST_ENTRY CurrentEntry;
1395   PMODULE_OBJECT Current;
1396   ULONG ModuleCount;
1397
1398   DPRINT1("\n");
1399
1400   ModuleCount = 0;
1401
1402   CurrentEntry = ModuleListHead.Flink;
1403   while (CurrentEntry != (&ModuleListHead))
1404     {
1405             Current = CONTAINING_RECORD (CurrentEntry, MODULE_OBJECT, ListEntry);
1406
1407       DbgPrint ("Module %S  Base 0x%.08x  Length 0x%.08x\n",
1408         Current->BaseName.Buffer, Current->Base, Current->Length);
1409
1410       ModuleCount++;
1411       CurrentEntry = CurrentEntry->Flink;
1412     }
1413
1414   DbgPrint ("%d modules listed\n", ModuleCount);
1415 }