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