1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * This part of the file contains the mdb support for dcmds: 30 * ::memseg_list 31 * ::page_num2pp 32 * and walkers for: 33 * memseg - a memseg list walker for ::memseg_list 34 * 35 */ 36 37 #include <sys/types.h> 38 #include <sys/machparam.h> 39 #include <sys/controlregs.h> 40 #include <vm/as.h> 41 42 #include <mdb/mdb_modapi.h> 43 #include <mdb/mdb_target.h> 44 45 #include <vm/page.h> 46 #include <vm/hat_i86.h> 47 48 struct pfn2pp { 49 pfn_t pfn; 50 page_t *pp; 51 }; 52 53 static int do_va2pfn(uintptr_t, struct as *, int, physaddr_t *); 54 static void get_mmu(void); 55 56 int 57 platform_vtop(uintptr_t addr, struct as *asp, physaddr_t *pap) 58 { 59 if (asp == NULL) 60 return (DCMD_ERR); 61 62 /* 63 * The kernel has to at least have made it thru mmu_init() 64 */ 65 get_mmu(); 66 if (mmu.num_level == 0) 67 return (DCMD_ERR); 68 69 return (do_va2pfn(addr, asp, 0, pap)); 70 } 71 72 73 /*ARGSUSED*/ 74 int 75 page_num2pp_cb(uintptr_t addr, void *ignored, uintptr_t *data) 76 { 77 struct memseg ms, *msp = &ms; 78 struct pfn2pp *p = (struct pfn2pp *)data; 79 80 if (mdb_vread(msp, sizeof (struct memseg), addr) == -1) { 81 mdb_warn("can't read memseg at %#lx", addr); 82 return (DCMD_ERR); 83 } 84 85 if (p->pfn >= msp->pages_base && p->pfn < msp->pages_end) { 86 p->pp = msp->pages + (p->pfn - msp->pages_base); 87 return (WALK_DONE); 88 } 89 90 return (WALK_NEXT); 91 } 92 93 /* 94 * ::page_num2pp dcmd 95 */ 96 /*ARGSUSED*/ 97 int 98 page_num2pp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 99 { 100 struct pfn2pp pfn2pp; 101 page_t page; 102 103 if ((flags & DCMD_ADDRSPEC) == 0) { 104 mdb_warn("page frame number missing\n"); 105 return (DCMD_USAGE); 106 } 107 108 pfn2pp.pfn = (pfn_t)addr; 109 pfn2pp.pp = NULL; 110 111 if (mdb_walk("memseg", (mdb_walk_cb_t)page_num2pp_cb, 112 (void *)&pfn2pp) == -1) { 113 mdb_warn("can't walk memseg"); 114 return (DCMD_ERR); 115 } 116 117 if (pfn2pp.pp == NULL) 118 return (DCMD_ERR); 119 120 mdb_printf("%x has page at %p\n", pfn2pp.pfn, pfn2pp.pp); 121 122 if (mdb_vread(&page, sizeof (page_t), 123 (uintptr_t)pfn2pp.pp) == -1) { 124 mdb_warn("can't read page at %p", &page); 125 return (DCMD_ERR); 126 } 127 128 if (page.p_pagenum != pfn2pp.pfn) { 129 mdb_warn("WARNING! Found page structure contains " 130 "different pagenumber %x\n", page.p_pagenum); 131 } 132 133 return (DCMD_OK); 134 } 135 136 137 138 139 140 /* 141 * ::memseg_list dcmd and walker to implement it. 142 */ 143 /*ARGSUSED*/ 144 int 145 memseg_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 146 { 147 struct memseg ms; 148 149 if (!(flags & DCMD_ADDRSPEC)) { 150 if (mdb_pwalk_dcmd("memseg", "memseg_list", 151 0, NULL, 0) == -1) { 152 mdb_warn("can't walk memseg"); 153 return (DCMD_ERR); 154 } 155 return (DCMD_OK); 156 } 157 158 if (DCMD_HDRSPEC(flags)) 159 mdb_printf("%<u>%?s %?s %?s %?s %?s%</u>\n", "ADDR", 160 "PAGES", "EPAGES", "BASE", "END"); 161 162 if (mdb_vread(&ms, sizeof (struct memseg), addr) == -1) { 163 mdb_warn("can't read memseg at %#lx", addr); 164 return (DCMD_ERR); 165 } 166 167 mdb_printf("%0?lx %0?lx %0?lx %0?lx %0?lx\n", addr, 168 ms.pages, ms.epages, ms.pages_base, ms.pages_end); 169 170 return (DCMD_OK); 171 } 172 173 /* 174 * walk the memseg structures 175 */ 176 int 177 memseg_walk_init(mdb_walk_state_t *wsp) 178 { 179 if (wsp->walk_addr != NULL) { 180 mdb_warn("memseg only supports global walks\n"); 181 return (WALK_ERR); 182 } 183 184 if (mdb_readvar(&wsp->walk_addr, "memsegs") == -1) { 185 mdb_warn("symbol 'memsegs' not found"); 186 return (WALK_ERR); 187 } 188 189 wsp->walk_data = mdb_alloc(sizeof (struct memseg), UM_SLEEP); 190 return (WALK_NEXT); 191 192 } 193 194 int 195 memseg_walk_step(mdb_walk_state_t *wsp) 196 { 197 int status; 198 199 if (wsp->walk_addr == 0) { 200 return (WALK_DONE); 201 } 202 203 if (mdb_vread(wsp->walk_data, sizeof (struct memseg), 204 wsp->walk_addr) == -1) { 205 mdb_warn("failed to read struct memseg at %p", wsp->walk_addr); 206 return (WALK_DONE); 207 } 208 209 status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data, 210 wsp->walk_cbdata); 211 212 wsp->walk_addr = (uintptr_t)(((struct memseg *)wsp->walk_data)->next); 213 214 return (status); 215 } 216 217 void 218 memseg_walk_fini(mdb_walk_state_t *wsp) 219 { 220 mdb_free(wsp->walk_data, sizeof (struct memseg)); 221 } 222 223 /* 224 * HAT related dcmds: 225 * 226 * ::pte [-p XXXXXXXX] [-l 0/1/2/3] 227 * 228 * dcmd that interprets the -p argument as a page table entry and 229 * prints it in more human readable form. The PTE is assumed to be in 230 * a level 0 page table, unless -l specifies another level. 231 * 232 * ::vatopfn [-v] [-a as] 233 * 234 * Given a virtual address, returns the PFN, if any, mapped at the address. 235 * -v shows the intermediate htable/page table entries used to resolve the 236 * mapping. By default the virtual address is assumed to be in the kernel's 237 * address space. -a is used to specify a different address space. 238 */ 239 240 struct hat *khat; /* value of kas.a_hat */ 241 struct hat_mmu_info mmu; 242 uintptr_t kernelbase; 243 244 /* 245 * read mmu parameters from kernel 246 */ 247 static void 248 get_mmu(void) 249 { 250 struct as kas; 251 252 if (mmu.num_level != 0) 253 return; 254 255 if (mdb_readsym(&mmu, sizeof (mmu), "mmu") == -1) 256 mdb_warn("Can't use HAT information before mmu_init()\n"); 257 if (mdb_readsym(&kas, sizeof (kas), "kas") == -1) 258 mdb_warn("Couldn't find kas - kernel's struct as\n"); 259 if (mdb_readsym(&kernelbase, sizeof (kernelbase), "kernelbase") == -1) 260 mdb_warn("Couldn't find kernelbase\n"); 261 khat = kas.a_hat; 262 } 263 264 /* 265 * Print a PTE in more human friendly way. The PTE is assumed to be in 266 * a level 0 page table, unless -l specifies another level. 267 * 268 * The PTE value can be specified as the -p option, since on a 32 bit kernel 269 * with PAE running it's larger than a uintptr_t. 270 */ 271 static int 272 do_pte_dcmd(int level, uint64_t pte) 273 { 274 static char *attr[] = { 275 "wrback", "wrthru", "uncached", "uncached", 276 "wrback", "wrthru", "wrcombine", "uncached"}; 277 int pat_index = 0; 278 279 mdb_printf("PTE=%llx: ", pte); 280 if (PTE_GET(pte, mmu.pt_nx)) 281 mdb_printf("noexec "); 282 283 mdb_printf("page=0x%llx ", PTE2PFN(pte, level)); 284 285 if (PTE_GET(pte, PT_NOCONSIST)) 286 mdb_printf("noconsist "); 287 288 if (PTE_GET(pte, PT_NOSYNC)) 289 mdb_printf("nosync "); 290 291 if (PTE_GET(pte, mmu.pt_global)) 292 mdb_printf("global "); 293 294 if (level > 0 && PTE_GET(pte, PT_PAGESIZE)) 295 mdb_printf("largepage "); 296 297 if (level > 0 && PTE_GET(pte, PT_MOD)) 298 mdb_printf("mod "); 299 300 if (level > 0 && PTE_GET(pte, PT_REF)) 301 mdb_printf("ref "); 302 303 if (PTE_GET(pte, PT_USER)) 304 mdb_printf("user "); 305 306 if (PTE_GET(pte, PT_WRITABLE)) 307 mdb_printf("write "); 308 309 /* 310 * Report non-standard cacheability 311 */ 312 pat_index = 0; 313 if (level > 0) { 314 if (PTE_GET(pte, PT_PAGESIZE) && PTE_GET(pte, PT_PAT_LARGE)) 315 pat_index += 4; 316 } else { 317 if (PTE_GET(pte, PT_PAT_4K)) 318 pat_index += 4; 319 } 320 321 if (PTE_GET(pte, PT_NOCACHE)) 322 pat_index += 2; 323 324 if (PTE_GET(pte, PT_WRITETHRU)) 325 pat_index += 1; 326 327 if (pat_index != 0) 328 mdb_printf("%s", attr[pat_index]); 329 330 if (PTE_GET(pte, PT_VALID) == 0) 331 mdb_printf(" !VALID "); 332 333 mdb_printf("\n"); 334 return (DCMD_OK); 335 } 336 337 /* 338 * Print a PTE in more human friendly way. The PTE is assumed to be in 339 * a level 0 page table, unless -l specifies another level. 340 * 341 * The PTE value can be specified as the -p option, since on a 32 bit kernel 342 * with PAE running it's larger than a uintptr_t. 343 */ 344 /*ARGSUSED*/ 345 int 346 pte_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 347 { 348 int level = 0; 349 uint64_t pte = 0; 350 char *level_str = NULL; 351 char *pte_str = NULL; 352 353 /* 354 * The kernel has to at least have made it thru mmu_init() 355 */ 356 get_mmu(); 357 if (mmu.num_level == 0) 358 return (DCMD_ERR); 359 360 if (mdb_getopts(argc, argv, 361 'p', MDB_OPT_STR, &pte_str, 362 'l', MDB_OPT_STR, &level_str) != argc) 363 return (DCMD_USAGE); 364 365 /* 366 * parse the PTE to decode, if it's 0, we don't do anything 367 */ 368 if (pte_str != NULL) { 369 pte = mdb_strtoull(pte_str); 370 } else { 371 if ((flags & DCMD_ADDRSPEC) == 0) 372 return (DCMD_USAGE); 373 pte = addr; 374 } 375 if (pte == 0) 376 return (DCMD_OK); 377 378 /* 379 * parse the level if supplied 380 */ 381 if (level_str != NULL) { 382 level = mdb_strtoull(level_str); 383 if (level < 0 || level > mmu.max_level) 384 return (DCMD_ERR); 385 } 386 387 return (do_pte_dcmd(level, pte)); 388 } 389 390 static int 391 do_va2pfn(uintptr_t addr, struct as *asp, int print_level, physaddr_t *pap) 392 { 393 struct as as; 394 struct hat *hatp; 395 struct hat hat; 396 htable_t *ht; 397 htable_t htable; 398 uintptr_t base; 399 int h; 400 int level; 401 int found = 0; 402 x86pte_t pte; 403 x86pte_t buf; 404 x86pte32_t *pte32 = (x86pte32_t *)&buf; 405 physaddr_t paddr; 406 size_t len; 407 408 if (asp != NULL) { 409 if (mdb_vread(&as, sizeof (as), (uintptr_t)asp) == -1) { 410 mdb_warn("Couldn't read struct as\n"); 411 return (DCMD_ERR); 412 } 413 hatp = as.a_hat; 414 } else { 415 hatp = khat; 416 } 417 418 /* 419 * read the hat and its hash table 420 */ 421 if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) { 422 mdb_warn("Couldn't read struct hat\n"); 423 return (DCMD_ERR); 424 } 425 426 /* 427 * read the htable hashtable 428 */ 429 *pap = 0; 430 for (level = 0; level <= mmu.max_level; ++level) { 431 if (level == mmu.max_level) 432 base = 0; 433 else 434 base = addr & mmu.level_mask[level + 1]; 435 436 for (h = 0; h < hat.hat_num_hash; ++h) { 437 if (mdb_vread(&ht, sizeof (htable_t *), 438 (uintptr_t)(hat.hat_ht_hash + h)) == -1) { 439 mdb_warn("Couldn't read htable\n"); 440 return (DCMD_ERR); 441 } 442 for (; ht != NULL; ht = htable.ht_next) { 443 if (mdb_vread(&htable, sizeof (htable_t), 444 (uintptr_t)ht) == -1) { 445 mdb_warn("Couldn't read htable\n"); 446 return (DCMD_ERR); 447 } 448 if (htable.ht_vaddr != base || 449 htable.ht_level != level) 450 continue; 451 452 /* 453 * found - read the page table entry 454 */ 455 paddr = htable.ht_pfn << MMU_PAGESHIFT; 456 paddr += ((addr - base) >> 457 mmu.level_shift[level]) << 458 mmu.pte_size_shift; 459 len = mdb_pread(&buf, mmu.pte_size, paddr); 460 if (len != mmu.pte_size) 461 return (DCMD_ERR); 462 if (mmu.pte_size == sizeof (x86pte_t)) 463 pte = buf; 464 else 465 pte = *pte32; 466 467 if (!found) { 468 if (PTE_IS_LGPG(pte, level)) 469 paddr = pte & PT_PADDR_LGPG; 470 else 471 paddr = pte & PT_PADDR; 472 paddr += addr & mmu.level_offset[level]; 473 *pap = paddr; 474 found = 1; 475 } 476 if (print_level == 0) 477 continue; 478 mdb_printf("\tlevel=%d htable=%p pte=%llx\n", 479 level, ht, pte); 480 } 481 } 482 } 483 484 done: 485 if (!found) 486 return (DCMD_ERR); 487 return (DCMD_OK); 488 } 489 490 int 491 va2pfn_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 492 { 493 uintptr_t addrspace; 494 char *addrspace_str = NULL; 495 uint64_t physaddr; 496 int rc; 497 498 /* 499 * The kernel has to at least have made it thru mmu_init() 500 */ 501 get_mmu(); 502 if (mmu.num_level == 0) 503 return (DCMD_ERR); 504 505 if (mdb_getopts(argc, argv, 506 'a', MDB_OPT_STR, &addrspace_str) != argc) 507 return (DCMD_USAGE); 508 509 if ((flags & DCMD_ADDRSPEC) == 0) 510 return (DCMD_USAGE); 511 512 /* 513 * parse the address space 514 */ 515 if (addrspace_str != NULL) 516 addrspace = mdb_strtoull(addrspace_str); 517 else 518 addrspace = 0; 519 520 rc = do_va2pfn(addr, (struct as *)addrspace, 1, &physaddr); 521 522 if (rc == DCMD_OK) 523 mdb_printf("Virtual %p maps Physical %llx\n", addr, physaddr); 524 525 return (rc); 526 } 527 528 /* 529 * Report all hat's that either use PFN as a page table or that map the page. 530 */ 531 static int 532 do_report_maps(pfn_t pfn) 533 { 534 struct hat *hatp; 535 struct hat hat; 536 htable_t *ht; 537 htable_t htable; 538 uintptr_t base; 539 int h; 540 int level; 541 int entry; 542 x86pte_t pte; 543 x86pte_t buf; 544 x86pte32_t *pte32 = (x86pte32_t *)&buf; 545 physaddr_t paddr; 546 size_t len; 547 548 /* 549 * The hats are kept in a list with khat at the head. 550 */ 551 for (hatp = khat; hatp != NULL; hatp = hat.hat_next) { 552 /* 553 * read the hat and its hash table 554 */ 555 if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) { 556 mdb_warn("Couldn't read struct hat\n"); 557 return (DCMD_ERR); 558 } 559 560 /* 561 * read the htable hashtable 562 */ 563 paddr = 0; 564 for (h = 0; h < hat.hat_num_hash; ++h) { 565 if (mdb_vread(&ht, sizeof (htable_t *), 566 (uintptr_t)(hat.hat_ht_hash + h)) == -1) { 567 mdb_warn("Couldn't read htable\n"); 568 return (DCMD_ERR); 569 } 570 for (; ht != NULL; ht = htable.ht_next) { 571 if (mdb_vread(&htable, sizeof (htable_t), 572 (uintptr_t)ht) == -1) { 573 mdb_warn("Couldn't read htable\n"); 574 return (DCMD_ERR); 575 } 576 577 /* 578 * only report kernel addresses once 579 */ 580 if (hatp != khat && 581 htable.ht_vaddr >= kernelbase) 582 continue; 583 584 /* 585 * Is the PFN a pagetable itself? 586 */ 587 if (htable.ht_pfn == pfn) { 588 mdb_printf("Pagetable for " 589 "hat=%p htable=%p\n", hatp, ht); 590 continue; 591 } 592 593 /* 594 * otherwise, examine page mappings 595 */ 596 level = htable.ht_level; 597 if (level > mmu.max_page_level) 598 continue; 599 paddr = htable.ht_pfn << MMU_PAGESHIFT; 600 for (entry = 0; entry < htable.ht_num_ptes; 601 ++entry) { 602 603 base = htable.ht_vaddr + entry * 604 mmu.level_size[level]; 605 606 /* 607 * only report kernel addresses once 608 */ 609 if (hatp != khat && 610 base >= kernelbase) 611 continue; 612 613 len = mdb_pread(&buf, mmu.pte_size, 614 paddr + entry * mmu.pte_size); 615 if (len != mmu.pte_size) 616 return (DCMD_ERR); 617 if (mmu.pte_size == sizeof (x86pte_t)) 618 pte = buf; 619 else 620 pte = *pte32; 621 622 if ((pte & PT_VALID) == 0) 623 continue; 624 if (level == 0 || !(pte & PT_PAGESIZE)) 625 pte &= PT_PADDR; 626 else 627 pte &= PT_PADDR_LGPG; 628 if ((pte >> MMU_PAGESHIFT) != pfn) 629 continue; 630 mdb_printf("hat=%p maps addr=%p\n", 631 hatp, (caddr_t)base); 632 } 633 } 634 } 635 } 636 637 done: 638 return (DCMD_OK); 639 } 640 641 /* 642 * given a PFN as its address argument, prints out the uses of it 643 */ 644 /*ARGSUSED*/ 645 int 646 report_maps_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 647 { 648 /* 649 * The kernel has to at least have made it thru mmu_init() 650 */ 651 get_mmu(); 652 if (mmu.num_level == 0) 653 return (DCMD_ERR); 654 655 if ((flags & DCMD_ADDRSPEC) == 0) 656 return (DCMD_USAGE); 657 658 return (do_report_maps((pfn_t)addr)); 659 } 660 661 /* 662 * Dump the page table at the given PFN 663 */ 664 static int 665 do_ptable_dcmd(pfn_t pfn) 666 { 667 struct hat *hatp; 668 struct hat hat; 669 htable_t *ht; 670 htable_t htable; 671 uintptr_t base; 672 int h; 673 int level; 674 int entry; 675 uintptr_t pagesize; 676 x86pte_t pte; 677 x86pte_t buf; 678 x86pte32_t *pte32 = (x86pte32_t *)&buf; 679 physaddr_t paddr; 680 size_t len; 681 682 /* 683 * The hats are kept in a list with khat at the head. 684 */ 685 for (hatp = khat; hatp != NULL; hatp = hat.hat_next) { 686 /* 687 * read the hat and its hash table 688 */ 689 if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) { 690 mdb_warn("Couldn't read struct hat\n"); 691 return (DCMD_ERR); 692 } 693 694 /* 695 * read the htable hashtable 696 */ 697 paddr = 0; 698 for (h = 0; h < hat.hat_num_hash; ++h) { 699 if (mdb_vread(&ht, sizeof (htable_t *), 700 (uintptr_t)(hat.hat_ht_hash + h)) == -1) { 701 mdb_warn("Couldn't read htable\n"); 702 return (DCMD_ERR); 703 } 704 for (; ht != NULL; ht = htable.ht_next) { 705 if (mdb_vread(&htable, sizeof (htable_t), 706 (uintptr_t)ht) == -1) { 707 mdb_warn("Couldn't read htable\n"); 708 return (DCMD_ERR); 709 } 710 711 /* 712 * Is this the PFN for this htable 713 */ 714 if (htable.ht_pfn == pfn) 715 goto found_it; 716 } 717 } 718 } 719 720 found_it: 721 if (htable.ht_pfn == pfn) { 722 mdb_printf("htable=%p\n", ht); 723 level = htable.ht_level; 724 base = htable.ht_vaddr; 725 pagesize = mmu.level_size[level]; 726 } else { 727 mdb_printf("Unknown pagetable - assuming level/addr 0"); 728 level = 0; /* assume level == 0 for PFN */ 729 base = 0; 730 pagesize = MMU_PAGESIZE; 731 } 732 733 paddr = pfn << MMU_PAGESHIFT; 734 for (entry = 0; entry < mmu.ptes_per_table; ++entry) { 735 len = mdb_pread(&buf, mmu.pte_size, 736 paddr + entry * mmu.pte_size); 737 if (len != mmu.pte_size) 738 return (DCMD_ERR); 739 if (mmu.pte_size == sizeof (x86pte_t)) 740 pte = buf; 741 else 742 pte = *pte32; 743 744 if (pte == 0) 745 continue; 746 747 mdb_printf("[%3d] va=%p ", entry, base + entry * pagesize); 748 do_pte_dcmd(level, pte); 749 } 750 751 done: 752 return (DCMD_OK); 753 } 754 755 /* 756 * given a PFN as its address argument, prints out the uses of it 757 */ 758 /*ARGSUSED*/ 759 int 760 ptable_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 761 { 762 /* 763 * The kernel has to at least have made it thru mmu_init() 764 */ 765 get_mmu(); 766 if (mmu.num_level == 0) 767 return (DCMD_ERR); 768 769 if ((flags & DCMD_ADDRSPEC) == 0) 770 return (DCMD_USAGE); 771 772 return (do_ptable_dcmd((pfn_t)addr)); 773 } 774