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 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2018 Joyent, Inc.
  26  */
  27 
  28 /*
  29  * This part of the file contains the mdb support for dcmds:
  30  *      ::memseg_list
  31  * and walkers for:
  32  *      memseg - a memseg list walker for ::memseg_list
  33  *
  34  */
  35 
  36 #include <sys/types.h>
  37 #include <sys/machparam.h>
  38 #include <sys/controlregs.h>
  39 #include <sys/mach_mmu.h>
  40 #ifdef __xpv
  41 #include <sys/hypervisor.h>
  42 #endif
  43 #include <vm/as.h>
  44 
  45 #include <mdb/mdb_modapi.h>
  46 #include <mdb/mdb_target.h>
  47 
  48 #include <vm/page.h>
  49 #include <vm/hat_i86.h>
  50 
  51 #define VA_SIGN_BIT (1UL << 47)
  52 #define VA_SIGN_EXTEND(va) (((va) ^ VA_SIGN_BIT) - VA_SIGN_BIT)
  53 
  54 struct pfn2pp {
  55         pfn_t pfn;
  56         page_t *pp;
  57 };
  58 
  59 static int do_va2pa(uintptr_t, struct as *, int, physaddr_t *, pfn_t *);
  60 static void init_mmu(void);
  61 
  62 int
  63 platform_vtop(uintptr_t addr, struct as *asp, physaddr_t *pap)
  64 {
  65         if (asp == NULL)
  66                 return (DCMD_ERR);
  67 
  68         init_mmu();
  69 
  70         if (mmu.num_level == 0)
  71                 return (DCMD_ERR);
  72 
  73         return (do_va2pa(addr, asp, 0, pap, NULL));
  74 }
  75 
  76 /*
  77  * ::memseg_list dcmd and walker to implement it.
  78  */
  79 /*ARGSUSED*/
  80 int
  81 memseg_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
  82 {
  83         struct memseg ms;
  84 
  85         if (!(flags & DCMD_ADDRSPEC)) {
  86                 if (mdb_pwalk_dcmd("memseg", "memseg_list",
  87                     0, NULL, 0) == -1) {
  88                         mdb_warn("can't walk memseg");
  89                         return (DCMD_ERR);
  90                 }
  91                 return (DCMD_OK);
  92         }
  93 
  94         if (DCMD_HDRSPEC(flags))
  95                 mdb_printf("%<u>%?s %?s %?s %?s %?s%</u>\n", "ADDR",
  96                     "PAGES", "EPAGES", "BASE", "END");
  97 
  98         if (mdb_vread(&ms, sizeof (struct memseg), addr) == -1) {
  99                 mdb_warn("can't read memseg at %#lx", addr);
 100                 return (DCMD_ERR);
 101         }
 102 
 103         mdb_printf("%0?lx %0?lx %0?lx %0?lx %0?lx\n", addr,
 104             ms.pages, ms.epages, ms.pages_base, ms.pages_end);
 105 
 106         return (DCMD_OK);
 107 }
 108 
 109 /*
 110  * walk the memseg structures
 111  */
 112 int
 113 memseg_walk_init(mdb_walk_state_t *wsp)
 114 {
 115         if (wsp->walk_addr != NULL) {
 116                 mdb_warn("memseg only supports global walks\n");
 117                 return (WALK_ERR);
 118         }
 119 
 120         if (mdb_readvar(&wsp->walk_addr, "memsegs") == -1) {
 121                 mdb_warn("symbol 'memsegs' not found");
 122                 return (WALK_ERR);
 123         }
 124 
 125         wsp->walk_data = mdb_alloc(sizeof (struct memseg), UM_SLEEP);
 126         return (WALK_NEXT);
 127 
 128 }
 129 
 130 int
 131 memseg_walk_step(mdb_walk_state_t *wsp)
 132 {
 133         int status;
 134 
 135         if (wsp->walk_addr == 0) {
 136                 return (WALK_DONE);
 137         }
 138 
 139         if (mdb_vread(wsp->walk_data, sizeof (struct memseg),
 140             wsp->walk_addr) == -1) {
 141                 mdb_warn("failed to read struct memseg at %p", wsp->walk_addr);
 142                 return (WALK_DONE);
 143         }
 144 
 145         status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
 146             wsp->walk_cbdata);
 147 
 148         wsp->walk_addr = (uintptr_t)(((struct memseg *)wsp->walk_data)->next);
 149 
 150         return (status);
 151 }
 152 
 153 void
 154 memseg_walk_fini(mdb_walk_state_t *wsp)
 155 {
 156         mdb_free(wsp->walk_data, sizeof (struct memseg));
 157 }
 158 
 159 /*
 160  * Now HAT related dcmds.
 161  */
 162 
 163 static struct hat *khat;                /* value of kas.a_hat */
 164 struct hat_mmu_info mmu;
 165 uintptr_t kernelbase;
 166 
 167 /*
 168  * stuff for i86xpv images
 169  */
 170 static int is_xpv;
 171 static uintptr_t mfn_list_addr; /* kernel MFN list address */
 172 uintptr_t xen_virt_start; /* address of mfn_to_pfn[] table */
 173 ulong_t mfn_count;      /* number of pfn's in the MFN list */
 174 pfn_t *mfn_list;        /* local MFN list copy */
 175 
 176 /*
 177  * read mmu parameters from kernel
 178  */
 179 static void
 180 init_mmu(void)
 181 {
 182         struct as kas;
 183 
 184         if (mmu.num_level != 0)
 185                 return;
 186 
 187         if (mdb_readsym(&mmu, sizeof (mmu), "mmu") == -1)
 188                 mdb_warn("Can't use HAT information before mmu_init()\n");
 189         if (mdb_readsym(&kas, sizeof (kas), "kas") == -1)
 190                 mdb_warn("Couldn't find kas - kernel's struct as\n");
 191         if (mdb_readsym(&kernelbase, sizeof (kernelbase), "kernelbase") == -1)
 192                 mdb_warn("Couldn't find kernelbase\n");
 193         khat = kas.a_hat;
 194 
 195         /*
 196          * Is this a paravirtualized domain image?
 197          */
 198         if (mdb_readsym(&mfn_list_addr, sizeof (mfn_list_addr),
 199             "mfn_list") == -1 ||
 200             mdb_readsym(&xen_virt_start, sizeof (xen_virt_start),
 201             "xen_virt_start") == -1 ||
 202             mdb_readsym(&mfn_count, sizeof (mfn_count), "mfn_count") == -1) {
 203                 mfn_list_addr = NULL;
 204         }
 205 
 206         is_xpv = mfn_list_addr != NULL;
 207 
 208 #ifndef _KMDB
 209         /*
 210          * recreate the local mfn_list
 211          */
 212         if (is_xpv) {
 213                 size_t sz = mfn_count * sizeof (pfn_t);
 214                 mfn_list = mdb_zalloc(sz, UM_SLEEP);
 215 
 216                 if (mdb_vread(mfn_list, sz, (uintptr_t)mfn_list_addr) == -1) {
 217                         mdb_warn("Failed to read MFN list\n");
 218                         mdb_free(mfn_list, sz);
 219                         mfn_list = NULL;
 220                 }
 221         }
 222 #endif
 223 }
 224 
 225 void
 226 free_mmu(void)
 227 {
 228 #ifdef __xpv
 229         if (mfn_list != NULL)
 230                 mdb_free(mfn_list, mfn_count * sizeof (mfn_t));
 231 #endif
 232 }
 233 
 234 #ifdef __xpv
 235 
 236 #ifdef _KMDB
 237 
 238 /*
 239  * Convert between MFNs and PFNs.  Since we're in kmdb we can go directly
 240  * through the machine to phys mapping and the MFN list.
 241  */
 242 
 243 pfn_t
 244 mdb_mfn_to_pfn(mfn_t mfn)
 245 {
 246         pfn_t pfn;
 247         mfn_t tmp;
 248         pfn_t *pfn_list;
 249 
 250         if (mfn_list_addr == NULL)
 251                 return (-(pfn_t)1);
 252 
 253         pfn_list = (pfn_t *)xen_virt_start;
 254         if (mdb_vread(&pfn, sizeof (pfn), (uintptr_t)(pfn_list + mfn)) == -1)
 255                 return (-(pfn_t)1);
 256 
 257         if (mdb_vread(&tmp, sizeof (tmp),
 258             (uintptr_t)(mfn_list_addr + (pfn * sizeof (mfn_t)))) == -1)
 259                 return (-(pfn_t)1);
 260 
 261         if (pfn >= mfn_count || tmp != mfn)
 262                 return (-(pfn_t)1);
 263 
 264         return (pfn);
 265 }
 266 
 267 mfn_t
 268 mdb_pfn_to_mfn(pfn_t pfn)
 269 {
 270         mfn_t mfn;
 271 
 272         init_mmu();
 273 
 274         if (mfn_list_addr == NULL || pfn >= mfn_count)
 275                 return (-(mfn_t)1);
 276 
 277         if (mdb_vread(&mfn, sizeof (mfn),
 278             (uintptr_t)(mfn_list_addr + (pfn * sizeof (mfn_t)))) == -1)
 279                 return (-(mfn_t)1);
 280 
 281         return (mfn);
 282 }
 283 
 284 #else /* _KMDB */
 285 
 286 /*
 287  * Convert between MFNs and PFNs.  Since a crash dump doesn't include the
 288  * MFN->PFN translation table (it's part of the hypervisor, not our image)
 289  * we do the MFN->PFN translation by searching the PFN->MFN (mfn_list)
 290  * table, if it's there.
 291  */
 292 
 293 pfn_t
 294 mdb_mfn_to_pfn(mfn_t mfn)
 295 {
 296         pfn_t pfn;
 297 
 298         init_mmu();
 299 
 300         if (mfn_list == NULL)
 301                 return (-(pfn_t)1);
 302 
 303         for (pfn = 0; pfn < mfn_count; ++pfn) {
 304                 if (mfn_list[pfn] != mfn)
 305                         continue;
 306                 return (pfn);
 307         }
 308 
 309         return (-(pfn_t)1);
 310 }
 311 
 312 mfn_t
 313 mdb_pfn_to_mfn(pfn_t pfn)
 314 {
 315         init_mmu();
 316 
 317         if (mfn_list == NULL || pfn >= mfn_count)
 318                 return (-(mfn_t)1);
 319 
 320         return (mfn_list[pfn]);
 321 }
 322 
 323 #endif /* _KMDB */
 324 
 325 static paddr_t
 326 mdb_ma_to_pa(uint64_t ma)
 327 {
 328         pfn_t pfn = mdb_mfn_to_pfn(mmu_btop(ma));
 329         if (pfn == -(pfn_t)1)
 330                 return (-(paddr_t)1);
 331 
 332         return (mmu_ptob((paddr_t)pfn) | (ma & (MMU_PAGESIZE - 1)));
 333 }
 334 
 335 #else /* __xpv */
 336 
 337 #define mdb_ma_to_pa(ma) (ma)
 338 #define mdb_mfn_to_pfn(mfn) (mfn)
 339 #define mdb_pfn_to_mfn(pfn) (pfn)
 340 
 341 #endif /* __xpv */
 342 
 343 /*
 344  * ::mfntopfn dcmd translates hypervisor machine page number
 345  * to physical page number
 346  */
 347 /*ARGSUSED*/
 348 int
 349 mfntopfn_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 350 {
 351         pfn_t pfn;
 352 
 353         if ((flags & DCMD_ADDRSPEC) == 0) {
 354                 mdb_warn("MFN missing\n");
 355                 return (DCMD_USAGE);
 356         }
 357 
 358         if ((pfn = mdb_mfn_to_pfn((pfn_t)addr)) == -(pfn_t)1) {
 359                 mdb_warn("Invalid mfn %lr\n", (pfn_t)addr);
 360                 return (DCMD_ERR);
 361         }
 362 
 363         mdb_printf("%lr\n", pfn);
 364 
 365         return (DCMD_OK);
 366 }
 367 
 368 /*
 369  * ::pfntomfn dcmd translates physical page number to
 370  * hypervisor machine page number
 371  */
 372 /*ARGSUSED*/
 373 int
 374 pfntomfn_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 375 {
 376         pfn_t mfn;
 377 
 378         if ((flags & DCMD_ADDRSPEC) == 0) {
 379                 mdb_warn("PFN missing\n");
 380                 return (DCMD_USAGE);
 381         }
 382 
 383         if ((mfn = mdb_pfn_to_mfn((pfn_t)addr)) == -(pfn_t)1) {
 384                 mdb_warn("Invalid pfn %lr\n", (pfn_t)addr);
 385                 return (DCMD_ABORT);
 386         }
 387 
 388         mdb_printf("%lr\n", mfn);
 389 
 390         if (flags & DCMD_LOOP)
 391                 mdb_set_dot(addr + 1);
 392         return (DCMD_OK);
 393 }
 394 
 395 static pfn_t
 396 pte2mfn(x86pte_t pte, uint_t level)
 397 {
 398         pfn_t mfn;
 399         if (level > 0 && (pte & PT_PAGESIZE))
 400                 mfn = mmu_btop(pte & PT_PADDR_LGPG);
 401         else
 402                 mfn = mmu_btop(pte & PT_PADDR);
 403         return (mfn);
 404 }
 405 
 406 static int
 407 do_pte_dcmd(int level, uint64_t pte)
 408 {
 409         static char *attr[] = {
 410             "wrback", "wrthru", "uncached", "uncached",
 411             "wrback", "wrthru", "wrcombine", "uncached"};
 412         int pat_index = 0;
 413         pfn_t mfn;
 414 
 415         mdb_printf("pte=0x%llr: ", pte);
 416 
 417         mfn = pte2mfn(pte, level);
 418         mdb_printf("%s=0x%lr ", is_xpv ? "mfn" : "pfn", mfn);
 419 
 420         if (PTE_GET(pte, mmu.pt_nx))
 421                 mdb_printf("noexec ");
 422 
 423         if (PTE_GET(pte, PT_NOCONSIST))
 424                 mdb_printf("noconsist ");
 425 
 426         if (PTE_GET(pte, PT_NOSYNC))
 427                 mdb_printf("nosync ");
 428 
 429         if (PTE_GET(pte, mmu.pt_global))
 430                 mdb_printf("global ");
 431 
 432         if (level > 0 && PTE_GET(pte, PT_PAGESIZE))
 433                 mdb_printf("largepage ");
 434 
 435         if (level > 0 && PTE_GET(pte, PT_MOD))
 436                 mdb_printf("mod ");
 437 
 438         if (level > 0 && PTE_GET(pte, PT_REF))
 439                 mdb_printf("ref ");
 440 
 441         if (PTE_GET(pte, PT_USER))
 442                 mdb_printf("user ");
 443 
 444         if (PTE_GET(pte, PT_WRITABLE))
 445                 mdb_printf("write ");
 446 
 447         /*
 448          * Report non-standard cacheability
 449          */
 450         pat_index = 0;
 451         if (level > 0) {
 452                 if (PTE_GET(pte, PT_PAGESIZE) && PTE_GET(pte, PT_PAT_LARGE))
 453                         pat_index += 4;
 454         } else {
 455                 if (PTE_GET(pte, PT_PAT_4K))
 456                         pat_index += 4;
 457         }
 458 
 459         if (PTE_GET(pte, PT_NOCACHE))
 460                 pat_index += 2;
 461 
 462         if (PTE_GET(pte, PT_WRITETHRU))
 463                 pat_index += 1;
 464 
 465         if (pat_index != 0)
 466                 mdb_printf("%s", attr[pat_index]);
 467 
 468         if (PTE_GET(pte, PT_VALID) == 0)
 469                 mdb_printf(" !VALID ");
 470 
 471         mdb_printf("\n");
 472         return (DCMD_OK);
 473 }
 474 
 475 /*
 476  * Print a PTE in more human friendly way. The PTE is assumed to be in
 477  * a level 0 page table, unless -l specifies another level.
 478  */
 479 /*ARGSUSED*/
 480 int
 481 pte_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 482 {
 483         uint64_t level = 0;
 484 
 485         init_mmu();
 486 
 487         if (mmu.num_level == 0)
 488                 return (DCMD_ERR);
 489 
 490         if ((flags & DCMD_ADDRSPEC) == 0)
 491                 return (DCMD_USAGE);
 492 
 493         if (mdb_getopts(argc, argv,
 494             'l', MDB_OPT_UINT64, &level) != argc)
 495                 return (DCMD_USAGE);
 496 
 497         if (level > mmu.max_level) {
 498                 mdb_warn("invalid level %lu\n", level);
 499                 return (DCMD_ERR);
 500         }
 501 
 502         if (addr == 0)
 503                 return (DCMD_OK);
 504 
 505         return (do_pte_dcmd((int)level, addr));
 506 }
 507 
 508 static size_t
 509 va2entry(htable_t *htable, uintptr_t addr)
 510 {
 511         size_t entry = (addr - htable->ht_vaddr);
 512 
 513         entry >>= mmu.level_shift[htable->ht_level];
 514         return (entry & HTABLE_NUM_PTES(htable) - 1);
 515 }
 516 
 517 static x86pte_t
 518 get_pte(hat_t *hat, htable_t *htable, uintptr_t addr)
 519 {
 520         x86pte_t buf;
 521 
 522         if (htable->ht_flags & HTABLE_COPIED) {
 523                 uintptr_t ptr = (uintptr_t)hat->hat_copied_ptes;
 524                 ptr += va2entry(htable, addr) << mmu.pte_size_shift;
 525                 return (*(x86pte_t *)ptr);
 526         }
 527 
 528         paddr_t paddr = mmu_ptob((paddr_t)htable->ht_pfn);
 529         paddr += va2entry(htable, addr) << mmu.pte_size_shift;
 530 
 531         if ((mdb_pread(&buf, mmu.pte_size, paddr)) == mmu.pte_size)
 532                 return (buf);
 533 
 534         return (0);
 535 }
 536 
 537 static int
 538 do_va2pa(uintptr_t addr, struct as *asp, int print_level, physaddr_t *pap,
 539     pfn_t *mfnp)
 540 {
 541         struct as as;
 542         struct hat *hatp;
 543         struct hat hat;
 544         htable_t *ht;
 545         htable_t htable;
 546         uintptr_t base;
 547         int h;
 548         int level;
 549         int found = 0;
 550         x86pte_t pte;
 551         physaddr_t paddr;
 552 
 553         if (asp != NULL) {
 554                 if (mdb_vread(&as, sizeof (as), (uintptr_t)asp) == -1) {
 555                         mdb_warn("Couldn't read struct as\n");
 556                         return (DCMD_ERR);
 557                 }
 558                 hatp = as.a_hat;
 559         } else {
 560                 hatp = khat;
 561         }
 562 
 563         /*
 564          * read the hat and its hash table
 565          */
 566         if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) {
 567                 mdb_warn("Couldn't read struct hat\n");
 568                 return (DCMD_ERR);
 569         }
 570 
 571         /*
 572          * read the htable hashtable
 573          */
 574         for (level = 0; level <= mmu.max_level; ++level) {
 575                 if (level == TOP_LEVEL(&hat))
 576                         base = 0;
 577                 else
 578                         base = addr & mmu.level_mask[level + 1];
 579 
 580                 for (h = 0; h < hat.hat_num_hash; ++h) {
 581                         if (mdb_vread(&ht, sizeof (htable_t *),
 582                             (uintptr_t)(hat.hat_ht_hash + h)) == -1) {
 583                                 mdb_warn("Couldn't read htable\n");
 584                                 return (DCMD_ERR);
 585                         }
 586                         for (; ht != NULL; ht = htable.ht_next) {
 587                                 if (mdb_vread(&htable, sizeof (htable_t),
 588                                     (uintptr_t)ht) == -1) {
 589                                         mdb_warn("Couldn't read htable\n");
 590                                         return (DCMD_ERR);
 591                                 }
 592 
 593                                 if (htable.ht_vaddr != base ||
 594                                     htable.ht_level != level)
 595                                         continue;
 596 
 597                                 pte = get_pte(&hat, &htable, addr);
 598 
 599                                 if (print_level) {
 600                                         mdb_printf("\tlevel=%d htable=0x%p "
 601                                             "pte=0x%llr\n", level, ht, pte);
 602                                 }
 603 
 604                                 if (!PTE_ISVALID(pte)) {
 605                                         mdb_printf("Address %p is unmapped.\n",
 606                                             addr);
 607                                         return (DCMD_ERR);
 608                                 }
 609 
 610                                 if (found)
 611                                         continue;
 612 
 613                                 if (PTE_IS_LGPG(pte, level))
 614                                         paddr = mdb_ma_to_pa(pte &
 615                                             PT_PADDR_LGPG);
 616                                 else
 617                                         paddr = mdb_ma_to_pa(pte & PT_PADDR);
 618                                 paddr += addr & mmu.level_offset[level];
 619                                 if (pap != NULL)
 620                                         *pap = paddr;
 621                                 if (mfnp != NULL)
 622                                         *mfnp = pte2mfn(pte, level);
 623                                 found = 1;
 624                         }
 625                 }
 626         }
 627 
 628 done:
 629         if (!found)
 630                 return (DCMD_ERR);
 631         return (DCMD_OK);
 632 }
 633 
 634 int
 635 va2pfn_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 636 {
 637         uintptr_t addrspace;
 638         char *addrspace_str = NULL;
 639         int piped = flags & DCMD_PIPE_OUT;
 640         pfn_t pfn;
 641         pfn_t mfn;
 642         int rc;
 643 
 644         init_mmu();
 645 
 646         if (mmu.num_level == 0)
 647                 return (DCMD_ERR);
 648 
 649         if (mdb_getopts(argc, argv,
 650             'a', MDB_OPT_STR, &addrspace_str) != argc)
 651                 return (DCMD_USAGE);
 652 
 653         if ((flags & DCMD_ADDRSPEC) == 0)
 654                 return (DCMD_USAGE);
 655 
 656         /*
 657          * parse the address space
 658          */
 659         if (addrspace_str != NULL)
 660                 addrspace = mdb_strtoull(addrspace_str);
 661         else
 662                 addrspace = 0;
 663 
 664         rc = do_va2pa(addr, (struct as *)addrspace, !piped, NULL, &mfn);
 665 
 666         if (rc != DCMD_OK)
 667                 return (rc);
 668 
 669         if ((pfn = mdb_mfn_to_pfn(mfn)) == -(pfn_t)1) {
 670                 mdb_warn("Invalid mfn %lr\n", mfn);
 671                 return (DCMD_ERR);
 672         }
 673 
 674         if (piped) {
 675                 mdb_printf("0x%lr\n", pfn);
 676                 return (DCMD_OK);
 677         }
 678 
 679         mdb_printf("Virtual address 0x%p maps pfn 0x%lr", addr, pfn);
 680 
 681         if (is_xpv)
 682                 mdb_printf(" (mfn 0x%lr)", mfn);
 683 
 684         mdb_printf("\n");
 685 
 686         return (DCMD_OK);
 687 }
 688 
 689 /*
 690  * Report all hat's that either use PFN as a page table or that map the page.
 691  */
 692 static int
 693 do_report_maps(pfn_t pfn)
 694 {
 695         struct hat *hatp;
 696         struct hat hat;
 697         htable_t *ht;
 698         htable_t htable;
 699         uintptr_t base;
 700         int h;
 701         int level;
 702         int entry;
 703         x86pte_t pte;
 704         physaddr_t paddr;
 705         size_t len;
 706 
 707         /*
 708          * The hats are kept in a list with khat at the head.
 709          */
 710         for (hatp = khat; hatp != NULL; hatp = hat.hat_next) {
 711                 /*
 712                  * read the hat and its hash table
 713                  */
 714                 if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) {
 715                         mdb_warn("Couldn't read struct hat\n");
 716                         return (DCMD_ERR);
 717                 }
 718 
 719                 /*
 720                  * read the htable hashtable
 721                  */
 722                 paddr = 0;
 723                 for (h = 0; h < hat.hat_num_hash; ++h) {
 724                         if (mdb_vread(&ht, sizeof (htable_t *),
 725                             (uintptr_t)(hat.hat_ht_hash + h)) == -1) {
 726                                 mdb_warn("Couldn't read htable\n");
 727                                 return (DCMD_ERR);
 728                         }
 729                         for (; ht != NULL; ht = htable.ht_next) {
 730                                 if (mdb_vread(&htable, sizeof (htable_t),
 731                                     (uintptr_t)ht) == -1) {
 732                                         mdb_warn("Couldn't read htable\n");
 733                                         return (DCMD_ERR);
 734                                 }
 735 
 736                                 /*
 737                                  * only report kernel addresses once
 738                                  */
 739                                 if (hatp != khat &&
 740                                     htable.ht_vaddr >= kernelbase)
 741                                         continue;
 742 
 743                                 /*
 744                                  * Is the PFN a pagetable itself?
 745                                  */
 746                                 if (htable.ht_pfn == pfn) {
 747                                         mdb_printf("Pagetable for "
 748                                             "hat=%p htable=%p\n", hatp, ht);
 749                                         continue;
 750                                 }
 751 
 752                                 /*
 753                                  * otherwise, examine page mappings
 754                                  */
 755                                 level = htable.ht_level;
 756                                 if (level > mmu.max_page_level)
 757                                         continue;
 758                                 paddr = mmu_ptob((physaddr_t)htable.ht_pfn);
 759                                 for (entry = 0;
 760                                     entry < HTABLE_NUM_PTES(&htable);
 761                                     ++entry) {
 762 
 763                                         base = htable.ht_vaddr + entry *
 764                                             mmu.level_size[level];
 765 
 766                                         /*
 767                                          * only report kernel addresses once
 768                                          */
 769                                         if (hatp != khat &&
 770                                             base >= kernelbase)
 771                                                 continue;
 772 
 773                                         len = mdb_pread(&pte, mmu.pte_size,
 774                                             paddr + entry * mmu.pte_size);
 775                                         if (len != mmu.pte_size)
 776                                                 return (DCMD_ERR);
 777 
 778                                         if ((pte & PT_VALID) == 0)
 779                                                 continue;
 780                                         if (level == 0 || !(pte & PT_PAGESIZE))
 781                                                 pte &= PT_PADDR;
 782                                         else
 783                                                 pte &= PT_PADDR_LGPG;
 784                                         if (mmu_btop(mdb_ma_to_pa(pte)) != pfn)
 785                                                 continue;
 786                                         mdb_printf("hat=%p maps addr=%p\n",
 787                                             hatp, (caddr_t)base);
 788                                 }
 789                         }
 790                 }
 791         }
 792 
 793 done:
 794         return (DCMD_OK);
 795 }
 796 
 797 /*
 798  * given a PFN as its address argument, prints out the uses of it
 799  */
 800 /*ARGSUSED*/
 801 int
 802 report_maps_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 803 {
 804         pfn_t pfn;
 805         uint_t mflag = 0;
 806 
 807         init_mmu();
 808 
 809         if (mmu.num_level == 0)
 810                 return (DCMD_ERR);
 811 
 812         if ((flags & DCMD_ADDRSPEC) == 0)
 813                 return (DCMD_USAGE);
 814 
 815         if (mdb_getopts(argc, argv,
 816             'm', MDB_OPT_SETBITS, TRUE, &mflag, NULL) != argc)
 817                 return (DCMD_USAGE);
 818 
 819         pfn = (pfn_t)addr;
 820         if (mflag)
 821                 pfn = mdb_mfn_to_pfn(pfn);
 822 
 823         return (do_report_maps(pfn));
 824 }
 825 
 826 static int
 827 do_ptable_dcmd(pfn_t pfn, uint64_t level)
 828 {
 829         struct hat *hatp;
 830         struct hat hat;
 831         htable_t *ht;
 832         htable_t htable;
 833         uintptr_t base;
 834         int h;
 835         int entry;
 836         uintptr_t pagesize;
 837         x86pte_t pte;
 838         x86pte_t buf;
 839         physaddr_t paddr;
 840         size_t len;
 841 
 842         /*
 843          * The hats are kept in a list with khat at the head.
 844          */
 845         for (hatp = khat; hatp != NULL; hatp = hat.hat_next) {
 846                 /*
 847                  * read the hat and its hash table
 848                  */
 849                 if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) {
 850                         mdb_warn("Couldn't read struct hat\n");
 851                         return (DCMD_ERR);
 852                 }
 853 
 854                 /*
 855                  * read the htable hashtable
 856                  */
 857                 paddr = 0;
 858                 for (h = 0; h < hat.hat_num_hash; ++h) {
 859                         if (mdb_vread(&ht, sizeof (htable_t *),
 860                             (uintptr_t)(hat.hat_ht_hash + h)) == -1) {
 861                                 mdb_warn("Couldn't read htable\n");
 862                                 return (DCMD_ERR);
 863                         }
 864                         for (; ht != NULL; ht = htable.ht_next) {
 865                                 if (mdb_vread(&htable, sizeof (htable_t),
 866                                     (uintptr_t)ht) == -1) {
 867                                         mdb_warn("Couldn't read htable\n");
 868                                         return (DCMD_ERR);
 869                                 }
 870 
 871                                 /*
 872                                  * Is this the PFN for this htable
 873                                  */
 874                                 if (htable.ht_pfn == pfn)
 875                                         goto found_it;
 876                         }
 877                 }
 878         }
 879 
 880 found_it:
 881         if (htable.ht_pfn == pfn) {
 882                 mdb_printf("htable=%p\n", ht);
 883                 if (level == (uint64_t)-1) {
 884                         level = htable.ht_level;
 885                 } else if (htable.ht_level != level) {
 886                         mdb_warn("htable has level %d but forcing level %lu\n",
 887                             htable.ht_level, level);
 888                 }
 889                 base = htable.ht_vaddr;
 890                 pagesize = mmu.level_size[level];
 891         } else {
 892                 if (level == (uint64_t)-1)
 893                         level = 0;
 894                 mdb_warn("couldn't find matching htable, using level=%lu, "
 895                     "base address=0x0\n", level);
 896                 base = 0;
 897                 pagesize = mmu.level_size[level];
 898         }
 899 
 900         paddr = mmu_ptob((physaddr_t)pfn);
 901         for (entry = 0; entry < mmu.ptes_per_table; ++entry) {
 902                 len = mdb_pread(&buf, mmu.pte_size,
 903                     paddr + entry * mmu.pte_size);
 904                 if (len != mmu.pte_size)
 905                         return (DCMD_ERR);
 906                         pte = buf;
 907 
 908                 if (pte == 0)
 909                         continue;
 910 
 911                 mdb_printf("[%3d] va=0x%p ", entry,
 912                     VA_SIGN_EXTEND(base + entry * pagesize));
 913                 do_pte_dcmd(level, pte);
 914         }
 915 
 916 done:
 917         return (DCMD_OK);
 918 }
 919 
 920 /*
 921  * Dump the page table at the given PFN
 922  */
 923 /*ARGSUSED*/
 924 int
 925 ptable_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 926 {
 927         pfn_t pfn;
 928         uint_t mflag = 0;
 929         uint64_t level = (uint64_t)-1;
 930 
 931         init_mmu();
 932 
 933         if (mmu.num_level == 0)
 934                 return (DCMD_ERR);
 935 
 936         if ((flags & DCMD_ADDRSPEC) == 0)
 937                 return (DCMD_USAGE);
 938 
 939         if (mdb_getopts(argc, argv,
 940             'm', MDB_OPT_SETBITS, TRUE, &mflag,
 941             'l', MDB_OPT_UINT64, &level, NULL) != argc)
 942                 return (DCMD_USAGE);
 943 
 944         if (level != (uint64_t)-1 && level > mmu.max_level) {
 945                 mdb_warn("invalid level %lu\n", level);
 946                 return (DCMD_ERR);
 947         }
 948 
 949         pfn = (pfn_t)addr;
 950         if (mflag)
 951                 pfn = mdb_mfn_to_pfn(pfn);
 952 
 953         return (do_ptable_dcmd(pfn, level));
 954 }
 955 
 956 static int
 957 do_htables_dcmd(hat_t *hatp)
 958 {
 959         struct hat hat;
 960         htable_t *ht;
 961         htable_t htable;
 962         int h;
 963 
 964         /*
 965          * read the hat and its hash table
 966          */
 967         if (mdb_vread(&hat, sizeof (hat), (uintptr_t)hatp) == -1) {
 968                 mdb_warn("Couldn't read struct hat\n");
 969                 return (DCMD_ERR);
 970         }
 971 
 972         /*
 973          * read the htable hashtable
 974          */
 975         for (h = 0; h < hat.hat_num_hash; ++h) {
 976                 if (mdb_vread(&ht, sizeof (htable_t *),
 977                     (uintptr_t)(hat.hat_ht_hash + h)) == -1) {
 978                         mdb_warn("Couldn't read htable ptr\\n");
 979                         return (DCMD_ERR);
 980                 }
 981                 for (; ht != NULL; ht = htable.ht_next) {
 982                         mdb_printf("%p\n", ht);
 983                         if (mdb_vread(&htable, sizeof (htable_t),
 984                             (uintptr_t)ht) == -1) {
 985                                 mdb_warn("Couldn't read htable\n");
 986                                 return (DCMD_ERR);
 987                         }
 988                 }
 989         }
 990         return (DCMD_OK);
 991 }
 992 
 993 /*
 994  * Dump the htables for the given hat
 995  */
 996 /*ARGSUSED*/
 997 int
 998 htables_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
 999 {
1000         hat_t *hat;
1001 
1002         init_mmu();
1003 
1004         if (mmu.num_level == 0)
1005                 return (DCMD_ERR);
1006 
1007         if ((flags & DCMD_ADDRSPEC) == 0)
1008                 return (DCMD_USAGE);
1009 
1010         hat = (hat_t *)addr;
1011 
1012         return (do_htables_dcmd(hat));
1013 }
1014 
1015 static uintptr_t
1016 entry2va(size_t *entries)
1017 {
1018         uintptr_t va = 0;
1019 
1020         for (level_t l = mmu.max_level; l >= 0; l--)
1021                 va += entries[l] << mmu.level_shift[l];
1022 
1023         return (VA_SIGN_EXTEND(va));
1024 }
1025 
1026 static void
1027 ptmap_report(size_t *entries, uintptr_t start,
1028     boolean_t user, boolean_t writable, boolean_t wflag)
1029 {
1030         uint64_t curva = entry2va(entries);
1031 
1032         mdb_printf("mapped %s,%s range of %lu bytes: %a-%a\n",
1033             user ? "user" : "kernel", writable ? "writable" : "read-only",
1034             curva - start, start, curva - 1);
1035         if (wflag && start >= kernelbase)
1036                 (void) mdb_call_dcmd("whatis", start, DCMD_ADDRSPEC, 0, NULL);
1037 }
1038 
1039 int
1040 ptmap_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1041 {
1042         physaddr_t paddrs[MAX_NUM_LEVEL] = { 0, };
1043         size_t entry[MAX_NUM_LEVEL] = { 0, };
1044         uintptr_t start = (uintptr_t)-1;
1045         boolean_t writable = B_FALSE;
1046         boolean_t user = B_FALSE;
1047         boolean_t wflag = B_FALSE;
1048         level_t curlevel;
1049 
1050         if ((flags & DCMD_ADDRSPEC) == 0)
1051                 return (DCMD_USAGE);
1052 
1053         if (mdb_getopts(argc, argv,
1054             'w', MDB_OPT_SETBITS, TRUE, &wflag, NULL) != argc)
1055                 return (DCMD_USAGE);
1056 
1057         init_mmu();
1058 
1059         if (mmu.num_level == 0)
1060                 return (DCMD_ERR);
1061 
1062         curlevel = mmu.max_level;
1063 
1064         paddrs[curlevel] = addr & MMU_PAGEMASK;
1065 
1066         for (;;) {
1067                 physaddr_t pte_addr;
1068                 x86pte_t pte;
1069 
1070                 pte_addr = paddrs[curlevel] +
1071                     (entry[curlevel] << mmu.pte_size_shift);
1072 
1073                 if (mdb_pread(&pte, sizeof (pte), pte_addr) != sizeof (pte)) {
1074                         mdb_warn("couldn't read pte at %p", pte_addr);
1075                         return (DCMD_ERR);
1076                 }
1077 
1078                 if (PTE_GET(pte, PT_VALID) == 0) {
1079                         if (start != (uintptr_t)-1) {
1080                                 ptmap_report(entry, start,
1081                                     user, writable, wflag);
1082                                 start = (uintptr_t)-1;
1083                         }
1084                 } else if (curlevel == 0 || PTE_GET(pte, PT_PAGESIZE)) {
1085                         if (start == (uintptr_t)-1) {
1086                                 start = entry2va(entry);
1087                                 user = PTE_GET(pte, PT_USER);
1088                                 writable = PTE_GET(pte, PT_WRITABLE);
1089                         } else if (user != PTE_GET(pte, PT_USER) ||
1090                             writable != PTE_GET(pte, PT_WRITABLE)) {
1091                                 ptmap_report(entry, start,
1092                                     user, writable, wflag);
1093                                 start = entry2va(entry);
1094                                 user = PTE_GET(pte, PT_USER);
1095                                 writable = PTE_GET(pte, PT_WRITABLE);
1096                         }
1097                 } else {
1098                         /* Descend a level. */
1099                         physaddr_t pa = mmu_ptob(pte2mfn(pte, curlevel));
1100                         paddrs[--curlevel] = pa;
1101                         entry[curlevel] = 0;
1102                         continue;
1103                 }
1104 
1105                 while (++entry[curlevel] == mmu.ptes_per_table) {
1106                         /* Ascend back up. */
1107                         entry[curlevel] = 0;
1108                         if (curlevel == mmu.max_level) {
1109                                 if (start != (uintptr_t)-1) {
1110                                         ptmap_report(entry, start,
1111                                             user, writable, wflag);
1112                                 }
1113                                 goto out;
1114                         }
1115 
1116                         curlevel++;
1117                 }
1118         }
1119 
1120 out:
1121         return (DCMD_OK);
1122 }