/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <sys/types.h>

#ifndef DEBUG
#define	DEBUG
#define	_SYS_DEBUG_H
#include <sys/xc_impl.h>
#undef	DEBUG
#else
#define	_SYS_DEBUG_H
#include <sys/xc_impl.h>
#endif

#include <sys/traptrace.h>
#include <sys/machparam.h>
#include <sys/intreg.h>
#include <sys/ivintr.h>
#include <sys/mutex_impl.h>

#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ctf.h>
#include <mdb/mdb_whatis.h>
#include "sfmmu.h"

#ifndef SYSTRAP_TT
#define	SYSTRAP_TT	0x1300
#endif

typedef struct trap_trace_fullrec {
	struct trap_trace_record ttf_rec;
	int ttf_cpu;
} trap_trace_fullrec_t;

#ifdef sun4v
typedef struct htrap_trace_fullrec {
	struct htrap_trace_record ttf_rec;
	int ttf_cpu;
} htrap_trace_fullrec_t;
#endif

/*
 * These strings and accompanying macros allow our string table to look
 * just like the real table in trap_table.s.
 */

static const char NOT[] = "reserved";	/* common reserved string */
static const char BAD[] = "unused";	/* common unused string */

#define	NOT4	NOT, NOT, NOT, NOT
#define	BAD4	BAD, BAD, BAD, BAD

