xref: /reactos/ntoskrnl/mm/ARM3/kdbg.c (revision 299e4305)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/kdbg.c
5  * PURPOSE:         ARM Memory Manager Kernel Debugger routines
6  * PROGRAMMERS:     ReactOS Portable Systems Group
7  *                  Pierre Schweitzer
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 typedef struct _IRP_FIND_CTXT
22 {
23     ULONG_PTR RestartAddress;
24     ULONG_PTR SData;
25     ULONG Criteria;
26 } IRP_FIND_CTXT, *PIRP_FIND_CTXT;
27 
28 extern PVOID MmNonPagedPoolEnd0;
29 extern SIZE_T PoolBigPageTableSize;
30 extern PPOOL_TRACKER_BIG_PAGES PoolBigPageTable;
31 
32 #define POOL_BIG_TABLE_ENTRY_FREE 0x1
33 
34 /* Pool block/header/list access macros */
35 #define POOL_ENTRY(x)       (PPOOL_HEADER)((ULONG_PTR)(x) - sizeof(POOL_HEADER))
36 #define POOL_FREE_BLOCK(x)  (PLIST_ENTRY)((ULONG_PTR)(x)  + sizeof(POOL_HEADER))
37 #define POOL_BLOCK(x, i)    (PPOOL_HEADER)((ULONG_PTR)(x) + ((i) * POOL_BLOCK_SIZE))
38 #define POOL_NEXT_BLOCK(x)  POOL_BLOCK((x), (x)->BlockSize)
39 #define POOL_PREV_BLOCK(x)  POOL_BLOCK((x), -((x)->PreviousSize))
40 
41 VOID MiDumpPoolConsumers(BOOLEAN CalledFromDbg, ULONG Tag, ULONG Mask, ULONG Flags);
42 
43 /* PRIVATE FUNCTIONS **********************************************************/
44 
45 #if DBG && defined(KDBG)
46 
47 #include <kdbg/kdb.h>
48 
49 BOOLEAN
50 ExpKdbgExtPool(
51     ULONG Argc,
52     PCHAR Argv[])
53 {
54     ULONG_PTR Address = 0, Flags = 0;
55     PVOID PoolPage;
56     PPOOL_HEADER Entry;
57     BOOLEAN ThisOne;
58     PULONG Data;
59 
60     if (Argc > 1)
61     {
62         /* Get address */
63         if (!KdbpGetHexNumber(Argv[1], &Address))
64         {
65             KdbpPrint("Invalid parameter: %s\n", Argv[1]);
66             return TRUE;
67         }
68     }
69 
70     if (Argc > 2)
71     {
72         /* Get flags */
73         if (!KdbpGetHexNumber(Argv[2], &Flags))
74         {
75             KdbpPrint("Invalid parameter: %s\n", Argv[2]);
76             return TRUE;
77         }
78     }
79 
80     /* Check if we got an address */
81     if (Address != 0)
82     {
83         /* Get the base page */
84         PoolPage = PAGE_ALIGN(Address);
85     }
86     else
87     {
88         KdbpPrint("Heap is unimplemented\n");
89         return TRUE;
90     }
91 
92     /* No paging support! */
93     if (!MmIsAddressValid(PoolPage))
94     {
95         KdbpPrint("Address not accessible!\n");
96         return TRUE;
97     }
98 
99     /* Get pool type */
100     if ((Address >= (ULONG_PTR)MmPagedPoolStart) && (Address <= (ULONG_PTR)MmPagedPoolEnd))
101         KdbpPrint("Allocation is from PagedPool region\n");
102     else if ((Address >= (ULONG_PTR)MmNonPagedPoolStart) && (Address <= (ULONG_PTR)MmNonPagedPoolEnd))
103         KdbpPrint("Allocation is from NonPagedPool region\n");
104     else
105     {
106         KdbpPrint("Address 0x%p is not within any pool!\n", (PVOID)Address);
107         return TRUE;
108     }
109 
110     /* Loop all entries of that page */
111     Entry = PoolPage;
112     do
113     {
114         /* Check if the address is within that entry */
115         ThisOne = ((Address >= (ULONG_PTR)Entry) &&
116                    (Address < (ULONG_PTR)(Entry + Entry->BlockSize)));
117 
118         if (!(Flags & 1) || ThisOne)
119         {
120             /* Print the line */
121             KdbpPrint("%c%p size: %4d previous size: %4d  %s  %.4s\n",
122                      ThisOne ? '*' : ' ', Entry, Entry->BlockSize, Entry->PreviousSize,
123                      (Flags & 0x80000000) ? "" : (Entry->PoolType ? "(Allocated)" : "(Free)     "),
124                      (Flags & 0x80000000) ? "" : (PCHAR)&Entry->PoolTag);
125         }
126 
127         if (Flags & 1)
128         {
129             Data = (PULONG)(Entry + 1);
130             KdbpPrint("    %p  %08lx %08lx %08lx %08lx\n"
131                      "    %p  %08lx %08lx %08lx %08lx\n",
132                      &Data[0], Data[0], Data[1], Data[2], Data[3],
133                      &Data[4], Data[4], Data[5], Data[6], Data[7]);
134         }
135 
136         /* Go to next entry */
137         Entry = POOL_BLOCK(Entry, Entry->BlockSize);
138     }
139     while ((Entry->BlockSize != 0) && ((ULONG_PTR)Entry < (ULONG_PTR)PoolPage + PAGE_SIZE));
140 
141     return TRUE;
142 }
143 
144 static
145 VOID
146 ExpKdbgExtPoolUsedGetTag(PCHAR Arg, PULONG Tag, PULONG Mask)
147 {
148     CHAR Tmp[4];
149     SIZE_T Len;
150     USHORT i;
151 
152     /* Get the tag */
153     Len = strlen(Arg);
154     if (Len > 4)
155     {
156         Len = 4;
157     }
158 
159     /* Generate the mask to have wildcards support */
160     for (i = 0; i < Len; ++i)
161     {
162         Tmp[i] = Arg[i];
163         if (Tmp[i] != '?')
164         {
165             *Mask |= (0xFF << i * 8);
166         }
167     }
168 
169     /* Get the tag in the ulong form */
170     *Tag = *((PULONG)Tmp);
171 }
172 
173 BOOLEAN
174 ExpKdbgExtPoolUsed(
175     ULONG Argc,
176     PCHAR Argv[])
177 {
178     ULONG Tag = 0;
179     ULONG Mask = 0;
180     ULONG_PTR Flags = 0;
181 
182     if (Argc > 1)
183     {
184         /* If we have 2+ args, easy: flags then tag */
185         if (Argc > 2)
186         {
187             ExpKdbgExtPoolUsedGetTag(Argv[2], &Tag, &Mask);
188             if (!KdbpGetHexNumber(Argv[1], &Flags))
189             {
190                 KdbpPrint("Invalid parameter: %s\n", Argv[1]);
191             }
192         }
193         else
194         {
195             /* Otherwise, try to find out whether that's flags */
196             if (strlen(Argv[1]) == 1 ||
197                 (strlen(Argv[1]) == 3 && Argv[1][0] == '0' && (Argv[1][1] == 'x' || Argv[1][1] == 'X')))
198             {
199                 /* Fallback: if reading flags failed, assume it's a tag */
200                 if (!KdbpGetHexNumber(Argv[1], &Flags))
201                 {
202                     ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
203                 }
204             }
205             /* Or tag */
206             else
207             {
208                 ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
209             }
210         }
211     }
212 
213     /* Call the dumper */
214     MiDumpPoolConsumers(TRUE, Tag, Mask, Flags);
215 
216     return TRUE;
217 }
218 
219 static
220 VOID
221 ExpKdbgExtPoolFindLargePool(
222     ULONG Tag,
223     ULONG Mask,
224     VOID (NTAPI* FoundCallback)(PPOOL_TRACKER_BIG_PAGES, PVOID),
225     PVOID CallbackContext)
226 {
227     ULONG i;
228 
229     KdbpPrint("Scanning large pool allocation table for Tag: %.4s (%p : %p)\n", (PCHAR)&Tag, &PoolBigPageTable[0], &PoolBigPageTable[PoolBigPageTableSize - 1]);
230 
231     for (i = 0; i < PoolBigPageTableSize; i++)
232     {
233         /* Free entry? */
234         if ((ULONG_PTR)PoolBigPageTable[i].Va & POOL_BIG_TABLE_ENTRY_FREE)
235         {
236             continue;
237         }
238 
239         if ((PoolBigPageTable[i].Key & Mask) == (Tag & Mask))
240         {
241             if (FoundCallback != NULL)
242             {
243                 FoundCallback(&PoolBigPageTable[i], CallbackContext);
244             }
245             else
246             {
247                 /* Print the line */
248                 KdbpPrint("%p: tag %.4s, size: %I64x\n",
249                           PoolBigPageTable[i].Va, (PCHAR)&PoolBigPageTable[i].Key,
250                           PoolBigPageTable[i].NumberOfPages << PAGE_SHIFT);
251             }
252         }
253     }
254 }
255 
256 static
257 BOOLEAN
258 ExpKdbgExtValidatePoolHeader(
259     PVOID BaseVa,
260     PPOOL_HEADER Entry,
261     POOL_TYPE BasePoolTye)
262 {
263     /* Block size cannot be NULL or negative and it must cover the page */
264     if (Entry->BlockSize <= 0)
265     {
266         return FALSE;
267     }
268     if (Entry->BlockSize * 8 + (ULONG_PTR)Entry - (ULONG_PTR)BaseVa > PAGE_SIZE)
269     {
270         return FALSE;
271     }
272 
273     /*
274      * PreviousSize cannot be 0 unless on page begin
275      * And it cannot be bigger that our current
276      * position in page
277      */
278     if (Entry->PreviousSize == 0 && BaseVa != Entry)
279     {
280         return FALSE;
281     }
282     if (Entry->PreviousSize * 8 > (ULONG_PTR)Entry - (ULONG_PTR)BaseVa)
283     {
284         return FALSE;
285     }
286 
287     /* Must be paged pool */
288     if (((Entry->PoolType - 1) & BASE_POOL_TYPE_MASK) != BasePoolTye)
289     {
290         return FALSE;
291     }
292 
293     /* Match tag mask */
294     if ((Entry->PoolTag & 0x00808080) != 0)
295     {
296         return FALSE;
297     }
298 
299     return TRUE;
300 }
301 
302 static
303 VOID
304 ExpKdbgExtPoolFindPagedPool(
305     ULONG Tag,
306     ULONG Mask,
307     VOID (NTAPI* FoundCallback)(PPOOL_HEADER, PVOID),
308     PVOID CallbackContext)
309 {
310     ULONG i = 0;
311     PPOOL_HEADER Entry;
312     PVOID BaseVa;
313     PMMPDE PointerPde;
314 
315     KdbpPrint("Searching Paged pool (%p : %p) for Tag: %.4s\n", MmPagedPoolStart, MmPagedPoolEnd, (PCHAR)&Tag);
316 
317     /*
318      * To speed up paged pool search, we will use the allocation bipmap.
319      * This is possible because we live directly in the kernel :-)
320      */
321     i = RtlFindSetBits(MmPagedPoolInfo.PagedPoolAllocationMap, 1, 0);
322     while (i != 0xFFFFFFFF)
323     {
324         BaseVa = (PVOID)((ULONG_PTR)MmPagedPoolStart + (i << PAGE_SHIFT));
325         Entry = BaseVa;
326 
327         /* Validate our address */
328         if ((ULONG_PTR)BaseVa > (ULONG_PTR)MmPagedPoolEnd || (ULONG_PTR)BaseVa + PAGE_SIZE > (ULONG_PTR)MmPagedPoolEnd)
329         {
330             break;
331         }
332 
333         /* Check whether we are beyond expansion */
334         PointerPde = MiAddressToPde(BaseVa);
335         if (PointerPde >= MmPagedPoolInfo.NextPdeForPagedPoolExpansion)
336         {
337             break;
338         }
339 
340         /* Check if allocation is valid */
341         if (MmIsAddressValid(BaseVa))
342         {
343             for (Entry = BaseVa;
344                  (ULONG_PTR)Entry + sizeof(POOL_HEADER) < (ULONG_PTR)BaseVa + PAGE_SIZE;
345                  Entry = (PVOID)((ULONG_PTR)Entry + 8))
346             {
347                 /* Try to find whether we have a pool entry */
348                 if (!ExpKdbgExtValidatePoolHeader(BaseVa, Entry, PagedPool))
349                 {
350                     continue;
351                 }
352 
353                 if ((Entry->PoolTag & Mask) == (Tag & Mask))
354                 {
355                     if (FoundCallback != NULL)
356                     {
357                         FoundCallback(Entry, CallbackContext);
358                     }
359                     else
360                     {
361                         /* Print the line */
362                         KdbpPrint("%p size: %4d previous size: %4d  %s  %.4s\n",
363                                   Entry, Entry->BlockSize, Entry->PreviousSize,
364                                   Entry->PoolType ? "(Allocated)" : "(Free)     ",
365                                   (PCHAR)&Entry->PoolTag);
366                     }
367                 }
368             }
369         }
370 
371         i = RtlFindSetBits(MmPagedPoolInfo.PagedPoolAllocationMap, 1, i + 1);
372     }
373 }
374 
375 static
376 VOID
377 ExpKdbgExtPoolFindNonPagedPool(
378     ULONG Tag,
379     ULONG Mask,
380     VOID (NTAPI* FoundCallback)(PPOOL_HEADER, PVOID),
381     PVOID CallbackContext)
382 {
383     PPOOL_HEADER Entry;
384     PVOID BaseVa;
385 
386     KdbpPrint("Searching NonPaged pool (%p : %p) for Tag: %.4s\n", MmNonPagedPoolStart, MmNonPagedPoolEnd0, (PCHAR)&Tag);
387 
388     /* Brute force search: start browsing the whole non paged pool */
389     for (BaseVa = MmNonPagedPoolStart;
390          (ULONG_PTR)BaseVa + PAGE_SIZE <= (ULONG_PTR)MmNonPagedPoolEnd0;
391          BaseVa = (PVOID)((ULONG_PTR)BaseVa + PAGE_SIZE))
392     {
393         Entry = BaseVa;
394 
395         /* Check whether we are beyond expansion */
396         if (BaseVa >= MmNonPagedPoolExpansionStart)
397         {
398             break;
399         }
400 
401         /* Check if allocation is valid */
402         if (!MmIsAddressValid(BaseVa))
403         {
404             continue;
405         }
406 
407         for (Entry = BaseVa;
408              (ULONG_PTR)Entry + sizeof(POOL_HEADER) < (ULONG_PTR)BaseVa + PAGE_SIZE;
409              Entry = (PVOID)((ULONG_PTR)Entry + 8))
410         {
411             /* Try to find whether we have a pool entry */
412             if (!ExpKdbgExtValidatePoolHeader(BaseVa, Entry, NonPagedPool))
413             {
414                 continue;
415             }
416 
417             if ((Entry->PoolTag & Mask) == (Tag & Mask))
418             {
419                 if (FoundCallback != NULL)
420                 {
421                     FoundCallback(Entry, CallbackContext);
422                 }
423                 else
424                 {
425                     /* Print the line */
426                     KdbpPrint("%p size: %4d previous size: %4d  %s  %.4s\n",
427                               Entry, Entry->BlockSize, Entry->PreviousSize,
428                               Entry->PoolType ? "(Allocated)" : "(Free)     ",
429                               (PCHAR)&Entry->PoolTag);
430                 }
431             }
432         }
433     }
434 }
435 
436 BOOLEAN
437 ExpKdbgExtPoolFind(
438     ULONG Argc,
439     PCHAR Argv[])
440 {
441     ULONG Tag = 0;
442     ULONG Mask = 0;
443     ULONG PoolType = NonPagedPool;
444 
445     if (Argc == 1)
446     {
447         KdbpPrint("Specify a tag string\n");
448         return TRUE;
449     }
450 
451     /* First arg is tag */
452     if (strlen(Argv[1]) != 1 || Argv[1][0] != '*')
453     {
454         ExpKdbgExtPoolUsedGetTag(Argv[1], &Tag, &Mask);
455     }
456 
457     /* Second arg might be pool to search */
458     if (Argc > 2)
459     {
460         PoolType = strtoul(Argv[2], NULL, 0);
461 
462         if (PoolType > 1)
463         {
464             KdbpPrint("Only (non) paged pool are supported\n");
465             return TRUE;
466         }
467     }
468 
469     /* First search for large allocations */
470     ExpKdbgExtPoolFindLargePool(Tag, Mask, NULL, NULL);
471 
472     if (PoolType == NonPagedPool)
473     {
474         ExpKdbgExtPoolFindNonPagedPool(Tag, Mask, NULL, NULL);
475     }
476     else if (PoolType == PagedPool)
477     {
478         ExpKdbgExtPoolFindPagedPool(Tag, Mask, NULL, NULL);
479     }
480 
481     return TRUE;
482 }
483 
484 VOID
485 NTAPI
486 ExpKdbgExtIrpFindPrint(
487     PPOOL_HEADER Entry,
488     PVOID Context)
489 {
490     PIRP Irp;
491     BOOLEAN IsComplete = FALSE;
492     PIRP_FIND_CTXT FindCtxt = Context;
493     PIO_STACK_LOCATION IoStack = NULL;
494     PUNICODE_STRING DriverName = NULL;
495     ULONG_PTR SData = FindCtxt->SData;
496     ULONG Criteria = FindCtxt->Criteria;
497 
498     /* Free entry, ignore */
499     if (Entry->PoolType == 0)
500     {
501         return;
502     }
503 
504     /* Get the IRP */
505     Irp = (PIRP)POOL_FREE_BLOCK(Entry);
506 
507     /* Bail out if not matching restart address */
508     if ((ULONG_PTR)Irp < FindCtxt->RestartAddress)
509     {
510         return;
511     }
512 
513     /* Avoid bogus IRP stack locations */
514     if (Irp->CurrentLocation <= Irp->StackCount + 1)
515     {
516         IoStack = IoGetCurrentIrpStackLocation(Irp);
517 
518         /* Get associated driver */
519         if (IoStack->DeviceObject && IoStack->DeviceObject->DriverObject)
520             DriverName = &IoStack->DeviceObject->DriverObject->DriverName;
521     }
522     else
523     {
524         IsComplete = TRUE;
525     }
526 
527     /* Display if: no data, no criteria or if criteria matches data */
528     if (SData == 0 || Criteria == 0 ||
529         (Criteria & 0x1 && IoStack && SData == (ULONG_PTR)IoStack->DeviceObject) ||
530         (Criteria & 0x2 && SData == (ULONG_PTR)Irp->Tail.Overlay.OriginalFileObject) ||
531         (Criteria & 0x4 && Irp->MdlAddress && SData == (ULONG_PTR)Irp->MdlAddress->Process) ||
532         (Criteria & 0x8 && SData == (ULONG_PTR)Irp->Tail.Overlay.Thread) ||
533         (Criteria & 0x10 && SData == (ULONG_PTR)Irp->UserEvent))
534     {
535         if (!IsComplete)
536         {
537             KdbpPrint("%p Thread %p current stack (%x, %x) belongs to %wZ\n", Irp, Irp->Tail.Overlay.Thread, IoStack->MajorFunction, IoStack->MinorFunction, DriverName);
538         }
539         else
540         {
541             KdbpPrint("%p Thread %p is complete (CurrentLocation %d > StackCount %d)\n", Irp, Irp->Tail.Overlay.Thread, Irp->CurrentLocation, Irp->StackCount + 1);
542         }
543     }
544 }
545 
546 BOOLEAN
547 ExpKdbgExtIrpFind(
548     ULONG Argc,
549     PCHAR Argv[])
550 {
551     ULONG PoolType = NonPagedPool;
552     IRP_FIND_CTXT FindCtxt;
553 
554     /* Pool type */
555     if (Argc > 1)
556     {
557         PoolType = strtoul(Argv[1], NULL, 0);
558 
559         if (PoolType > 1)
560         {
561             KdbpPrint("Only (non) paged pool are supported\n");
562             return TRUE;
563         }
564     }
565 
566     RtlZeroMemory(&FindCtxt, sizeof(IRP_FIND_CTXT));
567 
568     /* Restart address */
569     if (Argc > 2)
570     {
571         if (!KdbpGetHexNumber(Argv[2], &FindCtxt.RestartAddress))
572         {
573             KdbpPrint("Invalid parameter: %s\n", Argv[2]);
574             FindCtxt.RestartAddress = 0;
575         }
576     }
577 
578     if (Argc > 4)
579     {
580         if (!KdbpGetHexNumber(Argv[4], &FindCtxt.SData))
581         {
582             FindCtxt.SData = 0;
583         }
584         else
585         {
586             if (strcmp(Argv[3], "device") == 0)
587             {
588                 FindCtxt.Criteria = 0x1;
589             }
590             else if (strcmp(Argv[3], "fileobject") == 0)
591             {
592                 FindCtxt.Criteria = 0x2;
593             }
594             else if (strcmp(Argv[3], "mdlprocess") == 0)
595             {
596                 FindCtxt.Criteria = 0x4;
597             }
598             else if (strcmp(Argv[3], "thread") == 0)
599             {
600                 FindCtxt.Criteria = 0x8;
601             }
602             else if (strcmp(Argv[3], "userevent") == 0)
603             {
604                 FindCtxt.Criteria = 0x10;
605             }
606             else if (strcmp(Argv[3], "arg") == 0)
607             {
608                 FindCtxt.Criteria = 0x1f;
609             }
610         }
611     }
612 
613     if (PoolType == NonPagedPool)
614     {
615         ExpKdbgExtPoolFindNonPagedPool(TAG_IRP, 0xFFFFFFFF, ExpKdbgExtIrpFindPrint, &FindCtxt);
616     }
617     else if (PoolType == PagedPool)
618     {
619         ExpKdbgExtPoolFindPagedPool(TAG_IRP, 0xFFFFFFFF, ExpKdbgExtIrpFindPrint, &FindCtxt);
620     }
621 
622     return TRUE;
623 }
624 
625 #endif // DBG && defined(KDBG)
626 
627 /* EOF */
628