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