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