static const char *const ttdescr[] = {
	NOT,				/* 000	reserved */
	"power-on",			/* 001	power on reset */
	"watchdog",			/* 002	watchdog reset */
	"xir",				/* 003	externally initiated reset */
	"sir",				/* 004	software initiated reset */
	"red",				/* 005	red mode exception */
	NOT, NOT,			/* 006 - 007 reserved */
	"immu-xcp",			/* 008	instruction access exception */
	"immu-miss",			/* 009	instruction access MMU miss */
	"immu-err",			/* 00A	instruction access error */
	NOT, NOT4,			/* 00B - 00F reserved */
	"ill-inst",			/* 010	illegal instruction */
	"priv-inst",			/* 011	privileged opcode */
	"unimp-ldd",			/* 012	unimplemented LDD */
	"unimp-std",			/* 013	unimplemented STD */
	NOT4, NOT4, NOT4,		/* 014 - 01F reserved */
	"fp-disable",			/* 020	fp disabled */
	"fp-ieee754",			/* 021	fp exception ieee 754 */
	"fp-xcp-other",			/* 022	fp exception other */
	"tag-oflow",			/* 023	tag overflow */
	"cleanwin",			/* 024	clean window */
	"cleanwin",			/* 025	clean window */
	"cleanwin",			/* 026	clean window */
	"cleanwin",			/* 027	clean window */
	"div-zero",			/* 028	division by zero */
	"internal-err",			/* 029	internal processor error */
	NOT, NOT, NOT4,			/* 02A - 02F reserved */
	"dmmu-xcp",			/* 030	data access exception */
	"dmmu-miss",			/* 031	data access MMU miss */
	"dmmu-err",			/* 032	data access error */
	"dmmu-prot",			/* 033	data access protection */
	"unalign",			/* 034	mem address not aligned */
	"lddf-unalign",			/* 035	LDDF mem address not aligned */
	"stdf-unalign",			/* 036	STDF mem address not aligned */
	"priv-act",			/* 037	privileged action */
	"ldqf-unalign",			/* 038	LDQF mem address not aligned */
	"stqf-unalign",			/* 039	STQF mem address not aligned */
	NOT, NOT, NOT4,			/* 03A - 03F reserved */
	"async-d-err",			/* 040	async data error */
	"level-1",			/* 041	interrupt level 1 */
	"level-2",			/* 042	interrupt level 2 */
	"level-3",			/* 043	interrupt level 3 */
	"level-4",			/* 044	interrupt level 4 */
	"level-5",			/* 045	interrupt level 5 */
	"level-6",			/* 046	interrupt level 6 */
	"level-7",			/* 047	interrupt level 7 */
	"level-8",			/* 048	interrupt level 8 */
	"level-9",			/* 049	interrupt level 9 */
	"level-10",			/* 04A	interrupt level 10 */
	"level-11",			/* 04B	interrupt level 11 */
	"level-12",			/* 04C	interrupt level 12 */
	"level-13",			/* 04D	interrupt level 13 */
	"level-14",			/* 04E	interrupt level 14 */
	"level-15",			/* 04F	interrupt level 15 */
	NOT4, NOT4, NOT4, NOT4,		/* 050 - 05F reserved */
	"int-vec",			/* 060	interrupt vector */
	"pa-watch",			/* 061	PA watchpoint */
	"va-watch",			/* 062	VA watchpoint */
	"ecc-err",			/* 063	corrected ECC error */
	"itlb-miss",			/* 064	instruction access MMU miss */
	"itlb-miss",			/* 065	instruction access MMU miss */
	"itlb-miss",			/* 066	instruction access MMU miss */
	"itlb-miss",			/* 067	instruction access MMU miss */
	"dtlb-miss",			/* 068	data access MMU miss */
	"dtlb-miss",			/* 069	data access MMU miss */
	"dtlb-miss",			/* 06A	data access MMU miss */
	"dtlb-miss",			/* 06B	data access MMU miss */
	"dtlb-prot",			/* 06C	data access protection */
	"dtlb-prot",			/* 06D	data access protection */
	"dtlb-prot",			/* 06E	data access protection */
	"dtlb-prot",			/* 06F	data access protection */
	"fast-ecc-err",			/* 070	fast ecache ECC error */
	"dp-err",			/* 071	data cache parity error */
	"ip-err",			/* 072	instr cache parity error */
	NOT, NOT4, NOT4,		/* 073 - 07B reserved */
#ifdef sun4v
	"cpu-mondo",			/* 07C  CPU mondo */
	"dev-mondo",			/* 07D  device mondo */
	"res.-err",			/* 07E  resumable error */
	"non-res.-err",			/* 07F  non-resumable error */
#else
	NOT4,				/* 07C - 07F reserved */
#endif
	"spill-0-norm",			/* 080	spill 0 normal */
	"spill-0-norm",			/* 081	spill 0 normal */
	"spill-0-norm",			/* 082	spill 0 normal */
	"spill-0-norm",			/* 083	spill 0 normal */
	"spill-1-norm",			/* 084	spill 1 normal */
	"spill-1-norm",			/* 085	spill 1 normal */
	"spill-1-norm",			/* 086	spill 1 normal */
	"spill-1-norm",			/* 087	spill 1 normal */
	"spill-2-norm",			/* 088	spill 2 normal */
	"spill-2-norm",			/* 089	spill 2 normal */
	"spill-2-norm",			/* 08A	spill 2 normal */
	"spill-2-norm",			/* 08B	spill 2 normal */
	"spill-3-norm",			/* 08C	spill 3 normal */
	"spill-3-norm",			/* 08D	spill 3 normal */
	"spill-3-norm",			/* 08E	spill 3 normal */
	"spill-3-norm",			/* 08F	spill 3 normal */
	"spill-4-norm",			/* 090	spill 4 normal */
	"spill-4-norm",			/* 091	spill 4 normal */
	"spill-4-norm",			/* 092	spill 4 normal */
	"spill-4-norm",			/* 093	spill 4 normal */
	"spill-5-norm",			/* 094	spill 5 normal */
	"spill-5-norm",			/* 095	spill 5 normal */
	"spill-5-norm",			/* 096	spill 5 normal */
	"spill-5-norm",			/* 097	spill 5 normal */
	"spill-6-norm",			/* 098	spill 6 normal */
	"spill-6-norm",			/* 099	spill 6 normal */
	"spill-6-norm",			/* 09A	spill 6 normal */
	"spill-6-norm",			/* 09B	spill 6 normal */
	"spill-7-norm",			/* 09C	spill 7 normal */
	"spill-7-norm",			/* 09D	spill 7 normal */
	"spill-7-norm",			/* 09E	spill 7 normal */
	"spill-7-norm",			/* 09F	spill 7 normal */
	"spill-0-oth",			/* 0A0	spill 0 other */
	"spill-0-oth",			/* 0A1	spill 0 other */
	"spill-0-oth",			/* 0A2	spill 0 other */
	"spill-0-oth",			/* 0A3	spill 0 other */
	"spill-1-oth",			/* 0A4	spill 1 other */
	"spill-1-oth",			/* 0A5	spill 1 other */
	"spill-1-oth",			/* 0A6	spill 1 other */
	"spill-1-oth",			/* 0A7	spill 1 other */
	"spill-2-oth",			/* 0A8	spill 2 other */
	"spill-2-oth",			/* 0A9	spill 2 other */
	"spill-2-oth",			/* 0AA	spill 2 other */
	"spill-2-oth",			/* 0AB	spill 2 other */
	"spill-3-oth",			/* 0AC	spill 3 other */
	"spill-3-oth",			/* 0AD	spill 3 other */
	"spill-3-oth",			/* 0AE	spill 3 other */
	"spill-3-oth",			/* 0AF	spill 3 other */
	"spill-4-oth",			/* 0B0	spill 4 other */
	"spill-4-oth",			/* 0B1	spill 4 other */
	"spill-4-oth",			/* 0B2	spill 4 other */
	"spill-4-oth",			/* 0B3	spill 4 other */
	"spill-5-oth",			/* 0B4	spill 5 other */
	"spill-5-oth",			/* 0B5	spill 5 other */
	"spill-5-oth",			/* 0B6	spill 5 other */
	"spill-5-oth",			/* 0B7	spill 5 other */
	"spill-6-oth",			/* 0B8	spill 6 other */
	"spill-6-oth",			/* 0B9	spill 6 other */
	"spill-6-oth",			/* 0BA	spill 6 other */
	"spill-6-oth",			/* 0BB	spill 6 other */
	"spill-7-oth",			/* 0BC	spill 7 other */
	"spill-7-oth",			/* 0BD	spill 7 other */
	"spill-7-oth",			/* 0BE	spill 7 other */
	"spill-7-oth",			/* 0BF	spill 7 other */
	"fill-0-norm",			/* 0C0	fill 0 normal */
	"fill-0-norm",			/* 0C1	fill 0 normal */
	"fill-0-norm",			/* 0C2	fill 0 normal */
	"fill-0-norm",			/* 0C3	fill 0 normal */
	"fill-1-norm",			/* 0C4	fill 1 normal */
	"fill-1-norm",			/* 0C5	fill 1 normal */
	"fill-1-norm",			/* 0C6	fill 1 normal */
	"fill-1-norm",			/* 0C7	fill 1 normal */
	"fill-2-norm",			/* 0C8	fill 2 normal */
	"fill-2-norm",			/* 0C9	fill 2 normal */
	"fill-2-norm",			/* 0CA	fill 2 normal */
	"fill-2-norm",			/* 0CB	fill 2 normal */
	"fill-3-norm",			/* 0CC	fill 3 normal */
	"fill-3-norm",			/* 0CD	fill 3 normal */
	"fill-3-norm",			/* 0CE	fill 3 normal */
	"fill-3-norm",			/* 0CF	fill 3 normal */
	"fill-4-norm",			/* 0D0	fill 4 normal */
	"fill-4-norm",			/* 0D1	fill 4 normal */
	"fill-4-norm",			/* 0D2	fill 4 normal */
	"fill-4-norm",			/* 0D3	fill 4 normal */
	"fill-5-norm",			/* 0D4	fill 5 normal */
	"fill-5-norm",			/* 0D5	fill 5 normal */
	"fill-5-norm",			/* 0D6	fill 5 normal */
	"fill-5-norm",			/* 0D7	fill 5 normal */
	"fill-6-norm",			/* 0D8	fill 6 normal */
	"fill-6-norm",			/* 0D9	fill 6 normal */
	"fill-6-norm",			/* 0DA	fill 6 normal */
	"fill-6-norm",			/* 0DB	fill 6 normal */
	"fill-7-norm",			/* 0DC	fill 7 normal */
	"fill-7-norm",			/* 0DD	fill 7 normal */
	"fill-7-norm",			/* 0DE	fill 7 normal */
	"fill-7-norm",			/* 0DF	fill 7 normal */
	"fill-0-oth",			/* 0E0	fill 0 other */
	"fill-0-oth",			/* 0E1	fill 0 other */
	"fill-0-oth",			/* 0E2	fill 0 other */
	"fill-0-oth",			/* 0E3	fill 0 other */
	"fill-1-oth",			/* 0E4	fill 1 other */
	"fill-1-oth",			/* 0E5	fill 1 other */
	"fill-1-oth",			/* 0E6	fill 1 other */
	"fill-1-oth",			/* 0E7	fill 1 other */
	"fill-2-oth",			/* 0E8	fill 2 other */
	"fill-2-oth",			/* 0E9	fill 2 other */
	"fill-2-oth",			/* 0EA	fill 2 other */
	"fill-2-oth",			/* 0EB	fill 2 other */
	"fill-3-oth",			/* 0EC	fill 3 other */
	"fill-3-oth",			/* 0ED	fill 3 other */
	"fill-3-oth",			/* 0EE	fill 3 other */
	"fill-3-oth",			/* 0EF	fill 3 other */
	"fill-4-oth",			/* 0F0	fill 4 other */
	"fill-4-oth",			/* 0F1	fill 4 other */
	"fill-4-oth",			/* 0F2	fill 4 other */
	"fill-4-oth",			/* 0F3	fill 4 other */
	"fill-5-oth",			/* 0F4	fill 5 other */
	"fill-5-oth",			/* 0F5	fill 5 other */
	"fill-5-oth",			/* 0F6	fill 5 other */
	"fill-5-oth",			/* 0F7	fill 5 other */
	"fill-6-oth",			/* 0F8	fill 6 other */
	"fill-6-oth",			/* 0F9	fill 6 other */
	"fill-6-oth",			/* 0FA	fill 6 other */
	"fill-6-oth",			/* 0FB	fill 6 other */
	"fill-7-oth",			/* 0FC	fill 7 other */
	"fill-7-oth",			/* 0FD	fill 7 other */
	"fill-7-oth",			/* 0FE	fill 7 other */
	"fill-7-oth",			/* 0FF	fill 7 other */
	"syscall-4x",			/* 100	old system call */
	"usr-brkpt",			/* 101	user breakpoint */
	"usr-div-zero",			/* 102	user divide by zero */
	"flush-wins",			/* 103	flush windows */
	"clean-wins",			/* 104	clean windows */
	"range-chk",			/* 105	range check ?? */
	"fix-align",			/* 106	do unaligned references */
	BAD,				/* 107	unused */
	"syscall-32",			/* 108	ILP32 system call on LP64 */
	"set-t0-addr",			/* 109	set trap0 address */
	BAD, BAD, BAD4,			/* 10A - 10F unused */
	BAD4, BAD4, BAD4, BAD4,		/* 110 - 11F unused (V9 user traps?) */
	"get-cc",			/* 120	get condition codes */
	"set-cc",			/* 121	set condition codes */
	"get-psr",			/* 122	get psr */
	"set-psr",			/* 123	set psr (some fields) */
	"getts",			/* 124	get timestamp */
	"gethrvtime",			/* 125	get lwp virtual time */
	"self-xcall",			/* 126	self xcall */
	"gethrtime",			/* 127	get hrestime */
	BAD,				/* 128  unused (ST_SETV9STACK) */
	"getlgrp",			/* 129	get lgrpid */
	BAD, BAD, BAD4,			/* 12A - 12F unused */
	BAD4, BAD4,			/* 130 - 137 unused */
	"dtrace-pid",			/* 138  DTrace pid provider */
	BAD,				/* 139  unused */
	"dtrace-return",		/* 13A  DTrace pid provider */
	BAD, BAD4,			/* 13B - 13F unused */
	"syscall-64",			/* 140  LP64 system call */
	BAD,				/* 141  unused */
	"tt-freeze",			/* 142  freeze traptrace */
	"tt-unfreeze",			/* 143  unfreeze traptrace */
	BAD4, BAD4, BAD4,		/* 144 - 14F unused */
	BAD4, BAD4, BAD4, BAD4,		/* 150 - 15F unused */
	BAD4, BAD4, BAD4, BAD4,		/* 160 - 16F unused */
	BAD4, BAD4, BAD4,		/* 170 - 17B unused */
	"ptl1-panic",			/* 17C	test ptl1_panic */
	"kmdb-enter",			/* 17D	kmdb enter (L1-A) */
	"kmdb-brkpt",			/* 17E	kmdb breakpoint */
	"obp-brkpt",			/* 17F	obp breakpoint */
#ifdef sun4v
	"fast_trap",			/* 180  hypervisor fast trap */
	"cpu_tick_npt",			/* 181  cpu_tick_npt() hcall */
	"cpu_stick_npt",		/* 182  cpu_stick_npt() hcall */
	"mmu_map_addr",			/* 183  mmu_map_addr() hcall */
	"mmu_unmap_addr",		/* 184  mmu_unmap_addr() hcall */
	"ttrace_addentry",		/* 185  ttrace_addentry() hcall */
	NOT, NOT, NOT4, NOT4,		/* 186 - 18F reserved */
#else
	NOT4, NOT4, NOT4, NOT4,		/* 180 - 18F reserved */
#endif
	NOT4, NOT4, NOT4, NOT4,		/* 190 - 19F reserved */
	NOT4, NOT4, NOT4, NOT4,		/* 1A0 - 1AF reserved */
	NOT4, NOT4, NOT4, NOT4,		/* 1B0 - 1BF reserved */
	NOT4, NOT4, NOT4, NOT4,		/* 1C0 - 1CF reserved */
	NOT4, NOT4, NOT4, NOT4,		/* 1D0 - 1DF reserved */
	NOT4, NOT4, NOT4, NOT4,		/* 1E0 - 1EF reserved */
	NOT4, NOT4, NOT4, NOT4		/* 1F0 - 1FF reserved */
};
static const size_t ttndescr = sizeof (ttdescr) / sizeof (ttdescr[0]);

