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