update for HEAD-2003021201
[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 } CPU_REGISTER, *PCPU_REGISTER;
133
134 #define KTRAP_FRAME_X86 KTRAP_FRAME
135
136 #define EIP_REGNO 8
137
138 static CPU_REGISTER GspRegisters[NUMREGS] =
139 {
140   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eax) },
141   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ecx) },
142   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Edx) },
143   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ebx) },
144   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Esp) },
145   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ebp) },
146   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Esi) },
147   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Edi) },
148   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eip) },
149   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Eflags) },
150   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Cs) },
151   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ss) },
152   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Ds) },
153   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Es) },
154   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Fs) },
155   { 4, FIELD_OFFSET (KTRAP_FRAME_X86, Gs) }
156 };
157
158 static PCHAR GspThreadStates[THREAD_STATE_MAX] =
159 {
160   "Initialized",  /* THREAD_STATE_INITIALIZED */
161   "Ready",        /* THREAD_STATE_READY */
162   "Running",      /* THREAD_STATE_RUNNING */
163   "Suspended",    /* THREAD_STATE_SUSPENDED */
164   "Frozen",       /* THREAD_STATE_FROZEN */
165   "Terminated 1", /* THREAD_STATE_TERMINATED_1 */
166   "Terminated 2", /* THREAD_STATE_TERMINATED_2 */
167   "Blocked"       /* THREAD_STATE_BLOCKED */
168 };
169
170 char *
171 strtok(char *s, const char *delim)
172 {
173   const char *spanp;
174   int c, sc;
175   char *tok;
176   static char *last;
177
178
179   if (s == NULL && (s = last) == NULL)
180     return (NULL);
181
182   /*
183    * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
184    */
185  cont:
186   c = *s++;
187   for (spanp = delim; (sc = *spanp++) != 0;) {
188     if (c == sc)
189       goto cont;
190   }
191
192   if (c == 0) {                 /* no non-delimiter characters */
193     last = NULL;
194     return (NULL);
195   }
196   tok = s - 1;
197
198   /*
199    * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
200    * Note that delim must have one NUL; we stop if we see that, too.
201    */
202   for (;;) {
203     c = *s++;
204     spanp = delim;
205     do {
206       if ((sc = *spanp++) == c) {
207         if (c == 0)
208           s = NULL;
209         else
210           s[-1] = 0;
211         last = s;
212         return (tok);
213       }
214     } while (sc != 0);
215   }
216   /* NOTREACHED */
217 }
218
219
220 LONG
221 HexValue (CHAR ch)
222 {
223   if ((ch >= '0') && (ch <= '9')) return (ch - '0');
224   if ((ch >= 'a') && (ch <= 'f')) return (ch - 'a' + 10);
225   if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
226   return (-1);
227 }
228
229 static CHAR GspInBuffer[BUFMAX];
230 static CHAR GspOutBuffer[BUFMAX];
231
232 /* scan for the sequence $<data>#<Checksum>     */
233
234 PCHAR
235 GspGetPacket()
236 {
237   PCHAR Buffer = &GspInBuffer[0];
238   CHAR Checksum;
239   CHAR XmitChecksum;
240   ULONG Count;
241   CHAR ch;
242
243   while (TRUE)
244     {
245       /* wait around for the start character, ignore all other characters */
246       while ((ch = KdGetChar ()) != '$');
247
248     retry:
249       Checksum = 0;
250       XmitChecksum = -1;
251       Count = 0;
252
253       /* now, read until a # or end of Buffer is found */
254       while (Count < BUFMAX)
255         {
256           ch = KdGetChar ();
257           if (ch == '$')
258             goto retry;
259           if (ch == '#')
260             break;
261           Checksum = Checksum + ch;
262           Buffer[Count] = ch;
263           Count = Count + 1;
264         }
265       Buffer[Count] = 0;
266
267       if (ch == '#')
268         {
269           ch = KdGetChar ();
270           XmitChecksum = HexValue (ch) << 4;
271           ch = KdGetChar ();
272           XmitChecksum += HexValue (ch);
273
274           if (Checksum != XmitChecksum)
275             {
276               KdPutChar ('-');  /* failed checksum */
277             }
278           else
279             {
280               KdPutChar ('+');  /* successful transfer */
281
282               /* if a sequence char is present, reply the sequence ID */
283               if (Buffer[2] == ':')
284                 {
285                   KdPutChar (Buffer[0]);
286                   KdPutChar (Buffer[1]);
287
288                   return &Buffer[3];
289                 }
290
291               return &Buffer[0];
292             }
293         }
294     }
295 }
296
297 /* send the packet in Buffer.  */
298
299 VOID
300 GspPutPacket (PCHAR Buffer)
301 {
302   CHAR Checksum;
303   LONG Count;
304   CHAR ch;
305
306   /*  $<packet info>#<Checksum>. */
307   do
308     {
309       KdPutChar ('$');
310       Checksum = 0;
311       Count = 0;
312
313       while ((ch = Buffer[Count]))
314                                 {
315                                   KdPutChar (ch);
316                                   Checksum += ch;
317                                   Count += 1;
318                                 }
319
320       KdPutChar ('#');
321       KdPutChar (HexChars[(Checksum >> 4) & 0xf]);
322       KdPutChar (HexChars[Checksum & 0xf]);
323     }
324   while (KdGetChar () != '+');
325 }
326
327
328 VOID
329 GspPutPacketNoWait (PCHAR Buffer)
330 {
331   CHAR Checksum;
332   LONG Count;
333   CHAR ch;
334
335   /*  $<packet info>#<Checksum>. */
336   KdPutChar ('$');
337   Checksum = 0;
338   Count = 0;
339
340   while ((ch = Buffer[Count]))
341                 {
342                   KdPutChar (ch);
343                   Checksum += ch;
344                   Count += 1;
345                 }
346
347   KdPutChar ('#');
348   KdPutChar (HexChars[(Checksum >> 4) & 0xf]);
349   KdPutChar (HexChars[Checksum & 0xf]);
350 }
351
352
353 VOID
354 GspDebugError(LPSTR Message)
355 {
356   DbgPrint ("%s\n", Message);
357 }
358
359 /* Address of a routine to RTE to if we get a memory fault.  */
360 static VOID (*volatile MemoryFaultRoutine) () = NULL;
361
362 /* Indicate to caller of GspMem2Hex or GspHex2Mem that there has been an
363    error.  */
364 static volatile BOOLEAN GspMemoryError = FALSE;
365
366
367 /* Currently not used */
368 VOID
369 GspSetMemoryError()
370 {
371   GspMemoryError = TRUE;
372 }
373
374
375 /* These are separate functions so that they are so short and sweet
376    that the compiler won't save any Registers (if there is a fault
377    to MemoryFaultRoutine, they won't get restored, so there better
378    not be any saved).  */
379 CHAR
380 GspGetChar (PCHAR Address)
381 {
382   return *Address;
383 }
384
385
386 VOID
387 GspSetChar (PCHAR Address,
388   CHAR Value)
389 {
390   *Address = Value;
391 }
392
393
394 /* Convert the memory pointed to by Address into hex, placing result in Buffer */
395 /* Return a pointer to the last char put in Buffer (null) */
396 /* If MayFault is TRUE, then we should set GspMemoryError in response to
397    a fault; if FALSE treat a fault like any other fault in the stub.  */
398 PCHAR
399 GspMem2Hex (PCHAR Address,
400   PCHAR Buffer,
401   LONG Count,
402   BOOLEAN MayFault)
403 {
404   ULONG i;
405   CHAR ch;
406
407   if (MayFault)
408     MemoryFaultRoutine = GspSetMemoryError;
409   for (i = 0; i < (ULONG) Count; i++)
410     {
411       ch = GspGetChar (Address++);
412       if (MayFault && GspMemoryError)
413         return (Buffer);
414       *Buffer++ = HexChars[(ch >> 4) & 0xf];
415       *Buffer++ = HexChars[ch & 0xf];
416     }
417   *Buffer = 0;
418   if (MayFault)
419     MemoryFaultRoutine = NULL;
420   return (Buffer);
421 }
422
423
424 /* Convert the hex array pointed to by Buffer into binary to be placed at Address */
425 /* Return a pointer to the character AFTER the last byte read from Buffer */
426 PCHAR
427 GspHex2Mem (PCHAR Buffer,
428   PCHAR Address,
429   ULONG Count,
430   BOOLEAN MayFault)
431 {
432   ULONG i;
433   CHAR ch;
434
435   if (MayFault)
436     MemoryFaultRoutine = GspSetMemoryError;
437   for (i = 0; i < Count; i++)
438     {
439       ch = HexValue (*Buffer++) << 4;
440       ch = ch + HexValue (*Buffer++);
441       GspSetChar (Address++, ch);
442       if (MayFault && GspMemoryError)
443         return (Buffer);
444     }
445   if (MayFault)
446     MemoryFaultRoutine = NULL;
447   return (Buffer);
448 }
449
450
451 /* This function takes the 386 exception vector and attempts to
452    translate this number into a unix compatible signal value */
453 ULONG
454 GspComputeSignal (NTSTATUS ExceptionCode)
455 {
456   ULONG SigVal;
457
458   switch (ExceptionCode)
459     {
460     case STATUS_INTEGER_DIVIDE_BY_ZERO:
461       SigVal = 8;
462       break;                    /* divide by zero */
463     case STATUS_SINGLE_STEP:
464       SigVal = 5;
465       break;                    /* debug exception */
466     case STATUS_BREAKPOINT:
467       SigVal = 5;
468       break;                    /* breakpoint */
469     case STATUS_INTEGER_OVERFLOW:
470       SigVal = 16;
471       break;                    /* 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       SigVal = 11;
485       break;                    /* stack exception */
486     case STATUS_DATATYPE_MISALIGNMENT:
487       SigVal = 11;
488       break;                    /* page fault */
489     default:
490       SigVal = 7;               /* "software generated" */
491     }
492   return (SigVal);
493 }
494
495
496 /**********************************************/
497 /* WHILE WE FIND NICE HEX CHARS, BUILD A LONG */
498 /* RETURN NUMBER OF CHARS PROCESSED           */
499 /**********************************************/
500 LONG
501 GspHex2Long (PCHAR *Address,
502   PLONG Value)
503 {
504   LONG NumChars = 0;
505   LONG Hex;
506
507   *Value = 0;
508
509   while (**Address)
510     {
511       Hex = HexValue (**Address);
512       if (Hex >= 0)
513                                 {
514                                   *Value = (*Value << 4) | Hex;
515                                   NumChars++;
516                                 }
517         else
518                 break;
519
520       (*Address)++;
521     }
522
523   return (NumChars);
524 }
525
526
527 VOID
528 GspLong2Hex (PCHAR *Address,
529   LONG Value)
530 {
531   LONG Save;
532
533   Save = (((Value >> 0) & 0xf) << 24) |
534          (((Value >> 8) & 0xf) << 16) |
535          (((Value >> 16) & 0xf) << 8) |
536          (((Value >> 24) & 0xf) << 0);
537   *Address = GspMem2Hex ((PCHAR) &Save, *Address, 4, FALSE);
538 }
539
540
541 VOID
542 GspGetRegistersFromTrapFrame(PCHAR Address,
543   PKTRAP_FRAME TrapFrame)
544 {
545   PCPU_REGISTER Regs;
546   ULONG Value;
547   PCHAR Buffer;
548   PULONG p;
549   DWORD i;
550
551   Buffer = Address;
552   Regs = &GspRegisters[0];
553   for (i = 0; i < NUMREGS; i++)
554   {
555     if (TrapFrame)
556     {
557       p = (PULONG) ((ULONG_PTR) TrapFrame + Regs[i].OffsetInTF);
558       Value = *p;
559     }
560     else if (i == EIP_REGNO)
561                 {
562       /*
563        * This thread has not been sheduled yet so assume it
564        * is still in PsBeginThreadWithContextInternal().
565        */
566       Value = (ULONG) PsBeginThreadWithContextInternal;
567                 }
568     else
569                 {
570       Value = 0;
571     }
572     Buffer = GspMem2Hex ((PCHAR) &Value, Buffer, Regs[i].Size, FALSE);
573   }
574 }
575
576
577 VOID
578 GspSetRegistersInTrapFrame(PCHAR Address,
579   PKTRAP_FRAME TrapFrame)
580 {
581   PCPU_REGISTER Regs;
582   ULONG Value;
583   PCHAR Buffer;
584   PULONG p;
585   DWORD i;
586
587   if (!TrapFrame)
588     return;
589
590   Buffer = Address;
591   Regs = &GspRegisters[0];
592   for (i = 0; i < NUMREGS; i++)
593   {
594     p = (PULONG) ((ULONG_PTR) TrapFrame + Regs[i].OffsetInTF);
595     Value = 0;
596     Buffer = GspHex2Mem (Buffer, (PCHAR) &Value, Regs[i].Size, FALSE);
597     *p = Value;
598   }
599 }
600
601
602 VOID
603 GspSetSingleRegisterInTrapFrame(PCHAR Address,
604   LONG Number,
605   PKTRAP_FRAME TrapFrame)
606 {
607   ULONG Value;
608   PULONG p;
609
610   if (!TrapFrame)
611     return;
612
613   p = (PULONG) ((ULONG_PTR) TrapFrame + GspRegisters[Number].OffsetInTF);
614   Value = 0;
615   GspHex2Mem (Address, (PCHAR) &Value, GspRegisters[Number].Size, FALSE);
616   *p = Value;
617 }
618
619
620 BOOLEAN
621 GspFindThread(PCHAR Data,
622   PETHREAD *Thread)
623 {
624   PETHREAD ThreadInfo = NULL;
625
626   if (strcmp (Data, "-1") == 0)
627     {
628       /* All threads */
629       ThreadInfo = NULL;
630     }
631     else if (strcmp (Data, "0") == 0)
632     {
633        /* Pick any thread, pick the first thread,
634         * which is what most people are interested in
635         */
636        ThreadInfo = CONTAINING_RECORD (PiThreadListHead.Flink,
637          ETHREAD, Tcb.ThreadListEntry);
638     }
639     else
640     {
641       ULONG ThreadId;
642       PCHAR ptr = &Data[0];
643
644       GspHex2Long (&ptr, (PLONG) &ThreadId);
645
646       if (!NT_SUCCESS (PsLookupThreadByThreadId ((PVOID) ThreadId, &ThreadInfo)))
647                           {
648           *Thread = NULL;
649           return FALSE;
650         }
651     }
652   *Thread = ThreadInfo;
653   return TRUE;
654 }
655
656
657 VOID
658 GspSetThread(PCHAR Request)
659 {
660   PETHREAD ThreadInfo;
661   PCHAR ptr = &Request[1];
662
663   switch (Request[0])
664   {
665     case 'c': /* Run thread */
666       if (GspFindThread (ptr, &ThreadInfo))
667                           {
668                             GspOutBuffer[0] = 'O';
669                             GspOutBuffer[1] = 'K';
670                 GspRunThread = ThreadInfo;
671                           }
672                           else
673                           {
674                             GspOutBuffer[0] = 'E';
675                           }
676       break;
677     case 'g': /* Debug thread */
678       if (GspFindThread (ptr, &ThreadInfo))
679                           {
680                             GspOutBuffer[0] = 'O';
681                             GspOutBuffer[1] = 'K';
682                 GspDbgThread = ThreadInfo;
683                           }
684                           else
685                           {
686                             GspOutBuffer[0] = 'E';
687                           }
688       break;
689     default:
690       break;
691   }
692 }
693
694
695 VOID
696 GspQuery(PCHAR Request)
697 {
698   PCHAR Command;
699   ULONG Value;
700
701         Command = strtok (Request, ",");
702         if (strncmp (Command, "C", 1) == 0)
703   {
704     PCHAR ptr = &GspOutBuffer[2];
705
706     /* Get current thread id */
707     GspOutBuffer[0] = 'Q';
708     GspOutBuffer[1] = 'C';
709     Value = (ULONG) GspDbgThread->Cid.UniqueThread;
710     GspLong2Hex (&ptr, Value);
711   }
712   else if (strncmp (Command, "fThreadInfo", 11) == 0)
713   {
714     PCHAR ptr = &GspOutBuffer[1];
715
716     /* Get first thread id */
717     GspOutBuffer[0] = 'm';
718     GspEnumThread = CONTAINING_RECORD (PiThreadListHead.Flink,
719       ETHREAD, Tcb.ThreadListEntry);
720     Value = (ULONG) GspEnumThread->Cid.UniqueThread;
721     GspLong2Hex (&ptr, Value);
722   }
723   else if (strncmp (Command, "sThreadInfo", 11) == 0)
724   {
725     PCHAR ptr = &GspOutBuffer[1];
726
727     /* Get next thread id */
728     if ((GspEnumThread) && (GspEnumThread->Tcb.ThreadListEntry.Flink != PiThreadListHead.Flink))
729     {
730       GspEnumThread = CONTAINING_RECORD (GspEnumThread->Tcb.ThreadListEntry.Flink,
731         ETHREAD, Tcb.ThreadListEntry);
732             GspOutBuffer[0] = 'm';
733             Value = (ULONG) GspEnumThread->Cid.UniqueThread;
734       GspLong2Hex (&ptr, Value);
735     }
736                 else
737                 {
738             GspOutBuffer[0] = '1';
739                 }
740   }
741   else if (strncmp (Command, "ThreadExtraInfo", 15) == 0)
742   {
743     PETHREAD ThreadInfo;
744     PCHAR ptr = &Command[15];
745
746     /* Get thread information */
747     if (GspFindThread (ptr, &ThreadInfo))
748           {
749       PCHAR String = GspThreadStates[ThreadInfo->Tcb.State];
750       GspMem2Hex (String, &GspOutBuffer[0], strlen (String), FALSE);
751           }
752   }
753 #if 0
754         else if (strncmp (Command, "L", 1) == 0)
755   {
756     PLIST_ENTRY CurrentEntry;
757     PETHREAD Current;
758     ULONG MaxThreads = 0;
759     ULONG ThreadId = 0;
760     ULONG ThreadCount = 0;
761
762     /* List threads */
763     GspHex2Mem (&Request[1], (PCHAR) &MaxThreads, 2, TRUE);
764     GspHex2Mem (&Request[3], (PCHAR) &Value, 4, TRUE);
765     GspHex2Mem (&Request[11], (PCHAR) &ThreadId, 4, TRUE);
766
767     GspOutBuffer[0] = 'q';
768     GspOutBuffer[1] = 'M';
769     Value = 0;
770     GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[5], 4, TRUE);
771     GspMem2Hex ((PCHAR) &ThreadId, &GspOutBuffer[13], 4, TRUE);
772
773     CurrentEntry = PiThreadListHead.Flink;
774     while ((CurrentEntry != &PiThreadListHead) && (ThreadCount < MaxThreads))
775       {
776         Current = CONTAINING_RECORD (CurrentEntry, ETHREAD, Tcb.ThreadListEntry);
777         Value = 0;
778         GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[21+ThreadCount*16], 4, TRUE);
779         Value = (ULONG) Current->Cid.UniqueThread;
780         GspMem2Hex ((PCHAR) &Value, &GspOutBuffer[21+ThreadCount*16+8], 4, TRUE);
781         CurrentEntry = CurrentEntry->Flink;
782         ThreadCount++;
783       }
784
785     if (CurrentEntry != &PiThreadListHead)
786                 {
787       GspOutBuffer[4] = '0';
788                 }
789     else
790     {
791       GspOutBuffer[4] = '1';
792     }
793
794     GspMem2Hex ((PCHAR) &ThreadCount, &GspOutBuffer[2], 1, TRUE);
795   }
796 #endif
797   else if (strncmp (Command, "Offsets", 7) == 0)
798   {
799     strcpy (GspOutBuffer, "Text=0;Data=0;Bss=0");
800   }
801 }
802
803 VOID
804 GspQueryThreadStatus(PCHAR Request)
805 {
806   PETHREAD ThreadInfo;
807   PCHAR ptr = &Request[0];
808
809   if (GspFindThread (ptr, &ThreadInfo))
810   {
811     GspOutBuffer[0] = 'O';
812     GspOutBuffer[1] = 'K';
813     GspOutBuffer[2] = '\0';
814   }
815   else
816   {
817     GspOutBuffer[0] = 'E';
818     GspOutBuffer[1] = '\0';
819   }
820 }
821
822
823 typedef struct _GsHwBreakPoint
824 {
825   BOOLEAN Enabled;
826   ULONG Type;
827   ULONG Length;
828   ULONG Address;
829 } GsHwBreakPoint;
830
831 GsHwBreakPoint GspBreakpoints[4] =
832 {
833   { Enabled : FALSE },
834   { Enabled : FALSE },
835   { Enabled : FALSE },
836   { Enabled : FALSE }
837 };
838
839 VOID
840 GspCorrectHwBreakpoint()
841 {
842   ULONG BreakpointNumber;
843   BOOLEAN CorrectIt;
844   BOOLEAN Bit;
845   ULONG dr7;
846
847   asm volatile (
848     "movl %%db7, %0\n" : "=r" (dr7) : );
849   do
850     {
851       ULONG addr0, addr1, addr2, addr3;
852
853       asm volatile (
854         "movl %%db0, %0\n"
855         "movl %%db1, %1\n"
856         "movl %%db2, %2\n"
857         "movl %%db3, %3\n"
858           : "=r" (addr0), "=r" (addr1),
859             "=r" (addr2), "=r" (addr3) : );
860     } while (FALSE);
861     CorrectIt = FALSE;
862     for (BreakpointNumber = 0; BreakpointNumber < 3; BreakpointNumber++)
863     {
864                         Bit = 2 << (BreakpointNumber << 1);
865                         if (!(dr7 & Bit) && GspBreakpoints[BreakpointNumber].Enabled) {
866                   CorrectIt = TRUE;
867                         dr7 |= Bit;
868                         dr7 &= ~(0xf0000 << (BreakpointNumber << 2));
869                         dr7 |= (((GspBreakpoints[BreakpointNumber].Length << 2) |
870         GspBreakpoints[BreakpointNumber].Type) << 16) << (BreakpointNumber << 2);
871     switch (BreakpointNumber) {
872                         case 0:
873                           asm volatile ("movl %0, %%dr0\n"
874                             : : "r" (GspBreakpoints[BreakpointNumber].Address) );
875                           break;
876
877                         case 1:
878                                 asm volatile ("movl %0, %%dr1\n"
879                                   : : "r" (GspBreakpoints[BreakpointNumber].Address) );
880                                 break;
881
882                         case 2:
883                                 asm volatile ("movl %0, %%dr2\n"
884                                 : : "r" (GspBreakpoints[BreakpointNumber].Address) );
885                         break;
886
887       case 3:
888         asm volatile ("movl %0, %%dr3\n"
889           : : "r" (GspBreakpoints[BreakpointNumber].Address) );
890         break;
891       }
892     }
893     else if ((dr7 & Bit) && !GspBreakpoints[BreakpointNumber].Enabled)
894       {
895         CorrectIt = TRUE;
896         dr7 &= ~Bit;
897         dr7 &= ~(0xf0000 << (BreakpointNumber << 2));
898       }
899   }
900   if (CorrectIt)
901     {
902             asm volatile ( "movl %0, %%db7\n" : : "r" (dr7));
903     }
904 }
905
906 ULONG
907 GspRemoveHwBreakpoint(ULONG BreakpointNumber)
908 {
909         if (!GspBreakpoints[BreakpointNumber].Enabled)
910     {
911             return -1;
912         }
913         GspBreakpoints[BreakpointNumber].Enabled = 0;
914         return 0;
915 }
916
917
918 ULONG
919 GspSetHwBreakpoint(ULONG BreakpointNumber,
920   ULONG Type,
921   ULONG Length,
922   ULONG Address)
923 {
924         if (GspBreakpoints[BreakpointNumber].Enabled)
925           {
926                 return -1;
927                 }
928         GspBreakpoints[BreakpointNumber].Enabled = TRUE;
929         GspBreakpoints[BreakpointNumber].Type = Type;
930         GspBreakpoints[BreakpointNumber].Length = Length;
931         GspBreakpoints[BreakpointNumber].Address = Address;
932         return 0;
933 }
934
935
936 /*
937  * This function does all command procesing for interfacing to gdb.
938  */
939 KD_CONTINUE_TYPE
940 KdEnterDebuggerException(PEXCEPTION_RECORD ExceptionRecord,
941                          PCONTEXT Context,
942                          PKTRAP_FRAME TrapFrame)
943 {
944   BOOLEAN Stepping;
945   LONG Address;
946   LONG Length;
947   LONG SigVal;
948   LONG NewPC;
949   PCHAR ptr;
950
951   /* FIXME: Stop on other CPUs too */
952   /* Disable hardware debugging while we are inside the stub */
953   __asm__("movl %0,%%db7" : /* no output */ : "r" (0));
954
955   /* reply to host that an exception has occurred */
956   SigVal = GspComputeSignal (ExceptionRecord->ExceptionCode);
957
958   ptr = &GspOutBuffer[0];
959
960   *ptr++ = 'T';                 /* notify gdb with signo, PC, FP and SP */
961   *ptr++ = HexChars[(SigVal >> 4) & 0xf];
962   *ptr++ = HexChars[SigVal & 0xf];
963
964   *ptr++ = HexChars[ESP];
965   *ptr++ = ':';
966   ptr = GspMem2Hex ((PCHAR) &TrapFrame->Esp, ptr, 4, 0);        /* SP */
967   *ptr++ = ';';
968
969   *ptr++ = HexChars[EBP];
970   *ptr++ = ':';
971   ptr = GspMem2Hex ((PCHAR) &TrapFrame->Ebp, ptr, 4, 0);        /* FP */
972   *ptr++ = ';';
973
974   *ptr++ = HexChars[PC];
975   *ptr++ = ':';
976   ptr = GspMem2Hex((PCHAR) &TrapFrame->Eip, ptr, 4, 0);         /* PC */
977   *ptr++ = ';';
978
979   *ptr = '\0';
980
981   GspPutPacket (&GspOutBuffer[0]);
982
983   Stepping = FALSE;
984
985   while (TRUE)
986     {
987       /* Zero the buffer now so we don't have to worry about the terminating zero character */
988       memset (GspOutBuffer, 0, sizeof (GspInBuffer));
989       ptr = GspGetPacket ();
990
991       switch (*ptr++)
992         {
993         case '?':
994           GspOutBuffer[0] = 'S';
995           GspOutBuffer[1] = HexChars[SigVal >> 4];
996           GspOutBuffer[2] = HexChars[SigVal % 16];
997           GspOutBuffer[3] = 0;
998           break;
999         case 'd':
1000           GspRemoteDebug = !GspRemoteDebug; /* toggle debug flag */
1001           break;
1002         case 'g':               /* return the value of the CPU Registers */
1003     if (GspDbgThread)
1004       GspGetRegistersFromTrapFrame (&GspOutBuffer[0], GspDbgThread->Tcb.TrapFrame);
1005     else
1006       GspGetRegistersFromTrapFrame (&GspOutBuffer[0], TrapFrame);
1007           break;
1008         case 'G':               /* set the value of the CPU Registers - return OK */
1009     if (GspDbgThread)
1010       GspSetRegistersInTrapFrame (ptr, GspDbgThread->Tcb.TrapFrame);
1011     else
1012       GspSetRegistersInTrapFrame (ptr, TrapFrame);
1013           strcpy (GspOutBuffer, "OK");
1014           break;
1015         case 'P':               /* set the value of a single CPU register - return OK */
1016           {
1017             LONG Register;
1018
1019             if ((GspHex2Long (&ptr, &Register)) && (*ptr++ == '='))
1020               if ((Register >= 0) && (Register < NUMREGS))
1021                                         {
1022                                     if (GspDbgThread)
1023                                       GspSetSingleRegisterInTrapFrame (ptr, Register,
1024                 GspDbgThread->Tcb.TrapFrame);
1025                                     else
1026                                        GspSetSingleRegisterInTrapFrame (ptr, Register, TrapFrame);
1027                                           strcpy (GspOutBuffer, "OK");
1028                                           break;
1029                                         }
1030
1031             strcpy (GspOutBuffer, "E01");
1032             break;
1033           }
1034
1035           /* mAA..AA,LLLL  Read LLLL bytes at address AA..AA */
1036         case 'm':
1037           /* TRY TO READ %x,%x.  IF SUCCEED, SET PTR = 0 */
1038           if (GspHex2Long (&ptr, &Address))
1039             if (*(ptr++) == ',')
1040               if (GspHex2Long (&ptr, &Length))
1041                 {
1042                   ptr = 0;
1043                   GspMemoryError = FALSE;
1044                   GspMem2Hex ((PCHAR) Address, GspOutBuffer, Length, 1);
1045                   if (GspMemoryError)
1046                     {
1047                       strcpy (GspOutBuffer, "E03");
1048                       GspDebugError ("memory fault");
1049                     }
1050                 }
1051
1052           if (ptr)
1053             {
1054               strcpy (GspOutBuffer, "E01");
1055             }
1056           break;
1057
1058           /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */
1059         case 'M':
1060           /* TRY TO READ '%x,%x:'.  IF SUCCEED, SET PTR = 0 */
1061           if (GspHex2Long (&ptr, &Address))
1062             if (*(ptr++) == ',')
1063               if (GspHex2Long (&ptr, &Length))
1064                 if (*(ptr++) == ':')
1065                   {
1066         GspMemoryError = FALSE;
1067                     GspHex2Mem (ptr, (PCHAR) Address, Length, TRUE);
1068
1069                     if (GspMemoryError)
1070                       {
1071                                                 strcpy (GspOutBuffer, "E03");
1072                                                 GspDebugError ("memory fault");
1073                       }
1074                     else
1075                       {
1076                               strcpy (GspOutBuffer, "OK");
1077                       }
1078
1079                     ptr = NULL;
1080                   }
1081           if (ptr)
1082             {
1083               strcpy (GspOutBuffer, "E02");
1084             }
1085           break;
1086
1087           /* cAA..AA   Continue at address AA..AA(optional) */
1088           /* sAA..AA   Step one instruction from AA..AA(optional) */
1089         case 's':
1090           Stepping = TRUE;
1091         case 'c':
1092   {
1093     ULONG BreakpointNumber;
1094     ULONG dr6;
1095
1096           /* try to read optional parameter, pc unchanged if no parm */
1097           if (GspHex2Long (&ptr, &Address))
1098             Context->Eip = Address;
1099
1100           NewPC = Context->Eip;
1101
1102           /* clear the trace bit */
1103           Context->EFlags &= 0xfffffeff;
1104
1105           /* set the trace bit if we're Stepping */
1106           if (Stepping)
1107             Context->EFlags |= 0x100;
1108
1109     asm volatile ("movl %%db6, %0\n" : "=r" (dr6) : );
1110     if (!(dr6 & 0x4000))
1111             {
1112               for (BreakpointNumber = 0; BreakpointNumber < 4; ++BreakpointNumber)
1113                 {
1114                   if (dr6 & (1 << BreakpointNumber))
1115                     {
1116                       if (GspBreakpoints[BreakpointNumber].Type == 0)
1117                         {
1118                           /* Set restore flag */
1119                           Context->EFlags |= 0x10000;
1120                           break;
1121                         }
1122                     }
1123                 }
1124       }
1125     GspCorrectHwBreakpoint();
1126     asm volatile ("movl %0, %%db6\n" : : "r" (0));
1127
1128           return kdHandleException;
1129           break;
1130   }
1131         case 'k':       /* kill the program */
1132     strcpy (GspOutBuffer, "OK");
1133           break;
1134           /* kill the program */
1135         case 'H':               /* Set thread */
1136     GspSetThread (ptr);
1137           break;
1138   case 'q': /* Query */
1139     GspQuery (ptr);
1140     break;
1141   case 'T': /* Query thread status */
1142     GspQueryThreadStatus (ptr);
1143     break;
1144   case 'Y':
1145   {
1146     ULONG Number;
1147     ULONG Length;
1148     ULONG Type;
1149     ULONG Address;
1150
1151     ptr = &GspOutBuffer[1];
1152     GspHex2Long (&ptr, &Number);
1153     ptr++;
1154     GspHex2Long (&ptr, &Type);
1155     ptr++;
1156     GspHex2Long (&ptr, &Length);
1157     ptr++;
1158     GspHex2Long (&ptr, &Address);
1159     if (GspSetHwBreakpoint (Number & 0x3, Type & 0x3 , Length & 0x3, Address) == 0)
1160       {
1161         strcpy (GspOutBuffer, "OK");
1162       }
1163     else
1164       {
1165         strcpy (GspOutBuffer, "E");
1166       }
1167     break;
1168     /* Remove hardware breakpoint */
1169   }
1170   case 'y':
1171   {
1172     ULONG Number;
1173
1174     ptr = &GspOutBuffer[1];
1175     GspHex2Long(&ptr, &Number);
1176     if (GspRemoveHwBreakpoint (Number & 0x3) == 0)
1177       {
1178         strcpy (GspOutBuffer, "OK");
1179       }
1180     else
1181       {
1182         strcpy (GspOutBuffer, "E");
1183       }
1184     break;
1185   }
1186   default:
1187     break;
1188         }                       /* switch */
1189
1190       /* reply to the request */
1191       GspPutPacket (&GspOutBuffer[0]);
1192     }
1193
1194   return kdDoNotHandleException;
1195 }
1196
1197
1198 BOOLEAN
1199 STDCALL
1200 GspBreakIn(PKINTERRUPT Interrupt,
1201   PVOID ServiceContext)
1202 {
1203   PKTRAP_FRAME TrapFrame;
1204   BOOLEAN DoBreakIn;
1205   CONTEXT Context;
1206   KIRQL OldIrql;
1207   CHAR Value;
1208
1209   DPRINT ("Break In\n");
1210
1211   DoBreakIn = FALSE;
1212   while (KdPortGetByteEx (&GdbPortInfo, &Value))
1213     {
1214       if (Value == 0x03)
1215         DoBreakIn = TRUE;
1216     }
1217
1218   if (!DoBreakIn)
1219     return TRUE;
1220
1221   KeRaiseIrql (HIGH_LEVEL, &OldIrql);
1222
1223   TrapFrame = PsGetCurrentThread()->Tcb.TrapFrame;
1224
1225   KeTrapFrameToContext (TrapFrame, &Context);
1226
1227   KdEnterDebuggerException (NULL, &Context, TrapFrame);
1228
1229   KeContextToTrapFrame (&Context, TrapFrame);
1230
1231   KeLowerIrql (OldIrql);
1232
1233   return TRUE;
1234 }
1235
1236
1237 extern ULONG KdpPortIrq;
1238
1239 /* Initialize the GDB stub */
1240 VOID
1241 KdGdbStubInit(ULONG Phase)
1242 {
1243 #if 0
1244   KAFFINITY Affinity;
1245   NTSTATUS Status;
1246   ULONG MappedIrq;
1247   KIRQL Dirql;
1248 #endif
1249
1250   if (Phase == 0)
1251     {
1252                   DbgPrint("Module 'hal.dll' loaded at 0x%.08x.\n", LdrHalBase);
1253
1254                   GspInitialized = TRUE;
1255                   GspRunThread = PsGetCurrentThread();
1256                   GspDbgThread = PsGetCurrentThread();
1257                   GspEnumThread = NULL;
1258
1259       DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1260     }
1261   else if (Phase == 1)
1262     {
1263 #if 0
1264                   /* Hook an interrupt handler to allow the debugger to break into
1265                      the system */
1266                   MappedIrq = HalGetInterruptVector (Internal,
1267                     0,
1268                     0,
1269                     KdpPortIrq,
1270                     &Dirql,
1271                     &Affinity);
1272
1273                   Status = IoConnectInterrupt(&GspInterrupt,
1274                     GspBreakIn,
1275                     NULL,
1276                     NULL,
1277                     MappedIrq,
1278                     Dirql,
1279                     Dirql,
1280                     0,
1281                     FALSE,
1282                     Affinity,
1283                     FALSE);
1284       if (!NT_SUCCESS (Status))
1285       {
1286         DPRINT1("Could not connect to IRQ line %d (0x%x)\n",
1287           KdpPortIrq, Status);
1288         return;
1289       }
1290
1291        KdPortEnableInterrupts();
1292
1293        DbgBreakPointWithStatus (DBG_STATUS_CONTROL_C);
1294 #endif
1295   }
1296 }
1297
1298
1299 VOID
1300 KdGdbDebugPrint(LPSTR Message)
1301 {
1302 #if 0
1303   /* This can be quite annoying! */
1304   if (GspInitialized)
1305           {
1306             ULONG Length;
1307         
1308             GspOutBuffer[0] = 'O';
1309             GspOutBuffer[1] = '\0';
1310             strcat (&GspOutBuffer[0], Message);
1311             Length = strlen (Message);
1312             GspOutBuffer[2 + Length] = '\n';
1313             GspOutBuffer[3 + Length] = '\0';
1314             GspPutPacketNoWait (&GspOutBuffer[0]);
1315           }
1316 #else
1317   HalDisplayString(Message);
1318 #endif
1319 }
1320
1321
1322 extern LIST_ENTRY ModuleListHead;
1323
1324 VOID
1325 KdGdbListModules()
1326 {
1327   PLIST_ENTRY CurrentEntry;
1328   PMODULE_OBJECT Current;
1329   ULONG ModuleCount;
1330
1331   DPRINT1("\n");
1332
1333   ModuleCount = 0;
1334
1335   CurrentEntry = ModuleListHead.Flink;
1336   while (CurrentEntry != (&ModuleListHead))
1337     {
1338             Current = CONTAINING_RECORD (CurrentEntry, MODULE_OBJECT, ListEntry);
1339
1340       DbgPrint ("Module %S  Base 0x%.08x  Length 0x%.08x\n",
1341         Current->BaseName.Buffer, Current->Base, Current->Length);
1342
1343       ModuleCount++;
1344       CurrentEntry = CurrentEntry->Flink;
1345     }
1346
1347   DbgPrint ("%d modules listed\n", ModuleCount);
1348 }