static GElf_Sym iv_sym;

/*
 * Persistent data (shouldn't change).
 */
static int ncpu;		/* _ncpu */
static ssize_t mbox_size;	/* size of xc_mbox */
static ulong_t mbox_stoff;	/* offset of xc_mbox.xc_state */
static mdb_ctf_id_t mbox_states; /* xc_state enumeration */

static int
fetch_ncpu(void)
{
	if (ncpu == 0)
		if (mdb_readsym(&ncpu, sizeof (ncpu), "_ncpu") == -1) {
			mdb_warn("symbol '_ncpu' not found");
			return (1);
		}
	return (0);
}

static int
fetch_mbox(void)
{
	if (mbox_size <= 0) {
		mdb_ctf_id_t id;

		if (mdb_ctf_lookup_by_name("struct xc_mbox", &id) == -1) {
			mdb_warn("couldn't find type 'struct xc_mbox'");
			return (1);
		}

		/*
		 * These two could be combined into a single call to
		 * mdb_ctf_member_info if xc_state was actually of type
		 * enum xc_states.
		 */
		if (mdb_ctf_lookup_by_name("enum xc_states",
		    &mbox_states) == -1) {
			mdb_warn("couldn't find type 'enum xc_states'");
			return (1);
		}
		if (mdb_ctf_offsetof(id, "xc_state", &mbox_stoff) == -1) {
			mdb_warn("couldn't find 'xc_mbox.xc_state'");
			return (1);
		}
		mbox_stoff /= NBBY;

		if ((mbox_size = mdb_ctf_type_size(id)) == -1) {
			mdb_warn("couldn't size 'struct xc_mbox'");
			return (1);
		}
	}
	return (0);
}

static int
print_range(int start, int end, int separator)
{
	int	count;
	char	tmp;
	char	*format;

	if (start == end) {
		/* Unfortunately, mdb_printf returns void */
		format = separator ? ", %d" : "%d";
		mdb_printf(format, start);
		count = mdb_snprintf(&tmp, 1, format, start);
	} else {
		format = separator ? ", %d-%d" : "%d-%d";
		mdb_printf(format, start, end);
		count = mdb_snprintf(&tmp, 1, format, start, end);
	}

	return (count);
}

static void
print_cpuset_range(ulong_t *cs, int words, int width)
{
	int i, j;
	ulong_t m;
	int in = 0;
	int start;
	int end;
	int count = 0;
	int sep = 0;

	for (i = 0; i < words; i++)
		for (j = 0, m = 1; j < BT_NBIPUL; j++, m <<= 1)
			if (cs[i] & m) {
				if (in == 0) {
					start = i * BT_NBIPUL + j;
					in = 1;
				}
			} else {
				if (in == 1) {
					end = i * BT_NBIPUL + j - 1;
					count += print_range(start, end, sep);
					sep = 1;
					in = 0;
				}
			}
	if (in == 1) {
		end = i * BT_NBIPUL - 1;
		count += print_range(start, end, sep);
	}

	while (count++ < width)
		mdb_printf(" ");
}

/*ARGSUSED*/
static int
cmd_cpuset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	uint_t rflag = 0, lflag = 0;
	int words;
	ulong_t *setp, set = 0;

	if (mdb_getopts(argc, argv,
	    'l', MDB_OPT_SETBITS, TRUE, &lflag,
	    'r', MDB_OPT_SETBITS, TRUE, &rflag,  NULL) != argc)
		return (DCMD_USAGE);

	if (lflag && rflag)
		return (DCMD_USAGE);

	if (fetch_ncpu())
		return (DCMD_ERR);

	if ((words = BT_BITOUL(ncpu)) == 1) {
		setp = &set;
		mdb_vread(setp, sizeof (ulong_t), addr);
	} else {
		setp = mdb_alloc(words * sizeof (ulong_t), UM_SLEEP | UM_GC);
		mdb_vread(setp, words * sizeof (ulong_t), addr);
	}

	if (lflag) {
		int i, j;
		ulong_t m;

		for (i = 0; i < words; i++)
			for (j = 0, m = 1; j < BT_NBIPUL; j++, m <<= 1)
				if (setp[i] & m)
					mdb_printf("%r\n", i * BT_NBIPUL + j);
	} else if (rflag) {
		int i;
		int sep = 0;

		for (i = 0; i < words; i++) {
			mdb_printf(sep ? " %?0lx" : "%?0lx", setp[i]);
			sep = 1;
		}
	} else {
		print_cpuset_range(setp, words, 0);
	}

	return (DCMD_OK);
}

/*ARGSUSED*/
int
ttctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	TRAP_TRACE_CTL *ctls, *ctl;
	int i, traptrace_buf_inuse = 0;

	if (argc != 0)
		return (DCMD_USAGE);

	if (fetch_ncpu())
		return (DCMD_ERR);

	ctls = mdb_alloc(sizeof (TRAP_TRACE_CTL) * ncpu, UM_SLEEP | UM_GC);
	if (mdb_readsym(ctls, sizeof (TRAP_TRACE_CTL) * ncpu,
	    "trap_trace_ctl") == -1) {
		mdb_warn("symbol 'trap_trace_ctl' not found");
		return (DCMD_ERR);
	}

	for (ctl = &ctls[0], i = 0; i < ncpu; i++, ctl++) {
		if (ctl->d.vaddr_base == 0)
			continue;

		traptrace_buf_inuse = 1;
		mdb_printf("trap_trace_ctl[%d] = {\n", i);
		mdb_printf("  vaddr_base = 0x%lx\n", (long)ctl->d.vaddr_base);
		mdb_printf("  last_offset = 0x%x\n", ctl->d.last_offset);
		mdb_printf("  offset = 0x%x\n", ctl->d.offset);
		mdb_printf("  limit = 0x%x\n", ctl->d.limit);
		mdb_printf("  paddr_base = 0x%llx\n", ctl->d.paddr_base);
		mdb_printf("  asi = 0x%02x\n}\n", ctl->d.asi);
	}
	if (!traptrace_buf_inuse) {
		mdb_warn("traptrace not configured");
		return (DCMD_ERR);
	}

	return (DCMD_OK);
}

