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