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