/*ARGSUSED*/
static int
ttprint_short(uintptr_t addr, const trap_trace_fullrec_t *full, int *cpu)
{
	const char *ttstr;
	const struct trap_trace_record *ttp = &full->ttf_rec;

	if (*cpu == -1)
		mdb_printf("%3d ", full->ttf_cpu);
	else
		if (*cpu != full->ttf_cpu)
			return (0);

	/*
	 * Decoding the traptype field is a bit messy.  First we check for
	 * several well-defined 16-bit values defined in <sys/traptrace.h>.
	 */
	switch (ttp->tt_tt) {
		case TT_SC_ENTR:
			ttstr = "sys-enter";
			break;
		case TT_SC_RET:
			ttstr = "sys-exit";
			break;
		case TT_SYS_RTT_PROM:
			ttstr = "prom_rtt";
			break;
		case TT_SYS_RTT_PRIV:
			ttstr = "priv_rtt";
			break;
		case TT_SYS_RTT_USER:
			ttstr = "user_rtt";
			break;
		case TT_INTR_EXIT:
			ttstr = "int-thr-exit";
			break;
		default:
			/*
			 * Next we consider several prefixes (which are
			 * typically OR'd with other information such as the
			 * %pil or %tt value at the time of the trace).
			 */
			switch (ttp->tt_tt & 0xff00) {
				case TT_SERVE_INTR:
					ttstr = "serve-intr";
					break;
				case TT_XCALL:
					ttstr = "xcall";
					break;
				case TT_XCALL_CONT:
					ttstr = "xcall-cont";
					break;
				case SYSTRAP_TT:
					ttstr = "sys_trap";
					break;
				default:
					/*
					 * Otherwise we try to convert the
					 * tt value to a string using our
					 * giant lookup table.
					 */
					ttstr = ttp->tt_tt < ttndescr ?
					    ttdescr[ttp->tt_tt] : "?";
			}
	}

#ifdef sun4v
	mdb_printf("%016llx %04hx %-12s  %02x  %02x %0?p %A\n", ttp->tt_tick,
	    ttp->tt_tt, ttstr, ttp->tt_tl, ttp->tt_gl,
	    ttp->tt_tpc, ttp->tt_tpc);
#else
	mdb_printf("%016llx %04hx %-12s %04hx %0?p %A\n", ttp->tt_tick,
	    ttp->tt_tt, ttstr, ttp->tt_tl, ttp->tt_tpc, ttp->tt_tpc);
#endif

	return (WALK_NEXT);
}

/*ARGSUSED*/
static int
ttprint_long(uintptr_t addr, const trap_trace_fullrec_t *full, int *cpu)
{
	const struct trap_trace_record *ttp = &full->ttf_rec;

	if (*cpu == -1)
		mdb_printf("%3d ", full->ttf_cpu);
	else if (*cpu != full->ttf_cpu)
		return (WALK_NEXT);

#ifdef sun4v
	mdb_printf("%016llx %016llx %04hx  %02x  %02x %0?p %0?p %0?p "
	    "[%p,%p,%p,%p]\n",
	    ttp->tt_tick, ttp->tt_tstate, ttp->tt_tt, ttp->tt_tl, ttp->tt_gl,
	    ttp->tt_tpc, ttp->tt_sp, ttp->tt_tr,
	    ttp->tt_f1, ttp->tt_f2, ttp->tt_f3, ttp->tt_f4);
#else
	mdb_printf("%016llx %016llx %04hx %04hx %0?p %0?p %0?p [%p,%p,%p,%p]\n",
	    ttp->tt_tick, ttp->tt_tstate, ttp->tt_tt, ttp->tt_tl,
	    ttp->tt_tpc, ttp->tt_sp, ttp->tt_tr,
	    ttp->tt_f1, ttp->tt_f2, ttp->tt_f3, ttp->tt_f4);
#endif

	return (WALK_NEXT);
}

typedef struct ttrace_cpu_data {
	struct trap_trace_record *tc_buf;
	struct trap_trace_record *tc_rec;
	struct trap_trace_record *tc_stop;
	size_t tc_bufsiz;
	uintptr_t tc_base;
} ttrace_cpu_data_t;

typedef struct ttrace_walk_data {
	int tw_ncpu;
	ttrace_cpu_data_t *tw_cpus;
} ttrace_walk_data_t;

int
ttrace_walk_init(mdb_walk_state_t *wsp)
{
	TRAP_TRACE_CTL *ctls, *ctl;
	int i, traptrace_buf_inuse = 0;
	ttrace_walk_data_t *tw;
	ttrace_cpu_data_t *tc;
	struct trap_trace_record *buf;

	if (wsp->walk_addr != NULL) {
		mdb_warn("ttrace only supports global walks\n");
		return (WALK_ERR);
	}

	if (fetch_ncpu())
		return (WALK_ERR);

	ctls = mdb_alloc(sizeof (TRAP_TRACE_CTL) * ncpu, UM_SLEEP);
	if (mdb_readsym(ctls, sizeof (TRAP_TRACE_CTL) * ncpu,
	    "trap_trace_ctl") == -1) {
		mdb_warn("symbol 'trap_trace_ctl' not found");
		mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
		return (WALK_ERR);
	}

	tw = mdb_zalloc(sizeof (ttrace_walk_data_t), UM_SLEEP);
	tw->tw_ncpu = ncpu;
	tw->tw_cpus = mdb_zalloc(sizeof (ttrace_cpu_data_t) * ncpu, UM_SLEEP);

	for (i = 0; i < ncpu; i++) {
		ctl = &ctls[i];

		if (ctl->d.vaddr_base == 0)
			continue;

		traptrace_buf_inuse = 1;
		tc = &(tw->tw_cpus[i]);
		tc->tc_bufsiz = ctl->d.limit -
		    sizeof (struct trap_trace_record);
		tc->tc_buf = buf = mdb_alloc(tc->tc_bufsiz, UM_SLEEP);
		tc->tc_base = (uintptr_t)ctl->d.vaddr_base;

		if (mdb_vread(buf, tc->tc_bufsiz, tc->tc_base) == -1) {
			mdb_warn("failed to read trap trace buffer at %p",
			    ctl->d.vaddr_base);
			mdb_free(buf, tc->tc_bufsiz);
			tc->tc_buf = NULL;
		} else {
			tc->tc_rec = (struct trap_trace_record *)
			    ((uintptr_t)buf + (uintptr_t)ctl->d.last_offset);
			tc->tc_stop = (struct trap_trace_record *)
			    ((uintptr_t)buf + (uintptr_t)ctl->d.offset);
		}
	}
	if (!traptrace_buf_inuse) {
		mdb_warn("traptrace not configured");
		mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
		return (DCMD_ERR);
	}

	mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
	wsp->walk_data = tw;
	return (WALK_NEXT);
}

