17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5ae115bc7Smrj * Common Development and Distribution License (the "License"). 6ae115bc7Smrj * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 225cd376e8SJimmy Vetayases * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 23*799823bbSRobert Mustacchi * Copyright 2015 Joyent, Inc. 24f7b98820SBryan Cantrill */ 25f7b98820SBryan Cantrill 267c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 277c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 287c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 297c478bd9Sstevel@tonic-gate #include <sys/systm.h> 307c478bd9Sstevel@tonic-gate #include <sys/traptrace.h> 31ae115bc7Smrj #include <sys/x_call.h> 32f34a7178SJoe Bonasera #include <sys/xc_levels.h> 337c478bd9Sstevel@tonic-gate #include <sys/avintr.h> 347c478bd9Sstevel@tonic-gate #include <sys/systm.h> 357c478bd9Sstevel@tonic-gate #include <sys/trap.h> 367c478bd9Sstevel@tonic-gate #include <sys/mutex.h> 377c478bd9Sstevel@tonic-gate #include <sys/mutex_impl.h> 387c478bd9Sstevel@tonic-gate #include "i86mmu.h" 39*799823bbSRobert Mustacchi #include "unix_sup.h" 407ff178cdSJimmy Vetayases #include <sys/apix.h> 4129f78cfaSRobert Mustacchi #include <sys/x86_archext.h> 4229f78cfaSRobert Mustacchi #include <sys/bitmap.h> 43*799823bbSRobert Mustacchi #include <sys/controlregs.h> 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate #define TT_HDLR_WIDTH 17 467c478bd9Sstevel@tonic-gate 477ff178cdSJimmy Vetayases 487ff178cdSJimmy Vetayases /* apix only */ 497ff178cdSJimmy Vetayases static apix_impl_t *d_apixs[NCPU]; 507ff178cdSJimmy Vetayases static int use_apix = 0; 517ff178cdSJimmy Vetayases 527c478bd9Sstevel@tonic-gate static int 537c478bd9Sstevel@tonic-gate ttrace_ttr_size_check(void) 547c478bd9Sstevel@tonic-gate { 557c478bd9Sstevel@tonic-gate mdb_ctf_id_t ttrtid; 567c478bd9Sstevel@tonic-gate ssize_t ttr_size; 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate if (mdb_ctf_lookup_by_name("trap_trace_rec_t", &ttrtid) != 0 || 597c478bd9Sstevel@tonic-gate mdb_ctf_type_resolve(ttrtid, &ttrtid) != 0) { 607c478bd9Sstevel@tonic-gate mdb_warn("failed to determine size of trap_trace_rec_t; " 617c478bd9Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 627c478bd9Sstevel@tonic-gate return (0); 637c478bd9Sstevel@tonic-gate } 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate if ((ttr_size = mdb_ctf_type_size(ttrtid)) != 667c478bd9Sstevel@tonic-gate sizeof (trap_trace_rec_t)) { 677c478bd9Sstevel@tonic-gate /* 687c478bd9Sstevel@tonic-gate * On Intel machines, this will happen when TTR_STACK_DEPTH 697c478bd9Sstevel@tonic-gate * is changed. This code could be smarter, and could 707c478bd9Sstevel@tonic-gate * dynamically adapt to different depths, but not until a 717c478bd9Sstevel@tonic-gate * need for such adaptation is demonstrated. 727c478bd9Sstevel@tonic-gate */ 737c478bd9Sstevel@tonic-gate mdb_warn("size of trap_trace_rec_t (%d bytes) doesn't " 747c478bd9Sstevel@tonic-gate "match expected %d\n", ttr_size, sizeof (trap_trace_rec_t)); 757c478bd9Sstevel@tonic-gate return (0); 767c478bd9Sstevel@tonic-gate } 777c478bd9Sstevel@tonic-gate 787c478bd9Sstevel@tonic-gate return (1); 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate int 827c478bd9Sstevel@tonic-gate ttrace_walk_init(mdb_walk_state_t *wsp) 837c478bd9Sstevel@tonic-gate { 847c478bd9Sstevel@tonic-gate trap_trace_ctl_t *ttcp; 857c478bd9Sstevel@tonic-gate size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU; 867c478bd9Sstevel@tonic-gate int i; 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate if (!ttrace_ttr_size_check()) 897c478bd9Sstevel@tonic-gate return (WALK_ERR); 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate ttcp = mdb_zalloc(ttc_size, UM_SLEEP); 927c478bd9Sstevel@tonic-gate 937c478bd9Sstevel@tonic-gate if (wsp->walk_addr != NULL) { 947c478bd9Sstevel@tonic-gate mdb_warn("ttrace only supports global walks\n"); 957c478bd9Sstevel@tonic-gate return (WALK_ERR); 967c478bd9Sstevel@tonic-gate } 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate if (mdb_readsym(ttcp, ttc_size, "trap_trace_ctl") == -1) { 997c478bd9Sstevel@tonic-gate mdb_warn("symbol 'trap_trace_ctl' not found; " 1007c478bd9Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 1017c478bd9Sstevel@tonic-gate mdb_free(ttcp, ttc_size); 1027c478bd9Sstevel@tonic-gate return (WALK_ERR); 1037c478bd9Sstevel@tonic-gate } 1047c478bd9Sstevel@tonic-gate 1057c478bd9Sstevel@tonic-gate /* 1067c478bd9Sstevel@tonic-gate * We'll poach the ttc_current pointer (which isn't used for 1077c478bd9Sstevel@tonic-gate * anything) to store a pointer to our current TRAPTRACE record. 1087c478bd9Sstevel@tonic-gate * This allows us to only keep the array of trap_trace_ctl structures 1097c478bd9Sstevel@tonic-gate * as our walker state (ttc_current may be the only kernel data 1107c478bd9Sstevel@tonic-gate * structure member added exclusively to make writing the mdb walker 1117c478bd9Sstevel@tonic-gate * a little easier). 1127c478bd9Sstevel@tonic-gate */ 1137c478bd9Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 1147c478bd9Sstevel@tonic-gate trap_trace_ctl_t *ttc = &ttcp[i]; 1157c478bd9Sstevel@tonic-gate 1167c478bd9Sstevel@tonic-gate if (ttc->ttc_first == NULL) 1177c478bd9Sstevel@tonic-gate continue; 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate /* 1207c478bd9Sstevel@tonic-gate * Assign ttc_current to be the last completed record. 1217c478bd9Sstevel@tonic-gate * Note that the error checking (i.e. in the ttc_next == 1227c478bd9Sstevel@tonic-gate * ttc_first case) is performed in the step function. 1237c478bd9Sstevel@tonic-gate */ 1247c478bd9Sstevel@tonic-gate ttc->ttc_current = ttc->ttc_next - sizeof (trap_trace_rec_t); 1257c478bd9Sstevel@tonic-gate } 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate wsp->walk_data = ttcp; 1287c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1297c478bd9Sstevel@tonic-gate } 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate int 1327c478bd9Sstevel@tonic-gate ttrace_walk_step(mdb_walk_state_t *wsp) 1337c478bd9Sstevel@tonic-gate { 1347c478bd9Sstevel@tonic-gate trap_trace_ctl_t *ttcp = wsp->walk_data, *ttc, *latest_ttc; 1357c478bd9Sstevel@tonic-gate trap_trace_rec_t rec; 1367c478bd9Sstevel@tonic-gate int rval, i, recsize = sizeof (trap_trace_rec_t); 1377c478bd9Sstevel@tonic-gate hrtime_t latest = 0; 1387c478bd9Sstevel@tonic-gate 1397c478bd9Sstevel@tonic-gate /* 1407c478bd9Sstevel@tonic-gate * Loop through the CPUs, looking for the latest trap trace record 1417c478bd9Sstevel@tonic-gate * (we want to walk through the trap trace records in reverse 1427c478bd9Sstevel@tonic-gate * chronological order). 1437c478bd9Sstevel@tonic-gate */ 1447c478bd9Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 1457c478bd9Sstevel@tonic-gate ttc = &ttcp[i]; 1467c478bd9Sstevel@tonic-gate 1477c478bd9Sstevel@tonic-gate if (ttc->ttc_current == NULL) 1487c478bd9Sstevel@tonic-gate continue; 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate if (ttc->ttc_current < ttc->ttc_first) 1517c478bd9Sstevel@tonic-gate ttc->ttc_current = ttc->ttc_limit - recsize; 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) { 1547c478bd9Sstevel@tonic-gate mdb_warn("couldn't read rec at %p", ttc->ttc_current); 1557c478bd9Sstevel@tonic-gate return (WALK_ERR); 1567c478bd9Sstevel@tonic-gate } 1577c478bd9Sstevel@tonic-gate 1587c478bd9Sstevel@tonic-gate if (rec.ttr_stamp > latest) { 1597c478bd9Sstevel@tonic-gate latest = rec.ttr_stamp; 1607c478bd9Sstevel@tonic-gate latest_ttc = ttc; 1617c478bd9Sstevel@tonic-gate } 1627c478bd9Sstevel@tonic-gate } 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate if (latest == 0) 1657c478bd9Sstevel@tonic-gate return (WALK_DONE); 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate ttc = latest_ttc; 1687c478bd9Sstevel@tonic-gate 1697c478bd9Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) { 1707c478bd9Sstevel@tonic-gate mdb_warn("couldn't read rec at %p", ttc->ttc_current); 1717c478bd9Sstevel@tonic-gate return (WALK_ERR); 1727c478bd9Sstevel@tonic-gate } 1737c478bd9Sstevel@tonic-gate 1747c478bd9Sstevel@tonic-gate rval = wsp->walk_callback(ttc->ttc_current, &rec, wsp->walk_cbdata); 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate if (ttc->ttc_current == ttc->ttc_next) 1777c478bd9Sstevel@tonic-gate ttc->ttc_current = NULL; 1787c478bd9Sstevel@tonic-gate else 1797c478bd9Sstevel@tonic-gate ttc->ttc_current -= sizeof (trap_trace_rec_t); 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate return (rval); 1827c478bd9Sstevel@tonic-gate } 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate void 1857c478bd9Sstevel@tonic-gate ttrace_walk_fini(mdb_walk_state_t *wsp) 1867c478bd9Sstevel@tonic-gate { 1877c478bd9Sstevel@tonic-gate mdb_free(wsp->walk_data, sizeof (trap_trace_ctl_t) * NCPU); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate static int 1917c478bd9Sstevel@tonic-gate ttrace_syscall(trap_trace_rec_t *rec) 1927c478bd9Sstevel@tonic-gate { 1937c478bd9Sstevel@tonic-gate GElf_Sym sym; 1947c478bd9Sstevel@tonic-gate int sysnum = rec->ttr_sysnum; 1957c478bd9Sstevel@tonic-gate uintptr_t addr; 1967c478bd9Sstevel@tonic-gate struct sysent sys; 1977c478bd9Sstevel@tonic-gate 198ae115bc7Smrj mdb_printf("%-3x", sysnum); 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate if (rec->ttr_sysnum > NSYSCALL) { 2017c478bd9Sstevel@tonic-gate mdb_printf(" %-*d", TT_HDLR_WIDTH, rec->ttr_sysnum); 2027c478bd9Sstevel@tonic-gate return (0); 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("sysent", &sym) == -1) { 2067c478bd9Sstevel@tonic-gate mdb_warn("\ncouldn't find 'sysent'"); 2077c478bd9Sstevel@tonic-gate return (-1); 2087c478bd9Sstevel@tonic-gate } 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate addr = (uintptr_t)sym.st_value + sysnum * sizeof (struct sysent); 2117c478bd9Sstevel@tonic-gate 2127c478bd9Sstevel@tonic-gate if (addr >= (uintptr_t)sym.st_value + sym.st_size) { 2137c478bd9Sstevel@tonic-gate mdb_warn("\nsysnum %d out-of-range\n", sysnum); 2147c478bd9Sstevel@tonic-gate return (-1); 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate 2177c478bd9Sstevel@tonic-gate if (mdb_vread(&sys, sizeof (sys), addr) == -1) { 2187c478bd9Sstevel@tonic-gate mdb_warn("\nfailed to read sysent at %p", addr); 2197c478bd9Sstevel@tonic-gate return (-1); 2207c478bd9Sstevel@tonic-gate } 2217c478bd9Sstevel@tonic-gate 2227c478bd9Sstevel@tonic-gate mdb_printf(" %-*a", TT_HDLR_WIDTH, sys.sy_callc); 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate return (0); 2257c478bd9Sstevel@tonic-gate } 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate static int 2287c478bd9Sstevel@tonic-gate ttrace_interrupt(trap_trace_rec_t *rec) 2297c478bd9Sstevel@tonic-gate { 2307c478bd9Sstevel@tonic-gate GElf_Sym sym; 2317c478bd9Sstevel@tonic-gate uintptr_t addr; 2327c478bd9Sstevel@tonic-gate struct av_head hd; 2337c478bd9Sstevel@tonic-gate struct autovec av; 2347c478bd9Sstevel@tonic-gate 235ae115bc7Smrj switch (rec->ttr_regs.r_trapno) { 236ae115bc7Smrj case T_SOFTINT: 237ae115bc7Smrj mdb_printf("%-3s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)"); 2387c478bd9Sstevel@tonic-gate return (0); 239ae115bc7Smrj default: 240ae115bc7Smrj break; 2417c478bd9Sstevel@tonic-gate } 2427c478bd9Sstevel@tonic-gate 243ae115bc7Smrj mdb_printf("%-3x ", rec->ttr_vector); 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("autovect", &sym) == -1) { 2467c478bd9Sstevel@tonic-gate mdb_warn("\ncouldn't find 'autovect'"); 2477c478bd9Sstevel@tonic-gate return (-1); 2487c478bd9Sstevel@tonic-gate } 2497c478bd9Sstevel@tonic-gate 2507c478bd9Sstevel@tonic-gate addr = (uintptr_t)sym.st_value + 2517c478bd9Sstevel@tonic-gate rec->ttr_vector * sizeof (struct av_head); 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate if (addr >= (uintptr_t)sym.st_value + sym.st_size) { 2547c478bd9Sstevel@tonic-gate mdb_warn("\nav_head for vec %x is corrupt\n", rec->ttr_vector); 2557c478bd9Sstevel@tonic-gate return (-1); 2567c478bd9Sstevel@tonic-gate } 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate if (mdb_vread(&hd, sizeof (hd), addr) == -1) { 2597c478bd9Sstevel@tonic-gate mdb_warn("\ncouldn't read av_head for vec %x", rec->ttr_vector); 2607c478bd9Sstevel@tonic-gate return (-1); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate if (hd.avh_link == NULL) { 264ae115bc7Smrj if (rec->ttr_ipl == XC_CPUPOKE_PIL) 265ae115bc7Smrj mdb_printf("%-*s", TT_HDLR_WIDTH, "(cpupoke)"); 266ae115bc7Smrj else 2677c478bd9Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)"); 2687c478bd9Sstevel@tonic-gate } else { 2697c478bd9Sstevel@tonic-gate if (mdb_vread(&av, sizeof (av), (uintptr_t)hd.avh_link) == -1) { 2707c478bd9Sstevel@tonic-gate mdb_warn("couldn't read autovec at %p", 2717c478bd9Sstevel@tonic-gate (uintptr_t)hd.avh_link); 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector); 2757c478bd9Sstevel@tonic-gate } 2767c478bd9Sstevel@tonic-gate 2777c478bd9Sstevel@tonic-gate return (0); 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate 2807ff178cdSJimmy Vetayases static int 2817ff178cdSJimmy Vetayases ttrace_apix_interrupt(trap_trace_rec_t *rec) 2827ff178cdSJimmy Vetayases { 2837ff178cdSJimmy Vetayases struct autovec av; 2847ff178cdSJimmy Vetayases apix_impl_t apix; 2857ff178cdSJimmy Vetayases apix_vector_t apix_vector; 2867ff178cdSJimmy Vetayases 2877ff178cdSJimmy Vetayases switch (rec->ttr_regs.r_trapno) { 2887ff178cdSJimmy Vetayases case T_SOFTINT: 2897ff178cdSJimmy Vetayases mdb_printf("%-3s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)"); 2907ff178cdSJimmy Vetayases return (0); 2917ff178cdSJimmy Vetayases default: 2927ff178cdSJimmy Vetayases break; 2937ff178cdSJimmy Vetayases } 2947ff178cdSJimmy Vetayases 2957ff178cdSJimmy Vetayases mdb_printf("%-3x ", rec->ttr_vector); 2967ff178cdSJimmy Vetayases 2977ff178cdSJimmy Vetayases /* Read the per CPU apix entry */ 2987ff178cdSJimmy Vetayases if (mdb_vread(&apix, sizeof (apix_impl_t), 2997ff178cdSJimmy Vetayases (uintptr_t)d_apixs[rec->ttr_cpuid]) == -1) { 3007ff178cdSJimmy Vetayases mdb_warn("\ncouldn't read apix[%d]", rec->ttr_cpuid); 3017ff178cdSJimmy Vetayases return (-1); 3027ff178cdSJimmy Vetayases } 3037ff178cdSJimmy Vetayases if (mdb_vread(&apix_vector, sizeof (apix_vector_t), 3047ff178cdSJimmy Vetayases (uintptr_t)apix.x_vectbl[rec->ttr_vector]) == -1) { 3057ff178cdSJimmy Vetayases mdb_warn("\ncouldn't read apix_vector_t[%d]", rec->ttr_vector); 3067ff178cdSJimmy Vetayases return (-1); 3077ff178cdSJimmy Vetayases } 3087ff178cdSJimmy Vetayases if (apix_vector.v_share == 0) { 3097ff178cdSJimmy Vetayases if (rec->ttr_ipl == XC_CPUPOKE_PIL) 3107ff178cdSJimmy Vetayases mdb_printf("%-*s", TT_HDLR_WIDTH, "(cpupoke)"); 3117ff178cdSJimmy Vetayases else 3127ff178cdSJimmy Vetayases mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)"); 3137ff178cdSJimmy Vetayases } else { 3147ff178cdSJimmy Vetayases if (mdb_vread(&av, sizeof (struct autovec), 3157ff178cdSJimmy Vetayases (uintptr_t)(apix_vector.v_autovect)) == -1) { 3167ff178cdSJimmy Vetayases mdb_warn("couldn't read autovec at %p", 3177ff178cdSJimmy Vetayases (uintptr_t)apix_vector.v_autovect); 3187ff178cdSJimmy Vetayases } 3197ff178cdSJimmy Vetayases 3207ff178cdSJimmy Vetayases mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector); 3217ff178cdSJimmy Vetayases } 3227ff178cdSJimmy Vetayases 3237ff178cdSJimmy Vetayases return (0); 3247ff178cdSJimmy Vetayases } 3257ff178cdSJimmy Vetayases 3267ff178cdSJimmy Vetayases 3277c478bd9Sstevel@tonic-gate static struct { 3287c478bd9Sstevel@tonic-gate int tt_trapno; 3297c478bd9Sstevel@tonic-gate char *tt_name; 3307c478bd9Sstevel@tonic-gate } ttrace_traps[] = { 3317c478bd9Sstevel@tonic-gate { T_ZERODIV, "divide-error" }, 3327c478bd9Sstevel@tonic-gate { T_SGLSTP, "debug-exception" }, 3337c478bd9Sstevel@tonic-gate { T_NMIFLT, "nmi-interrupt" }, 3347c478bd9Sstevel@tonic-gate { T_BPTFLT, "breakpoint" }, 3357c478bd9Sstevel@tonic-gate { T_OVFLW, "into-overflow" }, 3367c478bd9Sstevel@tonic-gate { T_BOUNDFLT, "bound-exceeded" }, 3377c478bd9Sstevel@tonic-gate { T_ILLINST, "invalid-opcode" }, 3387c478bd9Sstevel@tonic-gate { T_NOEXTFLT, "device-not-avail" }, 3397c478bd9Sstevel@tonic-gate { T_DBLFLT, "double-fault" }, 3407c478bd9Sstevel@tonic-gate { T_EXTOVRFLT, "segment-overrun" }, 3417c478bd9Sstevel@tonic-gate { T_TSSFLT, "invalid-tss" }, 3427c478bd9Sstevel@tonic-gate { T_SEGFLT, "segment-not-pres" }, 3437c478bd9Sstevel@tonic-gate { T_STKFLT, "stack-fault" }, 3447c478bd9Sstevel@tonic-gate { T_GPFLT, "general-protectn" }, 3457c478bd9Sstevel@tonic-gate { T_PGFLT, "page-fault" }, 3467c478bd9Sstevel@tonic-gate { T_EXTERRFLT, "error-fault" }, 3477c478bd9Sstevel@tonic-gate { T_ALIGNMENT, "alignment-check" }, 3487c478bd9Sstevel@tonic-gate { T_MCE, "machine-check" }, 3497c478bd9Sstevel@tonic-gate { T_SIMDFPE, "sse-exception" }, 350ae115bc7Smrj 351ae115bc7Smrj { T_DBGENTR, "debug-enter" }, 352ae115bc7Smrj { T_FASTTRAP, "fasttrap-0xd2" }, 353ae115bc7Smrj { T_SYSCALLINT, "syscall-0x91" }, 354ae115bc7Smrj { T_DTRACE_RET, "dtrace-ret" }, 355ae115bc7Smrj { T_SOFTINT, "softint" }, 356ae115bc7Smrj { T_INTERRUPT, "interrupt" }, 357ae115bc7Smrj { T_FAULT, "fault" }, 358ae115bc7Smrj { T_AST, "ast" }, 359ae115bc7Smrj { T_SYSCALL, "syscall" }, 360ae115bc7Smrj 3617c478bd9Sstevel@tonic-gate { 0, NULL } 3627c478bd9Sstevel@tonic-gate }; 3637c478bd9Sstevel@tonic-gate 3647c478bd9Sstevel@tonic-gate static int 3657c478bd9Sstevel@tonic-gate ttrace_trap(trap_trace_rec_t *rec) 3667c478bd9Sstevel@tonic-gate { 3677c478bd9Sstevel@tonic-gate int i; 3687c478bd9Sstevel@tonic-gate 369ae115bc7Smrj if (rec->ttr_regs.r_trapno == T_AST) 370ae115bc7Smrj mdb_printf("%-3s ", "-"); 371ae115bc7Smrj else 372ae115bc7Smrj mdb_printf("%-3x ", rec->ttr_regs.r_trapno); 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate for (i = 0; ttrace_traps[i].tt_name != NULL; i++) { 3757c478bd9Sstevel@tonic-gate if (rec->ttr_regs.r_trapno == ttrace_traps[i].tt_trapno) 3767c478bd9Sstevel@tonic-gate break; 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate 3797c478bd9Sstevel@tonic-gate if (ttrace_traps[i].tt_name == NULL) 3807c478bd9Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, "(unknown)"); 3817c478bd9Sstevel@tonic-gate else 3827c478bd9Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, ttrace_traps[i].tt_name); 3837c478bd9Sstevel@tonic-gate 3847c478bd9Sstevel@tonic-gate return (0); 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate 387ae115bc7Smrj static void 388ae115bc7Smrj ttrace_intr_detail(trap_trace_rec_t *rec) 389ae115bc7Smrj { 390ae115bc7Smrj mdb_printf("\tirq %x ipl %d oldpri %d basepri %d\n", rec->ttr_vector, 391ae115bc7Smrj rec->ttr_ipl, rec->ttr_pri, rec->ttr_spl); 392ae115bc7Smrj } 393ae115bc7Smrj 394ae115bc7Smrj static struct { 3957c478bd9Sstevel@tonic-gate uchar_t t_marker; 3967c478bd9Sstevel@tonic-gate char *t_name; 3977c478bd9Sstevel@tonic-gate int (*t_hdlr)(trap_trace_rec_t *); 3987c478bd9Sstevel@tonic-gate } ttrace_hdlr[] = { 3997c478bd9Sstevel@tonic-gate { TT_SYSCALL, "sysc", ttrace_syscall }, 4007c478bd9Sstevel@tonic-gate { TT_SYSENTER, "syse", ttrace_syscall }, 4017c478bd9Sstevel@tonic-gate { TT_SYSC, "asys", ttrace_syscall }, 4027c478bd9Sstevel@tonic-gate { TT_SYSC64, "sc64", ttrace_syscall }, 4037c478bd9Sstevel@tonic-gate { TT_INTERRUPT, "intr", ttrace_interrupt }, 4047c478bd9Sstevel@tonic-gate { TT_TRAP, "trap", ttrace_trap }, 405ae115bc7Smrj { TT_EVENT, "evnt", ttrace_trap }, 4067c478bd9Sstevel@tonic-gate { 0, NULL, NULL } 4077c478bd9Sstevel@tonic-gate }; 4087c478bd9Sstevel@tonic-gate 4097c478bd9Sstevel@tonic-gate typedef struct ttrace_dcmd { 4107c478bd9Sstevel@tonic-gate processorid_t ttd_cpu; 4117c478bd9Sstevel@tonic-gate uint_t ttd_extended; 4127c478bd9Sstevel@tonic-gate trap_trace_ctl_t ttd_ttc[NCPU]; 4137c478bd9Sstevel@tonic-gate } ttrace_dcmd_t; 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate #if defined(__amd64) 4167c478bd9Sstevel@tonic-gate 4177c478bd9Sstevel@tonic-gate #define DUMP(reg) #reg, regs->r_##reg 4187c478bd9Sstevel@tonic-gate #define THREEREGS " %3s: %16lx %3s: %16lx %3s: %16lx\n" 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate static void 4217c478bd9Sstevel@tonic-gate ttrace_dumpregs(trap_trace_rec_t *rec) 4227c478bd9Sstevel@tonic-gate { 4237c478bd9Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rdi), DUMP(rsi), DUMP(rdx)); 4267c478bd9Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rcx), DUMP(r8), DUMP(r9)); 4277c478bd9Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rax), DUMP(rbx), DUMP(rbp)); 4287c478bd9Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(r10), DUMP(r11), DUMP(r12)); 4297c478bd9Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(r13), DUMP(r14), DUMP(r15)); 430ae115bc7Smrj mdb_printf(THREEREGS, DUMP(ds), DUMP(es), DUMP(fs)); 431ae115bc7Smrj mdb_printf(THREEREGS, DUMP(gs), "trp", regs->r_trapno, DUMP(err)); 432ae115bc7Smrj mdb_printf(THREEREGS, DUMP(rip), DUMP(cs), DUMP(rfl)); 433ae115bc7Smrj mdb_printf(THREEREGS, DUMP(rsp), DUMP(ss), "cr2", rec->ttr_cr2); 4347c478bd9Sstevel@tonic-gate mdb_printf("\n"); 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate #else 4387c478bd9Sstevel@tonic-gate 4397c478bd9Sstevel@tonic-gate #define DUMP(reg) #reg, regs->r_##reg 4407c478bd9Sstevel@tonic-gate #define FOURREGS " %3s: %08x %3s: %08x %3s: %08x %3s: %08x\n" 4417c478bd9Sstevel@tonic-gate 4427c478bd9Sstevel@tonic-gate static void 4437c478bd9Sstevel@tonic-gate ttrace_dumpregs(trap_trace_rec_t *rec) 4447c478bd9Sstevel@tonic-gate { 4457c478bd9Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(gs), DUMP(fs), DUMP(es), DUMP(ds)); 4487c478bd9Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(edi), DUMP(esi), DUMP(ebp), DUMP(esp)); 4497c478bd9Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(ebx), DUMP(edx), DUMP(ecx), DUMP(eax)); 4507c478bd9Sstevel@tonic-gate mdb_printf(FOURREGS, "trp", regs->r_trapno, DUMP(err), 4517c478bd9Sstevel@tonic-gate DUMP(pc), DUMP(cs)); 4527c478bd9Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(efl), "usp", regs->r_uesp, DUMP(ss), 4537c478bd9Sstevel@tonic-gate "cr2", rec->ttr_cr2); 4547c478bd9Sstevel@tonic-gate mdb_printf("\n"); 4557c478bd9Sstevel@tonic-gate } 4567c478bd9Sstevel@tonic-gate 4577c478bd9Sstevel@tonic-gate #endif /* __amd64 */ 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate int 4607c478bd9Sstevel@tonic-gate ttrace_walk(uintptr_t addr, trap_trace_rec_t *rec, ttrace_dcmd_t *dcmd) 4617c478bd9Sstevel@tonic-gate { 4627c478bd9Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 4637c478bd9Sstevel@tonic-gate processorid_t cpu = -1, i; 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 4667c478bd9Sstevel@tonic-gate if (addr >= dcmd->ttd_ttc[i].ttc_first && 4677c478bd9Sstevel@tonic-gate addr < dcmd->ttd_ttc[i].ttc_limit) { 4687c478bd9Sstevel@tonic-gate cpu = i; 4697c478bd9Sstevel@tonic-gate break; 4707c478bd9Sstevel@tonic-gate } 4717c478bd9Sstevel@tonic-gate } 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate if (cpu == -1) { 4747c478bd9Sstevel@tonic-gate mdb_warn("couldn't find %p in any trap trace ctl\n", addr); 4757c478bd9Sstevel@tonic-gate return (WALK_ERR); 4767c478bd9Sstevel@tonic-gate } 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate if (dcmd->ttd_cpu != -1 && cpu != dcmd->ttd_cpu) 4797c478bd9Sstevel@tonic-gate return (WALK_NEXT); 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate mdb_printf("%3d %15llx ", cpu, rec->ttr_stamp); 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate for (i = 0; ttrace_hdlr[i].t_hdlr != NULL; i++) { 4847c478bd9Sstevel@tonic-gate if (rec->ttr_marker != ttrace_hdlr[i].t_marker) 4857c478bd9Sstevel@tonic-gate continue; 4867c478bd9Sstevel@tonic-gate mdb_printf("%4s ", ttrace_hdlr[i].t_name); 4877c478bd9Sstevel@tonic-gate if (ttrace_hdlr[i].t_hdlr(rec) == -1) 4887c478bd9Sstevel@tonic-gate return (WALK_ERR); 4897c478bd9Sstevel@tonic-gate } 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate mdb_printf(" %a\n", regs->r_pc); 4927c478bd9Sstevel@tonic-gate 4937c478bd9Sstevel@tonic-gate if (dcmd->ttd_extended == FALSE) 4947c478bd9Sstevel@tonic-gate return (WALK_NEXT); 4957c478bd9Sstevel@tonic-gate 496f34a7178SJoe Bonasera if (rec->ttr_marker == TT_INTERRUPT) 497ae115bc7Smrj ttrace_intr_detail(rec); 498ae115bc7Smrj else 4997c478bd9Sstevel@tonic-gate ttrace_dumpregs(rec); 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate if (rec->ttr_sdepth > 0) { 5027c478bd9Sstevel@tonic-gate for (i = 0; i < rec->ttr_sdepth; i++) { 5037c478bd9Sstevel@tonic-gate if (i >= TTR_STACK_DEPTH) { 5047c478bd9Sstevel@tonic-gate mdb_printf("%17s*** invalid ttr_sdepth (is %d, " 5057c478bd9Sstevel@tonic-gate "should be <= %d)\n", " ", rec->ttr_sdepth, 5067c478bd9Sstevel@tonic-gate TTR_STACK_DEPTH); 5077c478bd9Sstevel@tonic-gate break; 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate mdb_printf("%17s %a()\n", " ", rec->ttr_stack[i]); 5117c478bd9Sstevel@tonic-gate } 5127c478bd9Sstevel@tonic-gate mdb_printf("\n"); 5137c478bd9Sstevel@tonic-gate } 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate return (WALK_NEXT); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate int 5197c478bd9Sstevel@tonic-gate ttrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 5207c478bd9Sstevel@tonic-gate { 5217c478bd9Sstevel@tonic-gate ttrace_dcmd_t dcmd; 5227c478bd9Sstevel@tonic-gate trap_trace_ctl_t *ttc = dcmd.ttd_ttc; 5237c478bd9Sstevel@tonic-gate trap_trace_rec_t rec; 5247c478bd9Sstevel@tonic-gate size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU; 5257c478bd9Sstevel@tonic-gate 5267c478bd9Sstevel@tonic-gate if (!ttrace_ttr_size_check()) 5277c478bd9Sstevel@tonic-gate return (WALK_ERR); 5287c478bd9Sstevel@tonic-gate 5297c478bd9Sstevel@tonic-gate bzero(&dcmd, sizeof (dcmd)); 5307c478bd9Sstevel@tonic-gate dcmd.ttd_cpu = -1; 5317c478bd9Sstevel@tonic-gate dcmd.ttd_extended = FALSE; 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate if (mdb_readsym(ttc, ttc_size, "trap_trace_ctl") == -1) { 5347c478bd9Sstevel@tonic-gate mdb_warn("symbol 'trap_trace_ctl' not found; " 5357c478bd9Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 5367c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 5407c478bd9Sstevel@tonic-gate 'x', MDB_OPT_SETBITS, TRUE, &dcmd.ttd_extended, NULL) != argc) 5417c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 5447c478bd9Sstevel@tonic-gate mdb_printf("%3s %15s %4s %2s %-*s%s\n", "CPU", 545ae115bc7Smrj "TIMESTAMP", "TYPE", "Vec", TT_HDLR_WIDTH, "HANDLER", 5467c478bd9Sstevel@tonic-gate " EIP"); 5477c478bd9Sstevel@tonic-gate } 5487c478bd9Sstevel@tonic-gate 5497c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 5507c478bd9Sstevel@tonic-gate if (addr >= NCPU) { 5517c478bd9Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), addr) == -1) { 5527c478bd9Sstevel@tonic-gate mdb_warn("couldn't read trap trace record " 5537c478bd9Sstevel@tonic-gate "at %p", addr); 5547c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5557c478bd9Sstevel@tonic-gate } 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate if (ttrace_walk(addr, &rec, &dcmd) == WALK_ERR) 5587c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5597c478bd9Sstevel@tonic-gate 5607c478bd9Sstevel@tonic-gate return (DCMD_OK); 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate dcmd.ttd_cpu = addr; 5637c478bd9Sstevel@tonic-gate } 5647c478bd9Sstevel@tonic-gate 5657ff178cdSJimmy Vetayases if (mdb_readvar(&use_apix, "apix_enable") == -1) { 5667ff178cdSJimmy Vetayases mdb_warn("failed to read apix_enable"); 5677ff178cdSJimmy Vetayases use_apix = 0; 5687ff178cdSJimmy Vetayases } 5697ff178cdSJimmy Vetayases 5707ff178cdSJimmy Vetayases if (use_apix) { 5717ff178cdSJimmy Vetayases if (mdb_readvar(&d_apixs, "apixs") == -1) { 5727ff178cdSJimmy Vetayases mdb_warn("\nfailed to read apixs."); 5737ff178cdSJimmy Vetayases return (DCMD_ERR); 5747ff178cdSJimmy Vetayases } 5757ff178cdSJimmy Vetayases /* change to apix ttrace interrupt handler */ 5767ff178cdSJimmy Vetayases ttrace_hdlr[4].t_hdlr = ttrace_apix_interrupt; 5777ff178cdSJimmy Vetayases } 5787ff178cdSJimmy Vetayases 5797c478bd9Sstevel@tonic-gate if (mdb_walk("ttrace", (mdb_walk_cb_t)ttrace_walk, &dcmd) == -1) { 5807c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'ttrace'"); 5817c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate 5847c478bd9Sstevel@tonic-gate return (DCMD_OK); 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5887c478bd9Sstevel@tonic-gate int 5897c478bd9Sstevel@tonic-gate mutex_owner_init(mdb_walk_state_t *wsp) 5907c478bd9Sstevel@tonic-gate { 5917c478bd9Sstevel@tonic-gate return (WALK_NEXT); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate int 5957c478bd9Sstevel@tonic-gate mutex_owner_step(mdb_walk_state_t *wsp) 5967c478bd9Sstevel@tonic-gate { 5977c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 5987c478bd9Sstevel@tonic-gate mutex_impl_t mtx; 5997c478bd9Sstevel@tonic-gate uintptr_t owner; 6007c478bd9Sstevel@tonic-gate kthread_t thr; 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate if (mdb_vread(&mtx, sizeof (mtx), addr) == -1) 6037c478bd9Sstevel@tonic-gate return (WALK_ERR); 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate if (!MUTEX_TYPE_ADAPTIVE(&mtx)) 6067c478bd9Sstevel@tonic-gate return (WALK_DONE); 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate if ((owner = (uintptr_t)MUTEX_OWNER(&mtx)) == NULL) 6097c478bd9Sstevel@tonic-gate return (WALK_DONE); 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate if (mdb_vread(&thr, sizeof (thr), owner) != -1) 6127c478bd9Sstevel@tonic-gate (void) wsp->walk_callback(owner, &thr, wsp->walk_cbdata); 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate return (WALK_DONE); 6157c478bd9Sstevel@tonic-gate } 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate static void 6187c478bd9Sstevel@tonic-gate gate_desc_dump(gate_desc_t *gate, const char *label, int header) 6197c478bd9Sstevel@tonic-gate { 6207c478bd9Sstevel@tonic-gate const char *lastnm; 6217c478bd9Sstevel@tonic-gate uint_t lastval; 6227c478bd9Sstevel@tonic-gate char type[4]; 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate switch (gate->sgd_type) { 6257c478bd9Sstevel@tonic-gate case SDT_SYSIGT: 6267c478bd9Sstevel@tonic-gate strcpy(type, "int"); 6277c478bd9Sstevel@tonic-gate break; 6287c478bd9Sstevel@tonic-gate case SDT_SYSTGT: 6297c478bd9Sstevel@tonic-gate strcpy(type, "trp"); 6307c478bd9Sstevel@tonic-gate break; 6317c478bd9Sstevel@tonic-gate case SDT_SYSTASKGT: 6327c478bd9Sstevel@tonic-gate strcpy(type, "tsk"); 6337c478bd9Sstevel@tonic-gate break; 6347c478bd9Sstevel@tonic-gate default: 6357c478bd9Sstevel@tonic-gate (void) mdb_snprintf(type, sizeof (type), "%3x", gate->sgd_type); 6367c478bd9Sstevel@tonic-gate } 6377c478bd9Sstevel@tonic-gate 6387c478bd9Sstevel@tonic-gate #if defined(__amd64) 6397c478bd9Sstevel@tonic-gate lastnm = "IST"; 6407c478bd9Sstevel@tonic-gate lastval = gate->sgd_ist; 6417c478bd9Sstevel@tonic-gate #else 6427c478bd9Sstevel@tonic-gate lastnm = "STK"; 6437c478bd9Sstevel@tonic-gate lastval = gate->sgd_stkcpy; 6447c478bd9Sstevel@tonic-gate #endif 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate if (header) { 6477c478bd9Sstevel@tonic-gate mdb_printf("%*s%<u>%-30s%</u> %<u>%-4s%</u> %<u>%3s%</u> " 6487c478bd9Sstevel@tonic-gate "%<u>%1s%</u> %<u>%3s%</u> %<u>%3s%</u>\n", strlen(label), 6497c478bd9Sstevel@tonic-gate "", "HANDLER", "SEL", "DPL", "P", "TYP", lastnm); 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate mdb_printf("%s", label); 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate if (gate->sgd_type == SDT_SYSTASKGT) 6557c478bd9Sstevel@tonic-gate mdb_printf("%-30s ", "-"); 6567c478bd9Sstevel@tonic-gate else 6577c478bd9Sstevel@tonic-gate mdb_printf("%-30a ", GATESEG_GETOFFSET(gate)); 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate mdb_printf("%4x %d %c %3s %2x\n", gate->sgd_selector, 6607c478bd9Sstevel@tonic-gate gate->sgd_dpl, (gate->sgd_p ? '+' : ' '), type, lastval); 6617c478bd9Sstevel@tonic-gate } 6627c478bd9Sstevel@tonic-gate 6637c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6647c478bd9Sstevel@tonic-gate static int 6657c478bd9Sstevel@tonic-gate gate_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 6667c478bd9Sstevel@tonic-gate { 6677c478bd9Sstevel@tonic-gate gate_desc_t gate; 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 6707c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate if (mdb_vread(&gate, sizeof (gate_desc_t), addr) != 6737c478bd9Sstevel@tonic-gate sizeof (gate_desc_t)) { 6747c478bd9Sstevel@tonic-gate mdb_warn("failed to read gate descriptor at %p\n", addr); 6757c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6767c478bd9Sstevel@tonic-gate } 6777c478bd9Sstevel@tonic-gate 6787c478bd9Sstevel@tonic-gate gate_desc_dump(&gate, "", DCMD_HDRSPEC(flags)); 6797c478bd9Sstevel@tonic-gate 6807c478bd9Sstevel@tonic-gate return (DCMD_OK); 6817c478bd9Sstevel@tonic-gate } 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 6847c478bd9Sstevel@tonic-gate static int 6857c478bd9Sstevel@tonic-gate idt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 6867c478bd9Sstevel@tonic-gate { 6877c478bd9Sstevel@tonic-gate int i; 6887c478bd9Sstevel@tonic-gate 6897c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 690ac19272fSjosephb GElf_Sym idt0_va; 691ac19272fSjosephb gate_desc_t *idt0; 6927c478bd9Sstevel@tonic-gate 693ac19272fSjosephb if (mdb_lookup_by_name("idt0", &idt0_va) < 0) { 694ac19272fSjosephb mdb_warn("failed to find VA of idt0"); 6957c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6967c478bd9Sstevel@tonic-gate } 6977c478bd9Sstevel@tonic-gate 698ac19272fSjosephb addr = idt0_va.st_value; 699ac19272fSjosephb if (mdb_vread(&idt0, sizeof (idt0), addr) != sizeof (idt0)) { 700ac19272fSjosephb mdb_warn("failed to read idt0 at %p\n", addr); 701ac19272fSjosephb return (DCMD_ERR); 702ac19272fSjosephb } 703ac19272fSjosephb 704ac19272fSjosephb addr = (uintptr_t)idt0; 7057c478bd9Sstevel@tonic-gate } 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate for (i = 0; i < NIDT; i++, addr += sizeof (gate_desc_t)) { 7087c478bd9Sstevel@tonic-gate gate_desc_t gate; 7097c478bd9Sstevel@tonic-gate char label[6]; 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate if (mdb_vread(&gate, sizeof (gate_desc_t), addr) != 7127c478bd9Sstevel@tonic-gate sizeof (gate_desc_t)) { 7137c478bd9Sstevel@tonic-gate mdb_warn("failed to read gate descriptor at %p\n", 7147c478bd9Sstevel@tonic-gate addr); 7157c478bd9Sstevel@tonic-gate return (DCMD_ERR); 7167c478bd9Sstevel@tonic-gate } 7177c478bd9Sstevel@tonic-gate 7187c478bd9Sstevel@tonic-gate (void) mdb_snprintf(label, sizeof (label), "%3d: ", i); 7197c478bd9Sstevel@tonic-gate gate_desc_dump(&gate, label, i == 0); 7207c478bd9Sstevel@tonic-gate } 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate return (DCMD_OK); 7237c478bd9Sstevel@tonic-gate } 7247c478bd9Sstevel@tonic-gate 725ae115bc7Smrj static void 726ae115bc7Smrj htables_help(void) 727ae115bc7Smrj { 728ae115bc7Smrj mdb_printf( 729ae115bc7Smrj "Given a (hat_t *), generates the list of all (htable_t *)s\n" 730ae115bc7Smrj "that correspond to that address space\n"); 731ae115bc7Smrj } 732ae115bc7Smrj 733ae115bc7Smrj static void 734ae115bc7Smrj report_maps_help(void) 735ae115bc7Smrj { 736ae115bc7Smrj mdb_printf( 737ae115bc7Smrj "Given a PFN, report HAT structures that map the page, or use\n" 738ae115bc7Smrj "the page as a pagetable.\n" 739ae115bc7Smrj "\n" 740843e1988Sjohnlev "-m Interpret the PFN as an MFN (machine frame number)\n"); 741ae115bc7Smrj } 742ae115bc7Smrj 743ae115bc7Smrj static void 744ae115bc7Smrj ptable_help(void) 745ae115bc7Smrj { 746ae115bc7Smrj mdb_printf( 747ae115bc7Smrj "Given a PFN holding a page table, print its contents, and\n" 748ae115bc7Smrj "the address of the corresponding htable structure.\n" 749ae115bc7Smrj "\n" 750843e1988Sjohnlev "-m Interpret the PFN as an MFN (machine frame number)\n"); 751ae115bc7Smrj } 752ae115bc7Smrj 753f7b98820SBryan Cantrill /* 754f7b98820SBryan Cantrill * NSEC_SHIFT is replicated here (it is not defined in a header file), 755f7b98820SBryan Cantrill * but for amusement, the reader is directed to the comment that explains 756f7b98820SBryan Cantrill * the rationale for this particular value on x86. Spoiler: the value is 757f7b98820SBryan Cantrill * selected to accommodate 60 MHz Pentiums! (And a confession: if the voice 758f7b98820SBryan Cantrill * in that comment sounds too familiar, it's because your author also wrote 759f7b98820SBryan Cantrill * that code -- some fifteen years prior to this writing in 2011...) 760f7b98820SBryan Cantrill */ 761f7b98820SBryan Cantrill #define NSEC_SHIFT 5 762f7b98820SBryan Cantrill 763f7b98820SBryan Cantrill /*ARGSUSED*/ 764f7b98820SBryan Cantrill static int 765f7b98820SBryan Cantrill scalehrtime_cmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 766f7b98820SBryan Cantrill { 767f7b98820SBryan Cantrill uint32_t nsec_scale; 768f7b98820SBryan Cantrill hrtime_t tsc = addr, hrt; 769f7b98820SBryan Cantrill unsigned int *tscp = (unsigned int *)&tsc; 770f7b98820SBryan Cantrill uintptr_t scalehrtimef; 771f7b98820SBryan Cantrill uint64_t scale; 772f7b98820SBryan Cantrill GElf_Sym sym; 773f7b98820SBryan Cantrill 774f7b98820SBryan Cantrill if (!(flags & DCMD_ADDRSPEC)) { 775f7b98820SBryan Cantrill if (argc != 1) 776f7b98820SBryan Cantrill return (DCMD_USAGE); 777f7b98820SBryan Cantrill 778f7b98820SBryan Cantrill switch (argv[0].a_type) { 779f7b98820SBryan Cantrill case MDB_TYPE_STRING: 780f7b98820SBryan Cantrill tsc = mdb_strtoull(argv[0].a_un.a_str); 781f7b98820SBryan Cantrill break; 782f7b98820SBryan Cantrill case MDB_TYPE_IMMEDIATE: 783f7b98820SBryan Cantrill tsc = argv[0].a_un.a_val; 784f7b98820SBryan Cantrill break; 785f7b98820SBryan Cantrill default: 786f7b98820SBryan Cantrill return (DCMD_USAGE); 787f7b98820SBryan Cantrill } 788f7b98820SBryan Cantrill } 789f7b98820SBryan Cantrill 790f7b98820SBryan Cantrill if (mdb_readsym(&scalehrtimef, 791f7b98820SBryan Cantrill sizeof (scalehrtimef), "scalehrtimef") == -1) { 792f7b98820SBryan Cantrill mdb_warn("couldn't read 'scalehrtimef'"); 793f7b98820SBryan Cantrill return (DCMD_ERR); 794f7b98820SBryan Cantrill } 795f7b98820SBryan Cantrill 796f7b98820SBryan Cantrill if (mdb_lookup_by_name("tsc_scalehrtime", &sym) == -1) { 797f7b98820SBryan Cantrill mdb_warn("couldn't find 'tsc_scalehrtime'"); 798f7b98820SBryan Cantrill return (DCMD_ERR); 799f7b98820SBryan Cantrill } 800f7b98820SBryan Cantrill 801f7b98820SBryan Cantrill if (sym.st_value != scalehrtimef) { 802f7b98820SBryan Cantrill mdb_warn("::scalehrtime requires that scalehrtimef " 803f7b98820SBryan Cantrill "be set to tsc_scalehrtime\n"); 804f7b98820SBryan Cantrill return (DCMD_ERR); 805f7b98820SBryan Cantrill } 806f7b98820SBryan Cantrill 807f7b98820SBryan Cantrill if (mdb_readsym(&nsec_scale, sizeof (nsec_scale), "nsec_scale") == -1) { 808f7b98820SBryan Cantrill mdb_warn("couldn't read 'nsec_scale'"); 809f7b98820SBryan Cantrill return (DCMD_ERR); 810f7b98820SBryan Cantrill } 811f7b98820SBryan Cantrill 812f7b98820SBryan Cantrill scale = (uint64_t)nsec_scale; 813f7b98820SBryan Cantrill 814f7b98820SBryan Cantrill hrt = ((uint64_t)tscp[1] * scale) << NSEC_SHIFT; 815f7b98820SBryan Cantrill hrt += ((uint64_t)tscp[0] * scale) >> (32 - NSEC_SHIFT); 816f7b98820SBryan Cantrill 817f7b98820SBryan Cantrill mdb_printf("0x%llx\n", hrt); 818f7b98820SBryan Cantrill 819f7b98820SBryan Cantrill return (DCMD_OK); 820f7b98820SBryan Cantrill } 821f7b98820SBryan Cantrill 82229f78cfaSRobert Mustacchi /* 82329f78cfaSRobert Mustacchi * The x86 feature set is implemented as a bitmap array. That bitmap array is 82429f78cfaSRobert Mustacchi * stored across a number of uchars based on the BT_SIZEOFMAP(NUM_X86_FEATURES) 82529f78cfaSRobert Mustacchi * macro. We have the names for each of these features in unix's text segment 82629f78cfaSRobert Mustacchi * so we do not have to duplicate them and instead just look them up. 82729f78cfaSRobert Mustacchi */ 82829f78cfaSRobert Mustacchi /*ARGSUSED*/ 82929f78cfaSRobert Mustacchi static int 83029f78cfaSRobert Mustacchi x86_featureset_cmd(uintptr_t addr, uint_t flags, int argc, 83129f78cfaSRobert Mustacchi const mdb_arg_t *argv) 83229f78cfaSRobert Mustacchi { 83329f78cfaSRobert Mustacchi void *fset; 83429f78cfaSRobert Mustacchi GElf_Sym sym; 83529f78cfaSRobert Mustacchi uintptr_t nptr; 83629f78cfaSRobert Mustacchi char name[128]; 83729f78cfaSRobert Mustacchi int ii; 83829f78cfaSRobert Mustacchi 83929f78cfaSRobert Mustacchi size_t sz = sizeof (uchar_t) * BT_SIZEOFMAP(NUM_X86_FEATURES); 84029f78cfaSRobert Mustacchi 84129f78cfaSRobert Mustacchi if (argc != 0) 84229f78cfaSRobert Mustacchi return (DCMD_USAGE); 84329f78cfaSRobert Mustacchi 84429f78cfaSRobert Mustacchi if (mdb_lookup_by_name("x86_feature_names", &sym) == -1) { 84529f78cfaSRobert Mustacchi mdb_warn("couldn't find x86_feature_names"); 84629f78cfaSRobert Mustacchi return (DCMD_ERR); 84729f78cfaSRobert Mustacchi } 84829f78cfaSRobert Mustacchi 84929f78cfaSRobert Mustacchi fset = mdb_zalloc(sz, UM_NOSLEEP); 85029f78cfaSRobert Mustacchi if (fset == NULL) { 85129f78cfaSRobert Mustacchi mdb_warn("failed to allocate memory for x86_featureset"); 85229f78cfaSRobert Mustacchi return (DCMD_ERR); 85329f78cfaSRobert Mustacchi } 85429f78cfaSRobert Mustacchi 85529f78cfaSRobert Mustacchi if (mdb_readvar(fset, "x86_featureset") != sz) { 85629f78cfaSRobert Mustacchi mdb_warn("failed to read x86_featureset"); 85729f78cfaSRobert Mustacchi mdb_free(fset, sz); 85829f78cfaSRobert Mustacchi return (DCMD_ERR); 85929f78cfaSRobert Mustacchi } 86029f78cfaSRobert Mustacchi 86129f78cfaSRobert Mustacchi for (ii = 0; ii < NUM_X86_FEATURES; ii++) { 86229f78cfaSRobert Mustacchi if (!BT_TEST((ulong_t *)fset, ii)) 86329f78cfaSRobert Mustacchi continue; 86429f78cfaSRobert Mustacchi 86529f78cfaSRobert Mustacchi if (mdb_vread(&nptr, sizeof (char *), sym.st_value + 86629f78cfaSRobert Mustacchi sizeof (void *) * ii) != sizeof (char *)) { 86729f78cfaSRobert Mustacchi mdb_warn("failed to read feature array %d", ii); 86829f78cfaSRobert Mustacchi mdb_free(fset, sz); 86929f78cfaSRobert Mustacchi return (DCMD_ERR); 87029f78cfaSRobert Mustacchi } 87129f78cfaSRobert Mustacchi 87229f78cfaSRobert Mustacchi if (mdb_readstr(name, sizeof (name), nptr) == -1) { 87329f78cfaSRobert Mustacchi mdb_warn("failed to read feature %d", ii); 87429f78cfaSRobert Mustacchi mdb_free(fset, sz); 87529f78cfaSRobert Mustacchi return (DCMD_ERR); 87629f78cfaSRobert Mustacchi } 87729f78cfaSRobert Mustacchi mdb_printf("%s\n", name); 87829f78cfaSRobert Mustacchi } 87929f78cfaSRobert Mustacchi 88029f78cfaSRobert Mustacchi mdb_free(fset, sz); 88129f78cfaSRobert Mustacchi return (DCMD_OK); 88229f78cfaSRobert Mustacchi } 88329f78cfaSRobert Mustacchi 884*799823bbSRobert Mustacchi #ifdef _KMDB 885*799823bbSRobert Mustacchi /* ARGSUSED */ 886*799823bbSRobert Mustacchi static int 887*799823bbSRobert Mustacchi crregs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 888*799823bbSRobert Mustacchi { 889*799823bbSRobert Mustacchi ulong_t cr0, cr4; 890*799823bbSRobert Mustacchi static const mdb_bitmask_t cr0_flag_bits[] = { 891*799823bbSRobert Mustacchi { "PE", CR0_PE, CR0_PE }, 892*799823bbSRobert Mustacchi { "MP", CR0_MP, CR0_MP }, 893*799823bbSRobert Mustacchi { "EM", CR0_EM, CR0_EM }, 894*799823bbSRobert Mustacchi { "TS", CR0_TS, CR0_TS }, 895*799823bbSRobert Mustacchi { "ET", CR0_ET, CR0_ET }, 896*799823bbSRobert Mustacchi { "NE", CR0_NE, CR0_NE }, 897*799823bbSRobert Mustacchi { "WP", CR0_WP, CR0_WP }, 898*799823bbSRobert Mustacchi { "AM", CR0_AM, CR0_AM }, 899*799823bbSRobert Mustacchi { "NW", CR0_NW, CR0_NW }, 900*799823bbSRobert Mustacchi { "CD", CR0_CD, CR0_CD }, 901*799823bbSRobert Mustacchi { "PG", CR0_PG, CR0_PG }, 902*799823bbSRobert Mustacchi { NULL, 0, 0 } 903*799823bbSRobert Mustacchi }; 904*799823bbSRobert Mustacchi 905*799823bbSRobert Mustacchi static const mdb_bitmask_t cr4_flag_bits[] = { 906*799823bbSRobert Mustacchi { "VME", CR4_VME, CR4_VME }, 907*799823bbSRobert Mustacchi { "PVI", CR4_PVI, CR4_PVI }, 908*799823bbSRobert Mustacchi { "TSD", CR4_TSD, CR4_TSD }, 909*799823bbSRobert Mustacchi { "DE", CR4_DE, CR4_DE }, 910*799823bbSRobert Mustacchi { "PSE", CR4_PSE, CR4_PSE }, 911*799823bbSRobert Mustacchi { "PAE", CR4_PAE, CR4_PAE }, 912*799823bbSRobert Mustacchi { "MCE", CR4_MCE, CR4_MCE }, 913*799823bbSRobert Mustacchi { "PGE", CR4_PGE, CR4_PGE }, 914*799823bbSRobert Mustacchi { "PCE", CR4_PCE, CR4_PCE }, 915*799823bbSRobert Mustacchi { "OSFXSR", CR4_OSFXSR, CR4_OSFXSR }, 916*799823bbSRobert Mustacchi { "OSXMMEXCPT", CR4_OSXMMEXCPT, CR4_OSXMMEXCPT }, 917*799823bbSRobert Mustacchi { "VMXE", CR4_VMXE, CR4_VMXE }, 918*799823bbSRobert Mustacchi { "SMXE", CR4_SMXE, CR4_SMXE }, 919*799823bbSRobert Mustacchi { "OSXSAVE", CR4_OSXSAVE, CR4_OSXSAVE }, 920*799823bbSRobert Mustacchi { "SMEP", CR4_SMEP, CR4_SMEP }, 921*799823bbSRobert Mustacchi { NULL, 0, 0 } 922*799823bbSRobert Mustacchi }; 923*799823bbSRobert Mustacchi 924*799823bbSRobert Mustacchi cr0 = kmdb_unix_getcr0(); 925*799823bbSRobert Mustacchi cr4 = kmdb_unix_getcr4(); 926*799823bbSRobert Mustacchi mdb_printf("%%cr0 = 0x%08x <%b>\n", cr0, cr0, cr0_flag_bits); 927*799823bbSRobert Mustacchi mdb_printf("%%cr4 = 0x%08x <%b>\n", cr4, cr4, cr4_flag_bits); 928*799823bbSRobert Mustacchi return (DCMD_OK); 929*799823bbSRobert Mustacchi } 930*799823bbSRobert Mustacchi #endif 931*799823bbSRobert Mustacchi 9327c478bd9Sstevel@tonic-gate static const mdb_dcmd_t dcmds[] = { 9337c478bd9Sstevel@tonic-gate { "gate_desc", ":", "dump a gate descriptor", gate_desc }, 9347c478bd9Sstevel@tonic-gate { "idt", ":[-v]", "dump an IDT", idt }, 9357c478bd9Sstevel@tonic-gate { "ttrace", "[-x]", "dump trap trace buffers", ttrace }, 9367c478bd9Sstevel@tonic-gate { "vatopfn", ":[-a as]", "translate address to physical page", 9377c478bd9Sstevel@tonic-gate va2pfn_dcmd }, 938ae115bc7Smrj { "report_maps", ":[-m]", 939ae115bc7Smrj "Given PFN, report mappings / page table usage", 940ae115bc7Smrj report_maps_dcmd, report_maps_help }, 941ae115bc7Smrj { "htables", "", "Given hat_t *, lists all its htable_t * values", 942ae115bc7Smrj htables_dcmd, htables_help }, 943ae115bc7Smrj { "ptable", ":[-m]", "Given PFN, dump contents of a page table", 944ae115bc7Smrj ptable_dcmd, ptable_help }, 9457c478bd9Sstevel@tonic-gate { "pte", ":[-p XXXXX] [-l N]", "print human readable page table entry", 9467c478bd9Sstevel@tonic-gate pte_dcmd }, 947843e1988Sjohnlev { "pfntomfn", ":", "convert physical page to hypervisor machine page", 948843e1988Sjohnlev pfntomfn_dcmd }, 949843e1988Sjohnlev { "mfntopfn", ":", "convert hypervisor machine page to physical page", 950843e1988Sjohnlev mfntopfn_dcmd }, 9517c478bd9Sstevel@tonic-gate { "memseg_list", ":", "show memseg list", memseg_list }, 952f7b98820SBryan Cantrill { "scalehrtime", ":", 953f7b98820SBryan Cantrill "scale an unscaled high-res time", scalehrtime_cmd }, 95429f78cfaSRobert Mustacchi { "x86_featureset", NULL, "dump the x86_featureset vector", 95529f78cfaSRobert Mustacchi x86_featureset_cmd }, 956*799823bbSRobert Mustacchi #ifdef _KMDB 957*799823bbSRobert Mustacchi { "crregs", NULL, "dump control registers", crregs_dcmd }, 958*799823bbSRobert Mustacchi #endif 9597c478bd9Sstevel@tonic-gate { NULL } 9607c478bd9Sstevel@tonic-gate }; 9617c478bd9Sstevel@tonic-gate 9627c478bd9Sstevel@tonic-gate static const mdb_walker_t walkers[] = { 9637c478bd9Sstevel@tonic-gate { "ttrace", "walks trap trace buffers in reverse chronological order", 9647c478bd9Sstevel@tonic-gate ttrace_walk_init, ttrace_walk_step, ttrace_walk_fini }, 9657c478bd9Sstevel@tonic-gate { "mutex_owner", "walks the owner of a mutex", 9667c478bd9Sstevel@tonic-gate mutex_owner_init, mutex_owner_step }, 9677c478bd9Sstevel@tonic-gate { "memseg", "walk the memseg structures", 9687c478bd9Sstevel@tonic-gate memseg_walk_init, memseg_walk_step, memseg_walk_fini }, 9697c478bd9Sstevel@tonic-gate { NULL } 9707c478bd9Sstevel@tonic-gate }; 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 9737c478bd9Sstevel@tonic-gate 9747c478bd9Sstevel@tonic-gate const mdb_modinfo_t * 9757c478bd9Sstevel@tonic-gate _mdb_init(void) 9767c478bd9Sstevel@tonic-gate { 9777c478bd9Sstevel@tonic-gate return (&modinfo); 9787c478bd9Sstevel@tonic-gate } 979843e1988Sjohnlev 980843e1988Sjohnlev void 981843e1988Sjohnlev _mdb_fini(void) 982843e1988Sjohnlev { 983843e1988Sjohnlev free_mmu(); 984843e1988Sjohnlev } 985