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