int
ttrace_walk_step(mdb_walk_state_t *wsp)
{
	ttrace_walk_data_t *tw = wsp->walk_data;
	ttrace_cpu_data_t *tc;
	struct trap_trace_record *rec;
	int oldest, i, status;
	uint64_t oldest_tick = 0;
	int done = 1;
	trap_trace_fullrec_t fullrec;

	for (i = 0; i < tw->tw_ncpu; i++) {
		tc = &(tw->tw_cpus[i]);

		if (tc->tc_rec == NULL)
			continue;
		done = 0;

		if (tc->tc_rec->tt_tick == 0)
			mdb_warn("Warning: tt_tick == 0\n");

		if (tc->tc_rec->tt_tick > oldest_tick) {
			oldest_tick = tc->tc_rec->tt_tick;
			oldest = i;
		}
	}

	if (done)
		return (-1);

	tc = &(tw->tw_cpus[oldest]);
	rec = tc->tc_rec;

	fullrec.ttf_rec = *rec;
	fullrec.ttf_cpu = oldest;

	if (oldest_tick != 0)
		status = wsp->walk_callback((uintptr_t)rec -
		    (uintptr_t)tc->tc_buf + tc->tc_base, &fullrec,
		    wsp->walk_cbdata);

	tc->tc_rec--;

	if (tc->tc_rec < tc->tc_buf)
		tc->tc_rec = (struct trap_trace_record *)((uintptr_t)
		    tc->tc_buf + (uintptr_t)tc->tc_bufsiz -
		    sizeof (struct trap_trace_record));

	if (tc->tc_rec == tc->tc_stop) {
		tc->tc_rec = NULL;
		mdb_free(tc->tc_buf, tc->tc_bufsiz);
	}

	return (status);
}

void
ttrace_walk_fini(mdb_walk_state_t *wsp)
{
	ttrace_walk_data_t *tw = wsp->walk_data;

	mdb_free(tw->tw_cpus, sizeof (ttrace_cpu_data_t) * tw->tw_ncpu);
	mdb_free(tw, sizeof (ttrace_walk_data_t));
}

int
ttrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	uint_t opt_x = FALSE;
	int cpu = -1;
	mdb_walk_cb_t ttprint;

	if (mdb_getopts(argc, argv,
	    'x', MDB_OPT_SETBITS, TRUE, &opt_x, NULL) != argc)
		return (DCMD_USAGE);

	if (flags & DCMD_ADDRSPEC) {
		if (fetch_ncpu())
			return (DCMD_ERR);
		if (addr >= ncpu) {
			mdb_warn("expected cpu between 0 and %d\n", ncpu - 1);
			return (DCMD_ERR);
		}
		cpu = (int)addr;
	}

	if (cpu == -1)
		mdb_printf("CPU ");

	if (opt_x) {
#ifdef sun4v
		mdb_printf("%-16s %-16s %-4s %-3s %-3s %-?s %-?s %-?s "
		    "F1-4\n", "%tick", "%tstate", "%tt", "%tl", "%gl",
		    "%tpc", "%sp", "TR");
#else
		mdb_printf("%-16s %-16s %-4s %-4s %-?s %-?s %-?s "
		    "F1-4\n", "%tick", "%tstate", "%tt", "%tl",
		    "%tpc", "%sp", "TR");
#endif

		ttprint = (mdb_walk_cb_t)ttprint_long;
	} else {
#ifdef sun4v
		mdb_printf("%-16s %-4s %-12s %-3s %-3s %s\n",
		    "%tick", "%tt", "", "%tl", "%gl", "%tpc");
#else
		mdb_printf("%-16s %-4s %-12s %-4s %s\n",
		    "%tick", "%tt", "", "%tl", "%tpc");
#endif

		ttprint = (mdb_walk_cb_t)ttprint_short;
	}

	if (mdb_walk("ttrace", ttprint, &cpu) == -1) {
		mdb_warn("couldn't walk ttrace");
		return (DCMD_ERR);
	}

	return (DCMD_OK);
}

#ifdef sun4v
/*ARGSUSED*/
int
httctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	TRAP_TRACE_CTL *ctls, *ctl;
	int i, htraptrace_buf_inuse = 0;
	htrap_trace_hdr_t hdr;

	if (argc != 0)
		return (DCMD_USAGE);

	if (fetch_ncpu())
		return (DCMD_ERR);

	ctls = mdb_alloc(sizeof (TRAP_TRACE_CTL) * ncpu, UM_SLEEP | UM_GC);
	if (mdb_readsym(ctls, sizeof (TRAP_TRACE_CTL) * ncpu,
	    "trap_trace_ctl") == -1) {
		mdb_warn("symbol 'trap_trace_ctl' not found");
		return (DCMD_ERR);
	}

	for (ctl = &ctls[0], i = 0; i < ncpu; i++, ctl++) {
		if (ctl->d.hvaddr_base == 0)
			continue;

		htraptrace_buf_inuse = 1;
		mdb_vread(&hdr, sizeof (htrap_trace_hdr_t),
		    (uintptr_t)ctl->d.hvaddr_base);
		mdb_printf("htrap_trace_ctl[%d] = {\n", i);
		mdb_printf("  vaddr_base = 0x%lx\n", (long)ctl->d.hvaddr_base);
		mdb_printf("  last_offset = 0x%lx\n", hdr.last_offset);
		mdb_printf("  offset = 0x%lx\n", hdr.offset);
		mdb_printf("  limit = 0x%x\n", ctl->d.hlimit);
		mdb_printf("  paddr_base = 0x%llx\n}\n", ctl->d.hpaddr_base);
	}
	if (!htraptrace_buf_inuse) {
		mdb_warn("hv traptrace not configured");
		return (DCMD_ERR);
	}

	return (DCMD_OK);
}

/*ARGSUSED*/
static int
httprint_short(uintptr_t addr, const htrap_trace_fullrec_t *full, int *cpu)
{
	const char *ttstr;
	const struct htrap_trace_record *ttp = &full->ttf_rec;

	if (*cpu == -1)
		mdb_printf("%3d ", full->ttf_cpu);
	else
		if (*cpu != full->ttf_cpu)
			return (0);

	/*
	 * Convert the tt value to a string using our gaint lookuo table
	 */
	ttstr = ttp->tt_tt < ttndescr ? ttdescr[ttp->tt_tt] : "?";

	mdb_printf("%016llx %02x  %04hx %04hx %-16s %02x  %02x  %0?p %A\n",
	    ttp->tt_tick, ttp->tt_ty, ttp->tt_tag, ttp->tt_tt, ttstr,
	    ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc, ttp->tt_tpc);

	return (WALK_NEXT);
}

/*ARGSUSED*/
static int
httprint_long(uintptr_t addr, const htrap_trace_fullrec_t *full, int *cpu)
{
	const struct htrap_trace_record *ttp = &full->ttf_rec;

	if (*cpu == -1)
		mdb_printf("%3d ", full->ttf_cpu);
	else if (*cpu != full->ttf_cpu)
		return (WALK_NEXT);

	mdb_printf("%016llx %016llx %02x  %02x  %04hx %04hx %02x  %02x  %0?p "
	    "[%p,%p,%p,%p]\n",
	    ttp->tt_tick, ttp->tt_tstate, ttp->tt_hpstate, ttp->tt_ty,
	    ttp->tt_tag, ttp->tt_tt, ttp->tt_tl, ttp->tt_gl, ttp->tt_tpc,
	    ttp->tt_f1, ttp->tt_f2, ttp->tt_f3, ttp->tt_f4);

	return (WALK_NEXT);
}

typedef struct httrace_cpu_data {
	struct htrap_trace_record *tc_buf;
	struct htrap_trace_record *tc_rec;
	struct htrap_trace_record *tc_stop;
	size_t tc_bufsiz;
	uintptr_t tc_base;
} httrace_cpu_data_t;

typedef struct httrace_walk_data {
	int tw_ncpu;
	httrace_cpu_data_t *tw_cpus;
} httrace_walk_data_t;

