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