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 }