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