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