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