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