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