int
httrace_walk_init(mdb_walk_state_t *wsp)
{
	TRAP_TRACE_CTL *ctls, *ctl;
	int i, htraptrace_buf_inuse = 0;
	httrace_walk_data_t *tw;
	httrace_cpu_data_t *tc;
	struct htrap_trace_record *buf;
	htrap_trace_hdr_t *hdr;

	if (wsp->walk_addr != NULL) {
		mdb_warn("httrace only supports global walks\n");
		return (WALK_ERR);
	}

	if (fetch_ncpu())
		return (WALK_ERR);

	ctls = mdb_alloc(sizeof (TRAP_TRACE_CTL) * ncpu, UM_SLEEP);
	if (mdb_readsym(ctls, sizeof (TRAP_TRACE_CTL) * ncpu,
	    "trap_trace_ctl") == -1) {
		mdb_warn("symbol 'trap_trace_ctl' not found");
		mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
		return (WALK_ERR);
	}

	tw = mdb_zalloc(sizeof (httrace_walk_data_t), UM_SLEEP);
	tw->tw_ncpu = ncpu;
	tw->tw_cpus = mdb_zalloc(sizeof (httrace_cpu_data_t) * ncpu, UM_SLEEP);

	for (i = 0; i < ncpu; i++) {
		ctl = &ctls[i];

		if (ctl->d.hvaddr_base == 0)
			continue;

		htraptrace_buf_inuse = 1;
		tc = &(tw->tw_cpus[i]);
		tc->tc_bufsiz = ctl->d.hlimit;
		tc->tc_buf = buf = mdb_alloc(tc->tc_bufsiz, UM_SLEEP);
		tc->tc_base = (uintptr_t)ctl->d.hvaddr_base;

		if (mdb_vread(buf, tc->tc_bufsiz, tc->tc_base) == -1) {
			mdb_warn("failed to read hv trap trace buffer at %p",
			    ctl->d.hvaddr_base);
			mdb_free(buf, tc->tc_bufsiz);
			tc->tc_buf = NULL;
		} else {
			hdr = (htrap_trace_hdr_t *)buf;
			tc->tc_rec = (struct htrap_trace_record *)
			    ((uintptr_t)buf + (uintptr_t)hdr->last_offset);
			tc->tc_stop = (struct htrap_trace_record *)
			    ((uintptr_t)buf + (uintptr_t)hdr->offset);
		}
	}
	if (!htraptrace_buf_inuse) {
		mdb_warn("hv traptrace not configured");
		mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
		return (DCMD_ERR);
	}

	mdb_free(ctls, sizeof (TRAP_TRACE_CTL) * ncpu);
	wsp->walk_data = tw;
	return (WALK_NEXT);
}

int
httrace_walk_step(mdb_walk_state_t *wsp)
{
	httrace_walk_data_t *tw = wsp->walk_data;
	httrace_cpu_data_t *tc;
	struct htrap_trace_record *rec;
	int oldest, i, status;
	uint64_t oldest_tick = 0;
	int done = 1;
	htrap_trace_fullrec_t fullrec;

	for (i = 0; i < tw->tw_ncpu; i++) {
		tc = &(tw->tw_cpus[i]);

		if (tc->tc_rec == NULL)
			continue;
		done = 0;

		if (tc->tc_rec->tt_tick == 0)
			mdb_warn("Warning: tt_tick == 0\n");

		if (tc->tc_rec->tt_tick >= oldest_tick) {
			oldest_tick = tc->tc_rec->tt_tick;
			oldest = i;
		}
	}

	if (done)
		return (-1);

	tc = &(tw->tw_cpus[oldest]);
	rec = tc->tc_rec;

	fullrec.ttf_rec = *rec;
	fullrec.ttf_cpu = oldest;

	if (oldest_tick != 0)
		status = wsp->walk_callback((uintptr_t)rec -
		    (uintptr_t)tc->tc_buf + tc->tc_base, &fullrec,
		    wsp->walk_cbdata);

	tc->tc_rec--;

	/* first record of the trap trace buffer is trap trace header */
	if (tc->tc_rec == tc->tc_buf)
		tc->tc_rec = (struct htrap_trace_record *)((uintptr_t)
		    tc->tc_buf + (uintptr_t)tc->tc_bufsiz -
		    sizeof (struct htrap_trace_record));

	if (tc->tc_rec == tc->tc_stop) {
		tc->tc_rec = NULL;
		mdb_free(tc->tc_buf, tc->tc_bufsiz);
	}

	return (status);
}

void
httrace_walk_fini(mdb_walk_state_t *wsp)
{
	httrace_walk_data_t *tw = wsp->walk_data;

	mdb_free(tw->tw_cpus, sizeof (httrace_cpu_data_t) * tw->tw_ncpu);
	mdb_free(tw, sizeof (httrace_walk_data_t));
}

int
httrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	uint_t opt_x = FALSE;
	int cpu = -1;
	mdb_walk_cb_t ttprint;

	if (mdb_getopts(argc, argv,
	    'x', MDB_OPT_SETBITS, TRUE, &opt_x, NULL) != argc)
		return (DCMD_USAGE);

	if (flags & DCMD_ADDRSPEC) {
		if (fetch_ncpu())
			return (DCMD_ERR);
		if (addr >= ncpu) {
			mdb_warn("expected cpu between 0 and %d\n", ncpu - 1);
			return (DCMD_ERR);
		}
		cpu = (int)addr;
	}

	if (cpu == -1)
		mdb_printf("CPU ");

	if (opt_x) {
		mdb_printf("%-16s %-16s %-3s %-3s %-4s %-4s %-3s %-3s %-?s "
		    "F1-4\n", "%tick", "%tstate", "%hp", "%ty", "%tag",
		    "%tt", "%tl", "%gl", "%tpc");
		ttprint = (mdb_walk_cb_t)httprint_long;
	} else {
		mdb_printf("%-16s %-3s %-4s %-4s %-16s %-3s %-3s %s\n",
		    "%tick", "%ty", "%tag", "%tt", "", "%tl", "%gl",
		    "%tpc");
		ttprint = (mdb_walk_cb_t)httprint_short;
	}

	if (mdb_walk("httrace", ttprint, &cpu) == -1) {
		mdb_warn("couldn't walk httrace");
		return (DCMD_ERR);
	}

	return (DCMD_OK);
}
#endif

struct {
	int xc_type;
	const char *xc_str;
} xc_data[] = {
	{ XT_ONE_SELF,		"xt-one-self" },
	{ XT_ONE_OTHER,		"xt-one-other" },
	{ XT_SOME_SELF,		"xt-some-self" },
	{ XT_SOME_OTHER,	"xt-some-other" },
	{ XT_ALL_SELF,		"xt-all-self" },
	{ XT_ALL_OTHER,		"xt-all-other" },
	{ XC_ONE_SELF,		"xc-one-self" },
	{ XC_ONE_OTHER,		"xc-one-other" },
	{ XC_ONE_OTHER_H,	"xc-one-other-h" },
	{ XC_SOME_SELF,		"xc-some-self" },
	{ XC_SOME_OTHER,	"xc-some-other" },
	{ XC_SOME_OTHER_H,	"xc-some-other-h" },
	{ XC_ALL_SELF,		"xc-all-self" },
	{ XC_ALL_OTHER,		"xc-all-other" },
	{ XC_ALL_OTHER_H,	"xc-all-other-h" },
	{ XC_ATTENTION,		"xc-attention" },
	{ XC_DISMISSED,		"xc-dismissed" },
	{ XC_LOOP_ENTER,	"xc-loop-enter" },
	{ XC_LOOP_DOIT,		"xc-loop-doit" },
	{ XC_LOOP_EXIT,		"xc-loop-exit" },
	{ 0,			NULL }
};

