update for HEAD-2003050101
[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       TrapFrame->Eip += 2;
1007       ExceptionRecord->ExceptionFlags &= ~EXCEPTION_NONCONTINUABLE;
1008     }
1009   else
1010     {
1011       /* reply to host that an exception has occurred */
1012       SigVal = GspComputeSignal (ExceptionRecord->ExceptionCode);
1013
1014       ptr = &GspOutBuffer[0];
1015
1016       *ptr++ = 'T';                     /* notify gdb with signo, PC, FP and SP */
1017       *ptr++ = HexChars[(SigVal >> 4) & 0xf];
1018       *ptr++ = HexChars[SigVal & 0xf];
1019
1020       *ptr++ = HexChars[ESP];
1021       *ptr++ = ':';
1022
1023       Esp = GspGetEspFromTrapFrame (TrapFrame);                 /* SP */
1024       ptr = GspMem2Hex ((PCHAR) &Esp, ptr, 4, 0);
1025       *ptr++ = ';';
1026
1027       *ptr++ = HexChars[EBP];
1028       *ptr++ = ':';
1029       ptr = GspMem2Hex ((PCHAR) &TrapFrame->Ebp, ptr, 4, 0);    /* FP */
1030       *ptr++ = ';';
1031
1032       *ptr++ = HexChars[PC];
1033       *ptr++ = ':';
1034       ptr = GspMem2Hex((PCHAR) &TrapFrame->Eip, ptr, 4, 0);     /* PC */
1035       *ptr++ = ';';
1036
1037       *ptr = '\0';
1038
1039       GspPutPacket (&GspOutBuffer[0]);
1040
1041       Stepping = FALSE;
1042
1043       while (TRUE)
1044         {
1045           /* Zero the buffer now so we don't have to worry about the terminating zero character */
1046           memset (GspOutBuffer, 0, sizeof (GspInBuffer));
1047           ptr = GspGetPacket ();
1048
1049           switch (*ptr++)
1050             {
1051             case '?':
1052               GspOutBuffer[0] = 'S';
1053               GspOutBuffer[1] = HexChars[SigVal >> 4];
1054               GspOutBuffer[2] = HexChars[SigVal % 16];
1055               GspOutBuffer[3] = 0;
1056               break;
1057             case 'd':
1058               GspRemoteDebug = !GspRemoteDebug; /* toggle debug flag */
1059               break;
1060             case 'g':           /* return the value of the CPU Registers */
1061               if (GspDbgThread)
1062                 GspGetRegistersFromTrapFrame (&GspOutBuffer[0], Context, GspDbgThread->Tcb.TrapFrame);
1063               else
1064                 GspGetRegistersFromTrapFrame (&GspOutBuffer[0], Context, TrapFrame);
1065               break;
1066             case 'G':           /* set the value of the CPU Registers - return OK */
1067               if (GspDbgThread)
1068 /*                GspSetRegistersInTrapFrame (ptr, Context, GspDbgThread->Tcb.TrapFrame);*/
1069 GspSetRegistersInTrapFrame (ptr, Context, TrapFrame);
1070               else
1071                 GspSetRegistersInTrapFrame (ptr, Context, TrapFrame);
1072               strcpy (GspOutBuffer, "OK");
1073               break;
1074             case 'P':           /* set the value of a single CPU register - return OK */
1075               {
1076                 LONG Register;
1077
1078                 if ((GspHex2Long (&ptr, &Register)) && (*ptr++ == '='))
1079                   if ((Register >= 0) && (Register < NUMREGS))
1080                     {
1081                       if (GspDbgThread)
1082 /*                        GspSetSingleRegisterInTrapFrame (ptr, Register,
1083                                                          Context, GspDbgThread->Tcb.TrapFrame);*/
1084 GspSetSingleRegisterInTrapFrame (ptr, Register, Context, TrapFrame);
1085                       else
1086                         GspSetSingleRegisterInTrapFrame (ptr, Register, Context, TrapFrame);
1087                       strcpy (GspOutBuffer, "OK");
1088                       break;
1089                     }
1090
1091                 strcpy (GspOutBuffer, "E01");
1092                 break;
1093               }
1094
1095             /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
1096             case 'm':
1097               /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
1098               if (GspHex2Long (&ptr, &Address))
1099                 if (*(ptr++) == ',')
1100                   if (GspHex2Long (&ptr, &Length))
1101                     {
1102                       ptr = 0;
1103                       GspMemoryError = FALSE;
1104                       GspMem2Hex ((PCHAR) Address, GspOutBuffer, Length, 1);
1105                       if (GspMemoryError)
1106                         {
1107                           strcpy (GspOutBuffer, "E03");
1108                           DPRINT ("Fault during memory read\n");
1109                         }
1110                     }
1111
1112               if (ptr)
1113                 strcpy (GspOutBuffer, "E01");
1114               break;
1115
1116             /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1117             case 'M':
1118               /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
1119               if (GspHex2Long (&ptr, &Address))
1120                 if (*(ptr++) == ',')
1121                   if (GspHex2Long (&ptr, &Length))
1122                     if (*(ptr++) == ':')
1123                       {
1124                         GspMemoryError = FALSE;
1125                         GspHex2Mem (ptr, (PCHAR) Address, Length, TRUE);
1126
1127                         if (GspMemoryError)
1128                           {
1129                             strcpy (GspOutBuffer, "E03");
1130                             DPRINT ("Fault during memory write\n");
1131                           }
1132                         else
1133                           {
1134                             strcpy (GspOutBuffer, "OK");
1135                           }
1136
1137                         ptr = NULL;
1138                       }
1139               if (ptr)
1140                 strcpy (GspOutBuffer, "E02");
1141               break;
1142
1143             /* cAA..AA   Continue at address AA..AA(optional) */
1144             /* sAA..AA   Step one instruction from AA..AA(optional) */
1145             case 's':
1146               Stepping = TRUE;
1147             case 'c':
1148               {
1149                 ULONG BreakpointNumber;
1150                 ULONG dr6;
1151
1152                 /* try to read optional parameter, pc unchanged if no parm */
1153                 if (GspHex2Long (&ptr, &Address))
1154                 Context->Eip = Address;
1155
1156                 NewPC = Context->Eip;
1157
1158                 /* clear the trace bit */
1159                 Context->EFlags &= 0xfffffeff;
1160
1161                 /* set the trace bit if we're Stepping */
1162                 if (Stepping)
1163                   Context->EFlags |= 0x100;
1164
1165                 asm volatile ("movl %%db6, %0\n" : "=r" (dr6) : );
1166                 if (!(dr6 & 0x4000))
1167                   {
1168                     for (BreakpointNumber = 0; BreakpointNumber < 4; ++BreakpointNumber)
1169                       {
1170                         if (dr6 & (1 << BreakpointNumber))
1171                           {
1172                             if (GspBreakpoints[BreakpointNumber].Type == 0)
1173                               {
1174                                 /* Set restore flag */
1175                                 Context->EFlags |= 0x10000;
1176                                 break;
1177                               }
1178                           }
1179                       }
1180                   }
1181                 GspCorrectHwBreakpoint();
1182                 asm volatile ("movl %0, %%db6\n" : : "r" (0));
1183
1184                 return kdHandleException;
1185                 break;
1186               }
1187
1188             case 'k':   /* kill the program */
1189               strcpy (GspOutBuffer, "OK");
1190               break;
1191               /* kill the program */
1192
1193             case 'H':           /* Set thread */
1194               GspSetThread (ptr);
1195               break;
1196
1197             case 'q': /* Query */
1198               GspQuery (ptr);
1199               break;
1200
1201             case 'T': /* Query thread status */
1202               GspQueryThreadStatus (ptr);
1203               break;
1204
1205             case 'Y':
1206               {
1207                 LONG Number;
1208                 LONG Length;
1209                 LONG Type;
1210                 LONG Address;
1211
1212                 ptr = &GspOutBuffer[1];
1213                 GspHex2Long (&ptr, &Number);
1214                 ptr++;
1215                 GspHex2Long (&ptr, &Type);
1216                 ptr++;
1217                 GspHex2Long (&ptr, &Length);
1218                 ptr++;
1219                 GspHex2Long (&ptr, &Address);
1220                 if (GspSetHwBreakpoint (Number & 0x3, Type & 0x3 , Length & 0x3, Address) == 0)
1221                   {
1222                     strcpy (GspOutBuffer, "OK");
1223                   }
1224                 else
1225                   {
1226                     strcpy (GspOutBuffer, "E");
1227                   }
1228                 break;
1229               }
1230
1231             /* Remove hardware breakpoint */
1232             case 'y':
1233               {
1234                 LONG Number;
1235
1236                 ptr = &GspOutBuffer[1];
1237                 GspHex2Long(&ptr, &Number);
1238                 if (GspRemoveHwBreakpoint (Number & 0x3) == 0)
1239                   {
1240                     strcpy (GspOutBuffer, "OK");
1241                   }
1242                 else
1243                   {
1244                     strcpy (GspOutBuffer, "E");
1245                   }
1246                 break;
1247               }
1248
1249             default:
1250               break;
1251             }                   /* switch */
1252
1253           /* reply to the request */
1254           GspPutPacket (&GspOutBuffer[0]);
1255         }
1256     }
1257
1258   return kdDoNotHandleException;
1259 }
1260
1261
1262 BOOLEAN
1263 STDCALL
1264 GspBreakIn(PKINTERRUPT Interrupt,
1265   PVOID ServiceContext)
1266 {
1267   PKTRAP_FRAME TrapFrame;
1268   BOOLEAN DoBreakIn;
1269   CONTEXT Context;
1270   KIRQL OldIrql;
1271   CHAR Value;
1272
1273   DPRINT ("Break In\n");
1274
1275   DoBreakIn = FALSE;
1276   while (KdPortGetByteEx (&GdbPortInfo, &Value))
1277     {
1278       if (Value == 0x03)
1279         DoBreakIn = TRUE;
1280     }
1281
1282   if (!DoBreakIn)
1283     return TRUE;
1284
1285   KeRaiseIrql (HIGH_LEVEL, &OldIrql);
1286
1287   TrapFrame = PsGetCurrentThread()->Tcb.TrapFrame;
1288
1289   KeTrapFrameToContext (TrapFrame, &Context);
1290
1291   KdEnterDebuggerException (NULL, &Context, TrapFrame);
1292
1293   KeContextToTrapFrame (&Context, TrapFrame);
1294
1295   KeLowerIrql (OldIrql);
1296
1297   return TRUE;
1298 }
1299
1300
1301 extern ULONG KdpPortIrq;
1302
1303 /* Initialize the GDB stub */
1304 VOID
1305 KdGdbStubInit(ULONG Phase)
1306 {
1307 #if 0
1308   KAFFINITY Affinity;
1309   NTSTATUS Status;
1310   ULONG MappedIrq;
1311   KIRQL Dirql;
1312 #endif
1313
1314   if (Phase == 0)
1315     {
1316                   DbgPrint("Module 'hal.dll' loaded at 0x%.08x.\n", LdrHalBase);
1317
1318                   GspInitialized = TRUE;
1319                   GspRunThread = PsGetCurrentThread();
1320      
1321                   ObReferenceObject(GspRunThread);
1322
1323 /*                GspDbgThread = PsGetCurrentThread(); */
1324                   GspDbgThread = NULL;
1325                   GspEnumThread = NULL;
1326
1327       DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1328     }
1329   else if (Phase == 1)
1330     {
1331 #if 0
1332                   /* Hook an interrupt handler to allow the debugger to break into
1333                      the system */
1334                   MappedIrq = HalGetInterruptVector (Internal,
1335                     0,
1336                     0,
1337                     KdpPortIrq,
1338                     &Dirql,
1339                     &Affinity);
1340
1341                   Status = IoConnectInterrupt(&GspInterrupt,
1342                     GspBreakIn,
1343                     NULL,
1344                     NULL,
1345                     MappedIrq,
1346                     Dirql,
1347                     Dirql,
1348                     0,
1349                     FALSE,
1350                     Affinity,
1351                     FALSE);
1352       if (!NT_SUCCESS (Status))
1353       {
1354         DPRINT1("Could not connect to IRQ line %d (0x%x)\n",
1355           KdpPortIrq, Status);
1356         return;
1357       }
1358
1359        KdPortEnableInterrupts();
1360
1361        DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1362 #endif
1363   }
1364 }
1365
1366
1367 VOID
1368 KdGdbDebugPrint(LPSTR Message)
1369 {
1370 #if 0
1371   /* This can be quite annoying! */
1372   if (GspInitialized)
1373           {
1374             ULONG Length;
1375         
1376             GspOutBuffer[0] = 'O';
1377             GspOutBuffer[1] = '\0';
1378             strcat (&GspOutBuffer[0], Message);
1379             Length = strlen (Message);
1380             GspOutBuffer[2 + Length] = '\n';
1381             GspOutBuffer[3 + Length] = '\0';
1382             GspPutPacketNoWait (&GspOutBuffer[0]);
1383           }
1384 #else
1385   HalDisplayString(Message);
1386 #endif
1387 }
1388
1389
1390 extern LIST_ENTRY ModuleListHead;
1391
1392 VOID
1393 KdGdbListModules()
1394 {
1395   PLIST_ENTRY CurrentEntry;
1396   PMODULE_OBJECT Current;
1397   ULONG ModuleCount;
1398
1399   DPRINT1("\n");
1400
1401   ModuleCount = 0;
1402
1403   CurrentEntry = ModuleListHead.Flink;
1404   while (CurrentEntry != (&ModuleListHead))
1405     {
1406             Current = CONTAINING_RECORD (CurrentEntry, MODULE_OBJECT, ListEntry);
1407
1408       DbgPrint ("Module %S  Base 0x%.08x  Length 0x%.08x\n",
1409         Current->BaseName.Buffer, Current->Base, Current->Length);
1410
1411       ModuleCount++;
1412       CurrentEntry = CurrentEntry->Flink;
1413     }
1414
1415   DbgPrint ("%d modules listed\n", ModuleCount);
1416 }