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