/*ARGSUSED*/
int
xctrace_walk(uintptr_t addr, const trap_trace_fullrec_t *full, int *cpu)
{
	const struct trap_trace_record *ttp = &full->ttf_rec;
	int i, type = ttp->tt_tt & 0xff;
	const char *str = "???";

	if ((ttp->tt_tt & 0xff00) == TT_XCALL) {
		for (i = 0; xc_data[i].xc_str != NULL; i++) {
			if (xc_data[i].xc_type == type) {
				str = xc_data[i].xc_str;
				break;
			}
		}
	} else if ((ttp->tt_tt & 0xff00) == TT_XCALL_CONT) {
		str = "xcall-cont";
		mdb_printf("%3d %016llx %-16s %08x %08x %08x %08x\n",
		    full->ttf_cpu, ttp->tt_tick, str, ttp->tt_f1, ttp->tt_f2,
		    ttp->tt_f3, ttp->tt_f4);
		return (WALK_NEXT);
	} else if (ttp->tt_tt == 0x60) {
		str = "int-vec";
	} else {
		return (WALK_NEXT);
	}

	mdb_printf("%3d %016llx %-16s %08x %a\n", full->ttf_cpu,
	    ttp->tt_tick, str, ttp->tt_sp, ttp->tt_tr);

	return (WALK_NEXT);
}

/*ARGSUSED*/
int
xctrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	if ((flags & DCMD_ADDRSPEC) || argc != 0)
		return (DCMD_USAGE);

	if (mdb_walk("ttrace", (mdb_walk_cb_t)xctrace_walk, NULL) == -1) {
		mdb_warn("couldn't walk ttrace");
		return (DCMD_ERR);
	}

	return (DCMD_OK);
}

/*
 * Grrr... xc_mbox isn't in an _impl header file; we define it here.
 */
typedef struct xc_mbox {
	xcfunc_t *xc_func;
	uint64_t xc_arg1;
	uint64_t xc_arg2;
	cpuset_t xc_cpuset;
	volatile uint_t xc_state;
} xc_mbox_t;

typedef struct xc_mbox_walk {
	int xw_ndx;
	uintptr_t xw_addr;
	xc_mbox_t *xw_mbox;
} xc_mbox_walk_t;

static int
xc_mbox_walk_init(mdb_walk_state_t *wsp)
{
	GElf_Sym sym;
	xc_mbox_walk_t *xw;

	if (mdb_lookup_by_name("xc_mbox", &sym) == -1) {
		mdb_warn("couldn't find 'xc_mbox'");
		return (WALK_ERR);
	}

	if (fetch_ncpu() || fetch_mbox())
		return (WALK_ERR);

	xw = mdb_zalloc(sizeof (xc_mbox_walk_t), UM_SLEEP);
	xw->xw_mbox = mdb_zalloc(mbox_size * ncpu, UM_SLEEP);

	if (mdb_readsym(xw->xw_mbox, mbox_size * ncpu, "xc_mbox") == -1) {
		mdb_warn("couldn't read 'xc_mbox'");
		mdb_free(xw->xw_mbox, mbox_size * ncpu);
		mdb_free(xw, sizeof (xc_mbox_walk_t));
		return (WALK_ERR);
	}

	xw->xw_addr = sym.st_value;
	wsp->walk_data = xw;

	return (WALK_NEXT);
}

static int
xc_mbox_walk_step(mdb_walk_state_t *wsp)
{
	xc_mbox_walk_t *xw = wsp->walk_data;
	int status;

	if (xw->xw_ndx == ncpu)
		return (WALK_DONE);

	status = wsp->walk_callback(xw->xw_addr,
	    &xw->xw_mbox[xw->xw_ndx++], wsp->walk_cbdata);

	xw->xw_addr += mbox_size;
	return (status);
}

static void
xc_mbox_walk_fini(mdb_walk_state_t *wsp)
{
	xc_mbox_walk_t *xw = wsp->walk_data;

	mdb_free(xw->xw_mbox, mbox_size * ncpu);
	mdb_free(xw, sizeof (xc_mbox_walk_t));
}

static int
xc_mbox(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	xc_mbox_t *mbox;
	GElf_Sym sym;
	const char *state;

	if (argc != 0)
		return (DCMD_USAGE);

	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_walk_dcmd("xc_mbox", "xc_mbox", argc, argv) == -1) {
			mdb_warn("can't walk 'xc_mbox'");
			return (DCMD_ERR);
		}
		return (DCMD_OK);
	}

	if (fetch_ncpu() || fetch_mbox())
		return (DCMD_ERR);

	if (DCMD_HDRSPEC(flags)) {
		mdb_printf("%3s %-8s %-8s %-9s %-16s %-16s %s\n",
		    "CPU", "ADDR", "STATE", "CPUSET", "ARG1", "ARG2", "HNDLR");
	}

	mbox = mdb_alloc(mbox_size, UM_SLEEP | UM_GC);
	if (mdb_vread(mbox, mbox_size, addr) == -1) {
		mdb_warn("couldn't read xc_mbox at %p", addr);
		return (DCMD_ERR);
	}

	if (mbox->xc_func == NULL)
		return (DCMD_OK);

	if (mdb_lookup_by_name("xc_mbox", &sym) == -1) {
		mdb_warn("couldn't read 'xc_mbox'");
		return (DCMD_ERR);
	}

	state = mdb_ctf_enum_name(mbox_states,
	    /* LINTED - alignment */
	    *(int *)((char *)mbox + mbox_stoff));

	mdb_printf("%3d %08x %-8s [ ",
	    (int)((addr - sym.st_value) / mbox_size), addr,
	    state ? state : "XC_???");

	print_cpuset_range((ulong_t *)&mbox->xc_cpuset, BT_BITOUL(ncpu), 5);

	mdb_printf(" ] %-16a %-16a %a\n",
	    mbox->xc_arg1, mbox->xc_arg2, mbox->xc_func);

	return (DCMD_OK);
}

typedef struct vecint_walk_data {
	intr_vec_t **vec_table;
	uintptr_t vec_base;
	size_t vec_idx;
	size_t vec_size;
} vecint_walk_data_t;

int
vecint_walk_init(mdb_walk_state_t *wsp)
{
	vecint_walk_data_t	*vecint;

	if (wsp->walk_addr != NULL) {
		mdb_warn("vecint walk only supports global walks\n");
		return (WALK_ERR);
	}

	vecint = mdb_zalloc(sizeof (vecint_walk_data_t), UM_SLEEP);

	vecint->vec_size = MAXIVNUM * sizeof (intr_vec_t *);
	vecint->vec_base = (uintptr_t)iv_sym.st_value;
	vecint->vec_table = mdb_zalloc(vecint->vec_size, UM_SLEEP);

	if (mdb_vread(vecint->vec_table, vecint->vec_size,
	    vecint->vec_base) == -1) {
		mdb_warn("couldn't read intr_vec_table");
		mdb_free(vecint->vec_table, vecint->vec_size);
		mdb_free(vecint, sizeof (vecint_walk_data_t));
		return (WALK_ERR);
	}

	wsp->walk_data = vecint;
	return (WALK_NEXT);
}

int
vecint_walk_step(mdb_walk_state_t *wsp)
{
	vecint_walk_data_t	*vecint = (vecint_walk_data_t *)wsp->walk_data;
	size_t			max = vecint->vec_size / sizeof (intr_vec_t *);
	intr_vec_t		iv;
	int			status;

	if (wsp->walk_addr == NULL) {
		while ((vecint->vec_idx < max) && ((wsp->walk_addr =
		    (uintptr_t)vecint->vec_table[vecint->vec_idx++]) == NULL))
			continue;
	}

	if (wsp->walk_addr == NULL)
		return (WALK_DONE);

	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
	    wsp->walk_cbdata);

	if (mdb_vread(&iv, sizeof (intr_vec_t),
	    (uintptr_t)wsp->walk_addr) == -1) {
		mdb_warn("failed to read iv_p %p\n", wsp->walk_addr);
		return (WALK_ERR);
	}

	wsp->walk_addr = (uintptr_t)iv.iv_vec_next;
	return (status);
}

void
vecint_walk_fini(mdb_walk_state_t *wsp)
{
	vecint_walk_data_t	*vecint = wsp->walk_data;

	mdb_free(vecint->vec_table, vecint->vec_size);
	mdb_free(vecint, sizeof (vecint_walk_data_t));
}

