xref: /illumos-gate/usr/src/cmd/mdb/i86pc/modules/unix/unix.c (revision ef2504f26d1ea5859db9838255bb63f488f1b050)
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 <mdb/mdb_modapi.h>
27 #include <mdb/mdb_ctf.h>
28 #include <sys/cpuvar.h>
29 #include <sys/systm.h>
30 #include <sys/traptrace.h>
31 #include <sys/x_call.h>
32 #include <sys/xc_levels.h>
33 #include <sys/avintr.h>
34 #include <sys/systm.h>
35 #include <sys/trap.h>
36 #include <sys/mutex.h>
37 #include <sys/mutex_impl.h>
38 #include "i86mmu.h"
39 
40 #define	TT_HDLR_WIDTH	17
41 
42 static int
43 ttrace_ttr_size_check(void)
44 {
45 	mdb_ctf_id_t ttrtid;
46 	ssize_t ttr_size;
47 
48 	if (mdb_ctf_lookup_by_name("trap_trace_rec_t", &ttrtid) != 0 ||
49 	    mdb_ctf_type_resolve(ttrtid, &ttrtid) != 0) {
50 		mdb_warn("failed to determine size of trap_trace_rec_t; "
51 		    "non-TRAPTRACE kernel?\n");
52 		return (0);
53 	}
54 
55 	if ((ttr_size = mdb_ctf_type_size(ttrtid)) !=
56 	    sizeof (trap_trace_rec_t)) {
57 		/*
58 		 * On Intel machines, this will happen when TTR_STACK_DEPTH
59 		 * is changed.  This code could be smarter, and could
60 		 * dynamically adapt to different depths, but not until a
61 		 * need for such adaptation is demonstrated.
62 		 */
63 		mdb_warn("size of trap_trace_rec_t (%d bytes) doesn't "
64 		    "match expected %d\n", ttr_size, sizeof (trap_trace_rec_t));
65 		return (0);
66 	}
67 
68 	return (1);
69 }
70 
71 int
72 ttrace_walk_init(mdb_walk_state_t *wsp)
73 {
74 	trap_trace_ctl_t *ttcp;
75 	size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU;
76 	int i;
77 
78 	if (!ttrace_ttr_size_check())
79 		return (WALK_ERR);
80 
81 	ttcp = mdb_zalloc(ttc_size, UM_SLEEP);
82 
83 	if (wsp->walk_addr != NULL) {
84 		mdb_warn("ttrace only supports global walks\n");
85 		return (WALK_ERR);
86 	}
87 
88 	if (mdb_readsym(ttcp, ttc_size, "trap_trace_ctl") == -1) {
89 		mdb_warn("symbol 'trap_trace_ctl' not found; "
90 		    "non-TRAPTRACE kernel?\n");
91 		mdb_free(ttcp, ttc_size);
92 		return (WALK_ERR);
93 	}
94 
95 	/*
96 	 * We'll poach the ttc_current pointer (which isn't used for
97 	 * anything) to store a pointer to our current TRAPTRACE record.
98 	 * This allows us to only keep the array of trap_trace_ctl structures
99 	 * as our walker state (ttc_current may be the only kernel data
100 	 * structure member added exclusively to make writing the mdb walker
101 	 * a little easier).
102 	 */
103 	for (i = 0; i < NCPU; i++) {
104 		trap_trace_ctl_t *ttc = &ttcp[i];
105 
106 		if (ttc->ttc_first == NULL)
107 			continue;
108 
109 		/*
110 		 * Assign ttc_current to be the last completed record.
111 		 * Note that the error checking (i.e. in the ttc_next ==
112 		 * ttc_first case) is performed in the step function.
113 		 */
114 		ttc->ttc_current = ttc->ttc_next - sizeof (trap_trace_rec_t);
115 	}
116 
117 	wsp->walk_data = ttcp;
118 	return (WALK_NEXT);
119 }
120 
121 int
122 ttrace_walk_step(mdb_walk_state_t *wsp)
123 {
124 	trap_trace_ctl_t *ttcp = wsp->walk_data, *ttc, *latest_ttc;
125 	trap_trace_rec_t rec;
126 	int rval, i, recsize = sizeof (trap_trace_rec_t);
127 	hrtime_t latest = 0;
128 
129 	/*
130 	 * Loop through the CPUs, looking for the latest trap trace record
131 	 * (we want to walk through the trap trace records in reverse
132 	 * chronological order).
133 	 */
134 	for (i = 0; i < NCPU; i++) {
135 		ttc = &ttcp[i];
136 
137 		if (ttc->ttc_current == NULL)
138 			continue;
139 
140 		if (ttc->ttc_current < ttc->ttc_first)
141 			ttc->ttc_current = ttc->ttc_limit - recsize;
142 
143 		if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) {
144 			mdb_warn("couldn't read rec at %p", ttc->ttc_current);
145 			return (WALK_ERR);
146 		}
147 
148 		if (rec.ttr_stamp > latest) {
149 			latest = rec.ttr_stamp;
150 			latest_ttc = ttc;
151 		}
152 	}
153 
154 	if (latest == 0)
155 		return (WALK_DONE);
156 
157 	ttc = latest_ttc;
158 
159 	if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) {
160 		mdb_warn("couldn't read rec at %p", ttc->ttc_current);
161 		return (WALK_ERR);
162 	}
163 
164 	rval = wsp->walk_callback(ttc->ttc_current, &rec, wsp->walk_cbdata);
165 
166 	if (ttc->ttc_current == ttc->ttc_next)
167 		ttc->ttc_current = NULL;
168 	else
169 		ttc->ttc_current -= sizeof (trap_trace_rec_t);
170 
171 	return (rval);
172 }
173 
174 void
175 ttrace_walk_fini(mdb_walk_state_t *wsp)
176 {
177 	mdb_free(wsp->walk_data, sizeof (trap_trace_ctl_t) * NCPU);
178 }
179 
180 static int
181 ttrace_syscall(trap_trace_rec_t *rec)
182 {
183 	GElf_Sym sym;
184 	int sysnum = rec->ttr_sysnum;
185 	uintptr_t addr;
186 	struct sysent sys;
187 
188 	mdb_printf("%-3x", sysnum);
189 
190 	if (rec->ttr_sysnum > NSYSCALL) {
191 		mdb_printf(" %-*d", TT_HDLR_WIDTH, rec->ttr_sysnum);
192 		return (0);
193 	}
194 
195 	if (mdb_lookup_by_name("sysent", &sym) == -1) {
196 		mdb_warn("\ncouldn't find 'sysent'");
197 		return (-1);
198 	}
199 
200 	addr = (uintptr_t)sym.st_value + sysnum * sizeof (struct sysent);
201 
202 	if (addr >= (uintptr_t)sym.st_value + sym.st_size) {
203 		mdb_warn("\nsysnum %d out-of-range\n", sysnum);
204 		return (-1);
205 	}
206 
207 	if (mdb_vread(&sys, sizeof (sys), addr) == -1) {
208 		mdb_warn("\nfailed to read sysent at %p", addr);
209 		return (-1);
210 	}
211 
212 	mdb_printf(" %-*a", TT_HDLR_WIDTH, sys.sy_callc);
213 
214 	return (0);
215 }
216 
217 static int
218 ttrace_interrupt(trap_trace_rec_t *rec)
219 {
220 	GElf_Sym sym;
221 	uintptr_t addr;
222 	struct av_head hd;
223 	struct autovec av;
224 
225 	switch (rec->ttr_regs.r_trapno) {
226 	case T_SOFTINT:
227 		mdb_printf("%-3s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)");
228 		return (0);
229 	default:
230 		break;
231 	}
232 
233 	mdb_printf("%-3x ", rec->ttr_vector);
234 
235 	if (mdb_lookup_by_name("autovect", &sym) == -1) {
236 		mdb_warn("\ncouldn't find 'autovect'");
237 		return (-1);
238 	}
239 
240 	addr = (uintptr_t)sym.st_value +
241 	    rec->ttr_vector * sizeof (struct av_head);
242 
243 	if (addr >= (uintptr_t)sym.st_value + sym.st_size) {
244 		mdb_warn("\nav_head for vec %x is corrupt\n", rec->ttr_vector);
245 		return (-1);
246 	}
247 
248 	if (mdb_vread(&hd, sizeof (hd), addr) == -1) {
249 		mdb_warn("\ncouldn't read av_head for vec %x", rec->ttr_vector);
250 		return (-1);
251 	}
252 
253 	if (hd.avh_link == NULL) {
254 		if (rec->ttr_ipl == XC_CPUPOKE_PIL)
255 			mdb_printf("%-*s", TT_HDLR_WIDTH, "(cpupoke)");
256 		else
257 			mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)");
258 	} else {
259 		if (mdb_vread(&av, sizeof (av), (uintptr_t)hd.avh_link) == -1) {
260 			mdb_warn("couldn't read autovec at %p",
261 			    (uintptr_t)hd.avh_link);
262 		}
263 
264 		mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector);
265 	}
266 
267 	return (0);
268 }
269 
270 static struct {
271 	int tt_trapno;
272 	char *tt_name;
273 } ttrace_traps[] = {
274 	{ T_ZERODIV,	"divide-error" },
275 	{ T_SGLSTP,	"debug-exception" },
276 	{ T_NMIFLT,	"nmi-interrupt" },
277 	{ T_BPTFLT,	"breakpoint" },
278 	{ T_OVFLW,	"into-overflow" },
279 	{ T_BOUNDFLT,	"bound-exceeded" },
280 	{ T_ILLINST,	"invalid-opcode" },
281 	{ T_NOEXTFLT,	"device-not-avail" },
282 	{ T_DBLFLT,	"double-fault" },
283 	{ T_EXTOVRFLT,	"segment-overrun" },
284 	{ T_TSSFLT,	"invalid-tss" },
285 	{ T_SEGFLT,	"segment-not-pres" },
286 	{ T_STKFLT,	"stack-fault" },
287 	{ T_GPFLT,	"general-protectn" },
288 	{ T_PGFLT,	"page-fault" },
289 	{ T_EXTERRFLT,	"error-fault" },
290 	{ T_ALIGNMENT,	"alignment-check" },
291 	{ T_MCE,	"machine-check" },
292 	{ T_SIMDFPE,	"sse-exception" },
293 
294 	{ T_DBGENTR,	"debug-enter" },
295 	{ T_FASTTRAP,	"fasttrap-0xd2" },
296 	{ T_SYSCALLINT,	"syscall-0x91" },
297 	{ T_DTRACE_RET,	"dtrace-ret" },
298 	{ T_SOFTINT,	"softint" },
299 	{ T_INTERRUPT,	"interrupt" },
300 	{ T_FAULT,	"fault" },
301 	{ T_AST,	"ast" },
302 	{ T_SYSCALL,	"syscall" },
303 
304 	{ 0,		NULL }
305 };
306 
307 static int
308 ttrace_trap(trap_trace_rec_t *rec)
309 {
310 	int i;
311 
312 	if (rec->ttr_regs.r_trapno == T_AST)
313 		mdb_printf("%-3s ", "-");
314 	else
315 		mdb_printf("%-3x ", rec->ttr_regs.r_trapno);
316 
317 	for (i = 0; ttrace_traps[i].tt_name != NULL; i++) {
318 		if (rec->ttr_regs.r_trapno == ttrace_traps[i].tt_trapno)
319 			break;
320 	}
321 
322 	if (ttrace_traps[i].tt_name == NULL)
323 		mdb_printf("%-*s", TT_HDLR_WIDTH, "(unknown)");
324 	else
325 		mdb_printf("%-*s", TT_HDLR_WIDTH, ttrace_traps[i].tt_name);
326 
327 	return (0);
328 }
329 
330 static void
331 ttrace_intr_detail(trap_trace_rec_t *rec)
332 {
333 	mdb_printf("\tirq %x ipl %d oldpri %d basepri %d\n", rec->ttr_vector,
334 	    rec->ttr_ipl, rec->ttr_pri, rec->ttr_spl);
335 }
336 
337 static struct {
338 	uchar_t t_marker;
339 	char *t_name;
340 	int (*t_hdlr)(trap_trace_rec_t *);
341 } ttrace_hdlr[] = {
342 	{ TT_SYSCALL, "sysc", ttrace_syscall },
343 	{ TT_SYSENTER, "syse", ttrace_syscall },
344 	{ TT_SYSC, "asys", ttrace_syscall },
345 	{ TT_SYSC64, "sc64", ttrace_syscall },
346 	{ TT_INTERRUPT, "intr", ttrace_interrupt },
347 	{ TT_TRAP, "trap", ttrace_trap },
348 	{ TT_EVENT, "evnt", ttrace_trap },
349 	{ 0, NULL, NULL }
350 };
351 
352 typedef struct ttrace_dcmd {
353 	processorid_t ttd_cpu;
354 	uint_t ttd_extended;
355 	trap_trace_ctl_t ttd_ttc[NCPU];
356 } ttrace_dcmd_t;
357 
358 #if defined(__amd64)
359 
360 #define	DUMP(reg) #reg, regs->r_##reg
361 #define	THREEREGS	"         %3s: %16lx %3s: %16lx %3s: %16lx\n"
362 
363 static void
364 ttrace_dumpregs(trap_trace_rec_t *rec)
365 {
366 	struct regs *regs = &rec->ttr_regs;
367 
368 	mdb_printf(THREEREGS, DUMP(rdi), DUMP(rsi), DUMP(rdx));
369 	mdb_printf(THREEREGS, DUMP(rcx), DUMP(r8), DUMP(r9));
370 	mdb_printf(THREEREGS, DUMP(rax), DUMP(rbx), DUMP(rbp));
371 	mdb_printf(THREEREGS, DUMP(r10), DUMP(r11), DUMP(r12));
372 	mdb_printf(THREEREGS, DUMP(r13), DUMP(r14), DUMP(r15));
373 	mdb_printf(THREEREGS, DUMP(ds), DUMP(es), DUMP(fs));
374 	mdb_printf(THREEREGS, DUMP(gs), "trp", regs->r_trapno, DUMP(err));
375 	mdb_printf(THREEREGS, DUMP(rip), DUMP(cs), DUMP(rfl));
376 	mdb_printf(THREEREGS, DUMP(rsp), DUMP(ss), "cr2", rec->ttr_cr2);
377 	mdb_printf("\n");
378 }
379 
380 #else
381 
382 #define	DUMP(reg) #reg, regs->r_##reg
383 #define	FOURREGS	"         %3s: %08x %3s: %08x %3s: %08x %3s: %08x\n"
384 
385 static void
386 ttrace_dumpregs(trap_trace_rec_t *rec)
387 {
388 	struct regs *regs = &rec->ttr_regs;
389 
390 	mdb_printf(FOURREGS, DUMP(gs), DUMP(fs), DUMP(es), DUMP(ds));
391 	mdb_printf(FOURREGS, DUMP(edi), DUMP(esi), DUMP(ebp), DUMP(esp));
392 	mdb_printf(FOURREGS, DUMP(ebx), DUMP(edx), DUMP(ecx), DUMP(eax));
393 	mdb_printf(FOURREGS, "trp", regs->r_trapno, DUMP(err),
394 	    DUMP(pc), DUMP(cs));
395 	mdb_printf(FOURREGS, DUMP(efl), "usp", regs->r_uesp, DUMP(ss),
396 	    "cr2", rec->ttr_cr2);
397 	mdb_printf("\n");
398 }
399 
400 #endif	/* __amd64 */
401 
402 int
403 ttrace_walk(uintptr_t addr, trap_trace_rec_t *rec, ttrace_dcmd_t *dcmd)
404 {
405 	struct regs *regs = &rec->ttr_regs;
406 	processorid_t cpu = -1, i;
407 
408 	for (i = 0; i < NCPU; i++) {
409 		if (addr >= dcmd->ttd_ttc[i].ttc_first &&
410 		    addr < dcmd->ttd_ttc[i].ttc_limit) {
411 			cpu = i;
412 			break;
413 		}
414 	}
415 
416 	if (cpu == -1) {
417 		mdb_warn("couldn't find %p in any trap trace ctl\n", addr);
418 		return (WALK_ERR);
419 	}
420 
421 	if (dcmd->ttd_cpu != -1 && cpu != dcmd->ttd_cpu)
422 		return (WALK_NEXT);
423 
424 	mdb_printf("%3d %15llx ", cpu, rec->ttr_stamp);
425 
426 	for (i = 0; ttrace_hdlr[i].t_hdlr != NULL; i++) {
427 		if (rec->ttr_marker != ttrace_hdlr[i].t_marker)
428 			continue;
429 		mdb_printf("%4s ", ttrace_hdlr[i].t_name);
430 		if (ttrace_hdlr[i].t_hdlr(rec) == -1)
431 			return (WALK_ERR);
432 	}
433 
434 	mdb_printf(" %a\n", regs->r_pc);
435 
436 	if (dcmd->ttd_extended == FALSE)
437 		return (WALK_NEXT);
438 
439 	if (rec->ttr_marker == TT_INTERRUPT)
440 		ttrace_intr_detail(rec);
441 	else
442 		ttrace_dumpregs(rec);
443 
444 	if (rec->ttr_sdepth > 0) {
445 		for (i = 0; i < rec->ttr_sdepth; i++) {
446 			if (i >= TTR_STACK_DEPTH) {
447 				mdb_printf("%17s*** invalid ttr_sdepth (is %d, "
448 				    "should be <= %d)\n", " ", rec->ttr_sdepth,
449 				    TTR_STACK_DEPTH);
450 				break;
451 			}
452 
453 			mdb_printf("%17s %a()\n", " ", rec->ttr_stack[i]);
454 		}
455 		mdb_printf("\n");
456 	}
457 
458 	return (WALK_NEXT);
459 }
460 
461 int
462 ttrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
463 {
464 	ttrace_dcmd_t dcmd;
465 	trap_trace_ctl_t *ttc = dcmd.ttd_ttc;
466 	trap_trace_rec_t rec;
467 	size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU;
468 
469 	if (!ttrace_ttr_size_check())
470 		return (WALK_ERR);
471 
472 	bzero(&dcmd, sizeof (dcmd));
473 	dcmd.ttd_cpu = -1;
474 	dcmd.ttd_extended = FALSE;
475 
476 	if (mdb_readsym(ttc, ttc_size, "trap_trace_ctl") == -1) {
477 		mdb_warn("symbol 'trap_trace_ctl' not found; "
478 		    "non-TRAPTRACE kernel?\n");
479 		return (DCMD_ERR);
480 	}
481 
482 	if (mdb_getopts(argc, argv,
483 	    'x', MDB_OPT_SETBITS, TRUE, &dcmd.ttd_extended, NULL) != argc)
484 		return (DCMD_USAGE);
485 
486 	if (DCMD_HDRSPEC(flags)) {
487 		mdb_printf("%3s %15s %4s %2s %-*s%s\n", "CPU",
488 		    "TIMESTAMP", "TYPE", "Vec", TT_HDLR_WIDTH, "HANDLER",
489 		    " EIP");
490 	}
491 
492 	if (flags & DCMD_ADDRSPEC) {
493 		if (addr >= NCPU) {
494 			if (mdb_vread(&rec, sizeof (rec), addr) == -1) {
495 				mdb_warn("couldn't read trap trace record "
496 				    "at %p", addr);
497 				return (DCMD_ERR);
498 			}
499 
500 			if (ttrace_walk(addr, &rec, &dcmd) == WALK_ERR)
501 				return (DCMD_ERR);
502 
503 			return (DCMD_OK);
504 		}
505 		dcmd.ttd_cpu = addr;
506 	}
507 
508 	if (mdb_walk("ttrace", (mdb_walk_cb_t)ttrace_walk, &dcmd) == -1) {
509 		mdb_warn("couldn't walk 'ttrace'");
510 		return (DCMD_ERR);
511 	}
512 
513 	return (DCMD_OK);
514 }
515 
516 /*ARGSUSED*/
517 int
518 mutex_owner_init(mdb_walk_state_t *wsp)
519 {
520 	return (WALK_NEXT);
521 }
522 
523 int
524 mutex_owner_step(mdb_walk_state_t *wsp)
525 {
526 	uintptr_t addr = wsp->walk_addr;
527 	mutex_impl_t mtx;
528 	uintptr_t owner;
529 	kthread_t thr;
530 
531 	if (mdb_vread(&mtx, sizeof (mtx), addr) == -1)
532 		return (WALK_ERR);
533 
534 	if (!MUTEX_TYPE_ADAPTIVE(&mtx))
535 		return (WALK_DONE);
536 
537 	if ((owner = (uintptr_t)MUTEX_OWNER(&mtx)) == NULL)
538 		return (WALK_DONE);
539 
540 	if (mdb_vread(&thr, sizeof (thr), owner) != -1)
541 		(void) wsp->walk_callback(owner, &thr, wsp->walk_cbdata);
542 
543 	return (WALK_DONE);
544 }
545 
546 static void
547 gate_desc_dump(gate_desc_t *gate, const char *label, int header)
548 {
549 	const char *lastnm;
550 	uint_t lastval;
551 	char type[4];
552 
553 	switch (gate->sgd_type) {
554 	case SDT_SYSIGT:
555 		strcpy(type, "int");
556 		break;
557 	case SDT_SYSTGT:
558 		strcpy(type, "trp");
559 		break;
560 	case SDT_SYSTASKGT:
561 		strcpy(type, "tsk");
562 		break;
563 	default:
564 		(void) mdb_snprintf(type, sizeof (type), "%3x", gate->sgd_type);
565 	}
566 
567 #if defined(__amd64)
568 	lastnm = "IST";
569 	lastval = gate->sgd_ist;
570 #else
571 	lastnm = "STK";
572 	lastval = gate->sgd_stkcpy;
573 #endif
574 
575 	if (header) {
576 		mdb_printf("%*s%<u>%-30s%</u> %<u>%-4s%</u> %<u>%3s%</u> "
577 		    "%<u>%1s%</u> %<u>%3s%</u> %<u>%3s%</u>\n", strlen(label),
578 		    "", "HANDLER", "SEL", "DPL", "P", "TYP", lastnm);
579 	}
580 
581 	mdb_printf("%s", label);
582 
583 	if (gate->sgd_type == SDT_SYSTASKGT)
584 		mdb_printf("%-30s ", "-");
585 	else
586 		mdb_printf("%-30a ", GATESEG_GETOFFSET(gate));
587 
588 	mdb_printf("%4x  %d  %c %3s %2x\n", gate->sgd_selector,
589 	    gate->sgd_dpl, (gate->sgd_p ? '+' : ' '), type, lastval);
590 }
591 
592 /*ARGSUSED*/
593 static int
594 gate_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
595 {
596 	gate_desc_t gate;
597 
598 	if (argc != 0 || !(flags & DCMD_ADDRSPEC))
599 		return (DCMD_USAGE);
600 
601 	if (mdb_vread(&gate, sizeof (gate_desc_t), addr) !=
602 	    sizeof (gate_desc_t)) {
603 		mdb_warn("failed to read gate descriptor at %p\n", addr);
604 		return (DCMD_ERR);
605 	}
606 
607 	gate_desc_dump(&gate, "", DCMD_HDRSPEC(flags));
608 
609 	return (DCMD_OK);
610 }
611 
612 /*ARGSUSED*/
613 static int
614 idt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
615 {
616 	int i;
617 
618 	if (!(flags & DCMD_ADDRSPEC)) {
619 		GElf_Sym idt0_va;
620 		gate_desc_t *idt0;
621 
622 		if (mdb_lookup_by_name("idt0", &idt0_va) < 0) {
623 			mdb_warn("failed to find VA of idt0");
624 			return (DCMD_ERR);
625 		}
626 
627 		addr = idt0_va.st_value;
628 		if (mdb_vread(&idt0, sizeof (idt0), addr) != sizeof (idt0)) {
629 			mdb_warn("failed to read idt0 at %p\n", addr);
630 			return (DCMD_ERR);
631 		}
632 
633 		addr = (uintptr_t)idt0;
634 	}
635 
636 	for (i = 0; i < NIDT; i++, addr += sizeof (gate_desc_t)) {
637 		gate_desc_t gate;
638 		char label[6];
639 
640 		if (mdb_vread(&gate, sizeof (gate_desc_t), addr) !=
641 		    sizeof (gate_desc_t)) {
642 			mdb_warn("failed to read gate descriptor at %p\n",
643 			    addr);
644 			return (DCMD_ERR);
645 		}
646 
647 		(void) mdb_snprintf(label, sizeof (label), "%3d: ", i);
648 		gate_desc_dump(&gate, label, i == 0);
649 	}
650 
651 	return (DCMD_OK);
652 }
653 
654 static void
655 htables_help(void)
656 {
657 	mdb_printf(
658 	    "Given a (hat_t *), generates the list of all (htable_t *)s\n"
659 	    "that correspond to that address space\n");
660 }
661 
662 static void
663 report_maps_help(void)
664 {
665 	mdb_printf(
666 	    "Given a PFN, report HAT structures that map the page, or use\n"
667 	    "the page as a pagetable.\n"
668 	    "\n"
669 	    "-m Interpret the PFN as an MFN (machine frame number)\n");
670 }
671 
672 static void
673 ptable_help(void)
674 {
675 	mdb_printf(
676 	    "Given a PFN holding a page table, print its contents, and\n"
677 	    "the address of the corresponding htable structure.\n"
678 	    "\n"
679 	    "-m Interpret the PFN as an MFN (machine frame number)\n");
680 }
681 
682 static const mdb_dcmd_t dcmds[] = {
683 	{ "gate_desc", ":", "dump a gate descriptor", gate_desc },
684 	{ "idt", ":[-v]", "dump an IDT", idt },
685 	{ "ttrace", "[-x]", "dump trap trace buffers", ttrace },
686 	{ "vatopfn", ":[-a as]", "translate address to physical page",
687 	    va2pfn_dcmd },
688 	{ "report_maps", ":[-m]",
689 	    "Given PFN, report mappings / page table usage",
690 	    report_maps_dcmd, report_maps_help },
691 	{ "htables", "", "Given hat_t *, lists all its htable_t * values",
692 	    htables_dcmd, htables_help },
693 	{ "ptable", ":[-m]", "Given PFN, dump contents of a page table",
694 	    ptable_dcmd, ptable_help },
695 	{ "pte", ":[-p XXXXX] [-l N]", "print human readable page table entry",
696 	    pte_dcmd },
697 	{ "page_num2pp", ":", "page frame number to page structure",
698 	    page_num2pp },
699 	{ "pfntomfn", ":", "convert physical page to hypervisor machine page",
700 	    pfntomfn_dcmd },
701 	{ "mfntopfn", ":", "convert hypervisor machine page to physical page",
702 	    mfntopfn_dcmd },
703 	{ "memseg_list", ":", "show memseg list", memseg_list },
704 	{ NULL }
705 };
706 
707 static const mdb_walker_t walkers[] = {
708 	{ "ttrace", "walks trap trace buffers in reverse chronological order",
709 		ttrace_walk_init, ttrace_walk_step, ttrace_walk_fini },
710 	{ "mutex_owner", "walks the owner of a mutex",
711 		mutex_owner_init, mutex_owner_step },
712 	{ "memseg", "walk the memseg structures",
713 		memseg_walk_init, memseg_walk_step, memseg_walk_fini },
714 	{ NULL }
715 };
716 
717 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
718 
719 const mdb_modinfo_t *
720 _mdb_init(void)
721 {
722 	return (&modinfo);
723 }
724 
725 void
726 _mdb_fini(void)
727 {
728 	free_mmu();
729 }
730