xref: /titanic_41/usr/src/cmd/mdb/sun4u/modules/unix/sfmmu.c (revision 4e968bc268231e8ec239c56e97b85a0969b6ff62)
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 #include <sys/types.h>
30 #include <sys/machparam.h>
31 #include <vm/as.h>
32 #include <vm/hat_sfmmu.h>
33 
34 #include <mdb/mdb_modapi.h>
35 #include <mdb/mdb_target.h>
36 #include <mdb/mdb_ctf.h>
37 
38 /*
39  * sfmmu mdb support
40  */
41 
42 #define	SFMMU_VTOP_DBG_SYMBOL	1
43 #define	SFMMU_VTOP_DBG_VERBOSE	2
44 #define	SFMMU_VTOP_DBG_DEBUG	4
45 #define	SFMMU_VTOP_DBG_ALL	(SFMMU_VTOP_DBG_SYMBOL|SFMMU_VTOP_DBG_VERBOSE|\
46 				SFMMU_VTOP_DBG_DEBUG)
47 
48 #define	SFMMU_VTOP_DBG_SYM	if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_SYMBOL) \
49 				    mdb_printf
50 #define	SFMMU_VTOP_DBG_VRB	if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_VERBOSE) \
51 				    mdb_printf
52 #define	SFMMU_VTOP_DBG_DBG	if (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_DEBUG) \
53 				    mdb_printf
54 
55 #define	SFMMU_VTOP_READSYM(dest, synm, where) \
56 	if (mdb_readsym(&(dest), sizeof (dest), (synm)) == -1) \
57 		mdb_warn("%s: couldn't find or read '%s'\n", (where), (synm));
58 
59 struct hme_blks_max {
60 	struct hme_blk	hmx_hmeblk;
61 	struct sf_hment	hmx_hmes[NHMENTS - 1];
62 };
63 
64 int sfmmu_vtop(uintptr_t, uint_t, int, const mdb_arg_t *);
65 static int sfmmu_vtop_common(struct as *, uintptr_t, physaddr_t *);
66 static int sfmmu_vtop_impl(uintptr_t, sfmmu_t *, sfmmu_t *, physaddr_t *);
67 static void sfmmu_vtop_print_hmeblk(struct hme_blk *);
68 static struct sf_hment *mdb_sfmmu_hblktohme(struct hme_blk *, caddr_t, int *);
69 
70 int sfmmu_vtop_dbg_wanted = 0;	/* set this as desired */
71 int sfmmu_vtop_dbg = 0;
72 
73 /*
74  * ::sfmmu_vtop [[-v] -a as]
75  * Extended version of the vtop builtin. The optional <as> argument is
76  * used as base address space for translating a virtual address into a
77  * physical address. The verbose option ("-v") shows intermediate
78  * translation steps. If <as> or kas is ommitted, the builtin ::vtop
79  * dcmd is called.
80  */
81 int
82 sfmmu_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
83 {
84 	int ret;
85 	struct as *asp = NULL;
86 	char *asnmp = NULL;
87 	int verbose = 0;
88 	physaddr_t paddr;
89 
90 	sfmmu_vtop_dbg = sfmmu_vtop_dbg_wanted;
91 
92 	if (mdb_getopts(argc, argv,
93 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
94 	    'a', MDB_OPT_STR, &asnmp,
95 	    NULL) != argc)
96 		return (DCMD_USAGE);
97 
98 	if (verbose != 0 && asnmp == NULL) {
99 		mdb_warn("-v requires -a option\n");
100 		return (DCMD_USAGE);
101 	}
102 
103 	if (verbose != 0 && (sfmmu_vtop_dbg & SFMMU_VTOP_DBG_VERBOSE) == 0) {
104 		sfmmu_vtop_dbg |= SFMMU_VTOP_DBG_VERBOSE;
105 	}
106 
107 	if (asnmp != NULL) {
108 		GElf_Sym sym;
109 
110 		SFMMU_VTOP_DBG_DBG("asnmp=%p asnm=%s\n", asnmp, asnmp);
111 		if (strcmp(asnmp, "kas") == 0) {
112 			if (mdb_lookup_by_name("kas", &sym) == -1) {
113 				mdb_warn("couldn't find 'kas'\n");
114 				return (DCMD_ERR);
115 			} else {
116 				asp = (struct as *)sym.st_value;
117 				SFMMU_VTOP_DBG_SYM("kas &sym=%p\n", &sym);
118 			}
119 		} else {
120 			asp = (struct as *)mdb_strtoull(asnmp);
121 		}
122 		SFMMU_VTOP_DBG_DBG("asp=0x%p\n", asp);
123 	}
124 
125 	if (asp == 0) {
126 		SFMMU_VTOP_DBG_DBG("sfmmu_vtop: call standard vtop\n");
127 		return (mdb_call_dcmd("vtop", addr, flags, argc, argv));
128 	}
129 
130 	if ((ret = sfmmu_vtop_common(asp, addr, &paddr)) == -1L) {
131 		mdb_printf("no mapping found for addr=%p\n", addr);
132 		return (DCMD_ERR);
133 	}
134 
135 	if (ret == 0) {
136 		mdb_printf("address space %p: virtual %lr mapped to physical "
137 			"%llr", asp, addr, paddr);
138 	} else {
139 		return (DCMD_ERR);
140 	}
141 
142 	return (DCMD_OK);
143 }
144 
145 static int
146 sfmmu_vtop_common(struct as *asp, uintptr_t addr, physaddr_t *pap)
147 {
148 	struct as mas;
149 	struct as *masp = &mas;
150 	sfmmu_t *hatp;
151 	sfmmu_t mhat;
152 	sfmmu_t *mhatp = &mhat;
153 	int ret;
154 
155 	if (mdb_vread(masp, sizeof (mas), (uintptr_t)asp) == -1) {
156 		mdb_warn("couldn't read as at %p\n", asp);
157 		return (DCMD_ERR);
158 	}
159 
160 	hatp = masp->a_hat;
161 
162 	SFMMU_VTOP_DBG_DBG("hatp=%p addr=%p masp=%p\n", hatp, addr, masp);
163 
164 	if (mdb_vread(mhatp, sizeof (mhat), (uintptr_t)hatp) == -1) {
165 		mdb_warn("couldn't read hat at %p\n", hatp);
166 		return (DCMD_ERR);
167 	}
168 	if (mhatp->sfmmu_as != asp) {
169 		mdb_warn("%p is not a valid address space\n", asp);
170 		return (DCMD_ERR);
171 	}
172 
173 	ret = sfmmu_vtop_impl(addr, hatp, mhatp, pap);
174 
175 	return (ret);
176 }
177 
178 static int
179 sfmmu_vtop_impl(uintptr_t addr, sfmmu_t *sfmmup, sfmmu_t *msfmmup,
180 	    physaddr_t *pap)
181 {
182 	struct hmehash_bucket *uhme_hash;
183 	struct hmehash_bucket *khme_hash;
184 	int uhmehash_num;
185 	int khmehash_num;
186 	sfmmu_t *ksfmmup;
187 	struct hmehash_bucket mbucket;
188 	struct hmehash_bucket *hmebp;
189 	struct hmehash_bucket *shmebp;
190 	hmeblk_tag hblktag;
191 	int hmeshift;
192 	int hashno = 1;
193 	struct hme_blk *hmeblkp = NULL;
194 	struct hme_blks_max mhmeblkmax;
195 	intptr_t thmeblkp;
196 	struct sf_hment *sfhmep;
197 	int i;
198 	ism_blk_t mism_blk;
199 	ism_map_t *ism_map;
200 	ism_blk_t *ism_blkp;
201 	ism_blk_t *sism_blkp;
202 	sfmmu_t *ism_hatid = NULL;
203 	int sfhmeinx = 0;
204 	tte_t tte;
205 	pfn_t pfn;
206 	pfn_t start_pfn;
207 	page_t *pp;
208 	int ret = -1;
209 
210 	SFMMU_VTOP_READSYM(uhme_hash, "uhme_hash", "sfmmu_vtop_impl");
211 	SFMMU_VTOP_DBG_DBG("uhme_hash=%p\t", uhme_hash);
212 	SFMMU_VTOP_READSYM(uhmehash_num, "uhmehash_num", "sfmmu_vtop_impl");
213 	SFMMU_VTOP_DBG_DBG("uhmehash_num=%lx\n", uhmehash_num);
214 	SFMMU_VTOP_READSYM(khme_hash, "khme_hash", "sfmmu_vtop_impl");
215 	SFMMU_VTOP_DBG_DBG("khme_hash=%p\t", khme_hash);
216 	SFMMU_VTOP_READSYM(khmehash_num, "khmehash_num", "sfmmu_vtop_impl");
217 	SFMMU_VTOP_DBG_DBG("khmehash_num=%lx\n", khmehash_num);
218 	SFMMU_VTOP_READSYM(ksfmmup, "ksfmmup", "sfmmu_vtop_impl");
219 	SFMMU_VTOP_DBG_DBG("ksfmmup=%p\n", ksfmmup);
220 
221 	ism_blkp = sism_blkp = msfmmup->sfmmu_iblk;
222 	while (ism_blkp != NULL && ism_hatid == NULL) {
223 		SFMMU_VTOP_DBG_DBG("ism_blkp=%p\n", ism_blkp);
224 		if (mdb_vread(&mism_blk, sizeof (mism_blk),
225 		    (uintptr_t)ism_blkp) == -1) {
226 			mdb_warn("couldn't read ism_blk at %p\n", ism_blkp);
227 			return (DCMD_ERR);
228 		}
229 		ism_blkp = &mism_blk;
230 		ism_map = ism_blkp->iblk_maps;
231 		for (i = 0; ism_map[i].imap_ismhat && i < ISM_MAP_SLOTS; i++) {
232 			if ((caddr_t)addr >= ism_start(ism_map[i]) &&
233 			    (caddr_t)addr < ism_end(ism_map[i])) {
234 				sfmmup = ism_hatid = ism_map[i].imap_ismhat;
235 				addr = (caddr_t)addr - ism_start(ism_map[i]);
236 				SFMMU_VTOP_DBG_VRB("ism_blkp=%p inx=%d\n",
237 				    sism_blkp, i);
238 				SFMMU_VTOP_DBG_DBG("ism map=%p ism hat=%p "
239 				    "addr=%llx\n",
240 				    (caddr_t)&ism_map[i] - (caddr_t)ism_blkp
241 				    + (caddr_t)sism_blkp, sfmmup, addr);
242 				break;
243 			}
244 		}
245 		ism_blkp = sism_blkp = ism_blkp->iblk_next;
246 	}
247 
248 	hblktag.htag_id = sfmmup;
249 	do {
250 		SFMMU_VTOP_DBG_DBG("-hashno=%d-\n", hashno);
251 		hmeshift = HME_HASH_SHIFT(hashno);
252 		SFMMU_VTOP_DBG_DBG("hmeshift=%d\n", hmeshift);
253 		hblktag.htag_bspage = HME_HASH_BSPAGE(addr, hmeshift);
254 		hblktag.htag_rehash = hashno;
255 
256 #ifdef __sparcv9
257 		SFMMU_VTOP_DBG_DBG("hblktag=%lx %lx\n",
258 				(uint64_t)hblktag.htag_tag[0],
259 				(uint64_t)hblktag.htag_tag[1]);
260 #else
261 		SFMMU_VTOP_DBG_DBG("hblktag=%llx\n",
262 				(uint64_t)hblktag.htag_tag);
263 #endif
264 
265 		hmebp = shmebp = HME_HASH_FUNCTION(sfmmup, addr, hmeshift);
266 		SFMMU_VTOP_DBG_DBG("hmebp=%p\n", hmebp);
267 
268 		if (mdb_vread(&mbucket, sizeof (mbucket),
269 				(uintptr_t)hmebp) == -1) {
270 			mdb_warn("couldn't read mbucket at %p\n", hmebp);
271 			return (DCMD_ERR);
272 		}
273 
274 		hmebp = &mbucket;
275 
276 		for (hmeblkp = hmebp->hmeblkp; hmeblkp;
277 			hmeblkp = hmeblkp->hblk_next) {
278 
279 			SFMMU_VTOP_DBG_DBG("hmeblkp=%p\n", hmeblkp);
280 
281 			if (hmeblkp == NULL)
282 				break;
283 
284 			if (mdb_vread(&mhmeblkmax, sizeof (struct hme_blk),
285 					(uintptr_t)hmeblkp) == -1) {
286 				mdb_warn("couldn't read hme_blk at %p\n",
287 					hmeblkp);
288 				return (DCMD_ERR);
289 			}
290 
291 			thmeblkp = (uintptr_t)hmeblkp;
292 			hmeblkp = &mhmeblkmax.hmx_hmeblk;
293 
294 			if (HTAGS_EQ(hmeblkp->hblk_tag, hblktag)) {
295 				/* found hme_blk */
296 				break;
297 			}
298 		}
299 
300 		if (hmeblkp != NULL) {
301 			sfmmu_vtop_print_hmeblk(hmeblkp);
302 
303 			sfhmep = mdb_sfmmu_hblktohme(hmeblkp, (caddr_t)addr,
304 					&sfhmeinx);
305 
306 			SFMMU_VTOP_DBG_DBG("sfhmeinx=%d ", sfhmeinx);
307 
308 			if (sfhmeinx > 0) {
309 				thmeblkp += sizeof (struct hme_blk) +
310 				    sizeof (struct sf_hment) * (sfhmeinx - 1);
311 
312 				if (mdb_vread(sfhmep, sizeof (struct sf_hment),
313 						thmeblkp) == -1) {
314 					mdb_warn("couldn't read msfhme at %p\n",
315 						sfhmep);
316 					return (DCMD_ERR);
317 				}
318 			}
319 
320 			SFMMU_VTOP_DBG_VRB("sfmmup=%p hmebp=%p hmeblkp=%p\n",
321 					sfmmup, shmebp, thmeblkp);
322 
323 			tte = sfhmep->hme_tte;
324 			SFMMU_VTOP_DBG_VRB("tte=%llx ", tte.ll);
325 			if (TTE_IS_VALID(&tte)) {
326 				start_pfn = TTE_TO_TTEPFN(&tte);
327 				*pap = (start_pfn << MMU_PAGESHIFT) +
328 					(addr & TTE_PAGE_OFFSET(tte.tte_size));
329 				pfn = *pap >> MMU_PAGESHIFT;
330 				pp = (sfhmep->hme_page != 0) ?
331 					sfhmep->hme_page + (pfn - start_pfn) :
332 					0;
333 				SFMMU_VTOP_DBG_VRB("pfn=%lx pp=%p\n",
334 					pfn, pp);
335 				ret = 0;
336 			}
337 			break;
338 		}
339 
340 		hashno++;
341 
342 	} while (HME_REHASH(msfmmup) && (hashno <= MAX_HASHCNT));
343 
344 	return (ret);
345 }
346 
347 static void
348 sfmmu_vtop_print_hmeblk(struct hme_blk *hmeblkp)
349 {
350 
351 	if ((sfmmu_vtop_dbg & SFMMU_VTOP_DBG_DEBUG) == NULL)
352 		return;
353 
354 	mdb_printf("    hblk_nextpa=%llx\n", hmeblkp->hblk_nextpa);
355 #ifdef __sparcv9
356 	mdb_printf("    hblktag=%lx %lx\n", hmeblkp->hblk_tag.htag_tag[0],
357 			hmeblkp->hblk_tag.htag_tag[1]);
358 #else
359 	mdb_printf("    hblktag=%llx\n", hmeblkp->hblk_tag.htag_tag);
360 #endif
361 	mdb_printf("    hblk_next=%p\n", hmeblkp->hblk_next);
362 	mdb_printf("    hblk_shadow=%p\n", hmeblkp->hblk_shadow);
363 	mdb_printf("    hblk_span=%d\n", hmeblkp->hblk_span);
364 	mdb_printf("    hblk_ttesz=%d\n", hmeblkp->hblk_ttesz);
365 	if (hmeblkp->hblk_shw_bit == 0) {
366 		mdb_printf("    hblk_hmecnt=%d\n", hmeblkp->hblk_hmecnt);
367 		mdb_printf("    hblk_vcnt=%d\n", hmeblkp->hblk_vcnt);
368 	} else {
369 		mdb_printf("    hblk_shw_mask=%x\n", hmeblkp->hblk_shw_mask);
370 	}
371 }
372 
373 static struct sf_hment *
374 mdb_sfmmu_hblktohme(struct hme_blk *hmeblkp, caddr_t addr, int *hmenump)
375 {
376 	int index = 0;
377 
378 	if (get_hblk_ttesz(hmeblkp) == TTE8K) {
379 		index = (((uintptr_t)addr >> MMU_PAGESHIFT) & (NHMENTS-1));
380 	}
381 
382 	if (hmenump) {
383 		*hmenump = index;
384 	}
385 
386 	return (&hmeblkp->hblk_hme[index]);
387 }
388 
389 /*
390  * memseg walker based callback function: used internal and for ::page_num2pp
391  */
392 
393 struct pfn2pp {
394 	pfn_t pfn;
395 	page_t *pp;
396 };
397 
398 /*ARGSUSED*/
399 int
400 page_num2pp_cb(uintptr_t addr, void *ignored, uintptr_t *data)
401 {
402 	struct memseg ms, *msp = &ms;
403 	struct pfn2pp *p = (struct pfn2pp *)data;
404 
405 	if (mdb_vread(msp, sizeof (struct memseg), addr) == -1) {
406 		mdb_warn("can't read memseg at %#lx", addr);
407 		return (DCMD_ERR);
408 	}
409 
410 	if (p->pfn >= msp->pages_base && p->pfn < msp->pages_end) {
411 		p->pp = msp->pages + (p->pfn - msp->pages_base);
412 		return (WALK_DONE);
413 	}
414 
415 	return (WALK_NEXT);
416 }
417 
418 /*
419  * ::page_num2pp dcmd
420  */
421 /*ARGSUSED*/
422 int
423 page_num2pp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
424 {
425 	struct pfn2pp pfn2pp;
426 	page_t page;
427 
428 	if ((flags & DCMD_ADDRSPEC) == 0) {
429 		mdb_warn("page frame number missing\n");
430 			return (DCMD_USAGE);
431 	}
432 
433 	pfn2pp.pfn = (pfn_t)addr;
434 	pfn2pp.pp = NULL;
435 
436 	if (mdb_walk("memseg", (mdb_walk_cb_t)page_num2pp_cb,
437 	    (void *)&pfn2pp) == -1) {
438 		mdb_warn("can't walk memseg");
439 		return (DCMD_ERR);
440 	}
441 
442 	if (pfn2pp.pp == NULL)
443 		return (DCMD_ERR);
444 
445 	mdb_printf("%lx has mpage at %p\n", pfn2pp.pfn, pfn2pp.pp);
446 
447 	if (mdb_vread(&page, sizeof (page_t),
448 	    (uintptr_t)pfn2pp.pp) == -1) {
449 		mdb_warn("can't read page at %p", &page);
450 		return (DCMD_ERR);
451 	}
452 
453 	if (page.p_pagenum != pfn2pp.pfn) {
454 		mdb_warn("WARNING! Found page structure contains "
455 			"different pagenumber %x\n", page.p_pagenum);
456 	}
457 
458 	return (DCMD_OK);
459 }
460 
461 /*
462  * ::memseg_list dcmd
463  */
464 /*ARGSUSED*/
465 int
466 memseg_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
467 {
468 	struct memseg ms;
469 
470 	if (!(flags & DCMD_ADDRSPEC)) {
471 		if (mdb_pwalk_dcmd("memseg", "memseg_list",
472 		    0, NULL, 0) == -1) {
473 			mdb_warn("can't walk memseg");
474 			return (DCMD_ERR);
475 		}
476 		return (DCMD_OK);
477 	}
478 
479 	if (DCMD_HDRSPEC(flags))
480 		mdb_printf("%<u>%?s %?s %?s %?s %?s%</u>\n", "ADDR",
481 			"PAGES", "EPAGES", "BASE", "END");
482 
483 	if (mdb_vread(&ms, sizeof (struct memseg), addr) == -1) {
484 		mdb_warn("can't read memseg at %#lx", addr);
485 		return (DCMD_ERR);
486 	}
487 
488 	mdb_printf("%0?lx %0?lx %0?lx %0?lx %0?lx\n", addr,
489 		ms.pages, ms.epages, ms.pages_base, ms.pages_end);
490 
491 	return (DCMD_OK);
492 }
493 
494 /*
495  * walk the memseg structures
496  */
497 int
498 memseg_walk_init(mdb_walk_state_t *wsp)
499 {
500 	if (wsp->walk_addr != NULL) {
501 		mdb_warn("memseg only supports global walks\n");
502 		return (WALK_ERR);
503 	}
504 
505 	if (mdb_readvar(&wsp->walk_addr, "memsegs") == -1) {
506 		mdb_warn("symbol 'memsegs' not found");
507 		return (WALK_ERR);
508 	}
509 
510 	wsp->walk_data = mdb_alloc(sizeof (struct memseg), UM_SLEEP);
511 	return (WALK_NEXT);
512 
513 }
514 
515 int
516 memseg_walk_step(mdb_walk_state_t *wsp)
517 {
518 	int status;
519 
520 	if (wsp->walk_addr == 0) {
521 		return (WALK_DONE);
522 	}
523 
524 	if (mdb_vread(wsp->walk_data, sizeof (struct memseg),
525 	    wsp->walk_addr) == -1) {
526 		mdb_warn("failed to read struct memseg at %p", wsp->walk_addr);
527 		return (WALK_DONE);
528 	}
529 
530 	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
531 	    wsp->walk_cbdata);
532 
533 	wsp->walk_addr = (uintptr_t)(((struct memseg *)wsp->walk_data)->next);
534 
535 	return (status);
536 }
537 
538 void
539 memseg_walk_fini(mdb_walk_state_t *wsp)
540 {
541 	mdb_free(wsp->walk_data, sizeof (struct memseg));
542 }
543 
544 int
545 platform_vtop(uintptr_t addr, struct as *asp, physaddr_t *pap)
546 {
547 	int rv;
548 
549 	sfmmu_vtop_dbg = sfmmu_vtop_dbg_wanted;
550 
551 	SFMMU_VTOP_DBG_DBG("platform_vtop: called.\n");
552 
553 	if (asp == NULL) {
554 		return (DCMD_ERR);
555 	}
556 
557 	if ((rv = sfmmu_vtop_common(asp, addr, pap)) == 0) {
558 		mdb_printf("address space %p: ", asp);
559 	}
560 
561 	return (rv);
562 }
563 
564 /*
565  * ::tsbinfo help
566  */
567 void
568 tsbinfo_help(void)
569 {
570 	mdb_printf("-l\tlist valid TSB entries.\n"
571 	    "-a\tlist all TSB entries.  Can only be used with -l.\n");
572 }
573 
574 /*
575  * ::tsbinfo dcmd
576  */
577 int
578 tsbinfo_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
579 {
580 	uint_t lflag = 0, aflag = 0;
581 	struct tsb_info tsbinfo;
582 	unsigned int entries = 0;
583 	struct tsbe *tsbp, *tsbend, *tsbstart;
584 	caddr_t va;
585 	uintptr_t pa;
586 	uint_t tsbbytes;
587 	char tsbsize[16];
588 #define	FLAGS_SIZE	sizeof ("RELOC,FLUSH,SWAPPED")
589 	char tsbflags[FLAGS_SIZE + 1];
590 
591 	static const mdb_bitmask_t ttesz_mask_bits[] = {
592 		{ "8K", TSB8K, TSB8K },
593 		{ "64K", TSB64K, TSB64K },
594 		{ "512K", TSB512K, TSB512K },
595 		{ "4M", TSB4M, TSB4M },
596 		{ "32M", TSB32M, TSB32M },
597 		{ "256M", TSB256M, TSB256M },
598 		{ NULL, 0, 0 }
599 	};
600 
601 	static const mdb_bitmask_t flags_bits[] = {
602 		{ "RELOC", TSB_RELOC_FLAG, TSB_RELOC_FLAG },
603 		{ "FLUSH", TSB_FLUSH_NEEDED, TSB_FLUSH_NEEDED },
604 		{ "SWAPPED", TSB_SWAPPED, TSB_SWAPPED },
605 		{ NULL, 0, 0 }
606 	};
607 
608 	if (!(flags & DCMD_ADDRSPEC)) {
609 		return (DCMD_USAGE);
610 	}
611 
612 	if (mdb_getopts(argc, argv,
613 		'l', MDB_OPT_SETBITS, TRUE, &lflag,
614 		'a', MDB_OPT_SETBITS, TRUE, &aflag,
615 		NULL) != argc) {
616 		return (DCMD_USAGE);
617 	}
618 
619 	/* -a only valid with -l */
620 	if (aflag && !lflag) {
621 		return (DCMD_USAGE);
622 	}
623 
624 	/* Print header? */
625 	if (DCMD_HDRSPEC(flags) || lflag) {
626 		mdb_printf("%<u>%-?s %-?s %-8s %-*s %s%</u>\n", "TSBINFO",
627 		    "TSB", "SIZE", FLAGS_SIZE, "FLAGS", "TTE SIZES");
628 	}
629 
630 	if (mdb_vread(&tsbinfo, sizeof (struct tsb_info), addr) == -1) {
631 		mdb_warn("failed to read struct tsb_info at %p", addr);
632 		return (DCMD_ERR);
633 	}
634 
635 	mdb_printf("%0?lx ", addr);
636 
637 	/* Print a "-" if the TSB is swapped out. */
638 	if ((tsbinfo.tsb_flags & TSB_SWAPPED) == 0) {
639 		mdb_printf("%0?lx ", tsbinfo.tsb_va);
640 	} else {
641 		mdb_printf("%0?-s ", "-");
642 	}
643 
644 	tsbbytes = TSB_BYTES(tsbinfo.tsb_szc);
645 
646 #define	KB 1024
647 #define	MB (KB*KB)
648 	if (tsbbytes >= MB) {
649 		mdb_snprintf(tsbsize, sizeof (tsbsize), "%dM", tsbbytes / MB);
650 	} else {
651 		mdb_snprintf(tsbsize, sizeof (tsbsize), "%dK", tsbbytes / KB);
652 	}
653 #undef MB
654 #undef KB
655 	mdb_printf("%-8s ", tsbsize);
656 
657 	if (tsbinfo.tsb_flags == 0) {
658 		mdb_printf("%-*s ", FLAGS_SIZE, "-");
659 	} else {
660 		mdb_snprintf(tsbflags, sizeof (tsbflags), "%b",
661 		    tsbinfo.tsb_flags, flags_bits);
662 		mdb_printf("%-*s ", FLAGS_SIZE, tsbflags);
663 	}
664 
665 	mdb_printf("%b\n", tsbinfo.tsb_ttesz_mask, ttesz_mask_bits);
666 
667 	/* Print TSB entries? */
668 	if (lflag) {
669 
670 		if ((tsbinfo.tsb_flags & TSB_SWAPPED) == 0) {
671 
672 			entries = TSB_ENTRIES(tsbinfo.tsb_szc);
673 
674 			tsbp = mdb_alloc(sizeof (struct tsbe) * entries,
675 			    UM_SLEEP);
676 
677 			if (mdb_vread(tsbp, sizeof (struct tsbe) * entries,
678 				(uintptr_t)tsbinfo.tsb_va) == -1) {
679 				mdb_warn("failed to read TSB at %p",
680 				    tsbinfo.tsb_va);
681 				return (DCMD_ERR);
682 			}
683 
684 			mdb_printf(
685 				"TSB @ %lx (%d entries)\n"
686 				    "%-?s %-17s %s\n"
687 				    "%<u>%-?s %1s %1s %-11s "
688 				    "%1s %1s %1s %1s %1s %1s %8s "
689 				    "%1s %1s %1s %1s %1s %1s %1s "
690 				    "%1s %1s %1s %1s %1s %1s%</u>\n",
691 				    tsbinfo.tsb_va, entries, "", "TAG", "TTE",
692 				    "ADDR", "I", "L", "VA 63:22",
693 				    "V", "S", "N", "I", "H", "S", "PA 42:13",
694 				    "N", "U", "R", "W", "E", "X", "L",
695 				    "P", "V", "E", "P", "W", "G");
696 
697 			tsbend = tsbp + entries;
698 			for (tsbstart = tsbp; tsbp < tsbend; tsbp++) {
699 				if (aflag ||
700 				    (tsbp->tte_tag.tag_invalid == 0)) {
701 
702 					va = (caddr_t)
703 					    (((uint64_t)tsbp->tte_tag.tag_vahi
704 						<< 32) +
705 						tsbp->tte_tag.tag_valo);
706 					pa = (tsbp->tte_data.tte_pahi << 19) +
707 					    tsbp->tte_data.tte_palo;
708 					mdb_printf("%0?lx %-1u %-1u %011lx "
709 					    "%1u %-1u %-1u %-1u %-1u %1u %08x "
710 					    "%1u %1u %1u %1u %1u %1u %1u "
711 					    "%1u %1u %1u %1u %1u %1u\n",
712 					    tsbinfo.tsb_va + (tsbp - tsbstart)
713 					    * sizeof (struct tsbe),
714 					    tsbp->tte_tag.tag_invalid,
715 					    tsbp->tte_tag.tag_locked, va,
716 					    tsbp->tte_data.tte_val,
717 					    tsbp->tte_data.tte_size,
718 					    tsbp->tte_data.tte_nfo,
719 					    tsbp->tte_data.tte_ie,
720 					    tsbp->tte_data.tte_hmenum,
721 #ifdef sun4v
722 					    0,
723 #else
724 					    tsbp->tte_data.tte_size2,
725 #endif
726 					    pa,
727 					    tsbp->tte_data.tte_no_sync,
728 					    tsbp->tte_data.tte_suspend,
729 					    tsbp->tte_data.tte_ref,
730 					    tsbp->tte_data.tte_wr_perm,
731 #ifdef sun4v
732 					    0,
733 #else
734 					    tsbp->tte_data.tte_exec_synth,
735 #endif
736 					    tsbp->tte_data.tte_exec_perm,
737 					    tsbp->tte_data.tte_lock,
738 					    tsbp->tte_data.tte_cp,
739 					    tsbp->tte_data.tte_cv,
740 					    tsbp->tte_data.tte_se,
741 					    tsbp->tte_data.tte_priv,
742 					    tsbp->tte_data.tte_hwwr,
743 #ifdef sun4v
744 					    0
745 #else
746 					    tsbp->tte_data.tte_glb
747 #endif
748 					    /*CSTYLED*/
749 					    );
750 				}
751 			}
752 
753 			mdb_printf("\n"); /* blank line for readability */
754 
755 			mdb_free(tsbstart, sizeof (struct tsbe) * entries);
756 
757 		} else {
758 
759 			mdb_printf("TSB swapped out\n");
760 		}
761 	}
762 
763 	return (DCMD_OK);
764 }
765