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