int
vecint_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	intr_vec_t	iv;

	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_walk_dcmd("vecint", "vecint", argc, argv) == -1) {
			mdb_warn("can't walk vecint");
			return (DCMD_ERR);
		}
		return (DCMD_OK);
	}

	if (DCMD_HDRSPEC(flags)) {
		mdb_printf("%4s %?s %4s %?s %?s %s\n", "INUM", "ADDR",
		    "PIL", "ARG1", "ARG2", "HANDLER");
	}

	if (mdb_vread(&iv, sizeof (iv), addr) == -1) {
		mdb_warn("couldn't read intr_vec_table at %p", addr);
		return (DCMD_ERR);
	}

	mdb_printf("%4x %?p %4d %?p %?p %a\n", iv.iv_inum, addr,
	    iv.iv_pil, iv.iv_arg1, iv.iv_arg2, iv.iv_handler);

	return (DCMD_OK);
}

int
softint_walk_init(mdb_walk_state_t *wsp)
{
	intr_vec_t	*list;

	if (wsp->walk_addr != NULL) {
		mdb_warn("softint walk only supports global walks\n");
		return (WALK_ERR);
	}

	/* Read global softint linked list pointer */
	if (mdb_readvar(&list, "softint_list") == -1) {
		mdb_warn("failed to read the global softint_list pointer\n");
		return (WALK_ERR);
	}

	wsp->walk_addr = (uintptr_t)list;
	return (WALK_NEXT);
}

/*ARGSUSED*/
void
softint_walk_fini(mdb_walk_state_t *wsp)
{
	/* Nothing to do here */
}

int
softint_walk_step(mdb_walk_state_t *wsp)
{
	intr_vec_t		iv;
	int			status;

	if (wsp->walk_addr == NULL)
		return (WALK_DONE);

	status = wsp->walk_callback(wsp->walk_addr, wsp->walk_data,
	    wsp->walk_cbdata);

	if (mdb_vread(&iv, sizeof (intr_vec_t),
	    (uintptr_t)wsp->walk_addr) == -1) {
		mdb_warn("failed to read iv_p %p\n", wsp->walk_addr);
		return (WALK_ERR);
	}

	wsp->walk_addr = (uintptr_t)iv.iv_vec_next;
	return (status);
}

int
softint_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	intr_vec_t	iv;

	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_walk_dcmd("softint", "softint", argc, argv) == -1) {
			mdb_warn("can't walk softint");
			return (DCMD_ERR);
		}
		return (DCMD_OK);
	}

	if (DCMD_HDRSPEC(flags)) {
		mdb_printf("%?s %4s %4s %4s %?s %?s %s\n", "ADDR", "TYPE",
		    "PEND", "PIL", "ARG1", "ARG2", "HANDLER");
	}

	if (mdb_vread(&iv, sizeof (iv), addr) == -1) {
		mdb_warn("couldn't read softint at %p", addr);
		return (DCMD_ERR);
	}

	mdb_printf("%?p %4s %4d %4d %?p %?p %a\n", addr,
	    (iv.iv_flags & IV_SOFTINT_MT) ? "M" : "S",
	    iv.iv_flags & IV_SOFTINT_PEND, iv.iv_pil,
	    iv.iv_arg1, iv.iv_arg2, iv.iv_handler);

	return (DCMD_OK);
}

static int
whatis_walk_tt(uintptr_t taddr, const trap_trace_fullrec_t *ttf,
    mdb_whatis_t *w)
{
	uintptr_t cur = 0;

	while (mdb_whatis_match(w, taddr, sizeof (struct trap_trace_record),
	    &cur))
		mdb_whatis_report_object(w, cur, taddr,
		    "trap trace record for cpu %d\n", ttf->ttf_cpu);

	return (WHATIS_WALKRET(w));
}

/*ARGSUSED*/
static int
whatis_run_traptrace(mdb_whatis_t *w, void *ignored)
{
	GElf_Sym sym;

	if (mdb_lookup_by_name("trap_trace_ctl", &sym) == -1)
		return (0);

	if (mdb_walk("ttrace", (mdb_walk_cb_t)whatis_walk_tt, w) == -1)
		mdb_warn("failed to walk 'ttrace'");

	return (0);
}

/*ARGSUSED*/
int
mutex_owner_init(mdb_walk_state_t *wsp)
{
	return (WALK_NEXT);
}

int
mutex_owner_step(mdb_walk_state_t *wsp)
{
	uintptr_t addr = wsp->walk_addr;
	mutex_impl_t mtx;
	uintptr_t owner;
	kthread_t thr;

	if (mdb_vread(&mtx, sizeof (mtx), addr) == -1)
		return (WALK_ERR);

	if (!MUTEX_TYPE_ADAPTIVE(&mtx))
		return (WALK_DONE);

	if ((owner = (uintptr_t)MUTEX_OWNER(&mtx)) == NULL)
		return (WALK_DONE);

	if (mdb_vread(&thr, sizeof (thr), owner) != -1)
		(void) wsp->walk_callback(owner, &thr, wsp->walk_cbdata);

	return (WALK_DONE);
}

static const mdb_dcmd_t dcmds[] = {
	{ "cpuset", ":[-l|-r]", "dump a cpuset_t", cmd_cpuset },
	{ "ttctl", NULL, "dump trap trace ctl records", ttctl },
	{ "ttrace", "[-x]", "dump trap trace buffer for a cpu", ttrace },
#ifdef sun4v
	{ "httctl", NULL, "dump hv trap trace ctl records", httctl },
	{ "httrace", "[-x]", "dump hv trap trace buffer for a cpu", httrace },
#endif
	{ "xc_mbox", "?", "dump xcall mboxes", xc_mbox },
	{ "xctrace", NULL, "dump xcall trace buffer", xctrace },
	{ "vecint", NULL, "display a registered hardware interrupt",
	    vecint_dcmd },
	{ "softint", NULL, "display a registered software interrupt",
	    softint_dcmd },
	{ "sfmmu_vtop", ":[[-v] -a as]", "print virtual to physical mapping",
	    sfmmu_vtop },
	{ "memseg_list", ":", "show memseg list", memseg_list },
	{ "tsbinfo", ":[-l [-a]]", "show tsbinfo", tsbinfo_list,
	    tsbinfo_help },
	{ NULL }
};

static const mdb_walker_t walkers[] = {
	{ "mutex_owner", "walks the owner of a mutex",
		mutex_owner_init, mutex_owner_step },
	{ "ttrace", "walks the trap trace buffer for a CPU",
		ttrace_walk_init, ttrace_walk_step, ttrace_walk_fini },
#ifdef sun4v
	{ "httrace", "walks the hv trap trace buffer for a CPU",
		httrace_walk_init, httrace_walk_step, httrace_walk_fini },
#endif
	{ "xc_mbox", "walks the cross call mail boxes",
		xc_mbox_walk_init, xc_mbox_walk_step, xc_mbox_walk_fini },
	{ "vecint", "walk the list of registered hardware interrupts",
		vecint_walk_init, vecint_walk_step, vecint_walk_fini },
	{ "softint", "walk the list of registered software interrupts",
		softint_walk_init, softint_walk_step, softint_walk_fini },
	{ "memseg", "walk the memseg structures",
		memseg_walk_init, memseg_walk_step, memseg_walk_fini },
	{ NULL }
};

static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };

const mdb_modinfo_t *
_mdb_init(void)
{
	if (mdb_lookup_by_name("intr_vec_table", &iv_sym) == -1) {
		mdb_warn("couldn't find intr_vec_table");
		return (NULL);
	}

	mdb_whatis_register("traptrace", whatis_run_traptrace, NULL,
	    WHATIS_PRIO_EARLY, WHATIS_REG_NO_ID);

	return (&modinfo);
}