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