/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2015 Joyent, Inc.
*/
#include <mdb/mdb_modapi.h>
#include <sys/traptrace.h>
#include <sys/xc_levels.h>
#include <sys/mutex_impl.h>
#include "i86mmu.h"
#include "unix_sup.h"
#include <sys/x86_archext.h>
#include <sys/controlregs.h>
/* apix only */
static int use_apix = 0;
static int
ttrace_ttr_size_check(void)
{
mdb_warn("failed to determine size of trap_trace_rec_t; "
"non-TRAPTRACE kernel?\n");
return (0);
}
sizeof (trap_trace_rec_t)) {
/*
* On Intel machines, this will happen when TTR_STACK_DEPTH
* is changed. This code could be smarter, and could
* dynamically adapt to different depths, but not until a
* need for such adaptation is demonstrated.
*/
mdb_warn("size of trap_trace_rec_t (%d bytes) doesn't "
return (0);
}
return (1);
}
int
{
int i;
if (!ttrace_ttr_size_check())
return (WALK_ERR);
mdb_warn("ttrace only supports global walks\n");
return (WALK_ERR);
}
mdb_warn("symbol 'trap_trace_ctl' not found; "
"non-TRAPTRACE kernel?\n");
return (WALK_ERR);
}
/*
* We'll poach the ttc_current pointer (which isn't used for
* anything) to store a pointer to our current TRAPTRACE record.
* This allows us to only keep the array of trap_trace_ctl structures
* as our walker state (ttc_current may be the only kernel data
* structure member added exclusively to make writing the mdb walker
* a little easier).
*/
for (i = 0; i < NCPU; i++) {
continue;
/*
* Assign ttc_current to be the last completed record.
* Note that the error checking (i.e. in the ttc_next ==
* ttc_first case) is performed in the step function.
*/
}
return (WALK_NEXT);
}
int
{
/*
* Loop through the CPUs, looking for the latest trap trace record
* (we want to walk through the trap trace records in reverse
* chronological order).
*/
for (i = 0; i < NCPU; i++) {
continue;
return (WALK_ERR);
}
latest_ttc = ttc;
}
}
if (latest == 0)
return (WALK_DONE);
ttc = latest_ttc;
return (WALK_ERR);
}
else
return (rval);
}
void
{
}
static int
{
return (0);
}
mdb_warn("\ncouldn't find 'sysent'");
return (-1);
}
return (-1);
}
return (-1);
}
return (0);
}
static int
{
case T_SOFTINT:
return (0);
default:
break;
}
mdb_warn("\ncouldn't find 'autovect'");
return (-1);
}
return (-1);
}
return (-1);
}
else
} else {
mdb_warn("couldn't read autovec at %p",
}
}
return (0);
}
static int
{
case T_SOFTINT:
return (0);
default:
break;
}
/* Read the per CPU apix entry */
return (-1);
}
return (-1);
}
if (apix_vector.v_share == 0) {
else
} else {
mdb_warn("couldn't read autovec at %p",
}
}
return (0);
}
static struct {
int tt_trapno;
char *tt_name;
} ttrace_traps[] = {
{ T_ZERODIV, "divide-error" },
{ T_SGLSTP, "debug-exception" },
{ T_NMIFLT, "nmi-interrupt" },
{ T_BPTFLT, "breakpoint" },
{ T_OVFLW, "into-overflow" },
{ T_BOUNDFLT, "bound-exceeded" },
{ T_ILLINST, "invalid-opcode" },
{ T_NOEXTFLT, "device-not-avail" },
{ T_DBLFLT, "double-fault" },
{ T_EXTOVRFLT, "segment-overrun" },
{ T_TSSFLT, "invalid-tss" },
{ T_SEGFLT, "segment-not-pres" },
{ T_STKFLT, "stack-fault" },
{ T_GPFLT, "general-protectn" },
{ T_PGFLT, "page-fault" },
{ T_EXTERRFLT, "error-fault" },
{ T_ALIGNMENT, "alignment-check" },
{ T_MCE, "machine-check" },
{ T_SIMDFPE, "sse-exception" },
{ T_DBGENTR, "debug-enter" },
{ T_FASTTRAP, "fasttrap-0xd2" },
{ T_SYSCALLINT, "syscall-0x91" },
{ T_DTRACE_RET, "dtrace-ret" },
{ T_SOFTINT, "softint" },
{ T_INTERRUPT, "interrupt" },
{ T_FAULT, "fault" },
{ T_AST, "ast" },
{ T_SYSCALL, "syscall" },
{ 0, NULL }
};
static int
{
int i;
else
break;
}
else
return (0);
}
static void
{
}
static struct {
char *t_name;
} ttrace_hdlr[] = {
};
typedef struct ttrace_dcmd {
#if defined(__amd64)
static void
{
mdb_printf("\n");
}
#else
static void
{
mdb_printf("\n");
}
#endif /* __amd64 */
int
{
for (i = 0; i < NCPU; i++) {
cpu = i;
break;
}
}
if (cpu == -1) {
return (WALK_ERR);
}
return (WALK_NEXT);
continue;
return (WALK_ERR);
}
return (WALK_NEXT);
else
if (rec->ttr_sdepth > 0) {
for (i = 0; i < rec->ttr_sdepth; i++) {
if (i >= TTR_STACK_DEPTH) {
mdb_printf("%17s*** invalid ttr_sdepth (is %d, "
break;
}
}
mdb_printf("\n");
}
return (WALK_NEXT);
}
int
{
if (!ttrace_ttr_size_check())
return (WALK_ERR);
mdb_warn("symbol 'trap_trace_ctl' not found; "
"non-TRAPTRACE kernel?\n");
return (DCMD_ERR);
}
return (DCMD_USAGE);
if (DCMD_HDRSPEC(flags)) {
" EIP");
}
if (flags & DCMD_ADDRSPEC) {
mdb_warn("couldn't read trap trace record "
"at %p", addr);
return (DCMD_ERR);
}
return (DCMD_ERR);
return (DCMD_OK);
}
}
mdb_warn("failed to read apix_enable");
use_apix = 0;
}
if (use_apix) {
mdb_warn("\nfailed to read apixs.");
return (DCMD_ERR);
}
/* change to apix ttrace interrupt handler */
}
mdb_warn("couldn't walk 'ttrace'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
int
{
return (WALK_NEXT);
}
int
{
return (WALK_ERR);
if (!MUTEX_TYPE_ADAPTIVE(&mtx))
return (WALK_DONE);
return (WALK_DONE);
return (WALK_DONE);
}
static void
{
const char *lastnm;
case SDT_SYSIGT:
break;
case SDT_SYSTGT:
break;
case SDT_SYSTASKGT:
break;
default:
}
#if defined(__amd64)
lastnm = "IST";
#else
lastnm = "STK";
#endif
if (header) {
mdb_printf("%*s%<u>%-30s%</u> %<u>%-4s%</u> %<u>%3s%</u> "
}
else
}
/*ARGSUSED*/
static int
{
return (DCMD_USAGE);
sizeof (gate_desc_t)) {
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
int i;
if (!(flags & DCMD_ADDRSPEC)) {
mdb_warn("failed to find VA of idt0");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
}
sizeof (gate_desc_t)) {
mdb_warn("failed to read gate descriptor at %p\n",
addr);
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
static void
htables_help(void)
{
"Given a (hat_t *), generates the list of all (htable_t *)s\n"
"that correspond to that address space\n");
}
static void
report_maps_help(void)
{
"Given a PFN, report HAT structures that map the page, or use\n"
"the page as a pagetable.\n"
"\n"
"-m Interpret the PFN as an MFN (machine frame number)\n");
}
static void
ptable_help(void)
{
"Given a PFN holding a page table, print its contents, and\n"
"the address of the corresponding htable structure.\n"
"\n"
"-m Interpret the PFN as an MFN (machine frame number)\n");
}
/*
* NSEC_SHIFT is replicated here (it is not defined in a header file),
* but for amusement, the reader is directed to the comment that explains
* the rationale for this particular value on x86. Spoiler: the value is
* selected to accommodate 60 MHz Pentiums! (And a confession: if the voice
* in that comment sounds too familiar, it's because your author also wrote
* that code -- some fifteen years prior to this writing in 2011...)
*/
/*ARGSUSED*/
static int
{
if (!(flags & DCMD_ADDRSPEC)) {
if (argc != 1)
return (DCMD_USAGE);
case MDB_TYPE_STRING:
break;
case MDB_TYPE_IMMEDIATE:
break;
default:
return (DCMD_USAGE);
}
}
if (mdb_readsym(&scalehrtimef,
mdb_warn("couldn't read 'scalehrtimef'");
return (DCMD_ERR);
}
mdb_warn("couldn't find 'tsc_scalehrtime'");
return (DCMD_ERR);
}
mdb_warn("::scalehrtime requires that scalehrtimef "
"be set to tsc_scalehrtime\n");
return (DCMD_ERR);
}
mdb_warn("couldn't read 'nsec_scale'");
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*
* The x86 feature set is implemented as a bitmap array. That bitmap array is
* stored across a number of uchars based on the BT_SIZEOFMAP(NUM_X86_FEATURES)
* macro. We have the names for each of these features in unix's text segment
* so we do not have to duplicate them and instead just look them up.
*/
/*ARGSUSED*/
static int
{
void *fset;
int ii;
if (argc != 0)
return (DCMD_USAGE);
mdb_warn("couldn't find x86_feature_names");
return (DCMD_ERR);
}
mdb_warn("failed to allocate memory for x86_featureset");
return (DCMD_ERR);
}
mdb_warn("failed to read x86_featureset");
return (DCMD_ERR);
}
continue;
sizeof (void *) * ii) != sizeof (char *)) {
return (DCMD_ERR);
}
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
#ifdef _KMDB
/* ARGSUSED */
static int
{
{ NULL, 0, 0 }
};
{ NULL, 0, 0 }
};
cr0 = kmdb_unix_getcr0();
cr4 = kmdb_unix_getcr4();
return (DCMD_OK);
}
#endif
{ "vatopfn", ":[-a as]", "translate address to physical page",
va2pfn_dcmd },
{ "report_maps", ":[-m]",
"Given PFN, report mappings / page table usage",
{ "htables", "", "Given hat_t *, lists all its htable_t * values",
{ "ptable", ":[-m]", "Given PFN, dump contents of a page table",
{ "pte", ":[-p XXXXX] [-l N]", "print human readable page table entry",
pte_dcmd },
{ "pfntomfn", ":", "convert physical page to hypervisor machine page",
{ "mfntopfn", ":", "convert hypervisor machine page to physical page",
{ "scalehrtime", ":",
"scale an unscaled high-res time", scalehrtime_cmd },
#ifdef _KMDB
#endif
{ NULL }
};
{ "ttrace", "walks trap trace buffers in reverse chronological order",
{ "mutex_owner", "walks the owner of a mutex",
{ "memseg", "walk the memseg structures",
{ NULL }
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}
void
_mdb_fini(void)
{
free_mmu();
}