OpenSolaris Launch
diff --git a/usr/src/cmd/mdb/common/kmdb/kaif_start.c b/usr/src/cmd/mdb/common/kmdb/kaif_start.c
new file mode 100644
index 0000000..74f0ec2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kaif_start.c
@@ -0,0 +1,368 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The main CPU-control loops, used to control masters and slaves.
+ */
+
+#include <sys/types.h>
+
+#include <kmdb/kaif.h>
+#include <kmdb/kaif_start.h>
+#include <kmdb/kmdb_asmutil.h>
+#include <kmdb/kmdb_dpi_impl.h>
+#include <kmdb/kmdb_kdi.h>
+
+#define	KAIF_SLAVE_CMD_SPIN	0
+#define	KAIF_SLAVE_CMD_SWITCH	1
+#define	KAIF_SLAVE_CMD_RESUME	2
+#define	KAIF_SLAVE_CMD_FLUSH	3
+#define	KAIF_SLAVE_CMD_REBOOT	4
+
+/*
+ * Used to synchronize attempts to set kaif_master_cpuid.  kaif_master_cpuid may
+ * be read without kaif_master_lock, and may be written by the current master
+ * CPU.
+ */
+int kaif_master_cpuid = KAIF_MASTER_CPUID_UNSET;
+static uintptr_t kaif_master_lock = 0;
+
+/*
+ * Used to ensure that all CPUs leave the debugger together. kaif_loop_lock must
+ * be held to write kaif_looping, but need not be held to read it.
+ */
+static volatile uint_t kaif_looping;
+static uintptr_t kaif_loop_lock;
+
+static volatile int kaif_slave_cmd;
+static volatile int kaif_slave_tgt;	/* target cpuid for CMD_SWITCH */
+
+static void
+kaif_lock_enter(uintptr_t *lock)
+{
+	while (cas(lock, 0, 1) != 0)
+		continue;
+	membar_producer();
+}
+
+static void
+kaif_lock_exit(uintptr_t *lock)
+{
+	*lock = 0;
+	membar_producer();
+}
+
+static int
+kaif_master_loop(kaif_cpusave_t *cpusave)
+{
+	int notflushed, i;
+
+#if defined(__sparc)
+	kaif_prom_rearm();
+#endif
+	kaif_trap_set_debugger();
+
+master_loop:
+	switch (kmdb_dpi_reenter()) {
+	case KMDB_DPI_CMD_SWITCH_CPU:
+		/*
+		 * We assume that the target CPU is a valid slave.  There's no
+		 * easy way to complain here, so we'll assume that the caller
+		 * has done the proper checking.
+		 */
+		if (kmdb_dpi_switch_target == cpusave->krs_cpu_id)
+			break;
+
+		kaif_slave_tgt = kaif_master_cpuid = kmdb_dpi_switch_target;
+		cpusave->krs_cpu_state = KAIF_CPU_STATE_SLAVE;
+		membar_producer();
+
+		/*
+		 * Switch back to the saved trap table before we switch CPUs --
+		 * we need to make sure that only one CPU is on the debugger's
+		 * table at a time.
+		 */
+		kaif_trap_set_saved(cpusave);
+
+		kaif_slave_cmd = KAIF_SLAVE_CMD_SWITCH;
+
+		/* The new master is now awake */
+		return (KAIF_CPU_CMD_SWITCH);
+
+	case KMDB_DPI_CMD_RESUME_ALL:
+	case KMDB_DPI_CMD_RESUME_UNLOAD:
+		/*
+		 * Resume everyone, clean up for next entry.
+		 */
+		kaif_master_cpuid = KAIF_MASTER_CPUID_UNSET;
+		membar_producer();
+		kaif_slave_cmd = KAIF_SLAVE_CMD_RESUME;
+
+		if (kmdb_dpi_work_required())
+			kmdb_dpi_wrintr_fire();
+
+		kaif_trap_set_saved(cpusave);
+
+		return (KAIF_CPU_CMD_RESUME);
+
+	case KMDB_DPI_CMD_RESUME_MASTER:
+		/*
+		 * Single-CPU resume, which is performed on the debugger's
+		 * trap table (so no need to switch back).
+		 */
+		return (KAIF_CPU_CMD_RESUME_MASTER);
+
+	case KMDB_DPI_CMD_FLUSH_CACHES:
+		kaif_slave_cmd = KAIF_SLAVE_CMD_FLUSH;
+
+		/*
+		 * Wait for the other cpus to finish flushing their caches.
+		 */
+		do {
+			notflushed = 0;
+			for (i = 0; i < kaif_ncpusave; i++) {
+				kaif_cpusave_t *save = &kaif_cpusave[i];
+
+				if (save->krs_cpu_state ==
+				    KAIF_CPU_STATE_SLAVE &&
+				    !save->krs_cpu_flushed) {
+					notflushed++;
+					break;
+				}
+			}
+		} while (notflushed > 0);
+
+		kaif_slave_cmd = KAIF_SLAVE_CMD_SPIN;
+		break;
+
+#if defined(__i386) || defined(__amd64)
+	case KMDB_DPI_CMD_REBOOT:
+		/*
+		 * Reboot must be initiated by CPU 0.  I could ask why, but I'm
+		 * afraid that I don't want to know the answer.
+		 */
+		if (cpusave->krs_cpu_id == 0)
+			return (KAIF_CPU_CMD_REBOOT);
+
+		kaif_slave_cmd = KAIF_SLAVE_CMD_REBOOT;
+
+		/*
+		 * Spin forever, waiting for CPU 0 (apparently a slave) to
+		 * reboot the system.
+		 */
+		for (;;)
+			continue;
+
+		/*NOTREACHED*/
+		break;
+#endif
+	}
+
+	goto master_loop;
+}
+
+static int
+kaif_slave_loop(kaif_cpusave_t *cpusave)
+{
+	int slavecmd, rv;
+
+#if defined(__sparc)
+	/*
+	 * If the user elects to drop to OBP from the debugger, some OBP
+	 * implementations will cross-call the slaves.  We have to turn
+	 * IE back on so we can receive the cross-calls.  If we don't,
+	 * some OBP implementations will wait forever.
+	 */
+	interrupts_on();
+#endif
+
+	/* Wait for duty to call */
+	for (;;) {
+		slavecmd = kaif_slave_cmd;
+
+		if (slavecmd == KAIF_SLAVE_CMD_SWITCH &&
+		    kaif_slave_tgt == cpusave->krs_cpu_id) {
+			kaif_slave_cmd = KAIF_SLAVE_CMD_SPIN;
+			cpusave->krs_cpu_state = KAIF_CPU_STATE_MASTER;
+			rv = KAIF_CPU_CMD_SWITCH;
+			break;
+
+		} else if (slavecmd == KAIF_SLAVE_CMD_FLUSH) {
+			kmdb_kdi_flush_caches();
+			cpusave->krs_cpu_flushed = 1;
+			continue;
+
+#if defined(__i386) || defined(__amd64)
+		} else if (slavecmd == KAIF_SLAVE_CMD_REBOOT &&
+		    cpusave->krs_cpu_id == 0) {
+			rv = KAIF_CPU_CMD_REBOOT;
+			break;
+#endif
+
+		} else if (slavecmd == KAIF_SLAVE_CMD_RESUME) {
+			rv = KAIF_CPU_CMD_RESUME;
+			break;
+		}
+	}
+
+#if defined(__sparc)
+	interrupts_off();
+#endif
+
+	return (rv);
+}
+
+static void
+kaif_select_master(kaif_cpusave_t *cpusave)
+{
+	kaif_lock_enter(&kaif_master_lock);
+
+	if (kaif_master_cpuid == KAIF_MASTER_CPUID_UNSET) {
+		/* This is the master. */
+		kaif_master_cpuid = cpusave->krs_cpu_id;
+		cpusave->krs_cpu_state = KAIF_CPU_STATE_MASTER;
+		kaif_slave_cmd = KAIF_SLAVE_CMD_SPIN;
+
+		membar_producer();
+
+		kmdb_kdi_stop_other_cpus(cpusave->krs_cpu_id,
+		    kaif_slave_entry);
+
+	} else {
+		/* The master was already chosen - go be a slave */
+		cpusave->krs_cpu_state = KAIF_CPU_STATE_SLAVE;
+		membar_producer();
+	}
+
+	kaif_lock_exit(&kaif_master_lock);
+}
+
+int
+kaif_main_loop(kaif_cpusave_t *cpusave)
+{
+	int cmd;
+
+	if (kaif_master_cpuid == KAIF_MASTER_CPUID_UNSET) {
+		if (!kmdb_dpi_resume_requested &&
+		    kmdb_kdi_get_unload_request()) {
+			/*
+			 * Special case: Unload requested before first debugger
+			 * entry.  Don't stop the world, as there's nothing to
+			 * clean up that can't be handled by the running kernel.
+			 */
+			cpusave->krs_cpu_state = KAIF_CPU_STATE_NONE;
+			return (KAIF_CPU_CMD_RESUME);
+		}
+
+		kaif_select_master(cpusave);
+
+#ifdef __sparc
+		if (kaif_master_cpuid == cpusave->krs_cpu_id) {
+			/*
+			 * Everyone has arrived, so we can disarm the post-PROM
+			 * entry point.
+			 */
+			*kaif_promexitarmp = 0;
+			membar_producer();
+		}
+#endif
+	} else if (kaif_master_cpuid == cpusave->krs_cpu_id) {
+		cpusave->krs_cpu_state = KAIF_CPU_STATE_MASTER;
+	} else {
+		cpusave->krs_cpu_state = KAIF_CPU_STATE_SLAVE;
+	}
+
+	cpusave->krs_cpu_flushed = 0;
+
+	kaif_lock_enter(&kaif_loop_lock);
+	kaif_looping++;
+	kaif_lock_exit(&kaif_loop_lock);
+
+	/*
+	 * We know who the master and slaves are, so now they can go off
+	 * to their respective loops.
+	 */
+	do {
+		if (kaif_master_cpuid == cpusave->krs_cpu_id)
+			cmd = kaif_master_loop(cpusave);
+		else
+			cmd = kaif_slave_loop(cpusave);
+	} while (cmd == KAIF_CPU_CMD_SWITCH);
+
+	kaif_lock_enter(&kaif_loop_lock);
+	kaif_looping--;
+	kaif_lock_exit(&kaif_loop_lock);
+
+	cpusave->krs_cpu_state = KAIF_CPU_STATE_NONE;
+
+	if (cmd == KAIF_CPU_CMD_RESUME) {
+		/*
+		 * By this point, the master has directed the slaves to resume,
+		 * and everyone is making their way to this point.  We're going
+		 * to block here until all CPUs leave the master and slave
+		 * loops.  When all have arrived, we'll turn them all loose.
+		 * This barrier is required for two reasons:
+		 *
+		 * 1. There exists a race condition whereby a CPU could reenter
+		 *    the debugger while another CPU is still in the slave loop
+		 *    from this debugger entry.  This usually happens when the
+		 *    current master releases the slaves, and makes it back to
+		 *    the world before the slaves notice the release.  The
+		 *    former master then triggers a debugger entry, and attempts
+		 *    to stop the slaves for this entry before they've even
+		 *    resumed from the last one.  When the slaves arrive here,
+		 *    they'll have re-disabled interrupts, and will thus ignore
+		 *    cross-calls until they finish resuming.
+		 *
+		 * 2. At the time of this writing, there exists a SPARC bug that
+		 *    causes an apparently unsolicited interrupt vector trap
+		 *    from OBP to one of the slaves.  This wouldn't normally be
+		 *    a problem but for the fact that the cross-called CPU
+		 *    encounters some sort of failure while in OBP.  OBP
+		 *    recovers by executing the debugger-hook word, which sends
+		 *    the slave back into the debugger, triggering a debugger
+		 *    fault.  This problem seems to only happen during resume,
+		 *    the result being that all CPUs save for the cross-called
+		 *    one make it back into the world, while the cross-called
+		 *    one is stuck at the debugger fault prompt.  Leave the
+		 *    world in that state too long, and you'll get a mondo
+		 *    timeout panic.  If we hold everyone here, we can give the
+		 *    the user a chance to trigger a panic for further analysis.
+		 *    To trigger the bug, "pool_unlock:b :c" and "while : ; do
+		 *    psrset -p ; done".
+		 *
+		 * When the second item is fixed, the barrier can move into
+		 * kaif_select_master(), immediately prior to the setting of
+		 * kaif_master_cpuid.
+		 */
+		while (kaif_looping != 0)
+			continue;
+	}
+
+	return (cmd);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kaif_start.h b/usr/src/cmd/mdb/common/kmdb/kaif_start.h
new file mode 100644
index 0000000..a0234cb
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kaif_start.h
@@ -0,0 +1,47 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KAIF_START_H
+#define	_KAIF_START_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <kmdb/kaif_regs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int kaif_main_loop(kaif_cpusave_t *);
+
+extern int kaif_master_cpuid;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KAIF_START_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl.h b/usr/src/cmd/mdb/common/kmdb/kctl/kctl.h
new file mode 100644
index 0000000..fd2c0c7
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl.h
@@ -0,0 +1,144 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KCTL_H
+#define	_KCTL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kmdb_auxv.h>
+#include <kmdb/kmdb_wr.h>
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/kdi.h>
+#include <sys/modctl.h>
+#include <sys/ksynch.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+	KCTL_ST_INACTIVE = 0,		/* kmdb is inactive */
+	KCTL_ST_DSEG_ALLOCED,		/* kmdb segment has been allocated */
+	KCTL_ST_INITIALIZED,		/* kmdb_init has been called */
+	KCTL_ST_KCTL_PREACTIVATED,	/* kctl preactivation completed */
+	KCTL_ST_MOD_NOTIFIERS,		/* krtld module notifiers registered */
+	KCTL_ST_THREAD_STARTED,		/* WR queue thread started */
+	KCTL_ST_DBG_ACTIVATED,		/* kmdb activated */
+	KCTL_ST_KCTL_ACTIVATED,		/* kctl activated */
+	KCTL_ST_ACTIVE,			/* kernel is aware of kmdb activation */
+	KCTL_ST_DEACTIVATING		/* debugger is being deactivated */
+} kctl_state_t;
+
+typedef enum {
+	KCTL_WR_ST_RUN,			/* WR queue thread is running */
+	KCTL_WR_ST_STOP,		/* WR queue thread is stopping */
+	KCTL_WR_ST_STOPPED		/* WR queue thread has stopped */
+} kctl_wr_state_t;
+
+typedef struct kctl {
+	dev_info_t *kctl_drv_dip;	/* Driver's device info structure */
+	size_t kctl_memgoalsz;		/* Desired size of debugger memory */
+	caddr_t	kctl_dseg;		/* Debugger segment (Oz) address */
+	size_t kctl_dseg_size;		/* Debugger segment (Oz) size */
+	caddr_t kctl_mrbase;		/* Add'l Oz memory range base address */
+	size_t kctl_mrsize;		/* Add'l Oz memory range size */
+	vnode_t kctl_vp;		/* vnode used to allocate dbgr seg */
+	kctl_state_t kctl_state;	/* State of debugger */
+	uint_t kctl_boot_loaded;	/* Set if debugger loaded at boot */
+	struct bootops *kctl_boot_ops;	/* Boot operations (during init only) */
+	const char *kctl_execname;	/* Path of this module */
+	uint_t kctl_wr_avail;		/* Work available on the WR queue */
+	ksema_t kctl_wr_avail_sem;	/* For WR thr: Work avail on WR queue */
+	kthread_t *kctl_wr_thr;		/* Thread that processes WR queue */
+	kctl_wr_state_t kctl_wr_state;	/* State of WR queue thread */
+	kmutex_t kctl_lock;		/* serializes (de)activation */
+	kcondvar_t kctl_wr_cv;		/* WR queue thread completion */
+	kmutex_t kctl_wr_lock;		/* WR queue thread completion */
+	uint_t kctl_flags;		/* KMDB_F_* from kmdb.h */
+#ifdef __sparc
+	caddr_t kctl_tba;		/* kmdb's native trap table */
+#endif
+} kctl_t;
+
+extern kctl_t kctl;
+
+struct bootops;
+
+extern void kctl_dprintf(const char *, ...);
+extern void kctl_warn(const char *, ...);
+
+extern int kctl_preactivate_isadep(void);
+extern int kctl_activate_isadep(kdi_debugvec_t *);
+extern void kctl_depreactivate_isadep(void);
+extern void kctl_deactivate_isadep(void);
+extern void kctl_cleanup(void);
+
+extern void *kctl_boot_tmpinit(void);
+extern void kctl_boot_tmpfini(void *);
+
+extern void kctl_auxv_init(kmdb_auxv_t *, const char *, const char **, void *);
+extern void kctl_auxv_init_isadep(kmdb_auxv_t *, void *);
+extern void kctl_auxv_fini(kmdb_auxv_t *);
+extern void kctl_auxv_fini_isadep(kmdb_auxv_t *);
+
+extern void kctl_wrintr(void);
+extern void kctl_wrintr_fire(void);
+extern void kctl_wr_thr_start(void);
+extern void kctl_wr_thr_stop(void);
+extern void kctl_wr_thr_join(void);
+
+extern int kctl_mod_decompress(struct modctl *);
+extern void kctl_mod_loaded(struct modctl *);
+extern void kctl_mod_changed(uint_t, struct modctl *);
+extern void kctl_mod_notify_reg(void);
+extern void kctl_mod_notify_unreg(void);
+
+extern void kctl_dmod_init(void);
+extern void kctl_dmod_fini(void);
+extern void kctl_dmod_sync(void);
+extern void kctl_dmod_autoload(const char *);
+extern void kctl_dmod_unload_all(void);
+extern void kctl_dmod_path_reset(void);
+
+extern int kctl_wr_process(void);
+extern void kctl_wr_unload(void);
+
+extern char *kctl_basename(char *);
+extern char *kctl_strdup(const char *);
+extern void kctl_strfree(char *);
+
+#if defined(__sparc)
+extern kthread_t *kctl_curthread_set(kthread_t *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KCTL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_auxv.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_auxv.c
new file mode 100644
index 0000000..1ea1216
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_auxv.c
@@ -0,0 +1,119 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kctl/kctl.h>
+
+#include <sys/modctl.h>
+#include <sys/bootconf.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/kmdb.h>
+
+static uintptr_t
+kctl_lookup_by_name(char *modname, char *symname)
+{
+	struct modctl *mctl;
+	Sym *ksym;
+	uintptr_t addr;
+
+	if ((mctl = mod_hold_by_name(modname)) == NULL)
+		return (0);
+	if ((ksym = kobj_lookup_all(mctl->mod_mp, symname, 1)) == NULL) {
+		mod_release_mod(mctl);
+		return (0);
+	}
+
+	addr = ksym->st_value;
+
+	mod_release_mod(mctl);
+
+	return (addr);
+}
+
+static uintptr_t
+kctl_boot_lookup_by_name(char *modname, char *symname)
+{
+	struct modctl *mctl;
+	Sym *ksym;
+
+	if ((mctl = kobj_boot_mod_lookup(modname)) == NULL)
+		return (0);
+
+	if ((ksym = kobj_lookup_all(mctl->mod_mp, symname, 1)) == NULL)
+		return (0);
+
+	return (ksym->st_value);
+}
+
+void
+kctl_auxv_init(kmdb_auxv_t *kav, const char *cfg, const char **argv, void *romp)
+{
+	bzero(kav, sizeof (kmdb_auxv_t));
+	kav->kav_dseg = kctl.kctl_dseg;
+	kav->kav_dseg_size = kctl.kctl_dseg_size;
+	kav->kav_pagesize = PAGESIZE;
+	kav->kav_ncpu = NCPU;
+	kav->kav_kdi = &kobj_kdi;
+	kav->kav_wrintr_fire = kctl_wrintr_fire;
+
+	kav->kav_config = cfg;
+	kav->kav_argv = argv;
+
+	if (kctl.kctl_boot_loaded) {
+		/*
+		 * default_path hasn't been set yet, so we have to fetch the
+		 * path from OBP.
+		 */
+		ssize_t sz;
+
+		if ((sz = BOP_GETPROPLEN(kctl.kctl_boot_ops,
+		    "module-path")) != -1) {
+			kav->kav_modpath = kobj_alloc(sz, KM_TMP);
+			(void) BOP_GETPROP(kctl.kctl_boot_ops, "module-path",
+			    (char *)kav->kav_modpath);
+		}
+
+		kav->kav_lookup_by_name = kctl_boot_lookup_by_name;
+		kav->kav_flags |= KMDB_AUXV_FL_NOUNLOAD;
+	} else {
+		kav->kav_modpath = default_path;
+
+		kav->kav_lookup_by_name = kctl_lookup_by_name;
+	}
+
+	if (kctl.kctl_flags & KMDB_F_TRAP_NOSWITCH)
+		kav->kav_flags |= KMDB_AUXV_FL_NOTRPSWTCH;
+
+	kctl_auxv_init_isadep(kav, romp); /* can modify anything in kav */
+}
+
+void
+kctl_auxv_fini(kmdb_auxv_t *kav)
+{
+	kctl_auxv_fini_isadep(kav);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_dmod.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_dmod.c
new file mode 100644
index 0000000..cbe134f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_dmod.c
@@ -0,0 +1,439 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Driver-side functions for loading and unloading dmods.
+ */
+
+#include <sys/types.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/modctl.h>
+#include <sys/systm.h>
+#include <sys/ctf_api.h>
+#include <sys/kmdb.h>
+
+#include <kmdb/kctl/kctl.h>
+#include <kmdb/kctl/kctl_wr.h>
+#include <kmdb/kmdb_wr_impl.h>
+#include <kmdb/kmdb_kdi.h>
+#include <mdb/mdb_errno.h>
+
+/*
+ * When a load is attempted, a check is first made of the modules on the
+ * kctl_dmods list.  If a module is found, the load will not proceed.
+ * kctl_dmods_lock must be held while traversing kctl_dmods, and while adding
+ * to and subtracting from it.
+ */
+static struct modctl	kctl_dmods;
+static kmutex_t		kctl_dmods_lock;
+
+static kmdb_wr_path_t	*kctl_dmod_path;
+
+/*
+ * Used to track outstanding driver-initiated load notifications.  These
+ * notifications have been allocated by driver, and thus must be freed by the
+ * driver in the event of an emergency unload.  If we don't free them free
+ * them ourselves, they'll leak.  Granted, the world is probably melting down
+ * at that point, but there's no reason why we shouldn't tidy up the deck
+ * chairs before we go.
+ */
+static kmdb_wr_load_t	*kctl_dmod_loads;
+static kmutex_t 	kctl_dmod_loads_lock;
+
+static int
+kctl_find_module(char *modname, char *fullname, size_t fullnamelen)
+{
+	intptr_t fd;
+	int i;
+
+	/* If they gave us an absolute path, we don't need to search */
+	if (modname[0] == '/') {
+		if (strlen(modname) + 1 > fullnamelen) {
+			cmn_err(CE_WARN, "Can't load dmod %s - name too long\n",
+			    modname);
+			return (0);
+		}
+
+		if ((fd = kobj_open(modname)) == -1)
+			return (0);
+		kobj_close(fd);
+
+		strcpy(fullname, modname);
+
+		return (1);
+	}
+
+	for (i = 0; kctl_dmod_path->dpth_path[i] != NULL; i++) {
+		const char *path = kctl_dmod_path->dpth_path[i];
+
+		if (strlen(path) + 1 + strlen(modname) + 1 > fullnamelen) {
+			kctl_dprintf("Can't load dmod from %s/%s - "
+			    "name too long", path, modname);
+			continue;
+		}
+
+		(void) snprintf(fullname, fullnamelen, "%s/%s", path, modname);
+
+		if ((fd = kobj_open(fullname)) == -1)
+			continue;
+
+		kobj_close(fd);
+
+		kctl_dprintf("kobj_open %s found", fullname);
+
+		/* Found it */
+		return (1);
+	}
+
+	/* No luck */
+	return (0);
+}
+
+static void
+kctl_dlr_free(kmdb_wr_load_t *dlr)
+{
+	if (dlr->dlr_node.wn_flags & WNFLAGS_NOFREE)
+		return;
+
+	kctl_strfree(dlr->dlr_fname);
+	kmem_free(dlr, sizeof (kmdb_wr_load_t));
+}
+
+int
+kctl_dmod_load(kmdb_wr_load_t *dlr)
+{
+	struct modctl *modp;
+	char modpath[MAXPATHLEN];
+	const char *modname = kctl_basename(dlr->dlr_fname);
+	int rc;
+
+	mutex_enter(&kctl_dmods_lock);
+
+	/* Have we already loaded this dmod? */
+	for (modp = kctl_dmods.mod_next; modp != &kctl_dmods;
+	    modp = modp->mod_next) {
+		if (strcmp(modname, modp->mod_modname) == 0) {
+			mutex_exit(&kctl_dmods_lock);
+			dlr->dlr_errno = EEXIST;
+			return (-1);
+		}
+	}
+
+	/*
+	 * If we find something that looks like a dmod, create a modctl for it,
+	 * and add said modctl to our dmods list.  This will allow us to drop
+	 * the dmods lock, while still preventing duplicate loads.  If we aren't
+	 * able to actually load the dmod, we can always remove the modctl
+	 * later.
+	 */
+	if (!kctl_find_module(dlr->dlr_fname, modpath, sizeof (modpath))) {
+		mutex_exit(&kctl_dmods_lock);
+		dlr->dlr_errno = ENOENT;
+		return (-1);
+	}
+
+	modp = kobj_zalloc(sizeof (struct modctl), KM_SLEEP);
+
+	modp->mod_filename = kctl_strdup(modpath);
+	modp->mod_modname = kctl_basename(modp->mod_filename);
+	modp->mod_busy = 1;
+	modp->mod_loadflags |= MOD_NOAUTOUNLOAD | MOD_NONOTIFY;
+	modp->mod_next = &kctl_dmods;
+	modp->mod_prev = kctl_dmods.mod_prev;
+	modp->mod_prev->mod_next = modp;
+	kctl_dmods.mod_prev = modp;
+
+	mutex_exit(&kctl_dmods_lock);
+
+	if (kctl.kctl_boot_ops == NULL)
+		rc = kobj_load_module(modp, 0);
+	else
+		rc = kobj_load_primary_module(modp);
+
+	if (rc < 0) {
+		kctl_warn("failed to load dmod %s", modp->mod_modname);
+
+		if (kctl.kctl_boot_ops == NULL)
+			mod_release_requisites(modp);
+
+		mutex_enter(&kctl_dmods_lock);
+		modp->mod_next->mod_prev = modp->mod_prev;
+		modp->mod_prev->mod_next = modp->mod_next;
+		mutex_exit(&kctl_dmods_lock);
+
+		kctl_strfree(modp->mod_filename);
+		kobj_free(modp, sizeof (struct modctl));
+
+		dlr->dlr_errno = EMDB_NOMOD;
+		return (-1);
+	}
+
+	/*
+	 * It worked!  If the module has any CTF data, decompress it, and make a
+	 * note of the load.
+	 */
+	mutex_enter(&mod_lock);
+	if ((rc = kctl_mod_decompress(modp)) != 0) {
+		kctl_warn("failed to decompress CTF data for dmod %s: %s",
+		    modpath, ctf_errmsg(rc));
+	}
+	mutex_exit(&mod_lock);
+
+	kctl_dprintf("loaded dmod %s at %p", modpath, modp);
+
+	modp->mod_ref = 1;
+	modp->mod_loaded = 1;
+
+	dlr->dlr_modctl = modp;
+
+	return (0);
+}
+
+/*
+ * Driver-initiated loads.  Load the module and announce it to the debugger.
+ */
+void
+kctl_dmod_autoload(const char *fname)
+{
+	kmdb_wr_load_t *dlr;
+
+	dlr = kobj_zalloc(sizeof (kmdb_wr_load_t), KM_SLEEP);
+	dlr->dlr_node.wn_task = WNTASK_DMOD_LOAD;
+	dlr->dlr_fname = kctl_strdup(fname);
+
+	/*
+	 * If we're loading at boot, the kmdb_wr_load_t will have been
+	 * "allocated" by krtld, and will thus not be under the control of
+	 * kmem.  We need to ensure that we don't attempt to free it when
+	 * we get it back from the debugger.
+	 */
+	if (kctl.kctl_boot_ops != NULL)
+		dlr->dlr_node.wn_flags |= WNFLAGS_NOFREE;
+
+	if (kctl_dmod_load(dlr) < 0) {
+		kctl_dlr_free(dlr);
+		return;
+	}
+
+	/*
+	 * Add to the list of open driver-initiated loads.  We need to track
+	 * these so we can free them (and thus avoid leaks) in the event that
+	 * the debugger needs to be blown away before it can return them.
+	 */
+	mutex_enter(&kctl_dmod_loads_lock);
+	dlr->dlr_next = kctl_dmod_loads;
+	if (kctl_dmod_loads != NULL)
+		kctl_dmod_loads->dlr_prev = dlr;
+	kctl_dmod_loads = dlr;
+	mutex_exit(&kctl_dmod_loads_lock);
+
+	kmdb_wr_debugger_notify(dlr);
+}
+
+void
+kctl_dmod_load_all(void)
+{
+	/*
+	 * The standard list of modules isn't populated until the tail end of
+	 * kobj_init().  Prior to that point, the only available list is that of
+	 * primaries.  We'll use that if the normal list isn't ready yet.
+	 */
+	if (modules.mod_mp == NULL) {
+		/* modules hasn't been initialized yet -- use primaries */
+		struct modctl_list *ml;
+
+		for (ml = kobj_linkmaps[KOBJ_LM_PRIMARY]; ml != NULL;
+		    ml = ml->modl_next)
+			kctl_dmod_autoload(ml->modl_modp->mod_modname);
+
+	} else {
+		struct modctl *modp = &modules;
+
+		do {
+			if (modp->mod_mp != NULL)
+				kctl_dmod_autoload(modp->mod_modname);
+		} while ((modp = modp->mod_next) != &modules);
+	}
+}
+
+void
+kctl_dmod_load_ack(kmdb_wr_load_t *dlr)
+{
+	/* Remove from the list of open driver-initiated requests */
+	mutex_enter(&kctl_dmod_loads_lock);
+	if (dlr->dlr_prev == NULL)
+		kctl_dmod_loads = dlr->dlr_next;
+	else
+		dlr->dlr_prev->dlr_next = dlr->dlr_next;
+
+	if (dlr->dlr_next != NULL)
+		dlr->dlr_next->dlr_prev = dlr->dlr_prev;
+	mutex_exit(&kctl_dmod_loads_lock);
+
+	kctl_dlr_free(dlr);
+}
+
+static int
+kctl_dmod_unload_common(struct modctl *modp)
+{
+	struct modctl *m;
+
+	kctl_dprintf("unloading dmod %s", modp->mod_modname);
+
+	mutex_enter(&kctl_dmods_lock);
+	for (m = kctl_dmods.mod_next; m != &kctl_dmods; m = m->mod_next) {
+		if (m == modp)
+			break;
+	}
+	mutex_exit(&kctl_dmods_lock);
+
+	if (m != modp)
+		return (ENOENT);
+
+	/* Found it */
+	modp->mod_ref = 0;
+	modp->mod_loaded = 0;
+
+	kobj_unload_module(modp);
+
+	mod_release_requisites(modp);
+
+	/* Remove it from our dmods list */
+	mutex_enter(&kctl_dmods_lock);
+	modp->mod_next->mod_prev = modp->mod_prev;
+	modp->mod_prev->mod_next = modp->mod_next;
+	mutex_exit(&kctl_dmods_lock);
+
+	kctl_strfree(modp->mod_filename);
+	kmem_free(modp, sizeof (struct modctl));
+
+	return (0);
+}
+
+void
+kctl_dmod_unload(kmdb_wr_unload_t *dur)
+{
+	int rc;
+
+	if ((rc = kctl_dmod_unload_common(dur->dur_modctl)) != 0) {
+		cmn_err(CE_WARN, "unexpected dmod unload failure: %d\n", rc);
+		dur->dur_errno = rc;
+	}
+}
+
+/*
+ * This will be called during shutdown.  The debugger has been stopped, we're
+ * off the module notification list, and we've already processed everything in
+ * the driver's work queue.  We should have received (and processed) unload
+ * requests for each of the dmods we've loaded.  To be safe, however, we'll
+ * double-check.
+ *
+ * If we're doing an emergency shutdown, there may be outstanding
+ * driver-initiated messages that haven't been returned to us.  The debugger is
+ * dead, so it's not going to be returning them.  We'll leak them unless we
+ * find and free them ourselves.
+ */
+void
+kctl_dmod_unload_all(void)
+{
+	kmdb_wr_load_t *dlr;
+	struct modctl *modp;
+
+	while ((modp = kctl_dmods.mod_next) != &kctl_dmods)
+		(void) kctl_dmod_unload_common(modp);
+
+	while ((dlr = kctl_dmod_loads) != NULL) {
+		kctl_dmod_loads = dlr->dlr_next;
+
+		kctl_dprintf("freed orphan load notification for %s",
+		    dlr->dlr_fname);
+		kctl_dlr_free(dlr);
+	}
+}
+
+kmdb_wr_path_t *
+kctl_dmod_path_set(kmdb_wr_path_t *pth)
+{
+	kmdb_wr_path_t *opth;
+
+	if (kctl.kctl_flags & KMDB_F_DRV_DEBUG) {
+		int i;
+		kctl_dprintf("changing dmod path to: %p", pth);
+		for (i = 0; pth->dpth_path[i] != NULL; i++)
+			kctl_dprintf(" %s", pth->dpth_path[i]);
+	}
+
+	opth = kctl_dmod_path;
+	kctl_dmod_path = pth;
+
+	return (opth);
+}
+
+void
+kctl_dmod_path_reset(void)
+{
+	kmdb_wr_path_t *pth;
+
+	if ((pth = kctl_dmod_path_set(NULL)) != NULL) {
+		WR_ACK(pth);
+		kmdb_wr_debugger_notify(pth);
+	}
+}
+
+void
+kctl_dmod_sync(void)
+{
+	struct modctl *modp;
+
+	/*
+	 * kobj_sync() has no visibility into our dmods, so we need to
+	 * explicitly tell krtld to export the portions of our dmods that were
+	 * allocated using boot scratch memory.
+	 */
+	for (modp = kctl_dmods.mod_next; modp != &kctl_dmods;
+	    modp = modp->mod_next)
+		kobj_export_module(modp->mod_mp);
+}
+
+void
+kctl_dmod_init(void)
+{
+	mutex_init(&kctl_dmod_loads_lock, NULL, MUTEX_DRIVER, NULL);
+	mutex_init(&kctl_dmods_lock, NULL, MUTEX_DRIVER, NULL);
+
+	bzero(&kctl_dmods, sizeof (struct modctl));
+	kctl_dmods.mod_next = kctl_dmods.mod_prev = &kctl_dmods;
+}
+
+void
+kctl_dmod_fini(void)
+{
+	mutex_destroy(&kctl_dmods_lock);
+	mutex_destroy(&kctl_dmod_loads_lock);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_err.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_err.c
new file mode 100644
index 0000000..beab90a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_err.c
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+#include <sys/bootconf.h>
+#include <sys/sysmacros.h>
+#include <sys/kmdb.h>
+
+/*
+ * The boot printing interfaces don't expose anything that'll allow us
+ * to print multiple arguments at once.  That is, they expose printf-like
+ * interfaces, but these interfaces only support one format specifier per
+ * invocation.  The routines in this file allow the rest of the driver
+ * to pretend that this limitation doesn't exist.
+ */
+
+#include <kmdb/kctl/kctl.h>
+
+static void
+kctl_vprintf(int code, const char *format, va_list ap)
+{
+	if (kctl.kctl_boot_ops == NULL) {
+		vcmn_err(code, format, ap);
+
+	} else {
+		char buf[128];
+
+		if (code == CE_WARN)
+			BOP_PUTSARG(kctl.kctl_boot_ops, "WARNING: ", NULL);
+		else if (code == CE_NOTE)
+			BOP_PUTSARG(kctl.kctl_boot_ops, "NOTE: ", NULL);
+
+		(void) vsnprintf(buf, sizeof (buf), format, ap);
+		BOP_PUTSARG(kctl.kctl_boot_ops, "%s\n", buf);
+	}
+}
+
+void
+kctl_warn(const char *format, ...)
+{
+	va_list ap;
+
+	va_start(ap, format);
+	kctl_vprintf(CE_WARN, format, ap);
+	va_end(ap);
+}
+
+void
+kctl_dprintf(const char *format, ...)
+{
+	va_list ap;
+
+	if (!(kctl.kctl_flags & KMDB_F_DRV_DEBUG))
+		return;
+
+	va_start(ap, format);
+	kctl_vprintf(CE_NOTE, format, ap);
+	va_end(ap);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_main.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_main.c
new file mode 100644
index 0000000..13992c6
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_main.c
@@ -0,0 +1,639 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kctl/kctl.h>
+#include <kmdb/kctl/kctl_wr.h>
+#include <kmdb/kmdb_kctl.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_auxv.h>
+#include <mdb/mdb_errno.h>
+
+#include <sys/sysmacros.h>
+#include <sys/reboot.h>
+#include <sys/atomic.h>
+#include <sys/bootconf.h>
+#include <sys/kmdb.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/promimpl.h>
+#include <sys/kdi_impl.h>
+#include <sys/ctf_api.h>
+#include <vm/seg_kmem.h>
+
+kctl_t kctl;
+
+#define	KCTL_EXECNAME		"/kernel/drv/kmdb"
+
+#if defined(_LP64)
+#define	KCTL_MEM_GOALSZ		(20 * 1024 * 1024)
+#else
+#define	KCTL_MEM_GOALSZ		(10 * 1024 * 1024)
+#endif
+
+/*
+ * kmdb will call its own copies of the promif routines during
+ * initialization.  As these routines are intended to be used when the
+ * world is stopped, they don't attempt to grab the PROM lock.  Very
+ * Bad Things could happen if kmdb called a prom routine while someone
+ * else was calling the kernel's copy of another prom routine, so we
+ * grab the PROM lock ourselves before we start initialization.
+ */
+#ifdef __sparc
+#define	KCTL_PROM_LOCK		promif_preprom()
+#define	KCTL_PROM_UNLOCK	promif_postprom()
+#else
+#define	KCTL_PROM_LOCK
+#define	KCTL_PROM_UNLOCK
+#endif
+
+static int
+kctl_init(void)
+{
+	if (kobj_kdi.kdi_version != KDI_VERSION) {
+		kctl_warn("kmdb/kernel version mismatch (expected %d, "
+		    "found %d)", KDI_VERSION, kobj_kdi.kdi_version);
+		return (-1);
+	}
+
+	sema_init(&kctl.kctl_wr_avail_sem, 0, NULL, SEMA_DRIVER, NULL);
+	mutex_init(&kctl.kctl_wr_lock, NULL, MUTEX_DRIVER, NULL);
+	cv_init(&kctl.kctl_wr_cv, NULL, CV_DRIVER, NULL);
+	mutex_init(&kctl.kctl_lock, NULL, MUTEX_DRIVER, NULL);
+
+	kctl.kctl_execname = KCTL_EXECNAME; /* XXX get from modctl? */
+
+	kctl.kctl_state = KCTL_ST_INACTIVE;
+
+	kctl.kctl_dseg = kctl.kctl_mrbase = NULL;
+	kctl.kctl_dseg_size = kctl.kctl_mrsize = 0;
+
+	kctl_dmod_init();
+
+	return (0);
+}
+
+static void
+kctl_fini(void)
+{
+	kctl_dmod_fini();
+
+	mutex_destroy(&kctl.kctl_lock);
+	cv_destroy(&kctl.kctl_wr_cv);
+	mutex_destroy(&kctl.kctl_wr_lock);
+	sema_destroy(&kctl.kctl_wr_avail_sem);
+}
+
+static uint_t
+kctl_set_state(uint_t state)
+{
+	uint_t ostate = kctl.kctl_state;
+
+	/* forward progess only, please */
+	if (state > ostate) {
+		kctl_dprintf("new kctl state: %d", state);
+		kctl.kctl_state = state;
+	}
+
+	return (ostate);
+}
+
+static int
+kctl_boot_dseg_alloc(caddr_t dsegaddr, size_t dsegsz)
+{
+	/*
+	 * The Intel boot memory allocator will cleverly map us onto a 4M
+	 * page if we request the whole 4M Intel segment at once.  This
+	 * will break physical memory r/w, so we break the request into
+	 * chunks.  The allocator isn't smart enough to combine requests,
+	 * so it'll give us a bunch of 4k pages.
+	 */
+	while (dsegsz >= 1024*1024) {
+		size_t sz = MIN(dsegsz, 1024*1024);
+
+		if (BOP_ALLOC(kctl.kctl_boot_ops, dsegaddr, sz, BO_NO_ALIGN) !=
+		    dsegaddr)
+			return (-1);
+
+		dsegaddr += sz;
+		dsegsz -= sz;
+	}
+
+	return (0);
+}
+
+static int
+kctl_dseg_alloc(caddr_t addr, size_t sz)
+{
+	ASSERT(((uintptr_t)addr & PAGEOFFSET) == 0);
+
+	/* make sure there isn't something there already (like kadb) */
+	if (hat_getpfnum(kas.a_hat, addr) != PFN_INVALID)
+		return (EAGAIN);
+
+	if (segkmem_xalloc(NULL, addr, sz, VM_NOSLEEP, 0, segkmem_page_create,
+	    NULL) == NULL)
+		return (ENOMEM);
+
+	return (0);
+}
+
+static void
+kctl_dseg_free(caddr_t addr, size_t sz)
+{
+	ASSERT(((uintptr_t)addr & PAGEOFFSET) == 0);
+
+	segkmem_free(NULL, addr, sz);
+}
+
+static void
+kctl_memavail(void)
+{
+	size_t needed;
+	caddr_t base;
+
+	/*
+	 * We're now free to allocate the non-fixed portion of the debugger's
+	 * memory region.
+	 */
+
+	needed = P2ROUNDUP(kctl.kctl_memgoalsz <= kctl.kctl_dseg_size ? 0 :
+	    kctl.kctl_memgoalsz - kctl.kctl_dseg_size, PAGESIZE);
+
+	if (needed == 0)
+		return;
+
+	if ((base = kmem_zalloc(needed, KM_NOSLEEP)) == NULL) {
+		/*
+		 * If we're going to wedge the machine during debugger startup,
+		 * at least let them know why it's going to wedge.
+		 */
+		cmn_err(CE_WARN, "retrying of kmdb allocation of 0x%lx bytes\n",
+		    (ulong_t)needed);
+
+		base = kmem_zalloc(needed, KM_SLEEP);
+	}
+
+	if (kdi_dvec->dv_memavail(base, needed) < 0) {
+		cmn_err(CE_WARN, "failed to add memory to debugger\n");
+		kmem_free(base, needed);
+	}
+
+	kctl.kctl_mrbase = base;
+	kctl.kctl_mrsize = needed;
+}
+
+void
+kctl_cleanup(void)
+{
+	uint_t state = kctl_set_state(KCTL_ST_DEACTIVATING);
+
+	kctl_dprintf("cleaning up from state %d", state);
+
+	ASSERT(kctl.kctl_boot_loaded == 0);
+
+	switch (state) {
+	case KCTL_ST_ACTIVE:
+		boothowto &= ~RB_DEBUG;
+		/* XXX there's a race here */
+		kdi_dvec = NULL;
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_KCTL_ACTIVATED:
+		kctl_deactivate_isadep();
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_DBG_ACTIVATED:
+		KCTL_PROM_LOCK;
+		kmdb_deactivate();
+		KCTL_PROM_UNLOCK;
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_THREAD_STARTED:
+		if (curthread != kctl.kctl_wr_thr) {
+			kctl_wr_thr_stop();
+			kctl_wr_thr_join();
+		}
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_MOD_NOTIFIERS:
+		kctl_mod_notify_unreg();
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_KCTL_PREACTIVATED:
+		kctl_depreactivate_isadep();
+		/*FALLTHROUGH*/
+
+	case KCTL_ST_INITIALIZED:
+		/* There's no kmdb_fini */
+	case KCTL_ST_DSEG_ALLOCED:
+		kctl_dseg_free(kctl.kctl_dseg, kctl.kctl_dseg_size);
+
+		if (kctl.kctl_mrbase != NULL)
+			kmem_free(kctl.kctl_mrbase, kctl.kctl_mrsize);
+		/*FALLTHROUGH*/
+	}
+
+	kctl.kctl_state = KCTL_ST_INACTIVE;
+}
+
+static void
+kctl_startup_modules(void)
+{
+	struct modctl *modp;
+
+	/*
+	 * Normal module load and unload is now available.  Prior to this point,
+	 * we could only load modules, and that only when the debugger was being
+	 * initialized.
+	 *
+	 * We'll need to prepare the modules we've already loaded (if any) for
+	 * the brave new world in which boot is unmapped.
+	 */
+	kctl_dmod_sync();
+
+	/*
+	 * Process any outstanding loads or unloads and prepare for automatic
+	 * module loading and unloading.
+	 */
+	(void) kctl_wr_process();
+
+	kctl_mod_notify_reg();
+
+	(void) kctl_set_state(KCTL_ST_MOD_NOTIFIERS);
+
+	modp = &modules;
+	do {
+		kctl_mod_loaded(modp);
+	} while ((modp = modp->mod_next) != &modules);
+}
+
+static void
+kctl_startup_thread(void)
+{
+	/*
+	 * Create the worker thread, which will handle future requests from the
+	 * debugger.
+	 */
+	kctl_wr_thr_start();
+
+	(void) kctl_set_state(KCTL_ST_THREAD_STARTED);
+}
+
+static int
+kctl_startup_boot(void)
+{
+	struct modctl_list *lp, **lpp;
+	int rc;
+
+	if (kctl_wr_process() < 0) {
+		kctl_warn("kmdb: failed to load modules");
+		return (-1);
+	}
+
+	mutex_enter(&mod_lock);
+
+	for (lpp = kobj_linkmaps; *lpp != NULL; lpp++) {
+		for (lp = *lpp; lp != NULL; lp = lp->modl_next) {
+			if ((rc = kctl_mod_decompress(lp->modl_modp)) != 0) {
+				kctl_warn("kmdb: failed to decompress CTF data "
+				    "for %s: %s", lp->modl_modp->mod_modname,
+				    ctf_errmsg(rc));
+			}
+		}
+	}
+
+	mutex_exit(&mod_lock);
+
+	return (0);
+}
+
+static int
+kctl_startup_preactivate(void *romp, const char *cfg, const char **argv)
+{
+	kmdb_auxv_t kav;
+	int rc;
+
+	kctl_auxv_init(&kav, cfg, argv, romp);
+	KCTL_PROM_LOCK;
+	rc = kmdb_init(kctl.kctl_execname, &kav);
+	KCTL_PROM_UNLOCK;
+	kctl_auxv_fini(&kav);
+
+	if (rc < 0)
+		return (EMDB_KNOLOAD);
+
+	(void) kctl_set_state(KCTL_ST_INITIALIZED);
+
+	if (kctl_preactivate_isadep() != 0)
+		return (EIO);
+
+	(void) kctl_set_state(KCTL_ST_KCTL_PREACTIVATED);
+
+	return (0);
+}
+
+static int
+kctl_startup_activate(uint_t flags)
+{
+	kdi_debugvec_t *dvec;
+
+	KCTL_PROM_LOCK;
+	kmdb_activate(&dvec, flags);
+	KCTL_PROM_UNLOCK;
+
+	(void) kctl_set_state(KCTL_ST_DBG_ACTIVATED);
+
+	/*
+	 * fill in a few remaining debugvec entries.
+	 */
+	dvec->dv_kctl_modavail = kctl_startup_modules;
+	dvec->dv_kctl_thravail = kctl_startup_thread;
+	dvec->dv_kctl_memavail = kctl_memavail;
+
+	if (kctl_activate_isadep(dvec) != 0)
+		return (EIO);
+
+	(void) kctl_set_state(KCTL_ST_KCTL_ACTIVATED);
+
+	kdi_dvec = dvec;
+	membar_producer();
+
+	boothowto |= RB_DEBUG;
+
+	(void) kctl_set_state(KCTL_ST_ACTIVE);
+
+	return (0);
+}
+
+static int
+kctl_state_check(uint_t state, uint_t ok_state)
+{
+	if (state == ok_state)
+		return (0);
+
+	if (state == KCTL_ST_INACTIVE)
+		return (EMDB_KINACTIVE);
+	else if (kctl.kctl_state > KCTL_ST_INACTIVE &&
+	    kctl.kctl_state < KCTL_ST_ACTIVE)
+		return (EMDB_KACTIVATING);
+	else if (kctl.kctl_state == KCTL_ST_ACTIVE)
+		return (EMDB_KACTIVE);
+	else if (kctl.kctl_state == KCTL_ST_DEACTIVATING)
+		return (EMDB_KDEACTIVATING);
+	else
+		return (EINVAL);
+}
+
+int
+kctl_deactivate(void)
+{
+	int rc;
+
+	mutex_enter(&kctl.kctl_lock);
+
+	if (kctl.kctl_boot_loaded) {
+		rc = EMDB_KNOUNLOAD;
+		goto deactivate_done;
+	}
+
+	if ((rc = kctl_state_check(kctl.kctl_state, KCTL_ST_ACTIVE)) != 0)
+		goto deactivate_done;
+
+	kmdb_kdi_set_unload_request();
+	kdi_dvec_enter();
+
+	/*
+	 * The debugger will pass the request to the work thread, which will
+	 * stop itself.
+	 */
+	kctl_wr_thr_join();
+
+deactivate_done:
+	mutex_exit(&kctl.kctl_lock);
+
+	return (rc);
+}
+
+/*
+ * Called from krtld, this indicates that the user loaded kmdb at boot.  We
+ * track activation states, but we don't attempt to clean up if activation
+ * fails, because boot debugger load failures are fatal.
+ *
+ * Further complicating matters, various kernel routines, such as bcopy and
+ * mutex_enter, assume the presence of some basic state.  On SPARC, it's the
+ * presence of a valid curthread pointer.  On AMD64, it's a valid curcpu
+ * pointer in GSBASE.  We set up temporary versions of these before beginning
+ * activation, and tear them down when we're done.
+ */
+int
+kctl_boot_activate(struct bootops *ops, void *romp, size_t memsz,
+    const char **argv)
+{
+	void *old;
+
+#ifdef __lint
+	{
+	/*
+	 * krtld does a name-based symbol lookup to find this routine.  It then
+	 * casts the address it gets, calling the result.  We want to make sure
+	 * that the call in krtld stays in sync with the prototype for this
+	 * function, so we define a type (kctl_boot_activate_f) that matches the
+	 * current prototype.  The following assignment ensures that the type
+	 * still matches the declaration, with lint as the enforcer.
+	 */
+	kctl_boot_activate_f *kba = kctl_boot_activate;
+	if (kba == NULL)	/* Make lint think kba is actually used */
+		return (0);
+	}
+#endif
+
+	old = kctl_boot_tmpinit();	/* Set up temporary state */
+
+	ASSERT(ops != NULL);
+	kctl.kctl_boot_ops = ops;	/* must be set before kctl_init */
+
+	if (kctl_init() < 0)
+		return (-1);
+
+	kctl.kctl_boot_loaded = 1;
+
+	kctl_dprintf("beginning kmdb initialization");
+
+	if (memsz == 0)
+		memsz = KCTL_MEM_GOALSZ;
+
+	kctl.kctl_dseg = (caddr_t)SEGDEBUGBASE;
+	kctl.kctl_dseg_size = (memsz > SEGDEBUGSIZE ? SEGDEBUGSIZE : memsz);
+	kctl.kctl_memgoalsz = memsz;
+
+	if (kctl_boot_dseg_alloc(kctl.kctl_dseg, kctl.kctl_dseg_size) < 0) {
+		kctl_warn("kmdb: failed to allocate %d-byte debugger area at "
+		    "%x", kctl.kctl_dseg_size, kctl.kctl_dseg);
+		return (-1);
+	}
+
+	(void) kctl_set_state(KCTL_ST_DSEG_ALLOCED);
+
+	if (kctl_startup_preactivate(romp, NULL, argv) != 0 ||
+	    kctl_startup_activate(KMDB_ACT_F_BOOT)) {
+		kctl_warn("kmdb: failed to activate");
+		return (-1);
+	}
+
+	if (kctl_startup_boot() < 0)
+		return (-1);
+
+	kctl_dprintf("finished with kmdb initialization");
+
+	kctl.kctl_boot_ops = NULL;
+	kctl_boot_tmpfini(old);
+
+	return (0);
+}
+
+int
+kctl_modload_activate(size_t memsz, const char *cfg, uint_t flags)
+{
+	int rc;
+
+	mutex_enter(&kctl.kctl_lock);
+
+	if ((rc = kctl_state_check(kctl.kctl_state, KCTL_ST_INACTIVE)) != 0) {
+		if ((flags & KMDB_F_AUTO_ENTRY) && rc == EMDB_KACTIVE) {
+			kdi_dvec_enter();
+			rc = 0;
+		}
+
+		mutex_exit(&kctl.kctl_lock);
+		return (rc);
+	}
+
+	kctl.kctl_flags = flags;
+
+	if (memsz == 0)
+		memsz = KCTL_MEM_GOALSZ;
+
+	kctl.kctl_dseg = (caddr_t)SEGDEBUGBASE;
+	kctl.kctl_dseg_size = (memsz > SEGDEBUGSIZE ? SEGDEBUGSIZE : memsz);
+	kctl.kctl_memgoalsz = memsz;
+
+	if ((rc = kctl_dseg_alloc(kctl.kctl_dseg, kctl.kctl_dseg_size)) != 0)
+		goto activate_fail;
+
+	(void) kctl_set_state(KCTL_ST_DSEG_ALLOCED);
+
+	if ((rc = kctl_startup_preactivate(NULL, cfg, NULL)) != 0)
+		goto activate_fail;
+
+	kctl_startup_modules();
+	kctl_startup_thread();
+
+	if ((rc = kctl_startup_activate(0)) != 0)
+		goto activate_fail;
+
+	kctl_memavail();	/* Must be after kdi_dvec is set */
+
+	if (kctl.kctl_flags & KMDB_F_AUTO_ENTRY)
+		kdi_dvec_enter();
+
+	mutex_exit(&kctl.kctl_lock);
+	return (0);
+
+activate_fail:
+	kctl_cleanup();
+	mutex_exit(&kctl.kctl_lock);
+	return (rc);
+}
+
+/*
+ * This interface will be called when drv/kmdb loads.  When we get the call, one
+ * of two things will have happened:
+ *
+ *  1. The debugger was loaded at boot.  We've progressed far enough into boot
+ *     as to allow drv/kmdb to be loaded as a non-primary.  Invocation of this
+ *     interface is the signal to the debugger that it can start allowing things
+ *     like dmod loading and automatic CTF decompression - things which require
+ *     the system services that have now been started.
+ *
+ *  2. The debugger was loaded after boot.  mdb opened /dev/kmdb, causing
+ *     drv/kmdb to load, followed by misc/kmdb.  Nothing has been set up yet,
+ *     so we need to initialize.  Activation will occur separately, so we don't
+ *     have to worry about that.
+ */
+int
+kctl_attach(dev_info_t *dip)
+{
+	kctl.kctl_drv_dip = dip;
+
+	return (0);
+}
+
+int
+kctl_detach(void)
+{
+	return (kctl.kctl_state == KCTL_ST_INACTIVE ? 0 : EBUSY);
+}
+
+static struct modlmisc modlmisc = {
+	&mod_miscops,
+	KMDB_VERSION
+};
+
+static struct modlinkage modlinkage = {
+	MODREV_1,
+	(void *)&modlmisc,
+	NULL
+};
+
+/*
+ * Invoked only when debugger is loaded via modload - not invoked when debugger
+ * is loaded at boot.  kctl_boot_activate needs to call anything (aside from
+ * mod_install) this function does.
+ */
+int
+_init(void)
+{
+	if (kctl_init() < 0)
+		return (EINVAL);
+
+	return (mod_install(&modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+	return (mod_info(&modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+	kctl_fini();
+
+	return (mod_remove(&modlinkage));
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_mod.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_mod.c
new file mode 100644
index 0000000..9caa353
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_mod.c
@@ -0,0 +1,111 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Tracking of kernel module loads and unloads
+ */
+
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kctl/kctl.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/ctf_api.h>
+
+static kobj_notify_list_t kctl_mod_notifiers[] = {
+	{ kctl_mod_changed, KOBJ_NOTIFY_MODLOADED },
+	{ kctl_mod_changed, KOBJ_NOTIFY_MODUNLOADING },
+	{ NULL, 0 }
+};
+
+int
+kctl_mod_decompress(struct modctl *modp)
+{
+	ctf_file_t *fp;
+	struct module *mp = modp->mod_mp;
+	int rc;
+
+	if ((kmdb_kdi_get_flags() & KMDB_KDI_FL_NOCTF) || mp->ctfdata == NULL)
+		return (0);
+
+	if ((fp = ctf_modopen(mp, &rc)) == NULL)
+		return (rc);
+
+	ctf_close(fp);
+
+	return (0);
+}
+
+void
+kctl_mod_loaded(struct modctl *modp)
+{
+	int rc;
+
+	mutex_enter(&mod_lock);
+	if (modp->mod_mp == NULL) {
+		mutex_exit(&mod_lock);
+		return;
+	}
+
+	if ((rc = kctl_mod_decompress(modp)) != 0) {
+		cmn_err(CE_WARN, "failed to decompress CTF data for %s: %s\n",
+		    modp->mod_modname, ctf_errmsg(rc));
+	}
+	mutex_exit(&mod_lock);
+
+	if (!(kmdb_kdi_get_flags() & KMDB_KDI_FL_NOMODS))
+		kctl_dmod_autoload(modp->mod_modname);
+}
+
+/*ARGSUSED*/
+void
+kctl_mod_changed(uint_t why, struct modctl *what)
+{
+	if (why == KOBJ_NOTIFY_MODLOADED)
+		kctl_mod_loaded(what);
+}
+
+/*
+ * Tell krtld to notify kmdb when modules have been loaded and unloaded
+ */
+void
+kctl_mod_notify_reg(void)
+{
+	kobj_notify_list_t *kn;
+
+	for (kn = kctl_mod_notifiers; kn->kn_func != NULL; kn++)
+		kobj_notify_add(kn);
+}
+
+void
+kctl_mod_notify_unreg(void)
+{
+	kobj_notify_list_t *kn;
+
+	for (kn = kctl_mod_notifiers; kn->kn_func != NULL; kn++)
+		kobj_notify_remove(kn);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_string.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_string.c
new file mode 100644
index 0000000..0dcbc86
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_string.c
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/systm.h>
+#include <sys/kobj.h>
+
+#include <kmdb/kctl/kctl.h>
+
+char *
+kctl_basename(char *s)
+{
+	char *p = strrchr(s, '/');
+
+	if (p == NULL)
+		return (s);
+
+	return (++p);
+}
+
+char *
+kctl_strdup(const char *s)
+{
+	char *s1 = kobj_alloc(strlen(s) + 1, KM_SLEEP);
+
+	(void) strcpy(s1, s);
+	return (s1);
+}
+
+void
+kctl_strfree(char *s)
+{
+	kobj_free(s, strlen(s) + 1);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.c b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.c
new file mode 100644
index 0000000..0b8103a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.c
@@ -0,0 +1,311 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Implements the kernel side of the debugger/kernel work queue.
+ */
+
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kctl/kctl.h>
+#include <kmdb/kctl/kctl_wr.h>
+
+#include <sys/proc.h>
+#include <sys/disp.h>
+#include <sys/kdi_impl.h>
+#include <sys/callb.h>
+
+#define	KCTL_WR_PROCESS_NORMAL		(void *)0
+#define	KCTL_WR_PROCESS_UNLOADING	(void *)1
+
+/*
+ * Processes events from the debugger -> driver notification queue.  Returns
+ * 1 if the debugger should be awakened after the queue has been processed.
+ */
+static int
+kctl_wr_process_cb(kmdb_wr_t *wn, void *arg)
+{
+	int unloading = (arg == KCTL_WR_PROCESS_UNLOADING);
+
+	switch (WR_TASK(wn)) {
+	case WNTASK_DMOD_LOAD: {
+		/*
+		 * If this is an ack, then we're getting back a message from a
+		 * load we initiated.  Free it.  If it's not an ack, we process
+		 * the message (attempt to load the requested module) and send
+		 * an ack back to the debugger.
+		 */
+		kmdb_wr_load_t *dlr = (kmdb_wr_load_t *)wn;
+
+		if (WR_ISACK(dlr)) {
+			kctl_dprintf("received ack for dmod load of %s",
+			    dlr->dlr_fname);
+			kctl_dmod_load_ack(dlr);
+			return (0);
+		} else
+			kctl_dprintf("received dmod load request %s",
+			    dlr->dlr_fname);
+
+		if (unloading) {
+			/*
+			 * If the user didn't wait for all dmods to load before
+			 * she triggered the debugger unload, we may have some
+			 * dmod load requests on the queue in front of the
+			 * blizzard of dmod unload requests that the debugger
+			 * will generate as part of its unload.  The debugger
+			 * won't have generated unloads for pending dmods, so
+			 * we can safely ignore the load requests.
+			 */
+			kctl_dprintf("skipping load of dmod %s due to "
+			    "in-process unload");
+		} else
+			(void) kctl_dmod_load(dlr); /* dlr will have errno */
+
+		WR_ACK(dlr);
+		kmdb_wr_debugger_notify(dlr);
+		return (1);
+	}
+
+	case WNTASK_DMOD_LOAD_ALL:
+		/*
+		 * We don't initiate all-module loads, so this can't be an
+		 * ack.  We process the load-all, and send the message back
+		 * to the driver as an ack.
+		 */
+		ASSERT(!WR_ISACK(wn));
+
+		kctl_dprintf("received request to load all dmods");
+
+		(void) kctl_dmod_load_all();
+
+		WR_ACK(wn);
+		kmdb_wr_debugger_notify(wn);
+		return (1);
+
+	case WNTASK_DMOD_UNLOAD: {
+		/*
+		 * The driver received an unload request.  We don't initiate
+		 * unloads, so this can't be an ack.  We process the unload,
+		 * and send the message back to the driver as an ack.
+		 */
+		kmdb_wr_unload_t *dur = (kmdb_wr_unload_t *)wn;
+
+		ASSERT(!WR_ISACK(dur));
+		ASSERT(kctl.kctl_boot_ops == NULL);
+
+		kctl_dprintf("received dmod unload message %s",
+		    dur->dur_modname);
+
+		kctl_dmod_unload(dur);
+
+		WR_ACK(dur);
+		kmdb_wr_debugger_notify(dur);
+		return (1);
+	}
+
+	case WNTASK_DMOD_PATH_CHANGE: {
+		/*
+		 * We don't initiate path changes, so this can't be an ack.
+		 * This request type differs from the others in that we only
+		 * return it (as an ack) when we're done with it.  We're only
+		 * done with it when we receive another one, or when the
+		 * debugger is unloading.
+		 */
+		kmdb_wr_path_t *pth = (kmdb_wr_path_t *)wn;
+		kmdb_wr_path_t *opth;
+
+		ASSERT(!WR_ISACK(pth));
+
+		kctl_dprintf("received path change message");
+
+		if ((opth = kctl_dmod_path_set(pth)) != NULL) {
+			/* We have an old path request to return */
+			WR_ACK(opth);
+			kmdb_wr_debugger_notify(opth);
+
+			/*
+			 * The debugger can process the returned path change
+			 * request at its leisure
+			 */
+			return (0);
+		}
+
+		/* Nothing to do */
+		return (0);
+	}
+
+	default:
+		cmn_err(CE_WARN, "Received unknown work request %d from kmdb\n",
+		    wn->wn_task);
+		/* Drop message */
+		return (0);
+	}
+
+	/*NOTREACHED*/
+}
+
+int
+kctl_wr_process(void)
+{
+	return (kmdb_wr_driver_process(kctl_wr_process_cb,
+	    KCTL_WR_PROCESS_NORMAL));
+}
+
+/*
+ * Catches the "work to do" soft interrupt, and passes the notification along
+ * to the worker thread.
+ */
+/*ARGSUSED*/
+void
+kctl_wrintr(void)
+{
+	kctl.kctl_wr_avail = 0;
+
+	sema_v(&kctl.kctl_wr_avail_sem);
+}
+
+/*
+ * This routine is called by the debugger while the world is resuming.
+ */
+void
+kctl_wrintr_fire(void)
+{
+	kctl.kctl_wr_avail = 1;
+
+	kdi_softcall(kctl_wrintr);
+}
+
+/*
+ * Given the possibility of asynchronous unload, the locking semantics are
+ * somewhat tricky.  See kctl_main.c
+ */
+/*ARGSUSED*/
+static void
+kctl_wr_thread(void *arg)
+{
+	callb_cpr_t cprinfo;
+	kmutex_t cprlock;
+
+	mutex_init(&cprlock, NULL, MUTEX_DEFAULT, NULL);
+	CALLB_CPR_INIT(&cprinfo, &cprlock, callb_generic_cpr, "kmdb work");
+
+	for (;;) {
+		/*
+		 * XXX what should I do here for panic?  It'll spin unless I
+		 * can figure out a way to park it.  Presumably I don't want to
+		 * let it exit.
+		 */
+		mutex_enter(&cprlock);
+		CALLB_CPR_SAFE_BEGIN(&cprinfo);
+		mutex_exit(&cprlock);
+
+		sema_p(&kctl.kctl_wr_avail_sem);
+
+		mutex_enter(&cprlock);
+		CALLB_CPR_SAFE_END(&cprinfo, &cprlock);
+		mutex_exit(&cprlock);
+
+		kctl_dprintf("kctl worker thread - waking up");
+
+		if (kmdb_kdi_get_unload_request() ||
+		    kctl.kctl_wr_state != KCTL_WR_ST_RUN) {
+			/*
+			 * We've either got a debugger-initiated unload (if
+			 * unload_request returned true), or we're stopping due
+			 * to an error discovered by the driver (if
+			 * kctl_worker_run is no longer non-zero).  Start
+			 * cleaning up.
+			 */
+
+			/*
+			 * The debugger has already deactivated itself, and will
+			 * have dumped a bunch of stuff on the queue.  We need
+			 * to process it before exiting.
+			 */
+			(void) kmdb_wr_driver_process(kctl_wr_process_cb,
+			    KCTL_WR_PROCESS_UNLOADING);
+			break;
+		}
+
+		/*
+		 * A non-zero return means we've passed messages back to the
+		 * debugger for processing, so we need to wake the debugger up.
+		 */
+		if (kctl_wr_process() > 0)
+			kdi_dvec_enter();
+	}
+
+	/*
+	 * NULL out the dmod search path, so we can send the current one back
+	 * to the debugger.  XXX this should probably be somewhere else.
+	 */
+	kctl_dmod_path_reset();
+
+	/*
+	 * The debugger will send us unload notifications for each dmod that it
+	 * noticed.  If, for example, the debugger is unloaded before the first
+	 * start, it won't have noticed any of the dmods we loaded.  We'll need
+	 * to initiate the unloads ourselves.
+	 */
+	kctl_dmod_unload_all();
+
+	kctl.kctl_wr_state = KCTL_WR_ST_STOPPED;
+
+	/*
+	 * Must be last, as it concludes by setting state to INACTIVE.  The
+	 * kctl data structure must not be accessed by this thread after that
+	 * point.
+	 */
+	kctl_cleanup();
+
+	mutex_enter(&cprlock);
+	CALLB_CPR_EXIT(&cprinfo);
+	mutex_destroy(&cprlock);
+}
+
+void
+kctl_wr_thr_start(void)
+{
+	kctl.kctl_wr_avail = 0;
+	kctl.kctl_wr_state = KCTL_WR_ST_RUN;
+	kctl.kctl_wr_thr = thread_create(NULL, 0, kctl_wr_thread, NULL, 0, &p0,
+	    TS_RUN, minclsyspri);
+}
+
+void
+kctl_wr_thr_stop(void)
+{
+	ASSERT(kctl.kctl_wr_state == KCTL_WR_ST_RUN);
+	kctl.kctl_wr_state = KCTL_WR_ST_STOP;
+	sema_v(&kctl.kctl_wr_avail_sem);
+}
+
+void
+kctl_wr_thr_join(void)
+{
+	thread_join(kctl.kctl_wr_thr->t_did);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.h b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.h
new file mode 100644
index 0000000..385b266
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kctl/kctl_wr.h
@@ -0,0 +1,48 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KCTL_WR_H
+#define	_KCTL_WR_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kmdb_wr_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void kctl_dmod_load_ack(kmdb_wr_load_t *);
+extern int kctl_dmod_load(kmdb_wr_load_t *);
+extern void kctl_dmod_load_all(void);
+extern void kctl_dmod_unload(kmdb_wr_unload_t *);
+extern kmdb_wr_path_t *kctl_dmod_path_set(kmdb_wr_path_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KCTL_WR_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_auxv.h b/usr/src/cmd/mdb/common/kmdb/kmdb_auxv.h
new file mode 100644
index 0000000..7faf9b9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_auxv.h
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_AUXV_H
+#define	_KMDB_AUXV_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The kmdb_auxv is the interface between the driver and the debugger portions
+ * of kmdb.  It is used for three purposes:
+ *
+ *  1) To pass system-specific configuration information to the debugger.  This
+ *     information is used by the debugger to tailor itself to the running
+ *     system.
+ *
+ *  2) To pass debugger state information to the driver.
+ *
+ *  3) To configure the DPI.
+ *
+ * We use this somewhat torturous method to initialize and configure kmdb due to
+ * the somewhat unique requirements of kmdb as a pseudo-standalone debugger.
+ * The debugger portion of kmdb is compiled as a standalone, without any
+ * external dependencies.  As a result, it cannot communicate directly to the
+ * outside world.  Any such communications must be through functions passed to
+ * it by the driver.  The auxv provides a means by which these pointers and
+ * other parameters may be passed to the debugger.
+ */
+
+#include <gelf.h>
+#include <sys/machelf.h>
+#include <sys/kdi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct kmdb_auxv_nv {
+	char kanv_name[25];
+	char kanv_val[50];
+} kmdb_auxv_nv_t;
+
+#define	KMDB_AUXV_FL_NOUNLOAD	0x1	/* don't allow debugger unload */
+#define	KMDB_AUXV_FL_NOTRPSWTCH	0x2	/* don't switch to kmdb's TBA/IDT */
+
+typedef struct kmdb_auxv {
+	caddr_t		kav_dseg;		/* Base of segdebug */
+	size_t		kav_dseg_size;		/* Size of segdebug */
+	size_t		kav_pagesize;		/* Base page size */
+	int		kav_ncpu;		/* Maximum number of CPUs */
+	kdi_t		*kav_kdi;		/* Ops vector for KDI */
+	void		*kav_romp;		/* Opaque PROM handle */
+
+#ifdef __sparc
+	int		*kav_promexitarmp;	/* PROM exit kmdb entry armer */
+
+	caddr_t		kav_tba_active;		/* Trap table to be used */
+	caddr_t		kav_tba_obp;		/* OBP's trap table */
+#ifdef	sun4v
+	caddr_t		kav_tba_kernel;		/* Kernel's trap table */
+#endif
+	caddr_t		kav_tba_native;		/* kmdb's trap table */
+	size_t		kav_tba_native_sz;	/* kmdb's trap table size */
+#endif
+
+#if defined(__i386) || defined(__amd64)
+	kmdb_auxv_nv_t	*kav_pcache;		/* Copies of common props */
+	int		kav_nprops;		/* Size of prop cache */
+#endif
+
+	uintptr_t (*kav_lookup_by_name)(char *, char *); /* Live kernel only */
+
+	void (*kav_wrintr_fire)(void);		/* Send softint to driver */
+
+	const char	*kav_config;		/* State string from MDB */
+	const char	**kav_argv;		/* Args from boot line */
+	uint_t		kav_flags;		/* KMDB_AUXV_FL_* */
+
+	const char	*kav_modpath;		/* kernel module_path */
+
+#ifdef __sparc
+	void (*kav_ktrap_install)(int, void (*)(void)); /* Add to krnl trptbl */
+	void (*kav_ktrap_restore)(void);	/* Restore krnl trap hdlrs */
+#endif
+
+} kmdb_auxv_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_AUXV_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_conf.c b/usr/src/cmd/mdb/common/kmdb/kmdb_conf.c
new file mode 100644
index 0000000..8ed02b9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_conf.c
@@ -0,0 +1,121 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/isa_defs.h>
+#include <sys/utsname.h>
+#include <strings.h>
+
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kmdb_kdi.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+static const char _mdb_version[] = KMDB_VERSION;
+
+const char *
+mdb_conf_version(void)
+{
+	return (_mdb_version);
+}
+
+const char *
+mdb_conf_isa(void)
+{
+#if defined(__sparc)
+#if defined(__sparcv9)
+	return ("sparcv9");
+#else	/* __sparcv9 */
+	return ("sparc");
+#endif	/* __sparcv9 */
+#elif defined(__amd64)
+	return ("amd64");
+#elif defined(__i386)
+	return ("i386");
+#else
+#error	"unknown ISA"
+#endif
+}
+
+/*
+ * These functions are needed for path evaluation, and must be run prior to
+ * target initialization.  The kmdb symbol resolution machinery hasn't been
+ * initialized at this point, so we have to rely on the kernel to look up
+ * utsname and platform for us.
+ */
+
+void
+mdb_conf_uname(struct utsname *utsp)
+{
+	bzero(utsp, sizeof (struct utsname));
+
+	if (kmdb_dpi_get_state(NULL) == DPI_STATE_INIT) {
+		struct utsname *utsaddr;
+
+		/*
+		 * The kernel is running during DPI initialization, so we'll ask
+		 * it to do the lookup.  Our own symbol resolution facilities
+		 * won't be available until after the debugger starts.
+		 */
+		if ((utsaddr = (struct utsname *)kmdb_kdi_lookup_by_name("unix",
+		    "utsname")) == NULL) {
+			warn("'utsname' symbol is missing from kernel\n");
+			strcpy(utsp->sysname, "unknown");
+			return;
+		}
+
+		bcopy(utsaddr, utsp, sizeof (struct utsname));
+	} else
+		(void) mdb_tgt_uname(mdb.m_target, utsp);
+}
+
+const char *
+mdb_conf_platform(void)
+{
+	if (kmdb_dpi_get_state(NULL) == DPI_STATE_INIT) {
+		static char plat[SYS_NMLN];
+		caddr_t plataddr;
+
+		/*
+		 * The kernel is running during DPI initialization, so we'll ask
+		 * it to do the lookup.  Our own symbol resolution facilities
+		 * won't be available until after the debugger starts.
+		 */
+		if ((plataddr = (caddr_t)kmdb_kdi_lookup_by_name("unix",
+		    "platform")) == NULL) {
+			warn("conf: 'platform' symbol is missing from "
+			    "kernel\n");
+			return ("unknown");
+		}
+
+		strncpy(plat, plataddr, sizeof (plat));
+		plat[sizeof (plat) - 1] = '\0';
+
+		return (plat);
+	} else
+		return (mdb_tgt_platform(mdb.m_target));
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_context.c b/usr/src/cmd/mdb/common/kmdb/kmdb_context.c
new file mode 100644
index 0000000..2a9ca2a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_context.c
@@ -0,0 +1,100 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Debugger co-routine context support.  kmdb co-routines are essentially the
+ * same as the ones used by mdb, with the exception that we allocate the stack
+ * for the co-routine from our heap.
+ */
+
+#include <kmdb/kmdb_context_impl.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_umem.h>
+#include <mdb/mdb.h>
+
+#include <sys/types.h>
+
+#include <ucontext.h>
+#include <setjmp.h>
+
+static void
+context_init(mdb_context_t *volatile c)
+{
+	c->ctx_status = c->ctx_func();
+	ASSERT(c->ctx_resumes > 0);
+	longjmp(c->ctx_pcb, 1);
+}
+
+mdb_context_t *
+mdb_context_create(int (*func)(void))
+{
+	mdb_context_t *c = mdb_zalloc(sizeof (mdb_context_t), UM_NOSLEEP);
+	size_t pagesize = mdb.m_pagesize;
+
+	if (c == NULL)
+		return (NULL);
+
+	c->ctx_func = func;
+	c->ctx_stacksize = pagesize * 4;
+	c->ctx_stack = mdb_alloc_align(c->ctx_stacksize, pagesize, UM_NOSLEEP);
+
+	if (c->ctx_stack == NULL) {
+		mdb_free(c, sizeof (mdb_context_t));
+		return (NULL);
+	}
+
+	kmdb_makecontext(&c->ctx_uc, (void (*)(void *))context_init, c,
+	    c->ctx_stack, c->ctx_stacksize);
+
+	return (c);
+}
+
+void
+mdb_context_destroy(mdb_context_t *c)
+{
+	mdb_free_align(c->ctx_stack, c->ctx_stacksize);
+	mdb_free(c, sizeof (mdb_context_t));
+}
+
+void
+mdb_context_switch(mdb_context_t *c)
+{
+	if (setjmp(c->ctx_pcb) == 0 && kmdb_setcontext(&c->ctx_uc) == -1)
+		fail("failed to change context to %p", (void *)c);
+	else
+		fail("unexpectedly returned from context %p", (void *)c);
+}
+
+jmp_buf *
+mdb_context_getpcb(mdb_context_t *c)
+{
+	c->ctx_resumes++;
+	return (&c->ctx_pcb);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_context_impl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_context_impl.h
new file mode 100644
index 0000000..4d61e90
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_context_impl.h
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_CONTEXT_IMPL_H
+#define	_KMDB_CONTEXT_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_context_impl.h>
+
+#include <sys/types.h>
+
+#include <ucontext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void kmdb_makecontext(ucontext_t *, void (*)(void *), void *, caddr_t, size_t);
+int kmdb_setcontext(ucontext_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_CONTEXT_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_create.c b/usr/src/cmd/mdb/common/kmdb/kmdb_create.c
new file mode 100644
index 0000000..ec2765b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_create.c
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_module.h>
+
+void
+mdb_create_builtin_tgts(void)
+{
+	mdb_module_t *mp;
+
+	if ((mp = mdb_module_load_builtin("kmdb_kvm")) != NULL)
+		mp->mod_tgt_ctor = kmdb_kvm_create;
+}
+
+void
+mdb_create_loadable_disasms(void)
+{
+#ifdef __sparc
+	(void) mdb_module_load("sparc", MDB_MOD_SILENT | MDB_MOD_DEFER);
+#endif
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c b/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c
new file mode 100644
index 0000000..bfa1d1d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_ctf_open.c
@@ -0,0 +1,103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * libctf open/close interposition layer
+ *
+ * This interposition layer serves two purposes.  First, it allows the
+ * introduction of a kmdb-specific implementation of ctf_open().  The normal
+ * ctf_open() call open(2)s a file, and reads the CTF data directly from it.
+ * No such facility is available in kmdb, as kmdb does not run in userland.  We
+ * can, however, observe that kmdb uses this interface only to read CTF data
+ * from dmods.  dmods, as viewed by kmdb, do have CTF data, but they have it
+ * in a form that is best accessed by ctf_bufopen().  This interposition layer
+ * allows us to translate ctf_open() calls into ctf_bufopen() calls.
+ *
+ * The second purpose of the interposition layer is to reduce the work needed
+ * to call mdb_ctf_bufopen.
+ */
+
+#include <sys/types.h>
+#include <sys/kobj.h>
+#include <libctf.h>
+#include <ctf_impl.h>
+
+#include <kmdb/kmdb_module.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+static kmdb_modctl_t *
+mdb_ctf_name2ctl(const char *pathname)
+{
+	mdb_var_t *v;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, strbasename(pathname))) ==
+	    NULL)
+		return (NULL);
+
+	return ((kmdb_modctl_t *)MDB_NV_COOKIE(v));
+}
+
+ctf_file_t *
+mdb_ctf_open(const char *pathname, int *errp)
+{
+	struct module *mp;
+	kmdb_modctl_t *kmc;
+	ctf_file_t *ctfp;
+
+	if ((kmc = mdb_ctf_name2ctl(pathname)) == NULL) {
+		if (errp != NULL)
+			*errp = ENOENT;
+		return (NULL);
+	}
+
+	mp = kmc->kmc_modctl->mod_mp;
+	if (mp->ctfdata == NULL) {
+		if (errp != NULL)
+			*errp = ECTF_NOCTFDATA;
+		return (NULL);
+	}
+
+	if ((ctfp = mdb_ctf_bufopen(mp->ctfdata, mp->ctfsize, mp->symtbl,
+	    mp->symhdr, mp->strings, mp->strhdr, errp)) == NULL)
+		return (NULL);
+
+	mdb_dprintf(MDB_DBG_MODULE, "loaded %lu bytes of CTF data for %s\n",
+	    (ulong_t)mp->ctfsize, kmc->kmc_modname);
+
+	return (ctfp);
+}
+
+void
+mdb_ctf_close(ctf_file_t *fp)
+{
+	ctf_close(fp);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_demangle.c b/usr/src/cmd/mdb/common/kmdb/kmdb_demangle.c
new file mode 100644
index 0000000..efa73d0
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_demangle.c
@@ -0,0 +1,77 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_err.h>
+
+/*ARGSUSED*/
+mdb_demangler_t *
+mdb_dem_load(const char *path)
+{
+	(void) set_errno(ENOTSUP);
+	return (NULL);
+}
+
+void
+mdb_dem_unload(mdb_demangler_t *dmp)
+{
+	if (dmp != NULL)
+		fail("attempted to unload demangler %p\n", (void *)dmp);
+}
+
+/*ARGSUSED*/
+const char *
+mdb_dem_convert(mdb_demangler_t *dmp, const char *name)
+{
+	return (name);
+}
+
+/*ARGSUSED*/
+int
+cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_warn("C++ symbol demangling not available\n");
+	return (DCMD_ERR);
+}
+
+/*ARGSUSED*/
+int
+cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_warn("C++ demangling facility is currently disabled\n");
+	return (DCMD_ERR);
+}
+
+/*ARGSUSED*/
+int
+cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_warn("C++ symbol demangling not available\n");
+	return (DCMD_ERR);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_dl.c b/usr/src/cmd/mdb/common/kmdb/kmdb_dl.c
new file mode 100644
index 0000000..2b96ae1
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_dl.c
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <dlfcn.h>
+#include <sys/modctl.h>
+#include <sys/kobj.h>
+
+#include <kmdb/kmdb_module.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb.h>
+
+/*
+ * kmdb libdl-style interface for the manipulation of dmods.
+ */
+
+static char *dl_errstr;
+
+static kmdb_modctl_t *
+dl_name2ctl(const char *pathname)
+{
+	mdb_var_t *v;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, strbasename(pathname))) ==
+	    NULL)
+		return (NULL);
+
+	return ((kmdb_modctl_t *)MDB_NV_COOKIE(v));
+}
+
+/*ARGSUSED*/
+void *
+dlmopen(Lmid_t lmid, const char *pathname, int mode)
+{
+	kmdb_modctl_t *kmc;
+
+	if ((kmc = dl_name2ctl(pathname)) == NULL) {
+		dl_errstr = "unregistered module";
+		return (NULL);
+	}
+
+	kmc->kmc_dlrefcnt++;
+
+	dl_errstr = NULL;
+
+	return (kmc);
+}
+
+int
+dlclose(void *dlp)
+{
+	kmdb_modctl_t *kmc = dlp;
+
+	dl_errstr = NULL;
+
+	ASSERT(kmc->kmc_dlrefcnt > 0);
+	if (--kmc->kmc_dlrefcnt > 0)
+		return (0);
+
+	return (0);
+}
+
+char *
+dlerror(void)
+{
+	char *str = dl_errstr;
+
+	dl_errstr = NULL;
+
+	return (str);
+}
+
+static void *
+dl_findsym(kmdb_modctl_t *kmc, const char *name)
+{
+	GElf_Sym sym;
+	uint_t symid;
+
+	if (mdb_gelf_symtab_lookup_by_name(kmc->kmc_symtab, name, &sym,
+	    &symid) < 0)
+		return (NULL);
+
+	return ((void *)(uintptr_t)sym.st_value);
+}
+
+/*ARGSUSED*/
+void *
+dlsym(void *dlp, const char *name)
+{
+	kmdb_modctl_t *kmc = dlp;
+	mdb_var_t *v;
+	void *addr;
+
+	switch ((uintptr_t)dlp) {
+	case (uintptr_t)RTLD_NEXT:
+		mdb_nv_rewind(&mdb.m_dmodctl);
+		while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
+			if ((addr = dl_findsym(MDB_NV_COOKIE(v), name)) != NULL)
+				break;
+		}
+		break;
+
+	case (uintptr_t)RTLD_DEFAULT:
+	case (uintptr_t)RTLD_SELF:
+		dl_errstr = "invalid handle";
+		return (NULL);
+
+	default:
+		addr = dl_findsym(kmc, name);
+	}
+
+	dl_errstr = (addr == NULL) ? "symbol not found" : NULL;
+
+	return (addr);
+}
+
+/*ARGSUSED*/
+int
+dladdr1(void *address, Dl_info *dlip, void **info, int flags)
+{
+	/*
+	 * umem uses this for debugging information.  We'll pretend to fail.
+	 */
+
+	return (0);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.c b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.c
new file mode 100644
index 0000000..1e6cfd6
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.c
@@ -0,0 +1,472 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The DPI, or debugger/PROM interface, is used to isolate the debugger from the
+ * means by which we use the PROM to control the machine.
+ */
+
+#include <sys/types.h>
+#include <setjmp.h>
+
+#include <kmdb/kmdb_dpi_impl.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_auxv.h>
+#include <kmdb/kmdb_wr_impl.h>
+#include <kmdb/kmdb_module.h>
+#include <kmdb/kmdb_start.h>
+#include <kmdb/kmdb_asmutil.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb.h>
+
+jmp_buf *kmdb_dpi_fault_pcb;
+jmp_buf kmdb_dpi_resume_pcb;
+jmp_buf kmdb_dpi_entry_pcb;
+
+static int kmdb_dpi_state;
+static int kmdb_dpi_state_why;
+
+uint_t kmdb_dpi_resume_requested;
+uint_t kmdb_dpi_switch_target = (uint_t)-1;
+
+/* Used by the style-specific resume interfaces to signal the driver */
+void (*kmdb_dpi_wrintr_fire)(void);
+
+int
+kmdb_dpi_init(kmdb_auxv_t *kav)
+{
+	kmdb_dpi_state = DPI_STATE_INIT;
+	kmdb_dpi_resume_requested = 0;
+	kmdb_dpi_wrintr_fire = kav->kav_wrintr_fire;
+
+	mdb.m_dpi = &kmdb_dpi_ops;
+	return (mdb.m_dpi->dpo_init(kav));
+}
+
+/*ARGSUSED1*/
+void
+kmdb_activate(kdi_debugvec_t **dvecp, uint_t flags)
+{
+	mdb.m_dpi->dpo_debugger_activate(dvecp, flags);
+}
+
+void
+kmdb_deactivate(void)
+{
+	mdb.m_dpi->dpo_debugger_deactivate();
+}
+
+int
+kmdb_dpi_reenter(void)
+{
+	int cmd;
+
+	kmdb_kdi_system_claim();
+
+	if ((cmd = setjmp(kmdb_dpi_entry_pcb)) == 0) {
+		/* Direct entry from the driver */
+		if (kmdb_dpi_resume_requested)
+			longjmp(kmdb_dpi_resume_pcb, 1);
+
+		kmdb_first_start();
+
+		fail("kmdb_first_start returned");
+		/*NOTREACHED*/
+	}
+
+	mdb_dprintf(MDB_DBG_DPI, "returning to driver - cmd %d%s\n", cmd,
+	    (kmdb_dpi_work_required() ? " (work required)" : ""));
+
+	kmdb_kdi_system_release();
+
+	membar_producer();
+
+	/*
+	 * The debugger wants us to do something - it returned a command
+	 * via the setjmp().  The driver will know what to do with the
+	 * command.
+	 */
+	return (cmd);
+}
+
+void
+kmdb_dpi_enter_mon(void)
+{
+	mdb.m_dpi->dpo_enter_mon();
+}
+
+void
+kmdb_dpi_modchg_register(void (*func)(struct modctl *, int))
+{
+	mdb.m_dpi->dpo_modchg_register(func);
+}
+
+void
+kmdb_dpi_modchg_cancel(void)
+{
+	mdb.m_dpi->dpo_modchg_cancel();
+}
+
+int
+kmdb_dpi_get_cpu_state(int cpuid)
+{
+	return (mdb.m_dpi->dpo_get_cpu_state(cpuid));
+}
+
+int
+kmdb_dpi_get_master_cpuid(void)
+{
+	return (mdb.m_dpi->dpo_get_master_cpuid());
+}
+
+const mdb_tgt_gregset_t *
+kmdb_dpi_get_gregs(int cpuid)
+{
+	return (mdb.m_dpi->dpo_get_gregs(cpuid));
+}
+
+jmp_buf *
+kmdb_dpi_set_fault_hdlr(jmp_buf *jb)
+{
+	jmp_buf *oldpcb = kmdb_dpi_fault_pcb;
+
+	kmdb_dpi_fault_pcb = jb;
+
+	return (oldpcb);
+}
+
+void
+kmdb_dpi_restore_fault_hdlr(jmp_buf *jb)
+{
+	(void) kmdb_dpi_set_fault_hdlr(jb);
+}
+
+/*
+ * Used to tell the driver that it needs to do work after the resume.
+ *
+ * CAUTION: This routine may be called *after* mdb_destroy
+ */
+int
+kmdb_dpi_work_required(void)
+{
+	return (kmdb_kdi_get_unload_request() ||
+	    !kmdb_wr_driver_notify_isempty());
+}
+
+void
+kmdb_dpi_resume_master(void)
+{
+	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_MASTER);
+}
+
+void
+kmdb_dpi_resume(void)
+{
+	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_ALL);
+}
+
+void
+kmdb_dpi_resume_unload(void)
+{
+	kmdb_dpi_resume_common(KMDB_DPI_CMD_RESUME_UNLOAD);
+}
+
+int
+kmdb_dpi_switch_master(int tgt_cpuid)
+{
+	if (kmdb_dpi_get_cpu_state(tgt_cpuid) < 0)
+		return (-1); /* errno is set for us */
+
+	kmdb_dpi_switch_target = tgt_cpuid;
+	kmdb_dpi_resume_common(KMDB_DPI_CMD_SWITCH_CPU);
+
+	return (0);
+}
+
+void
+kmdb_dpi_flush_slave_caches(void)
+{
+	kmdb_dpi_resume_common(KMDB_DPI_CMD_FLUSH_CACHES);
+}
+
+typedef struct work_results {
+	mdb_nv_t res_loads;
+	mdb_nv_t res_unloads;
+} work_results_t;
+
+static int
+kmdb_dbgnotify_cb(kmdb_wr_t *wn, void *arg)
+{
+	work_results_t *res = arg;
+
+	switch (WR_TASK(wn)) {
+	case WNTASK_DMOD_LOAD: {
+		/*
+		 * If this is an ack, the driver finished processing a load we
+		 * requested.  We process it and free the message.  If this
+		 * isn't an ack, then it's a driver-initiated load.  We process
+		 * the message, and send it back as an ack so the driver can
+		 * free it.
+		 */
+		kmdb_wr_load_t *dlr = (kmdb_wr_load_t *)wn;
+
+		mdb_dprintf(MDB_DBG_DPI, "received module load message\n");
+
+		if (kmdb_module_loaded(dlr) && res != NULL) {
+			(void) mdb_nv_insert(&res->res_loads,
+			    strbasename(dlr->dlr_fname), NULL, 0, 0);
+		}
+
+		if (WR_ISACK(dlr)) {
+			kmdb_module_load_ack(dlr);
+			return (0);
+		}
+
+		/* Send it back as an ack */
+		mdb_dprintf(MDB_DBG_DPI, "Sending load request for %s back "
+		    "as an ack\n", dlr->dlr_fname);
+		WR_ACK(wn);
+		kmdb_wr_driver_notify(wn);
+		return (0);
+	}
+
+	case WNTASK_DMOD_LOAD_ALL:
+		/*
+		 * We initiated the load-all, so this must be an ack.  The
+		 * individual module load messages will arrive separately -
+		 * there's no need to do anything further with this message.
+		 */
+		ASSERT(WR_ISACK(wn));
+
+		mdb_dprintf(MDB_DBG_DPI, "received module load all ack\n");
+
+		kmdb_module_load_all_ack(wn);
+		return (0);
+
+	case WNTASK_DMOD_UNLOAD: {
+		/*
+		 * The debugger received an unload message.  The driver isn't
+		 * supposed to initiate unloads, so we shouldn't see anything
+		 * but acks.  We tell the dmod subsystem that the module has
+		 * been unloaded, and we free the message.
+		 */
+		kmdb_wr_unload_t *dur = (kmdb_wr_unload_t *)wn;
+
+		ASSERT(WR_ISACK(dur));
+
+		mdb_dprintf(MDB_DBG_DPI, "received module unload ack\n");
+
+		if (kmdb_module_unloaded(dur) && res != NULL) {
+			(void) mdb_nv_insert(&res->res_unloads,
+			    dur->dur_modname, NULL, 0, 0);
+		}
+
+		/* Done with message */
+		kmdb_module_unload_ack(dur);
+		return (0);
+	}
+
+	case WNTASK_DMOD_PATH_CHANGE: {
+		/*
+		 * The debugger received a path change message.  The driver
+		 * can't initiate these, so it must be an acknowledgement.
+		 * There's no processing to be done, so just free the message.
+		 */
+		kmdb_wr_path_t *dpth = (kmdb_wr_path_t *)wn;
+
+		ASSERT(WR_ISACK(dpth));
+
+		mdb_dprintf(MDB_DBG_DPI, "received path change ack\n");
+
+		kmdb_module_path_ack(dpth);
+		return (0);
+	}
+
+	default:
+		mdb_warn("Received unknown message type %d from driver\n",
+		    wn->wn_task);
+		/* Ignore it */
+		return (0);
+	}
+}
+
+static void
+print_modules(mdb_nv_t *mods)
+{
+	mdb_var_t *v;
+
+	mdb_nv_rewind(mods);
+	while ((v = mdb_nv_advance(mods)) != NULL)
+		mdb_printf(" %s", mdb_nv_get_name(v));
+}
+
+void
+kmdb_dpi_process_work_queue(void)
+{
+	work_results_t res;
+
+	(void) mdb_nv_create(&res.res_loads, UM_SLEEP);
+	(void) mdb_nv_create(&res.res_unloads, UM_SLEEP);
+
+	mdb_dprintf(MDB_DBG_DPI, "processing work queue\n");
+	(void) kmdb_wr_debugger_process(kmdb_dbgnotify_cb, &res);
+
+	if (mdb_nv_size(&res.res_loads)) {
+		mdb_printf("Loaded modules: [");
+		print_modules(&res.res_loads);
+		mdb_printf(" ]\n");
+	}
+
+	if (mdb_nv_size(&res.res_unloads)) {
+		mdb_printf("Unloaded modules: [");
+		print_modules(&res.res_unloads);
+		mdb_printf(" ]\n");
+	}
+
+	mdb_nv_destroy(&res.res_loads);
+	mdb_nv_destroy(&res.res_unloads);
+}
+
+int
+kmdb_dpi_step(void)
+{
+	return (mdb.m_dpi->dpo_step());
+}
+
+uintptr_t
+kmdb_dpi_call(uintptr_t func, uint_t argc, const uintptr_t *argv)
+{
+	return (mdb.m_dpi->dpo_call(func, argc, argv));
+}
+
+int
+kmdb_dpi_brkpt_arm(uintptr_t addr, mdb_instr_t *instrp)
+{
+	int rc;
+
+	if ((rc = mdb.m_dpi->dpo_brkpt_arm(addr, instrp)) < 0)
+		mdb_warn("failed to arm breakpoint at %a", addr);
+
+	mdb_dprintf(MDB_DBG_DPI, "brkpt armed at %p %A\n", (void *)addr, addr);
+
+	return (rc);
+}
+
+int
+kmdb_dpi_brkpt_disarm(uintptr_t addr, mdb_instr_t instrp)
+{
+	int rc;
+
+	if ((rc = mdb.m_dpi->dpo_brkpt_disarm(addr, instrp)) < 0)
+		mdb_warn("failed to disarm breakpoint at %a", addr);
+
+	mdb_dprintf(MDB_DBG_DPI, "brkpt disarmed at %p %A\n", (void *)addr,
+	    addr);
+
+	return (rc);
+}
+
+int
+kmdb_dpi_wapt_validate(kmdb_wapt_t *wp)
+{
+	if (mdb.m_dpi->dpo_wapt_validate(wp) < 0)
+		return (-1); /* errno is set for us */
+
+	return (0);
+}
+
+int
+kmdb_dpi_wapt_reserve(kmdb_wapt_t *wp)
+{
+	if (mdb.m_dpi->dpo_wapt_reserve(wp) < 0)
+		return (-1); /* errno is set for us */
+
+	mdb_dprintf(MDB_DBG_DPI, "wapt reserve type %d at %p, priv %p\n",
+	    wp->wp_type, (void *)wp->wp_addr, wp->wp_priv);
+
+	return (0);
+}
+
+void
+kmdb_dpi_wapt_release(kmdb_wapt_t *wp)
+{
+	mdb.m_dpi->dpo_wapt_release(wp);
+}
+
+void
+kmdb_dpi_wapt_arm(kmdb_wapt_t *wp)
+{
+	mdb.m_dpi->dpo_wapt_arm(wp);
+
+	mdb_dprintf(MDB_DBG_DPI, "wapt armed at %p (type %d, priv %p)\n",
+	    (void *)wp->wp_addr, wp->wp_type, wp->wp_priv);
+}
+
+void
+kmdb_dpi_wapt_disarm(kmdb_wapt_t *wp)
+{
+	mdb.m_dpi->dpo_wapt_disarm(wp);
+
+	mdb_dprintf(MDB_DBG_DPI, "wapt disarmed at %p (type %d, priv %p)\n",
+	    (void *)wp->wp_addr, wp->wp_type, wp->wp_priv);
+}
+
+int
+kmdb_dpi_wapt_match(kmdb_wapt_t *wp)
+{
+	return (mdb.m_dpi->dpo_wapt_match(wp));
+}
+
+void
+kmdb_dpi_set_state(int state, int why)
+{
+	if (kmdb_dpi_state != DPI_STATE_LOST) {
+		mdb_dprintf(MDB_DBG_DPI, "dpi_set_state %d why %d\n",
+		    state, why);
+
+		kmdb_dpi_state = state;
+		kmdb_dpi_state_why = why;
+	}
+}
+
+int
+kmdb_dpi_get_state(int *whyp)
+{
+	if (whyp != NULL)
+		*whyp = kmdb_dpi_state_why;
+
+	return (kmdb_dpi_state);
+}
+
+void
+kmdb_dpi_dump_crumbs(uintptr_t addr, int cpuid)
+{
+	mdb.m_dpi->dpo_dump_crumbs(addr, cpuid);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.h b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.h
new file mode 100644
index 0000000..7af4d28
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi.h
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_DPI_H
+#define	_KMDB_DPI_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Retargetable Kmdb/PROM interface
+ */
+
+#include <sys/types.h>
+#include <setjmp.h>
+#ifdef	__sparc
+#include <sys/regset.h>
+#endif	/* __sparc */
+
+#include <mdb/mdb_kreg.h>
+#include <mdb/mdb_target.h>
+#include <kmdb/kmdb_auxv.h>
+#include <kmdb/kmdb_dpi_isadep.h>
+#include <kmdb/kmdb_kctl.h>
+
+/*
+ * The following directive tells the mapfile generator that only those
+ * prototypes and declarations ending with a "Driver OK" comment should be
+ * included in the mapfile.
+ *
+ * MAPFILE: export "Driver OK"
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	DPI_MASTER_CPUID	(-1)	/* matches CPU ID for master */
+
+#define	DPI_ALLOW_FAULTS	NULL
+
+#define	DPI_STATE_INIT		1	/* debugger initializing */
+#define	DPI_STATE_STOPPED	2	/* User-requested stop (debug_enter) */
+#define	DPI_STATE_FAULTED	3	/* Breakpoint, watchpoint, etc. */
+#define	DPI_STATE_LOST		4	/* debugger fault */
+
+#define	DPI_STATE_WHY_BKPT	1
+#define	DPI_STATE_WHY_V_WAPT	2
+#define	DPI_STATE_WHY_P_WAPT	3
+#define	DPI_STATE_WHY_TRAP	4
+
+#define	DPI_WAPT_TYPE_PHYS	0x1	/* Physical address (SPARC only) */
+#define	DPI_WAPT_TYPE_VIRT	0x2	/* Virtual address */
+#define	DPI_WAPT_TYPE_IO	0x4	/* I/O space (Intel only) */
+
+#define	DPI_CPU_STATE_NONE	0
+#define	DPI_CPU_STATE_MASTER	1
+#define	DPI_CPU_STATE_SLAVE	2
+
+typedef struct dpi_ops dpi_ops_t;
+
+typedef struct kmdb_wapt {
+	uintptr_t wp_addr;		/* Watchpoint base address */
+	size_t wp_size;			/* Size of watched area, in bytes */
+	int wp_type;			/* DPI_WAPT_TYPE_* */
+	uint_t wp_wflags;		/* access modes */
+	void *wp_priv;			/* DPI-private data */
+} kmdb_wapt_t;
+
+extern int kmdb_dpi_init(kmdb_auxv_t *);
+
+extern void kmdb_dpi_enter_mon(void);
+
+extern void kmdb_dpi_modchg_register(void (*)(struct modctl *, int));
+extern void kmdb_dpi_modchg_cancel(void);
+
+extern int kmdb_dpi_get_cpu_state(int);
+extern int kmdb_dpi_get_master_cpuid(void);
+
+extern const mdb_tgt_gregset_t *kmdb_dpi_get_gregs(int);
+
+extern int kmdb_dpi_get_register(const char *, kreg_t *);
+extern int kmdb_dpi_set_register(const char *, kreg_t);
+
+extern jmp_buf *kmdb_dpi_set_fault_hdlr(jmp_buf *);
+extern void kmdb_dpi_restore_fault_hdlr(jmp_buf *);
+
+extern int kmdb_dpi_brkpt_arm(uintptr_t, mdb_instr_t *);
+extern int kmdb_dpi_brkpt_disarm(uintptr_t, mdb_instr_t);
+
+extern int kmdb_dpi_wapt_validate(kmdb_wapt_t *);
+extern int kmdb_dpi_wapt_reserve(kmdb_wapt_t *);
+extern void kmdb_dpi_wapt_release(kmdb_wapt_t *);
+extern void kmdb_dpi_wapt_arm(kmdb_wapt_t *);
+extern void kmdb_dpi_wapt_disarm(kmdb_wapt_t *);
+extern int kmdb_dpi_wapt_match(kmdb_wapt_t *);
+
+extern void kmdb_dpi_set_state(int, int);
+extern int kmdb_dpi_get_state(int *);
+
+extern int kmdb_dpi_step(void);
+
+extern uintptr_t kmdb_dpi_call(uintptr_t, uint_t, const uintptr_t *);
+
+extern void kmdb_dpi_process_work_queue(void);
+extern int kmdb_dpi_work_required(void);			/* Driver OK */
+
+extern void kmdb_dpi_flush_slave_caches(void);
+
+extern void kmdb_dpi_dump_crumbs(uintptr_t, int);
+
+/*
+ * Debugger/Kernel suspend
+ */
+
+extern jmp_buf kmdb_dpi_entry_pcb;
+extern uint_t kmdb_dpi_resume_requested;			/* Driver OK */
+extern uint_t kmdb_dpi_switch_target;				/* Driver OK */
+extern jmp_buf kmdb_dpi_resume_pcb;
+
+extern int kmdb_dpi_reenter(void);
+extern void kmdb_dpi_resume(void);
+extern void kmdb_dpi_resume_unload(void);
+extern int kmdb_dpi_switch_master(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_DPI_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_dpi_impl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi_impl.h
new file mode 100644
index 0000000..8e3b8e9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_dpi_impl.h
@@ -0,0 +1,132 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_DPI_IMPL_H
+#define	_KMDB_DPI_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <setjmp.h>
+#ifdef	__sparc
+#include <sys/regset.h>
+#endif	/* __sparc */
+#include <sys/types.h>
+
+#include <kmdb/kmdb_auxv.h>
+#include <kmdb/kmdb_dpi.h>
+#include <mdb/mdb_kreg.h>
+#include <mdb/mdb_target.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern jmp_buf *kmdb_dpi_fault_pcb;
+
+/*
+ * The routines used by the kmdb side of the DPI to access the saved state
+ * of the current kernel instance, and to control that instance.  A populated
+ * version of this vector is provided by the DPI backend used to control the
+ * machine.  General use of the kmdb DPI is not via direct invocation of the
+ * functions in this ops vector, but rather flows through the convenience
+ * wrappers in kmdb_dpi.c.
+ */
+struct dpi_ops {
+	int (*dpo_init)(kmdb_auxv_t *);
+
+	void (*dpo_debugger_activate)(kdi_debugvec_t **, uint_t);
+	void (*dpo_debugger_deactivate)(void);
+
+	void (*dpo_enter_mon)(void);
+
+	void (*dpo_modchg_register)(void (*)(struct modctl *, int));
+	void (*dpo_modchg_cancel)(void);
+
+	int (*dpo_get_cpu_state)(int);
+	int (*dpo_get_master_cpuid)(void);
+
+	const mdb_tgt_gregset_t *(*dpo_get_gregs)(int);
+#ifdef __sparc
+	int (*dpo_get_cpu_register)(int, int, const char *, kreg_t *);
+	int (*dpo_set_cpu_register)(int, int, const char *, kreg_t);
+	int (*dpo_get_rwin)(int, int, struct rwindow *);
+	int (*dpo_get_nwin)(int);
+#else
+	int (*dpo_get_cpu_register)(int, const char *, kreg_t *);
+	int (*dpo_set_cpu_register)(int, const char *, kreg_t);
+#endif
+
+	int (*dpo_brkpt_arm)(uintptr_t, mdb_instr_t *);
+	int (*dpo_brkpt_disarm)(uintptr_t, mdb_instr_t);
+
+	int (*dpo_wapt_validate)(kmdb_wapt_t *);
+	int (*dpo_wapt_reserve)(kmdb_wapt_t *);
+	void (*dpo_wapt_release)(kmdb_wapt_t *);
+	void (*dpo_wapt_arm)(kmdb_wapt_t *);
+	void (*dpo_wapt_disarm)(kmdb_wapt_t *);
+	int (*dpo_wapt_match)(kmdb_wapt_t *);
+
+	int (*dpo_step)(void);
+#if defined(__i386) || defined(__amd64)
+	void (*dpo_step_branch)(void);
+#endif
+
+	uintptr_t (*dpo_call)(uintptr_t, uint_t, const uintptr_t *);
+
+	void (*dpo_dump_crumbs)(uintptr_t, int);
+
+	int (*dpo_memrange_add)(caddr_t, size_t);
+
+#if defined(__i386) || defined(__amd64)
+	void (*dpo_msr_add)(const kmdb_msr_t *);
+	uint64_t (*dpo_msr_get)(int, uint_t);
+#endif
+
+#ifdef __sparc
+	void (*dpo_kernpanic)(int);
+#endif
+};
+
+extern void (*kmdb_dpi_wrintr_fire)(void);
+
+extern dpi_ops_t kmdb_dpi_ops;
+
+extern void kmdb_dpi_resume_common(int);
+extern void kmdb_dpi_resume_master(void);
+
+/* Used by the debugger to tell the driver how to resume */
+#define	KMDB_DPI_CMD_RESUME_ALL		1	/* Resume all CPUs */
+#define	KMDB_DPI_CMD_RESUME_MASTER	2	/* Resume only master CPU */
+#define	KMDB_DPI_CMD_RESUME_UNLOAD	3	/* Resume for debugger unload */
+#define	KMDB_DPI_CMD_SWITCH_CPU		4	/* Switch to another CPU */
+#define	KMDB_DPI_CMD_FLUSH_CACHES	5	/* Flush slave caches */
+#define	KMDB_DPI_CMD_REBOOT		6	/* Reboot the machine */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_DPI_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_fault.c b/usr/src/cmd/mdb/common/kmdb/kmdb_fault.c
new file mode 100644
index 0000000..e595f7b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_fault.c
@@ -0,0 +1,134 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Handling of unintentional faults (i.e. bugs) in the debugger.
+ */
+
+#include <stdlib.h>
+
+#include <kmdb/kmdb_fault.h>
+#include <kmdb/kmdb_promif.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_dpi.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_kreg.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb.h>
+
+void
+kmdb_fault(kreg_t tt, kreg_t pc, kreg_t sp, int cpuid)
+{
+	int debug_self_confirm = 0, try;
+	jmp_buf pcb, *old;
+	char c;
+
+	/* Make absolutely sure */
+	kmdb_kdi_system_claim();
+
+	try = 1;
+	if (setjmp(pcb) != 0) {
+		if (++try == 2) {
+			mdb_iob_printf(mdb.m_err,
+			    "\n*** First stack trace attempt failed.  "
+			    "Trying safe mode.\n\n");
+
+			kmdb_fault_display(tt, pc, sp, 1);
+		} else {
+			mdb_iob_printf(mdb.m_err,
+			    "\n*** Unable to print stack trace.\n");
+		}
+
+	} else {
+		old = kmdb_dpi_set_fault_hdlr(&pcb);
+
+		mdb_iob_printf(mdb.m_err, "\n*** Debugger Fault (CPU %d)\n\n",
+		    cpuid);
+		kmdb_fault_display(tt, pc, sp, 0);
+	}
+
+	kmdb_dpi_restore_fault_hdlr(old);
+
+	if (mdb.m_term != NULL) {
+		for (;;) {
+			mdb_iob_printf(mdb.m_err, "\n%s: "
+#if defined(__sparc)
+			    "(o)bp, (p)anic"
+#else
+			    "reboo(t)"
+#endif
+			    ", or (d)ebug with self? ", mdb.m_pname);
+			mdb_iob_flush(mdb.m_err);
+
+			if (IOP_READ(mdb.m_term, &c, sizeof (c)) != sizeof (c))
+				goto fault_obp;
+
+			mdb_iob_printf(mdb.m_err, "\n");
+
+			switch (c) {
+#ifdef __sparc
+			case 'p':
+				kmdb_dpi_kernpanic(cpuid);
+				/*NOTREACHED*/
+				continue;
+#endif
+
+			case 'o':
+			case 'O':
+			case 't':
+			case 'T':
+				kmdb_dpi_enter_mon();
+				continue;
+
+			case 'd':
+			case 'D':
+				/*
+				 * Debug self - get confirmation, because they
+				 * can't go back to their running system if
+				 * they choose this one.
+				 */
+				if (debug_self_confirm == 0) {
+					mdb_iob_printf(mdb.m_err,
+					    "NOTE: You will not be able to "
+					    "resume your system if you "
+					    "choose this option.\nPlease "
+					    "select 'd' again to confirm.\n");
+					debug_self_confirm = 1;
+					continue;
+				}
+
+				kmdb_dpi_set_state(DPI_STATE_LOST, 0);
+				return;
+			}
+		}
+	}
+
+fault_obp:
+	exit(1);
+	/*NOTREACHED*/
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_fault.h b/usr/src/cmd/mdb/common/kmdb/kmdb_fault.h
new file mode 100644
index 0000000..449cc14
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_fault.h
@@ -0,0 +1,47 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_FAULT_H
+#define	_KMDB_FAULT_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_kreg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void kmdb_fault(kreg_t, kreg_t, kreg_t, int);
+extern void kmdb_fault_display(kreg_t, kreg_t, kreg_t, int);
+
+extern void kmdb_print_stack(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_FAULT_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_fdio.c b/usr/src/cmd/mdb/common/kmdb/kmdb_fdio.c
new file mode 100644
index 0000000..b198efa
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_fdio.c
@@ -0,0 +1,49 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * kmdb version of the File Descriptor I/O Backend
+ *
+ * We don't have any files to open, so this is just a stub.
+ */
+
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+
+/*ARGSUSED*/
+mdb_io_t *
+mdb_fdio_create_path(const char *path[], const char *fname,
+    int flags, mode_t mode)
+{
+	(void) set_errno(ENOENT);
+	return (NULL);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_io.h b/usr/src/cmd/mdb/common/kmdb/kmdb_io.h
new file mode 100644
index 0000000..6dc049f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_io.h
@@ -0,0 +1,46 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_IO_H
+#define	_KMDB_IO_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * KMDB-specific I/O backend constructors
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern mdb_io_t *kmdb_promio_create(char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_IO_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kctl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_kctl.h
new file mode 100644
index 0000000..54b7df2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kctl.h
@@ -0,0 +1,63 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_KCTL_H
+#define	_KMDB_KCTL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Interfaces used by the driver and mdb to interact with kmdb, and vice versa
+ */
+
+#include <sys/types.h>
+#include <sys/kdi.h>
+
+#include <kmdb/kmdb_auxv.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Interfaces used by mdb to control kmdb
+ */
+#define	KMDB_IOC		(0xDB << 16)
+#define	KMDB_IOC_START		(KMDB_IOC|1)
+#define	KMDB_IOC_STOP		(KMDB_IOC|2)
+
+#define	KMDB_ACT_F_BOOT		0x1		/* activated during boot */
+
+extern int kmdb_init(const char *, kmdb_auxv_t *);
+
+extern void kmdb_activate(kdi_debugvec_t **, uint_t);
+extern void kmdb_deactivate(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_KCTL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.c
new file mode 100644
index 0000000..7d2f674
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.c
@@ -0,0 +1,295 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The KDI, or kernel/debugger interface, is used to allow the kernel and the
+ * debugger to communicate.  These communications take two forms:
+ *
+ *  1. kernel to debugger.  Interfaces of this type are used by the kernel to
+ *     inform the debugger of changes in the state of the system that need to
+ *     be noted by the debugger.  For example, the kernel uses one of these
+ *     interfaces to tell debugger that the set of currently-loaded modules
+ *     has changed.
+ *
+ *  2. debugger to kernel.  Interfaces of this type are used by the debugger
+ *     to extract information from the kernel that would otherwise be difficult
+ *     to get, or to perform services that are specific to the machine being
+ *     used.  An example of the former is the module iterator, which is needed
+ *     to allow symbol resolution, but which needs to resolve symbols prior
+ *     to the iteration.  The latter class include machine-specific or
+ *     cpu-type-specific functions, such as the I-cache flusher.  By directly
+ *     using the kernel versions of these functions, we avoid the need to
+ *     include multiple versions of each function - one per cpu and/or machine -
+ *     in kmdb.
+ */
+
+#include <sys/kdi_impl.h>
+
+#include <kmdb/kmdb_kdi_impl.h>
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kmdb_kvm.h>
+#include <kmdb/kmdb_promif.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+static int kdi_unload_request;
+
+typedef struct mod_interp_data {
+	int	(*mid_usercb)(struct modctl *, void *);
+	void	*mid_userarg;
+	jmp_buf mid_pcb;
+	jmp_buf *mid_oldpcb;
+} mod_interp_data_t;
+
+static kmdb_auxv_t *kdi_auxv;
+
+int
+kmdb_kdi_mods_changed(void)
+{
+	return (mdb.m_kdi->kdi_mods_changed());
+}
+
+static int
+kmdb_kdi_mod_interp(struct modctl *mp, void *arg)
+{
+	mod_interp_data_t *mid = arg;
+	int rc;
+
+	kmdb_dpi_restore_fault_hdlr(mid->mid_oldpcb);
+	rc = mid->mid_usercb(mp, mid->mid_userarg);
+	mid->mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid->mid_pcb);
+
+	return (rc);
+}
+
+/*
+ * We need to protect ourselves against any problems that may occur while
+ * executing the module iterator, currently located in krtld.  If, for
+ * example, one of the next pointers in the module list points to an invalid
+ * address, we don't want kmdb to explode.  As such, we protect ourselves
+ * with the DPI fault-protection routines.  We don't want our fault-protection
+ * callback to protect the callback that the kmdb consumer provided, so we
+ * provide our own interposition callback that removes our fault-protector
+ * before invoking the user's callback.
+ */
+int
+kmdb_kdi_mod_iter(int (*cb)(struct modctl *, void *), void *arg)
+{
+	mod_interp_data_t mid;
+	int rc;
+
+	if (setjmp(mid.mid_pcb) != 0) {
+		/* We took a fault while iterating through the modules */
+		kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);
+		return (-1);
+	}
+
+	mid.mid_usercb = cb;
+	mid.mid_userarg = arg;
+	mid.mid_oldpcb = kmdb_dpi_set_fault_hdlr(&mid.mid_pcb);
+
+	rc = mdb.m_kdi->kdi_mod_iter(kmdb_kdi_mod_interp, &mid);
+
+	kmdb_dpi_restore_fault_hdlr(mid.mid_oldpcb);
+
+	return (rc);
+}
+
+int
+kmdb_kdi_mod_isloaded(struct modctl *modp)
+{
+	return (mdb.m_kdi->kdi_mod_isloaded(modp));
+}
+
+int
+kmdb_kdi_mod_haschanged(struct modctl *mc1, struct module *mp1,
+    struct modctl *mc2, struct module *mp2)
+{
+	return (mdb.m_kdi->kdi_mod_haschanged(mc1, mp1, mc2, mp2));
+}
+
+static ssize_t
+kdi_prw(void *buf, size_t nbytes, physaddr_t addr, int (*rw)(caddr_t, size_t,
+    physaddr_t, size_t *))
+{
+	size_t sz;
+	int rc;
+
+	kmdb_dpi_flush_slave_caches();
+	if ((rc = rw(buf, nbytes, addr, &sz)) != 0)
+		return (set_errno(rc));
+
+	return (sz);
+}
+
+ssize_t
+kmdb_kdi_pread(void *buf, size_t nbytes, physaddr_t addr)
+{
+	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pread));
+}
+
+ssize_t
+kmdb_kdi_pwrite(void *buf, size_t nbytes, physaddr_t addr)
+{
+	return (kdi_prw(buf, nbytes, addr, mdb.m_kdi->kdi_pwrite));
+}
+
+void
+kmdb_kdi_flush_caches(void)
+{
+	mdb.m_kdi->kdi_flush_caches();
+}
+
+int
+kmdb_kdi_get_unload_request(void)
+{
+	return (kdi_unload_request);
+}
+
+void
+kmdb_kdi_set_unload_request(void)
+{
+	kdi_unload_request = 1;
+}
+
+int
+kmdb_kdi_get_flags(void)
+{
+	uint_t flags = 0;
+
+	if (mdb.m_flags & MDB_FL_NOCTF)
+		flags |= KMDB_KDI_FL_NOCTF;
+	if (mdb.m_flags & MDB_FL_NOMODS)
+		flags |= KMDB_KDI_FL_NOMODS;
+
+	return (flags);
+}
+
+size_t
+kmdb_kdi_range_is_nontoxic(uintptr_t va, size_t sz, int write)
+{
+	return (mdb.m_kdi->kdi_range_is_nontoxic(va, sz, write));
+}
+
+void
+kmdb_kdi_system_claim(void)
+{
+	(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_claim, 0, NULL);
+	kmdb_prom_debugger_entry();
+}
+
+void
+kmdb_kdi_system_release(void)
+{
+	kmdb_prom_debugger_exit();
+
+	if (mdb.m_kdi->kdi_system_release != NULL) {
+		(void) kmdb_dpi_call((uintptr_t)mdb.m_kdi->kdi_system_release,
+		    0, NULL);
+	}
+}
+
+struct cons_polledio *
+kmdb_kdi_get_polled_io(void)
+{
+	return (mdb.m_kdi->kdi_get_polled_io());
+}
+
+int
+kmdb_kdi_vtop(uintptr_t va, physaddr_t *pap)
+{
+	jmp_buf pcb, *oldpcb;
+	int rc = 0;
+
+	if (setjmp(pcb) == 0) {
+		int err;
+
+		oldpcb = kmdb_dpi_set_fault_hdlr(&pcb);
+
+		if ((err = mdb.m_kdi->kdi_vtop(va, pap)) != 0)
+			rc = set_errno(err == ENOENT ? EMDB_NOMAP : err);
+	} else {
+		/* We faulted during the translation */
+		rc = set_errno(EMDB_NOMAP);
+	}
+
+	kmdb_dpi_restore_fault_hdlr(oldpcb);
+
+	return (rc);
+}
+
+kdi_dtrace_state_t
+kmdb_kdi_dtrace_get_state(void)
+{
+	return (mdb.m_kdi->kdi_dtrace_get_state());
+}
+
+int
+kmdb_kdi_dtrace_set(int state)
+{
+	int err;
+
+	if ((err = mdb.m_kdi->kdi_dtrace_set(state)) != 0)
+		return (set_errno(err));
+
+	return (0);
+}
+
+/*
+ * This function is to be called only during kmdb initialization, as it
+ * uses the running kernel for symbol translation facilities.
+ */
+uintptr_t
+kmdb_kdi_lookup_by_name(char *modname, char *symname)
+{
+	ASSERT(kmdb_dpi_get_state(NULL) == DPI_STATE_INIT);
+
+	return (kdi_auxv->kav_lookup_by_name(modname, symname));
+}
+
+void
+kmdb_kdi_init(kdi_t *kdi, kmdb_auxv_t *kav)
+{
+	mdb.m_kdi = kdi;
+	mdb.m_pagesize = kav->kav_pagesize;
+
+	kdi_unload_request = 0;
+
+	kdi_auxv = kav;
+
+	kmdb_kdi_init_isadep(kdi, kav);
+
+	kdi_cpu_init();
+}
+
+void
+kmdb_kdi_end_init(void)
+{
+	kdi_auxv = NULL;
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.h b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.h
new file mode 100644
index 0000000..8ffd0f2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi.h
@@ -0,0 +1,103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_KDI_H
+#define	_KMDB_KDI_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/kdi.h>
+#include <sys/modctl.h>
+#include <gelf.h>
+
+#include <kmdb/kmdb_auxv.h>
+#include <mdb/mdb_target.h>
+#include <kmdb/kmdb_kdi_isadep.h>
+
+/*
+ * The following directive tells the mapfile generator that only those
+ * prototypes and declarations ending with a "Driver OK" comment should be
+ * included in the mapfile.
+ *
+ * MAPFILE: export "Driver OK"
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct module;
+
+/*
+ * KDI initialization
+ */
+extern void kmdb_kdi_init(kdi_t *, kmdb_auxv_t *);
+extern void kmdb_kdi_init_isadep(kdi_t *, kmdb_auxv_t *);
+extern void kmdb_kdi_end_init(void);
+
+/*
+ * Debugger -> Kernel functions for use when the kernel is stopped
+ */
+extern int kmdb_kdi_mods_changed(void);
+extern int kmdb_kdi_mod_iter(int (*)(struct modctl *, void *), void *);
+extern int kmdb_kdi_mod_isloaded(struct modctl *);
+extern int kmdb_kdi_mod_haschanged(struct modctl *, struct module *,
+    struct modctl *, struct module *);
+extern ssize_t kmdb_kdi_pread(void *, size_t, physaddr_t);
+extern ssize_t kmdb_kdi_pwrite(void *, size_t, physaddr_t);
+extern void kmdb_kdi_stop_other_cpus(int, void (*)(void));	/* Driver OK */
+extern void kmdb_kdi_system_claim(void);
+extern void kmdb_kdi_system_release(void);
+extern size_t kmdb_kdi_range_is_nontoxic(uintptr_t, size_t, int);
+extern void kmdb_kdi_flush_caches(void);
+extern struct cons_polledio *kmdb_kdi_get_polled_io(void);
+extern int kmdb_kdi_vtop(uintptr_t, physaddr_t *);
+extern kdi_dtrace_state_t kmdb_kdi_dtrace_get_state(void);
+extern int kmdb_kdi_dtrace_set(int);
+
+/*
+ * Driver -> Debugger notifications
+ */
+
+extern int kmdb_kdi_get_unload_request(void);			/* Driver OK */
+extern void kmdb_kdi_set_unload_request(void);			/* Driver OK */
+
+#define	KMDB_KDI_FL_NOMODS		0x1
+#define	KMDB_KDI_FL_NOCTF		0x2
+
+extern int kmdb_kdi_get_flags(void);				/* Driver OK */
+
+/*
+ * Debugger -> Kernel functions for use only when the kernel is running
+ */
+extern uintptr_t kmdb_kdi_lookup_by_name(char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_KDI_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kdi_impl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi_impl.h
new file mode 100644
index 0000000..3ff5deb
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kdi_impl.h
@@ -0,0 +1,45 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_KDI_IMPL_H
+#define	_KMDB_KDI_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kmdb_kdi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void kdi_cpu_init(void);
+extern void kdi_usecwait(clock_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_KDI_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
new file mode 100644
index 0000000..3237aa5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
@@ -0,0 +1,2536 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kmdb_kvm.h>
+#include <kmdb/kvm.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_promif.h>
+#include <kmdb/kmdb_module.h>
+#include <kmdb/kmdb_asmutil.h>
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_target_impl.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_kreg_impl.h>
+#include <mdb/mdb.h>
+
+#include <strings.h>
+#include <dlfcn.h>
+#include <sys/isa_defs.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <sys/bitmap.h>
+#include <vm/as.h>
+
+static const char KMT_RTLD_NAME[] = "krtld";
+static const char KMT_MODULE[] = "mdb_ks";
+static const char KMT_CTFPARENT[] = "genunix";
+
+static mdb_list_t kmt_defbp_list;	/* List of current deferred bp's */
+static int kmt_defbp_lock;		/* For list, running kernel holds */
+static uint_t kmt_defbp_modchg_isload;	/* Whether mod change is load/unload */
+static struct modctl *kmt_defbp_modchg_modctl; /* modctl for defbp checking */
+static uint_t kmt_defbp_num;		/* Number of referenced def'd bp's */
+static int kmt_defbp_bpspec;		/* vespec for def'd bp activation bp */
+
+static const mdb_se_ops_t kmt_brkpt_ops;
+static const mdb_se_ops_t kmt_wapt_ops;
+
+static void kmt_sync(mdb_tgt_t *);
+
+typedef struct kmt_symarg {
+	mdb_tgt_sym_f *sym_cb;		/* Caller's callback function */
+	void *sym_data;			/* Callback function argument */
+	uint_t sym_type;		/* Symbol type/binding filter */
+	mdb_syminfo_t sym_info;		/* Symbol id and table id */
+	const char *sym_obj;		/* Containing object */
+} kmt_symarg_t;
+
+typedef struct kmt_maparg {
+	mdb_tgt_t *map_target;		/* Target used for mapping iter */
+	mdb_tgt_map_f *map_cb;		/* Caller's callback function */
+	void *map_data;			/* Callback function argument */
+} kmt_maparg_t;
+
+/*ARGSUSED*/
+int
+kmt_setflags(mdb_tgt_t *t, int flags)
+{
+	/*
+	 * We only handle one flag (ALLOWIO), and we can't fail to set or clear
+	 * it, so we just blindly replace the t_flags version with the one
+	 * passed.
+	 */
+	t->t_flags = (t->t_flags & ~MDB_TGT_F_ALLOWIO) |
+	    (flags & MDB_TGT_F_ALLOWIO);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+const char *
+kmt_name(mdb_tgt_t *t)
+{
+	return ("kmdb_kvm");
+}
+
+/*ARGSUSED*/
+static const char *
+kmt_platform(mdb_tgt_t *t)
+{
+	static char platform[SYS_NMLN];
+
+	if (kmdb_dpi_get_state(NULL) == DPI_STATE_INIT)
+		return (mdb_conf_platform());
+
+	if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, platform,
+	    sizeof (platform), "unix", "platform") != sizeof (platform)) {
+		warn("'platform' symbol is missing from kernel\n");
+		return ("unknown");
+	}
+
+	return (platform);
+}
+
+static int
+kmt_uname(mdb_tgt_t *t, struct utsname *utsp)
+{
+	return (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, utsp,
+	    sizeof (struct utsname), MDB_TGT_OBJ_EXEC, "utsname"));
+}
+
+/*ARGSUSED*/
+static int
+kmt_dmodel(mdb_tgt_t *t)
+{
+	return (MDB_TGT_MODEL_NATIVE);
+}
+
+/*ARGSUSED*/
+ssize_t
+kmt_rw(mdb_tgt_t *t, void *buf, size_t nbytes, uint64_t addr,
+    ssize_t (*rw)(void *, size_t, uint64_t))
+{
+	size_t n, ndone, chunksz;
+	jmp_buf *oldpcb = NULL;
+	jmp_buf pcb;
+	ssize_t res;
+
+	kmdb_prom_check_interrupt();
+
+	if (nbytes == 0)
+		return (0);
+
+	/*
+	 * Try to process the entire buffer, as requested.  If we catch a fault,
+	 * try smaller chunks.  This allows us to handle regions that cross
+	 * mapping boundaries.
+	 */
+	chunksz = nbytes;
+	ndone = 0;
+	if (setjmp(pcb) != 0) {
+		if (chunksz == 1) {
+			/* We failed with the smallest chunk - give up */
+			kmdb_dpi_restore_fault_hdlr(oldpcb);
+			return (ndone > 0 ? ndone : -1); /* errno set for us */
+		} else if (chunksz > 4)
+			chunksz = 4;
+		else
+			chunksz = 1;
+	}
+
+	oldpcb = kmdb_dpi_set_fault_hdlr(&pcb);
+	while (nbytes > 0) {
+		n = MIN(chunksz, nbytes);
+
+		if ((res = rw(buf, n, addr)) != n)
+			return (res < 0 ? res : ndone + res);
+
+		addr += n;
+		nbytes -= n;
+		ndone += n;
+		buf = ((caddr_t)buf + n);
+	}
+
+	kmdb_dpi_restore_fault_hdlr(oldpcb);
+
+	return (ndone);
+}
+
+static void
+kmt_bcopy(const void *s1, void *s2, size_t n)
+{
+	/*
+	 * We need to guarantee atomic accesses for certain sizes.  bcopy won't
+	 * make that guarantee, so we need to do it ourselves.
+	 */
+#ifdef	_LP64
+	if (n == 8 && ((uintptr_t)s1 & 7) == 0 && ((uintptr_t)s2 & 7) == 0)
+		*(uint64_t *)s2 = *(uint64_t *)s1;
+	else
+#endif
+	if (n == 4 && ((uintptr_t)s1 & 3) == 0 && ((uintptr_t)s2 & 3) == 0)
+		*(uint32_t *)s2 = *(uint32_t *)s1;
+	else if (n == 2 && ((uintptr_t)s1 & 1) == 0 && ((uintptr_t)s2 & 1) == 0)
+		*(uint16_t *)s2 = *(uint16_t *)s1;
+	else if (n == 1)
+		*(uint8_t *)s2 = *(uint8_t *)s1;
+	else
+		bcopy(s1, s2, n);
+}
+
+static ssize_t
+kmt_reader(void *buf, size_t nbytes, uint64_t addr)
+{
+	kmt_bcopy((void *)(uintptr_t)addr, buf, nbytes);
+	return (nbytes);
+}
+
+ssize_t
+kmt_writer(void *buf, size_t nbytes, uint64_t addr)
+{
+	kmt_bcopy(buf, (void *)(uintptr_t)addr, nbytes);
+	return (nbytes);
+}
+
+/*ARGSUSED*/
+static ssize_t
+kmt_read(mdb_tgt_t *t, void *buf, size_t nbytes, uintptr_t addr)
+{
+	/*
+	 * We don't want to allow reads of I/O-mapped memory.  Multi-page reads
+	 * that cross into I/O-mapped memory should be restricted to the initial
+	 * non-I/O region.  Reads that begin in I/O-mapped memory are failed
+	 * outright.
+	 */
+	if (!(t->t_flags & MDB_TGT_F_ALLOWIO) &&
+	    (nbytes = kmdb_kdi_range_is_nontoxic(addr, nbytes, 0)) == 0)
+		return (set_errno(EMDB_NOMAP));
+
+	return (kmt_rw(t, buf, nbytes, addr, kmt_reader));
+}
+
+/*ARGSUSED*/
+static ssize_t
+kmt_pread(mdb_tgt_t *t, void *buf, size_t nbytes, physaddr_t addr)
+{
+	return (kmt_rw(t, buf, nbytes, addr, kmdb_kdi_pread));
+}
+
+/*ARGSUSED*/
+ssize_t
+kmt_pwrite(mdb_tgt_t *t, const void *buf, size_t nbytes, physaddr_t addr)
+{
+	return (kmt_rw(t, (void *)buf, nbytes, addr, kmdb_kdi_pwrite));
+}
+
+static uintptr_t
+kmt_read_kas(mdb_tgt_t *t)
+{
+	GElf_Sym sym;
+
+	if (mdb_tgt_lookup_by_name(t, "unix", "kas", &sym, NULL) < 0) {
+		warn("'kas' symbol is missing from kernel\n");
+		(void) set_errno(EMDB_NOSYM);
+		return (0);
+	}
+
+	return ((uintptr_t)sym.st_value);
+}
+
+static int
+kmt_vtop(mdb_tgt_t *t, mdb_tgt_as_t as, uintptr_t va, physaddr_t *pap)
+{
+	mdb_module_t *mod;
+	struct as *asp;
+	mdb_var_t *v;
+
+	switch ((uintptr_t)as) {
+	case (uintptr_t)MDB_TGT_AS_PHYS:
+	case (uintptr_t)MDB_TGT_AS_FILE:
+	case (uintptr_t)MDB_TGT_AS_IO:
+		return (set_errno(EINVAL));
+	case (uintptr_t)MDB_TGT_AS_VIRT:
+		if ((asp = (struct as *)kmt_read_kas(t)) == NULL)
+			return (-1); /* errno is set for us */
+		break;
+	default:
+		asp = (struct as *)as;
+
+		/* We don't support non-kas vtop */
+		if (asp != (struct as *)kmt_read_kas(t))
+			return (set_errno(EMDB_TGTNOTSUP));
+	}
+
+	if (kmdb_prom_vtop(va, pap) == 0)
+		return (0);
+
+	if ((v = mdb_nv_lookup(&mdb.m_modules, "unix")) != NULL &&
+	    (mod = mdb_nv_get_cookie(v)) != NULL) {
+		int (*fptr)(uintptr_t, struct as *, physaddr_t *);
+
+		fptr = (int (*)(uintptr_t, struct as *, physaddr_t *))
+		    dlsym(mod->mod_hdl, "platform_vtop");
+
+		if ((fptr != NULL) && ((*fptr)(va, asp, pap) == 0))
+			return (0);
+	}
+
+	return (set_errno(EMDB_NOMAP));
+}
+
+/*ARGSUSED*/
+static int
+kmt_cpuregs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	const mdb_tgt_gregset_t *gregs;
+	intptr_t cpu = DPI_MASTER_CPUID;
+	int i;
+
+	if (flags & DCMD_ADDRSPEC) {
+		if (argc != 0)
+			return (DCMD_USAGE);
+		cpu = addr;
+	}
+
+	i = mdb_getopts(argc, argv,
+	    'c', MDB_OPT_UINTPTR, &cpu,
+	    NULL);
+
+	argc -= i;
+	argv += i;
+
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	if ((gregs = kmdb_dpi_get_gregs(cpu)) == NULL) {
+		warn("failed to retrieve registers for cpu %d", (int)cpu);
+		return (DCMD_ERR);
+	}
+
+	kmt_printregs(gregs);
+
+	return (DCMD_OK);
+}
+
+static int
+kmt_regs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (flags & DCMD_ADDRSPEC)
+		return (DCMD_USAGE);
+
+	return (kmt_cpuregs(addr, flags, argc, argv));
+}
+
+/*
+ * Lasciate ogne speranza, voi ch'intrate.
+ */
+static int
+kmt_call(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uintptr_t *call_argv, rval;
+	int parse_strings = 1;
+	GElf_Sym sym;
+	jmp_buf *oldpcb = NULL;
+	jmp_buf pcb;
+	int i;
+
+	if (!(flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	if (mdb_tgt_lookup_by_addr(mdb.m_target, addr, MDB_TGT_SYM_EXACT,
+	    NULL, 0, &sym, NULL) == 0 && GELF_ST_TYPE(sym.st_info) !=
+	    STT_FUNC) {
+		warn("%a is not a function\n", addr);
+		return (DCMD_ERR);
+	}
+
+	if (argc > 1 && argv[0].a_type == MDB_TYPE_STRING &&
+	    strcmp(argv[0].a_un.a_str, "-s") == 0) {
+		parse_strings = 0;
+		argc--;
+		argv++;
+	}
+
+	call_argv = mdb_alloc(sizeof (uintptr_t) * argc, UM_SLEEP);
+
+	for (i = 0; i < argc; i++) {
+		switch (argv[i].a_type) {
+		case MDB_TYPE_STRING:
+			/*
+			 * mdb_strtoull doesn't return on error, so we have to
+			 * pre-check strings suspected to contain numbers.
+			 */
+			if (parse_strings && strisbasenum(argv[i].a_un.a_str)) {
+				call_argv[i] = (uintptr_t)mdb_strtoull(
+				    argv[i].a_un.a_str);
+			} else
+				call_argv[i] = (uintptr_t)argv[i].a_un.a_str;
+
+			break;
+
+		case MDB_TYPE_IMMEDIATE:
+			call_argv[i] = argv[i].a_un.a_val;
+			break;
+
+		default:
+			mdb_free(call_argv,
+			    sizeof (uintptr_t) * argc);
+			return (DCMD_USAGE);
+		}
+	}
+
+	if (setjmp(pcb) != 0) {
+		warn("call failed: caught a trap\n");
+
+		kmdb_dpi_restore_fault_hdlr(oldpcb);
+		mdb_free(call_argv, sizeof (uintptr_t) * argc);
+		return (DCMD_ERR);
+	}
+
+	oldpcb = kmdb_dpi_set_fault_hdlr(&pcb);
+	rval = kmdb_dpi_call(addr, argc, call_argv);
+	kmdb_dpi_restore_fault_hdlr(oldpcb);
+
+	if (flags & DCMD_PIPE_OUT) {
+		mdb_printf("%p\n", rval);
+	} else {
+		/* pretty-print the results */
+		mdb_printf("%p = %a(", rval, addr);
+		for (i = 0; i < argc; i++) {
+			if (i > 0)
+				mdb_printf(", ");
+			if (argv[i].a_type == MDB_TYPE_STRING) {
+				/* I'm ashamed but amused */
+				char *quote = &("\""[parse_strings &&
+				    strisbasenum(argv[i].a_un.a_str)]);
+
+				mdb_printf("%s%s%s", quote, argv[i].a_un.a_str,
+				    quote);
+			} else
+				mdb_printf("%p", argv[i].a_un.a_val);
+		}
+		mdb_printf(");\n");
+	}
+
+	mdb_free(call_argv, sizeof (uintptr_t) * argc);
+
+	return (DCMD_OK);
+}
+
+static int
+kmt_cpustack_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	intptr_t cpu = DPI_MASTER_CPUID;
+	uint_t verbose = 0;
+	int i;
+
+	if (flags & DCMD_ADDRSPEC) {
+		cpu = addr;
+		flags &= ~DCMD_ADDRSPEC;
+	}
+
+	i = mdb_getopts(argc, argv,
+	    'c', MDB_OPT_UINTPTR, &cpu,
+	    'v', MDB_OPT_SETBITS, 1, &verbose,
+	    NULL);
+
+	argc -= i;
+	argv += i;
+
+	return (kmt_cpustack(addr, flags, argc, argv, cpu, verbose));
+}
+
+/*ARGSUSED*/
+int
+kmt_dump_crumbs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	intptr_t cpu = -1;
+
+	if (flags & DCMD_ADDRSPEC) {
+		if (argc != 0)
+			return (DCMD_USAGE);
+	} else {
+		addr = 0;
+
+		if (mdb_getopts(argc, argv,
+		    'c', MDB_OPT_UINTPTR, &cpu,
+		    NULL) != argc)
+			return (DCMD_USAGE);
+	}
+
+	kmdb_dpi_dump_crumbs(addr, cpu);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+kmt_noducttape(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int a = 0;
+
+	return (a/a);
+}
+
+static int
+kmt_dmod_status(char *msg, int state)
+{
+	kmdb_modctl_t *kmc;
+	mdb_var_t *v;
+	int first = 1, n = 0;
+
+	mdb_nv_rewind(&mdb.m_dmodctl);
+	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
+		kmc = MDB_NV_COOKIE(v);
+
+		if (kmc->kmc_state != state)
+			continue;
+
+		n++;
+
+		if (msg != NULL) {
+			if (first) {
+				mdb_printf(msg, NULL);
+				first = 0;
+			}
+
+			mdb_printf(" %s", kmc->kmc_modname);
+		}
+	}
+
+	if (!first && msg != NULL)
+		mdb_printf("\n");
+
+	return (n);
+}
+
+/*ARGSUSED*/
+static int
+kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	kmt_data_t *kmt = mdb.m_target->t_data;
+	struct utsname uts;
+	kreg_t tt;
+
+	if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, &uts, sizeof (uts),
+	    "unix", "utsname") != sizeof (uts)) {
+		warn("failed to read 'utsname' struct from kernel\n");
+		bzero(&uts, sizeof (uts));
+		(void) strcpy(uts.nodename, "unknown machine");
+	}
+
+	mdb_printf("debugging live kernel (%d-bit) on %s\n",
+	    (int)(sizeof (void *) * NBBY),
+	    (*uts.nodename == '\0' ? "(not set)" : uts.nodename));
+	mdb_printf("operating system: %s %s (%s)\n",
+	    uts.release, uts.version, uts.machine);
+
+	if (kmt->kmt_cpu != NULL) {
+		mdb_printf("CPU-specific support: %s\n",
+		    kmt_cpu_name(kmt->kmt_cpu));
+	}
+
+	mdb_printf("DTrace state: %s\n", (kmdb_kdi_dtrace_get_state() ==
+	    KDI_DTSTATE_DTRACE_ACTIVE ? "active (debugger breakpoints cannot "
+	    "be armed)" : "inactive"));
+
+	(void) kmdb_dpi_get_register("tt", &tt);
+	mdb_printf("stopped on: %s\n", kmt_trapname(tt));
+
+	(void) kmt_dmod_status("pending dmod loads:", KMDB_MC_STATE_LOADING);
+	(void) kmt_dmod_status("pending dmod unloads:",
+	    KMDB_MC_STATE_UNLOADING);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+kmt_switch(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (!(flags & DCMD_ADDRSPEC) || argc != 0)
+		return (DCMD_USAGE);
+
+	if (kmdb_dpi_switch_master((int)addr) < 0) {
+		warn("failed to switch to CPU %d", (int)addr);
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+static const mdb_dcmd_t kmt_dcmds[] = {
+	{ "$c", "?[cnt]", "print stack backtrace", kmt_stack },
+	{ "$C", "?[cnt]", "print stack backtrace", kmt_stackv },
+	{ "$r", NULL, "print general-purpose registers", kmt_regs },
+	{ "$?", NULL, "print status and registers", kmt_regs },
+	{ ":x", ":", "change the active CPU", kmt_switch },
+	{ "call", ":[arg ...]", "call a kernel function", kmt_call },
+	{ "cpustack", "?[-v] [-c cpuid] [cnt]", "print stack backtrace for a "
+	    "specific CPU", kmt_cpustack_dcmd },
+	{ "cpuregs", "?[-c cpuid]", "print general-purpose registers for a "
+	    "specific CPU", kmt_cpuregs },
+	{ "crumbs", NULL, NULL, kmt_dump_crumbs },
+#if defined(__i386) || defined(__amd64)
+	{ "in", ":[-L len]", "read from I/O port", kmt_in_dcmd },
+	{ "out", ":[-L len] val", "write to I/O port", kmt_out_dcmd },
+	{ "rdmsr", ":", "read an MSR", kmt_rdmsr },
+	{ "wrmsr", ": val", "write an MSR", kmt_wrmsr },
+#endif
+	{ "noducttape", NULL, NULL, kmt_noducttape },
+	{ "regs", NULL, "print general-purpose registers", kmt_regs },
+	{ "stack", "?[cnt]", "print stack backtrace", kmt_stack },
+	{ "stackregs", "?", "print stack backtrace and registers", kmt_stackr },
+	{ "status", NULL, "print summary of current target", kmt_status_dcmd },
+	{ "switch", ":", "change the active CPU", kmt_switch },
+	{ NULL }
+};
+
+static uintmax_t
+kmt_reg_disc_get(const mdb_var_t *v)
+{
+	mdb_tgt_reg_t r = 0;
+
+	(void) mdb_tgt_getareg(MDB_NV_COOKIE(v), 0, mdb_nv_get_name(v), &r);
+
+	return (r);
+}
+
+static void
+kmt_reg_disc_set(mdb_var_t *v, uintmax_t r)
+{
+	if (mdb_tgt_putareg(MDB_NV_COOKIE(v), 0, mdb_nv_get_name(v), r) == -1)
+		warn("failed to modify %%%s register", mdb_nv_get_name(v));
+}
+
+static const mdb_nv_disc_t kmt_reg_disc = {
+	kmt_reg_disc_set,
+	kmt_reg_disc_get
+};
+
+/*ARGSUSED*/
+static int
+kmt_getareg(mdb_tgt_t *t, mdb_tgt_tid_t tid, const char *rname,
+    mdb_tgt_reg_t *rp)
+{
+	kreg_t val;
+
+	if (kmdb_dpi_get_register(rname, &val) < 0)
+		return (set_errno(EMDB_BADREG));
+
+	*rp = val;
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+kmt_putareg(mdb_tgt_t *t, mdb_tgt_tid_t tid, const char *rname, mdb_tgt_reg_t r)
+{
+	if (kmdb_dpi_set_register(rname, r) < 0)
+		return (set_errno(EMDB_BADREG));
+
+	return (0);
+}
+
+static void
+kmt_mod_destroy(kmt_module_t *km)
+{
+	if (km->km_name != NULL)
+		strfree(km->km_name);
+	if (km->km_symtab != NULL)
+		mdb_gelf_symtab_destroy(km->km_symtab);
+	if (km->km_ctfp != NULL)
+		mdb_ctf_close(km->km_ctfp);
+}
+
+static kmt_module_t *
+kmt_mod_create(mdb_tgt_t *t, struct modctl *ctlp, char *name)
+{
+	kmt_module_t *km = mdb_zalloc(sizeof (kmt_module_t), UM_SLEEP);
+	struct module *mod;
+
+	km->km_name = mdb_alloc(strlen(name) + 1, UM_SLEEP);
+	strcpy(km->km_name, name);
+
+	bcopy(ctlp, &km->km_modctl, sizeof (struct modctl));
+
+	if (mdb_tgt_vread(t, &km->km_module, sizeof (struct module),
+	    (uintptr_t)km->km_modctl.mod_mp) != sizeof (struct module))
+		goto create_module_cleanup;
+	mod = &km->km_module;
+
+	if (mod->symhdr != NULL && mod->strhdr != NULL && mod->symtbl != NULL &&
+	    mod->strings != NULL) {
+		mdb_gelf_ehdr_to_gehdr(&mod->hdr, &km->km_ehdr);
+
+		km->km_symtab = mdb_gelf_symtab_create_raw(&km->km_ehdr,
+		    mod->symhdr, mod->symtbl, mod->strhdr, mod->strings,
+		    MDB_TGT_SYMTAB);
+
+		km->km_symtab_va = mod->symtbl;
+		km->km_strtab_va = mod->strings;
+
+		if (mdb_tgt_vread(t, &km->km_symtab_hdr, sizeof (Shdr),
+		    (uintptr_t)mod->symhdr) != sizeof (Shdr) ||
+		    mdb_tgt_vread(t, &km->km_strtab_hdr, sizeof (Shdr),
+		    (uintptr_t)mod->strhdr) != sizeof (Shdr))
+			goto create_module_cleanup;
+	}
+
+	/*
+	 * We don't want everyone rooting around in the module structure, so we
+	 * make copies of the interesting members.
+	 */
+	km->km_text_va = (uintptr_t)mod->text;
+	km->km_text_size = mod->text_size;
+	km->km_data_va = (uintptr_t)mod->data;
+	km->km_data_size = mod->data_size;
+	km->km_bss_va = (uintptr_t)mod->bss;
+	km->km_bss_size = mod->bss_size;
+	km->km_ctf_va = mod->ctfdata;
+	km->km_ctf_size = mod->ctfsize;
+
+	if (mod->flags & KOBJ_PRIM)
+		km->km_flags |= KM_F_PRIMARY;
+
+	return (km);
+
+create_module_cleanup:
+	warn("failed to read module %s\n", name);
+	kmt_mod_destroy(km);
+	return (NULL);
+}
+
+static void
+kmt_mod_remove(kmt_data_t *kmt, kmt_module_t *km)
+{
+	mdb_var_t *v = mdb_nv_lookup(&kmt->kmt_modules, km->km_name);
+
+	ASSERT(v != NULL);
+
+	mdb_dprintf(MDB_DBG_KMOD, "removing module %s\n", km->km_name);
+
+	mdb_list_delete(&kmt->kmt_modlist, km);
+	mdb_nv_remove(&kmt->kmt_modules, v);
+	kmt_mod_destroy(km);
+}
+
+static int
+kmt_modlist_update_cb(struct modctl *modp, void *arg)
+{
+	mdb_tgt_t *t = arg;
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+	mdb_var_t *v;
+	char name[MAXNAMELEN];
+
+	if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT, name, MAXNAMELEN,
+	    (uintptr_t)modp->mod_modname) <= 0) {
+		warn("failed to read module name at %p",
+		    (void *)modp->mod_modname);
+	}
+
+	/* We only care about modules that are actually loaded */
+	if (!kmdb_kdi_mod_isloaded(modp))
+		return (0);
+
+	/*
+	 * Skip the modules we already know about and that haven't
+	 * changed since last time we were here.
+	 */
+	if ((v = mdb_nv_lookup(&kmt->kmt_modules, name)) != NULL) {
+		km = MDB_NV_COOKIE(v);
+
+		if (kmdb_kdi_mod_haschanged(&km->km_modctl, &km->km_module,
+		    modp, modp->mod_mp)) {
+			/*
+			 * The module has changed since last we saw it.  For
+			 * safety, remove our old version, and treat it as a
+			 * new module.
+			 */
+			mdb_dprintf(MDB_DBG_KMOD, "stutter module %s\n", name);
+			kmt_mod_remove(kmt, km);
+		} else {
+			km->km_seen = 1;
+			return (0);
+		}
+	}
+
+	mdb_dprintf(MDB_DBG_KMOD, "found new module %s\n", name);
+
+	if ((km = kmt_mod_create(t, modp, name)) != NULL) {
+		mdb_list_append(&kmt->kmt_modlist, km);
+		(void) mdb_nv_insert(&kmt->kmt_modules, name, NULL,
+		    (uintptr_t)km, 0);
+		km->km_seen = 1;
+	}
+
+	return (0);
+}
+
+static void
+kmt_modlist_update(mdb_tgt_t *t)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km, *kmn;
+
+	if (kmdb_kdi_mod_iter(kmt_modlist_update_cb, t) < 0) {
+		warn("failed to complete update of kernel module list\n");
+		return;
+	}
+
+	km = mdb_list_next(&kmt->kmt_modlist);
+	while (km != NULL) {
+		kmn = mdb_list_next(km);
+
+		if (km->km_seen == 1) {
+			/* Reset the mark for next time */
+			km->km_seen = 0;
+		} else {
+			/*
+			 * We didn't see it on the kernel's module list, so
+			 * remove it from our view of the world.
+			 */
+			kmt_mod_remove(kmt, km);
+		}
+
+		km = kmn;
+	}
+}
+
+static void
+kmt_periodic(mdb_tgt_t *t)
+{
+	(void) mdb_tgt_status(t, &t->t_status);
+}
+
+int
+kmt_lookup_by_addr(mdb_tgt_t *t, uintptr_t addr, uint_t flags,
+    char *buf, size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km = mdb_list_next(&kmt->kmt_modlist);
+	kmt_module_t *sym_km = NULL;
+	kmt_module_t prmod;
+	GElf_Sym sym;
+	uint_t symid;
+	const char *name;
+
+	/*
+	 * We look through the private symbols (if any), then through the module
+	 * symbols.  We can simplify the loop if we pretend the private symbols
+	 * come from a module.
+	 */
+	if (mdb.m_prsym != NULL) {
+		bzero(&prmod, sizeof (kmt_module_t));
+		prmod.km_name = "<<<prmod>>>";
+		prmod.km_symtab = mdb.m_prsym;
+		prmod.km_list.ml_next = (mdb_list_t *)km;
+		km = &prmod;
+	}
+
+	/* Symbol resolution isn't available during initialization */
+	if (kmdb_dpi_get_state(NULL) == DPI_STATE_INIT)
+		return (set_errno(EMDB_NOSYM));
+
+	for (; km != NULL; km = mdb_list_next(km)) {
+		if (km != &prmod && !kmt->kmt_symavail)
+			continue;
+
+		if (km->km_symtab == NULL)
+			continue;
+
+		if (mdb_gelf_symtab_lookup_by_addr(km->km_symtab, addr, flags,
+		    buf, nbytes, symp, &sip->sym_id) != 0 ||
+		    symp->st_value == 0)
+			continue;
+
+		if (flags & MDB_TGT_SYM_EXACT) {
+			sym_km = km;
+			goto found;
+		}
+
+		/*
+		 * If this is the first match we've found, or if this symbol is
+		 * closer to the specified address than the last one we found,
+		 * use it.
+		 */
+		if (sym_km == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) {
+			sym_km = km;
+			sym = *symp;
+			symid = sip->sym_id;
+		}
+	}
+
+	/*
+	 * kmdb dmods are normal kernel modules, loaded by krtld as such.  To
+	 * avoid polluting modinfo, and to keep from confusing the module
+	 * subsystem (many dmods have the same names as real kernel modules),
+	 * kmdb keeps their modctls separate, and doesn't allow their loading
+	 * to be broadcast via the krtld module load/unload mechanism.  As a
+	 * result, kmdb_kvm doesn't find out about them, and can't turn their
+	 * addresses into symbols.  This can be most inconvenient during
+	 * debugger faults, as the dmod frames will show up without names.
+	 * We weren't able to turn the requeted address into a symbol, so we'll
+	 * take a spin through the dmods, trying to match our address against
+	 * their symbols.
+	 */
+	if (sym_km == NULL) {
+		return (kmdb_module_lookup_by_addr(addr, flags, buf, nbytes,
+		    symp, sip));
+	}
+
+	*symp = sym;
+	sip->sym_id = symid;
+
+found:
+	/*
+	 * Once we've found something, copy the final name into the caller's
+	 * buffer and prefix it with the load object name if appropriate.
+	 */
+	name = mdb_gelf_sym_name(sym_km->km_symtab, symp);
+
+	if (sym_km == &prmod) {
+		if (buf != NULL) {
+			(void) strncpy(buf, name, nbytes);
+			buf[nbytes - 1] = '\0';
+		}
+		sip->sym_table = MDB_TGT_PRVSYM;
+	} else {
+		if (buf != NULL) {
+			if (sym_km->km_flags & KM_F_PRIMARY) {
+				(void) strncpy(buf, name, nbytes);
+				buf[nbytes - 1] = '\0';
+			} else {
+				(void) mdb_snprintf(buf, nbytes, "%s`%s",
+				    sym_km->km_name, name);
+			}
+		}
+		sip->sym_table = MDB_TGT_SYMTAB;
+	}
+
+	return (0);
+}
+
+static int
+kmt_lookup_by_name(mdb_tgt_t *t, const char *obj, const char *name,
+    GElf_Sym *symp, mdb_syminfo_t *sip)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+	mdb_var_t *v;
+	GElf_Sym sym;
+	uint_t symid;
+	int n;
+
+	if (!kmt->kmt_symavail)
+		return (set_errno(EMDB_NOSYM));
+
+	switch ((uintptr_t)obj) {
+	case (uintptr_t)MDB_TGT_OBJ_EXEC:
+	case (uintptr_t)MDB_TGT_OBJ_EVERY:
+		km = mdb_list_next(&kmt->kmt_modlist);
+		n = mdb_nv_size(&kmt->kmt_modules);
+		break;
+
+	case (uintptr_t)MDB_TGT_OBJ_RTLD:
+		obj = KMT_RTLD_NAME;
+		/*FALLTHROUGH*/
+
+	default:
+		/*
+		 * If this is a request for a dmod symbol, let kmdb_module
+		 * handle it.
+		 */
+		if (obj != NULL && strncmp(obj, "DMOD`", 5) == 0) {
+			return (kmdb_module_lookup_by_name(obj + 5, name,
+			    symp, sip));
+		}
+
+		if ((v = mdb_nv_lookup(&kmt->kmt_modules, obj)) == NULL)
+			return (set_errno(EMDB_NOOBJ));
+
+		km = mdb_nv_get_cookie(v);
+		n = 1;
+	}
+
+	/*
+	 * kmdb's kvm target is at a bit of a disadvantage compared to mdb's
+	 * kvm target when it comes to global symbol lookups.  mdb has ksyms,
+	 * which hides pesky things like symbols that are undefined in unix,
+	 * but which are defined in genunix.  We don't have such a facility -
+	 * we simply iterate through the modules, looking for a given symbol
+	 * in each.  Unless we're careful, we'll return the undef in the
+	 * aforementioned case.
+	 */
+	for (; n > 0; n--, km = mdb_list_next(km)) {
+		if (mdb_gelf_symtab_lookup_by_name(km->km_symtab, name,
+		    &sym, &symid) == 0 && sym.st_shndx != SHN_UNDEF)
+			break;
+	}
+
+	if (n == 0)
+		return (set_errno(EMDB_NOSYM));
+
+found:
+	bcopy(&sym, symp, sizeof (GElf_Sym));
+	sip->sym_id = symid;
+	sip->sym_table = MDB_TGT_SYMTAB;
+
+	return (0);
+}
+
+static int
+kmt_symtab_func(void *data, const GElf_Sym *sym, const char *name, uint_t id)
+{
+	kmt_symarg_t *arg = data;
+
+	if (mdb_tgt_sym_match(sym, arg->sym_type)) {
+		arg->sym_info.sym_id = id;
+
+		return (arg->sym_cb(arg->sym_data, sym, name, &arg->sym_info,
+		    arg->sym_obj));
+	}
+
+	return (0);
+}
+
+static void
+kmt_symtab_iter(mdb_gelf_symtab_t *gst, uint_t type, const char *obj,
+    mdb_tgt_sym_f *cb, void *p)
+{
+	kmt_symarg_t arg;
+
+	arg.sym_cb = cb;
+	arg.sym_data = p;
+	arg.sym_type = type;
+	arg.sym_info.sym_table = gst->gst_tabid;
+	arg.sym_obj = obj;
+
+	mdb_gelf_symtab_iter(gst, kmt_symtab_func, &arg);
+}
+
+static int
+kmt_symbol_iter(mdb_tgt_t *t, const char *obj, uint_t which, uint_t type,
+    mdb_tgt_sym_f *cb, void *data)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+
+	mdb_gelf_symtab_t *symtab = NULL;
+	mdb_var_t *v;
+
+	if (which == MDB_TGT_DYNSYM)
+		return (set_errno(EMDB_TGTNOTSUP));
+
+	switch ((uintptr_t)obj) {
+	case (uintptr_t)MDB_TGT_OBJ_EXEC:
+	case (uintptr_t)MDB_TGT_OBJ_EVERY:
+		mdb_nv_rewind(&kmt->kmt_modules);
+		while ((v = mdb_nv_advance(&kmt->kmt_modules)) != NULL) {
+			km = mdb_nv_get_cookie(v);
+
+			if (km->km_symtab != NULL) {
+				kmt_symtab_iter(km->km_symtab, type,
+				    km->km_name, cb, data);
+			}
+		}
+		return (0);
+
+	case (uintptr_t)MDB_TGT_OBJ_RTLD:
+		obj = KMT_RTLD_NAME;
+		/*FALLTHROUGH*/
+
+	default:
+		if (strncmp(obj, "DMOD`", 5) == 0) {
+			return (kmdb_module_symbol_iter(obj + 5, type,
+			    cb, data));
+		}
+
+		if ((v = mdb_nv_lookup(&kmt->kmt_modules, obj)) == NULL)
+			return (set_errno(EMDB_NOOBJ));
+		km = mdb_nv_get_cookie(v);
+
+		symtab = km->km_symtab;
+	}
+
+	if (symtab != NULL)
+		kmt_symtab_iter(symtab, type, obj, cb, data);
+
+	return (0);
+}
+
+static int
+kmt_mapping_walk(uintptr_t addr, const void *data, kmt_maparg_t *marg)
+{
+	/*
+	 * This is a bit sketchy but avoids problematic compilation of this
+	 * target against the current VM implementation.  Now that we have
+	 * vmem, we can make this less broken and more informative by changing
+	 * this code to invoke the vmem walker in the near future.
+	 */
+	const struct kmt_seg {
+		caddr_t s_base;
+		size_t s_size;
+	} *segp = (const struct kmt_seg *)data;
+
+	mdb_map_t map;
+	GElf_Sym sym;
+	mdb_syminfo_t info;
+
+	map.map_base = (uintptr_t)segp->s_base;
+	map.map_size = segp->s_size;
+	map.map_flags = MDB_TGT_MAP_R | MDB_TGT_MAP_W | MDB_TGT_MAP_X;
+
+	if (kmt_lookup_by_addr(marg->map_target, addr, MDB_TGT_SYM_EXACT,
+	    map.map_name, MDB_TGT_MAPSZ, &sym, &info) == -1) {
+
+		(void) mdb_iob_snprintf(map.map_name, MDB_TGT_MAPSZ,
+		    "%lr", addr);
+	}
+
+	return (marg->map_cb(marg->map_data, &map, map.map_name));
+}
+
+static int
+kmt_mapping_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
+{
+	kmt_maparg_t m;
+	uintptr_t kas;
+
+	m.map_target = t;
+	m.map_cb = func;
+	m.map_data = private;
+
+	if ((kas = kmt_read_kas(t)) == NULL)
+		return (-1); /* errno is set for us */
+
+	return (mdb_pwalk("seg", (mdb_walk_cb_t)kmt_mapping_walk, &m, kas));
+}
+
+static const mdb_map_t *
+kmt_mod_to_map(kmt_module_t *km, mdb_map_t *map)
+{
+	(void) strncpy(map->map_name, km->km_name, MDB_TGT_MAPSZ);
+	map->map_name[MDB_TGT_MAPSZ - 1] = '\0';
+	map->map_base = km->km_text_va;
+	map->map_size = km->km_text_size;
+	map->map_flags = MDB_TGT_MAP_R | MDB_TGT_MAP_W | MDB_TGT_MAP_X;
+
+	return (map);
+}
+
+static int
+kmt_object_iter(mdb_tgt_t *t, mdb_tgt_map_f *func, void *private)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+	mdb_map_t m;
+
+	for (km = mdb_list_next(&kmt->kmt_modlist); km != NULL;
+	    km = mdb_list_next(km)) {
+		if (func(private, kmt_mod_to_map(km, &m), km->km_name) == -1)
+			break;
+	}
+
+	return (0);
+}
+
+static const mdb_map_t *
+kmt_addr_to_map(mdb_tgt_t *t, uintptr_t addr)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+
+	for (km = mdb_list_next(&kmt->kmt_modlist); km != NULL;
+	    km = mdb_list_next(km)) {
+		if (addr - km->km_text_va < km->km_text_size ||
+		    addr - km->km_data_va < km->km_data_size ||
+		    addr - km->km_bss_va < km->km_bss_size)
+			return (kmt_mod_to_map(km, &kmt->kmt_map));
+	}
+
+	(void) set_errno(EMDB_NOMAP);
+	return (NULL);
+}
+
+static const mdb_map_t *
+kmt_name_to_map(mdb_tgt_t *t, const char *name)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+	mdb_map_t m;
+
+	/*
+	 * If name is MDB_TGT_OBJ_EXEC, return the first module on the list,
+	 * which will be unix since we keep kmt_modlist in load order.
+	 */
+	if (name == MDB_TGT_OBJ_EXEC) {
+		return (kmt_mod_to_map(mdb_list_next(&kmt->kmt_modlist),
+		    &m));
+	}
+
+	if (name == MDB_TGT_OBJ_RTLD)
+		name = KMT_RTLD_NAME; /* replace MDB_TGT_OBJ_RTLD with krtld */
+
+	for (km = mdb_list_next(&kmt->kmt_modlist); km != NULL;
+	    km = mdb_list_next(km)) {
+		if (strcmp(name, km->km_name) == 0)
+			return (kmt_mod_to_map(km, &m));
+	}
+
+	(void) set_errno(EMDB_NOOBJ);
+	return (NULL);
+}
+
+static ctf_file_t *
+kmt_load_ctfdata(mdb_tgt_t *t, kmt_module_t *km)
+{
+	kmt_data_t *kmt = t->t_data;
+	int err;
+
+	if (km->km_ctfp != NULL)
+		return (km->km_ctfp);
+
+	if (km->km_ctf_va == NULL || km->km_symtab == NULL) {
+		(void) set_errno(EMDB_NOCTF);
+		return (NULL);
+	}
+
+	if ((km->km_ctfp = mdb_ctf_bufopen(km->km_ctf_va, km->km_ctf_size,
+	    km->km_symtab_va, &km->km_symtab_hdr, km->km_strtab_va,
+	    &km->km_strtab_hdr, &err)) == NULL) {
+		(void) set_errno(ctf_to_errno(err));
+		return (NULL);
+	}
+
+	mdb_dprintf(MDB_DBG_KMOD, "loaded %lu bytes of CTF data for %s\n",
+	    (ulong_t)km->km_ctf_size, km->km_name);
+
+	if (ctf_parent_name(km->km_ctfp) != NULL) {
+		mdb_var_t *v;
+
+		if ((v = mdb_nv_lookup(&kmt->kmt_modules,
+		    ctf_parent_name(km->km_ctfp))) != NULL) {
+			kmt_module_t *pm = mdb_nv_get_cookie(v);
+
+			if (pm->km_ctfp == NULL)
+				(void) kmt_load_ctfdata(t, pm);
+
+			if (pm->km_ctfp != NULL && ctf_import(km->km_ctfp,
+			    pm->km_ctfp) == CTF_ERR) {
+				warn("failed to import parent types into "
+				    "%s: %s\n", km->km_name,
+				    ctf_errmsg(ctf_errno(km->km_ctfp)));
+			}
+		} else {
+			warn("failed to load CTF data for %s - parent %s not "
+			    "loaded\n", km->km_name,
+			    ctf_parent_name(km->km_ctfp));
+		}
+	}
+
+	return (km->km_ctfp);
+}
+
+ctf_file_t *
+kmt_addr_to_ctf(mdb_tgt_t *t, uintptr_t addr)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km;
+
+	for (km = mdb_list_next(&kmt->kmt_modlist); km != NULL;
+	    km = mdb_list_next(km)) {
+		if (addr - km->km_text_va < km->km_text_size ||
+		    addr - km->km_data_va < km->km_data_size ||
+		    addr - km->km_bss_va < km->km_bss_size)
+			return (kmt_load_ctfdata(t, km));
+	}
+
+	(void) set_errno(EMDB_NOMAP);
+	return (NULL);
+}
+
+ctf_file_t *
+kmt_name_to_ctf(mdb_tgt_t *t, const char *name)
+{
+	kmt_data_t *kt = t->t_data;
+	kmt_module_t *km;
+
+	if (name == MDB_TGT_OBJ_EXEC)
+		name = KMT_CTFPARENT; /* base CTF data is kept in genunix */
+	else if (name == MDB_TGT_OBJ_RTLD)
+		name = KMT_RTLD_NAME; /* replace with krtld */
+
+	for (km = mdb_list_next(&kt->kmt_modlist); km != NULL;
+	    km = mdb_list_next(km)) {
+		if (strcmp(name, km->km_name) == 0)
+			return (kmt_load_ctfdata(t, km));
+	}
+
+	(void) set_errno(EMDB_NOOBJ);
+	return (NULL);
+}
+
+/*ARGSUSED*/
+static int
+kmt_status(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
+{
+	int state;
+
+	bzero(tsp, sizeof (mdb_tgt_status_t));
+
+	switch ((state = kmdb_dpi_get_state(NULL))) {
+	case DPI_STATE_INIT:
+		tsp->st_state = MDB_TGT_RUNNING;
+		tsp->st_pc = 0;
+		break;
+
+	case DPI_STATE_STOPPED:
+		tsp->st_state = MDB_TGT_STOPPED;
+
+		(void) kmdb_dpi_get_register("pc", &tsp->st_pc);
+		break;
+
+	case DPI_STATE_FAULTED:
+		tsp->st_state = MDB_TGT_STOPPED;
+
+		(void) kmdb_dpi_get_register("pc", &tsp->st_pc);
+
+		tsp->st_flags |= MDB_TGT_ISTOP;
+		break;
+
+	case DPI_STATE_LOST:
+		tsp->st_state = MDB_TGT_LOST;
+
+		(void) kmdb_dpi_get_register("pc", &tsp->st_pc);
+		break;
+	}
+
+	mdb_dprintf(MDB_DBG_KMOD, "kmt_status, dpi: %d tsp: %d, pc = %p %A\n",
+	    state, tsp->st_state, (void *)tsp->st_pc, tsp->st_pc);
+
+	return (0);
+}
+
+/*
+ * Invoked when kmt_defbp_enter_debugger is called, this routine activates and
+ * deactivates deferred breakpoints in response to module load and unload
+ * events.
+ */
+/*ARGSUSED*/
+static void
+kmt_defbp_event(mdb_tgt_t *t, int vid, void *private)
+{
+	if (kmt_defbp_modchg_isload) {
+		if (!mdb_tgt_sespec_activate_all(t) &&
+		    (mdb.m_flags & MDB_FL_BPTNOSYMSTOP)) {
+			/*
+			 * We weren't able to activate the breakpoints.
+			 * If so requested, we'll return without calling
+			 * continue, thus throwing the user into the debugger.
+			 */
+			return;
+		}
+
+	} else {
+		mdb_sespec_t *sep, *nsep;
+		const mdb_map_t *map, *bpmap;
+		mdb_map_t modmap;
+
+		if ((map = kmt_addr_to_map(t,
+		    (uintptr_t)kmt_defbp_modchg_modctl->mod_text)) == NULL) {
+			warn("module unload notification for unknown module %s",
+			    kmt_defbp_modchg_modctl->mod_modname);
+			return; /* drop into the debugger */
+		}
+
+		bcopy(map, &modmap, sizeof (mdb_map_t));
+
+		for (sep = mdb_list_next(&t->t_active); sep; sep = nsep) {
+			nsep = mdb_list_next(sep);
+
+			if (sep->se_ops == &kmt_brkpt_ops) {
+				kmt_brkpt_t *kb = sep->se_data;
+
+				if ((bpmap = kmt_addr_to_map(t,
+				    kb->kb_addr)) == NULL ||
+				    (bpmap->map_base == modmap.map_base &&
+				    bpmap->map_size == modmap.map_size)) {
+					mdb_tgt_sespec_idle_one(t, sep,
+					    EMDB_NOMAP);
+				}
+			}
+		}
+	}
+
+	(void) mdb_tgt_continue(t, NULL);
+}
+
+static void
+kmt_defbp_enter_debugger(void)
+{
+	/*
+	 * The debugger places a breakpoint here.  We can't have a simple
+	 * nop function here, because GCC knows much more than we do, and
+	 * will optimize away the call to this function.
+	 */
+	(void) get_fp();
+}
+
+/*
+ * This routine is called while the kernel is running.  It attempts to determine
+ * whether any deferred breakpoints exist for the module being changed (loaded
+ * or unloaded).  If any such breakpoints exist, the debugger will be entered to
+ * process them.
+ */
+static void
+kmt_defbp_modchg(struct modctl *mctl, int isload)
+{
+	kmt_defbp_t *dbp;
+
+	kmt_defbp_lock = 1;
+
+	for (dbp = mdb_list_next(&kmt_defbp_list); dbp;
+	    dbp = mdb_list_next(dbp)) {
+		if (!dbp->dbp_ref)
+			continue;
+
+		if (strcmp(mctl->mod_modname, dbp->dbp_objname) == 0) {
+			/*
+			 * Activate the breakpoint
+			 */
+			kmt_defbp_modchg_isload = isload;
+			kmt_defbp_modchg_modctl = mctl;
+
+			kmt_defbp_enter_debugger();
+			break;
+		}
+	}
+
+	kmt_defbp_lock = 0;
+}
+
+/*ARGSUSED*/
+static int
+kmt_continue(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
+{
+	int n;
+
+	kmdb_dpi_resume();
+
+	/*
+	 * The order of the following two calls is important.  If there are
+	 * load acks on the work queue, we'll initialize the dmods they
+	 * represent.  This will involve a call to _mdb_init, which may very
+	 * well result in a symbol lookup.  If we haven't resynced our view
+	 * of symbols with the current state of the world, this lookup could
+	 * end very badly.  We therefore make sure to sync before processing
+	 * the work queue.
+	 */
+	kmt_sync(t);
+	kmdb_dpi_process_work_queue();
+
+	if (kmdb_kdi_get_unload_request())
+		t->t_flags |= MDB_TGT_F_UNLOAD;
+
+	(void) mdb_tgt_status(t, &t->t_status);
+
+	if ((n = kmt_dmod_status(NULL, KMDB_MC_STATE_LOADING) +
+	    kmt_dmod_status(NULL, KMDB_MC_STATE_UNLOADING)) != 0) {
+		mdb_warn("%d dmod load%c/unload%c pending\n", n,
+		    "s"[n == 1], "s"[n == 1]);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+kmt_step(mdb_tgt_t *t, mdb_tgt_status_t *tsp)
+{
+	int rc;
+
+	if ((rc = kmdb_dpi_step()) == 0)
+		(void) mdb_tgt_status(t, &t->t_status);
+
+	return (rc);
+}
+
+static int
+kmt_defbp_activate(mdb_tgt_t *t)
+{
+	kmdb_dpi_modchg_register(kmt_defbp_modchg);
+
+	/*
+	 * The routines that add and arm breakpoints will check for the proper
+	 * DTrace state, but they'll just put this breakpoint on the idle list
+	 * if DTrace is active.  It'll correctly move to the active list when
+	 * DTrace deactivates, but that's insufficient for our purposes -- we
+	 * need to do extra processing at that point.  We won't get to do said
+	 * processing with with a normal idle->active transition, so we just
+	 * won't add it add it until we're sure that it'll stick.
+	 */
+
+	if (kmdb_kdi_dtrace_get_state() == KDI_DTSTATE_DTRACE_ACTIVE)
+		return (set_errno(EMDB_DTACTIVE));
+
+	kmt_defbp_bpspec = mdb_tgt_add_vbrkpt(t,
+	    (uintptr_t)kmt_defbp_enter_debugger,
+	    MDB_TGT_SPEC_HIDDEN, kmt_defbp_event, NULL);
+
+	return (0);
+}
+
+static void
+kmt_defbp_deactivate(mdb_tgt_t *t)
+{
+	kmdb_dpi_modchg_cancel();
+
+	if (kmt_defbp_bpspec != 0) {
+		if (t != NULL)
+			(void) mdb_tgt_vespec_delete(t, kmt_defbp_bpspec);
+
+		kmt_defbp_bpspec = 0;
+	}
+}
+
+static kmt_defbp_t *
+kmt_defbp_create(mdb_tgt_t *t, const char *objname, const char *symname)
+{
+	kmt_defbp_t *dbp = mdb_alloc(sizeof (kmt_defbp_t), UM_SLEEP);
+
+	mdb_dprintf(MDB_DBG_KMOD, "defbp_create %s`%s\n", objname, symname);
+
+	dbp->dbp_objname = strdup(objname);
+	dbp->dbp_symname = strdup(symname);
+	dbp->dbp_ref = 1;
+
+	kmt_defbp_num++;
+
+	if (kmt_defbp_num == 1 || kmt_defbp_bpspec == 0) {
+		if (kmt_defbp_activate(t) < 0)
+			warn("failed to activate deferred breakpoints");
+	}
+
+	mdb_list_append(&kmt_defbp_list, dbp);
+
+	return (dbp);
+}
+
+static void
+kmt_defbp_destroy(kmt_defbp_t *dbp)
+{
+	mdb_dprintf(MDB_DBG_KMOD, "defbp_destroy %s`%s\n", dbp->dbp_objname,
+	    dbp->dbp_symname);
+
+	mdb_list_delete(&kmt_defbp_list, dbp);
+
+	strfree(dbp->dbp_objname);
+	strfree(dbp->dbp_symname);
+	mdb_free(dbp, sizeof (kmt_defbp_t));
+}
+
+static void
+kmt_defbp_prune_common(int all)
+{
+	kmt_defbp_t *dbp, *ndbp;
+
+	/* We can't remove items from the list while the driver is using it. */
+	if (kmt_defbp_lock)
+		return;
+
+	for (dbp = mdb_list_next(&kmt_defbp_list); dbp != NULL; dbp = ndbp) {
+		ndbp = mdb_list_next(dbp);
+
+		if (!all && dbp->dbp_ref)
+			continue;
+
+		kmt_defbp_destroy(dbp);
+	}
+}
+
+static void
+kmt_defbp_prune(void)
+{
+	kmt_defbp_prune_common(0);
+}
+
+static void
+kmt_defbp_destroy_all(void)
+{
+	kmt_defbp_prune_common(1);
+}
+
+static void
+kmt_defbp_delete(mdb_tgt_t *t, kmt_defbp_t *dbp)
+{
+	dbp->dbp_ref = 0;
+
+	ASSERT(kmt_defbp_num > 0);
+	kmt_defbp_num--;
+
+	if (kmt_defbp_num == 0)
+		kmt_defbp_deactivate(t);
+
+	kmt_defbp_prune();
+}
+
+static int
+kmt_brkpt_ctor(mdb_tgt_t *t, mdb_sespec_t *sep, void *args)
+{
+	mdb_tgt_status_t tsp;
+	kmt_bparg_t *ka = args;
+	kmt_brkpt_t *kb;
+	GElf_Sym s;
+	mdb_instr_t instr;
+
+	(void) mdb_tgt_status(t, &tsp);
+	if (tsp.st_state != MDB_TGT_RUNNING && tsp.st_state != MDB_TGT_STOPPED)
+		return (set_errno(EMDB_NOPROC));
+
+	if (ka->ka_symbol != NULL) {
+		if (mdb_tgt_lookup_by_scope(t, ka->ka_symbol, &s, NULL) == -1) {
+			if (errno != EMDB_NOOBJ && !(errno == EMDB_NOSYM &&
+			    !(mdb.m_flags & MDB_FL_BPTNOSYMSTOP))) {
+				warn("breakpoint %s activation failed",
+				    ka->ka_symbol);
+			}
+			return (-1); /* errno is set for us */
+		}
+
+		ka->ka_addr = (uintptr_t)s.st_value;
+	}
+
+#ifdef __sparc
+	if (ka->ka_addr & 3)
+		return (set_errno(EMDB_BPALIGN));
+#endif
+
+	if (mdb_vread(&instr, sizeof (instr), ka->ka_addr) != sizeof (instr))
+		return (-1); /* errno is set for us */
+
+	if (kmdb_kdi_dtrace_get_state() == KDI_DTSTATE_DTRACE_ACTIVE)
+		warn("breakpoint will not arm until DTrace is inactive\n");
+
+	kb = mdb_zalloc(sizeof (kmt_brkpt_t), UM_SLEEP);
+	kb->kb_addr = ka->ka_addr;
+	sep->se_data = kb;
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+kmt_brkpt_dtor(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	mdb_free(sep->se_data, sizeof (kmt_brkpt_t));
+}
+
+/*ARGSUSED*/
+static char *
+kmt_brkpt_info(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_vespec_t *vep,
+    mdb_tgt_spec_desc_t *sp, char *buf, size_t nbytes)
+{
+	uintptr_t addr = NULL;
+
+	if (vep != NULL) {
+		kmt_bparg_t *ka = vep->ve_args;
+
+		if (ka->ka_symbol != NULL) {
+			(void) mdb_iob_snprintf(buf, nbytes, "stop at %s",
+			    ka->ka_symbol);
+		} else {
+			(void) mdb_iob_snprintf(buf, nbytes, "stop at %a",
+			    ka->ka_addr);
+			addr = ka->ka_addr;
+		}
+
+	} else {
+		addr = ((kmt_brkpt_t *)sep->se_data)->kb_addr;
+		(void) mdb_iob_snprintf(buf, nbytes, "stop at %a", addr);
+	}
+
+	sp->spec_base = addr;
+	sp->spec_size = sizeof (mdb_instr_t);
+
+	return (buf);
+}
+
+static int
+kmt_brkpt_secmp(mdb_tgt_t *t, mdb_sespec_t *sep, void *args)
+{
+	kmt_brkpt_t *kb = sep->se_data;
+	kmt_bparg_t *ka = args;
+	GElf_Sym sym;
+
+	if (ka->ka_symbol != NULL) {
+		return (mdb_tgt_lookup_by_scope(t, ka->ka_symbol,
+		    &sym, NULL) == 0 && sym.st_value == kb->kb_addr);
+	}
+
+	return (ka->ka_addr == kb->kb_addr);
+}
+
+/*ARGSUSED*/
+static int
+kmt_brkpt_vecmp(mdb_tgt_t *t, mdb_vespec_t *vep, void *args)
+{
+	kmt_bparg_t *ka1 = vep->ve_args;
+	kmt_bparg_t *ka2 = args;
+
+	if (ka1->ka_symbol != NULL && ka2->ka_symbol != NULL)
+		return (strcmp(ka1->ka_symbol, ka2->ka_symbol) == 0);
+
+	if (ka1->ka_symbol == NULL && ka2->ka_symbol == NULL)
+		return (ka1->ka_addr == ka2->ka_addr);
+
+	return (0); /* fail if one is symbolic, other is an explicit address */
+}
+
+static int
+kmt_brkpt_arm(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_brkpt_t *kb = sep->se_data;
+	int rv;
+
+	if (kmdb_kdi_dtrace_get_state() == KDI_DTSTATE_DTRACE_ACTIVE)
+		return (set_errno(EMDB_DTACTIVE));
+
+	if ((rv = kmdb_dpi_brkpt_arm(kb->kb_addr, &kb->kb_oinstr)) != 0)
+		return (rv);
+
+	if (kmt->kmt_narmedbpts++ == 0)
+		(void) kmdb_kdi_dtrace_set(KDI_DTSET_KMDB_BPT_ACTIVATE);
+
+	return (0);
+}
+
+static int
+kmt_brkpt_disarm(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_brkpt_t *kb = sep->se_data;
+	int rv;
+
+	ASSERT(kmdb_kdi_dtrace_get_state() == KDI_DTSTATE_KMDB_BPT_ACTIVE);
+
+	if ((rv = kmdb_dpi_brkpt_disarm(kb->kb_addr, kb->kb_oinstr)) != 0)
+		return (rv);
+
+	if (--kmt->kmt_narmedbpts == 0)
+		(void) kmdb_kdi_dtrace_set(KDI_DTSET_KMDB_BPT_DEACTIVATE);
+
+	return (0);
+}
+
+/*
+ * Determine whether the specified sespec is an armed watchpoint that overlaps
+ * with the given breakpoint and has the given flags set.  We use this to find
+ * conflicts with breakpoints, below.
+ */
+static int
+kmt_wp_overlap(mdb_sespec_t *sep, kmt_brkpt_t *kb, int flags)
+{
+	const kmdb_wapt_t *wp = sep->se_data;
+
+	return (sep->se_state == MDB_TGT_SPEC_ARMED &&
+	    sep->se_ops == &kmt_wapt_ops && (wp->wp_wflags & flags) &&
+	    kb->kb_addr - wp->wp_addr < wp->wp_size);
+}
+
+/*
+ * We step over breakpoints using our single-stepper.  If a conflicting
+ * watchpoint is present, we must temporarily remove it before stepping over the
+ * breakpoint so we don't immediately re-trigger the watchpoint.  We know the
+ * watchpoint has already triggered on our trap instruction as part of fetching
+ * it.  Before we return, we must re-install any disabled watchpoints.
+ */
+static int
+kmt_brkpt_cont(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_tgt_status_t *tsp)
+{
+	kmt_brkpt_t *kb = sep->se_data;
+	int status = -1;
+	int error;
+
+	for (sep = mdb_list_next(&t->t_active); sep; sep = mdb_list_next(sep)) {
+		if (kmt_wp_overlap(sep, kb, MDB_TGT_WA_X))
+			(void) kmdb_dpi_wapt_disarm(sep->se_data);
+	}
+
+	if (kmdb_dpi_brkpt_disarm(kb->kb_addr, kb->kb_oinstr) == 0 &&
+	    kmt_step(t, tsp) == 0)
+		status = kmt_status(t, tsp);
+
+	error = errno; /* save errno from disarm, step, or status */
+
+	for (sep = mdb_list_next(&t->t_active); sep; sep = mdb_list_next(sep)) {
+		if (kmt_wp_overlap(sep, kb, MDB_TGT_WA_X))
+			kmdb_dpi_wapt_arm(sep->se_data);
+	}
+
+	(void) set_errno(error);
+	return (status);
+}
+
+/*ARGSUSED*/
+static int
+kmt_brkpt_match(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_tgt_status_t *tsp)
+{
+	kmt_brkpt_t *kb = sep->se_data;
+	int state, why;
+	kreg_t pc;
+
+	state = kmdb_dpi_get_state(&why);
+	(void) kmdb_dpi_get_register("pc", &pc);
+
+	return (state == DPI_STATE_FAULTED && why == DPI_STATE_WHY_BKPT &&
+	    pc == kb->kb_addr);
+}
+
+static const mdb_se_ops_t kmt_brkpt_ops = {
+	kmt_brkpt_ctor,		/* se_ctor */
+	kmt_brkpt_dtor,		/* se_dtor */
+	kmt_brkpt_info,		/* se_info */
+	kmt_brkpt_secmp,	/* se_secmp */
+	kmt_brkpt_vecmp,	/* se_vecmp */
+	kmt_brkpt_arm,		/* se_arm */
+	kmt_brkpt_disarm,	/* se_disarm */
+	kmt_brkpt_cont,		/* se_cont */
+	kmt_brkpt_match		/* se_match */
+};
+
+static int
+kmt_wapt_ctor(mdb_tgt_t *t, mdb_sespec_t *sep, void *args)
+{
+	mdb_tgt_status_t tsp;
+	kmdb_wapt_t *vwp = args;
+	kmdb_wapt_t *swp;
+
+	(void) mdb_tgt_status(t, &tsp);
+	if (tsp.st_state != MDB_TGT_RUNNING && tsp.st_state != MDB_TGT_STOPPED)
+		return (set_errno(EMDB_NOPROC));
+
+	swp = mdb_alloc(sizeof (kmdb_wapt_t), UM_SLEEP);
+	bcopy(vwp, swp, sizeof (kmdb_wapt_t));
+
+	if (kmdb_dpi_wapt_reserve(swp) < 0) {
+		mdb_free(swp, sizeof (kmdb_wapt_t));
+		return (-1); /* errno is set for us */
+	}
+
+	sep->se_data = swp;
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static void
+kmt_wapt_dtor(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	kmdb_wapt_t *wp = sep->se_data;
+
+	kmdb_dpi_wapt_release(wp);
+	mdb_free(wp, sizeof (kmdb_wapt_t));
+}
+
+/*ARGSUSED*/
+static char *
+kmt_wapt_info(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_vespec_t *vep,
+    mdb_tgt_spec_desc_t *sp, char *buf, size_t nbytes)
+{
+	kmdb_wapt_t *wp = vep != NULL ? vep->ve_args : sep->se_data;
+	const char *fmt;
+	char desc[24];
+
+	ASSERT(wp->wp_wflags != 0);
+	desc[0] = '\0';
+
+	switch (wp->wp_wflags) {
+	case MDB_TGT_WA_R:
+		(void) strcat(desc, "/read");
+		break;
+	case MDB_TGT_WA_W:
+		(void) strcat(desc, "/write");
+		break;
+	case MDB_TGT_WA_X:
+		(void) strcat(desc, "/exec");
+		break;
+	default:
+		if (wp->wp_wflags & MDB_TGT_WA_R)
+			(void) strcat(desc, "/r");
+		if (wp->wp_wflags & MDB_TGT_WA_W)
+			(void) strcat(desc, "/w");
+		if (wp->wp_wflags & MDB_TGT_WA_X)
+			(void) strcat(desc, "/x");
+	}
+
+	switch (wp->wp_type) {
+	case DPI_WAPT_TYPE_PHYS:
+		fmt = "stop on %s of phys [%p, %p)";
+		break;
+
+	case DPI_WAPT_TYPE_VIRT:
+		fmt = "stop on %s of [%la, %la)";
+		break;
+
+	case DPI_WAPT_TYPE_IO:
+		if (wp->wp_size == 1)
+			fmt = "stop on %s of I/O port %p";
+		else
+			fmt = "stop on %s of I/O port [%p, %p)";
+		break;
+	}
+
+	(void) mdb_iob_snprintf(buf, nbytes, fmt, desc + 1, wp->wp_addr,
+	    wp->wp_addr + wp->wp_size);
+
+	sp->spec_base = wp->wp_addr;
+	sp->spec_size = wp->wp_size;
+
+	return (buf);
+}
+
+/*ARGSUSED*/
+static int
+kmt_wapt_secmp(mdb_tgt_t *t, mdb_sespec_t *sep, void *args)
+{
+	kmdb_wapt_t *wp1 = sep->se_data;
+	kmdb_wapt_t *wp2 = args;
+
+	return (wp1->wp_addr == wp2->wp_addr && wp1->wp_size == wp2->wp_size &&
+	    wp1->wp_wflags == wp2->wp_wflags);
+}
+
+/*ARGSUSED*/
+static int
+kmt_wapt_vecmp(mdb_tgt_t *t, mdb_vespec_t *vep, void *args)
+{
+	kmdb_wapt_t *wp1 = vep->ve_args;
+	kmdb_wapt_t *wp2 = args;
+
+	return (wp1->wp_addr == wp2->wp_addr && wp1->wp_size == wp2->wp_size &&
+	    wp1->wp_wflags == wp2->wp_wflags);
+}
+
+/*ARGSUSED*/
+static int
+kmt_wapt_arm(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	kmdb_dpi_wapt_arm(sep->se_data);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+kmt_wapt_disarm(mdb_tgt_t *t, mdb_sespec_t *sep)
+{
+	kmdb_dpi_wapt_disarm(sep->se_data);
+
+	return (0);
+}
+
+/*
+ * Determine whether the specified sespec is an armed breakpoint at the given
+ * %pc.  We use this to find conflicts with watchpoints below.
+ */
+static int
+kmt_bp_overlap(mdb_sespec_t *sep, uintptr_t pc)
+{
+	kmt_brkpt_t *kb = sep->se_data;
+
+	return (sep->se_state == MDB_TGT_SPEC_ARMED &&
+	    sep->se_ops == &kmt_brkpt_ops && kb->kb_addr == pc);
+}
+
+/*
+ * We step over watchpoints using our single-stepper.  If a conflicting
+ * breakpoint is present, we must temporarily disarm it before stepping over
+ * the watchpoint so we do not immediately re-trigger the breakpoint.  This is
+ * similar to the case handled in kmt_brkpt_cont(), above.
+ */
+static int
+kmt_wapt_cont(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_tgt_status_t *tsp)
+{
+	mdb_sespec_t *bep = NULL;
+	int status = -1;
+	int error, why;
+
+	/*
+	 * If we stopped for anything other than a watchpoint, check to see
+	 * if there's a breakpoint here.
+	 */
+	if (!(kmdb_dpi_get_state(&why) == DPI_STATE_FAULTED &&
+	    (why == DPI_STATE_WHY_V_WAPT || why == DPI_STATE_WHY_P_WAPT))) {
+		kreg_t pc;
+
+		(void) kmdb_dpi_get_register("pc", &pc);
+
+		for (bep = mdb_list_next(&t->t_active); bep != NULL;
+		    bep = mdb_list_next(bep)) {
+			if (kmt_bp_overlap(bep, pc)) {
+				(void) bep->se_ops->se_disarm(t, bep);
+				bep->se_state = MDB_TGT_SPEC_ACTIVE;
+				break;
+			}
+		}
+	}
+
+	kmdb_dpi_wapt_disarm(sep->se_data);
+	if (kmt_step(t, tsp) == 0)
+		status = kmt_status(t, tsp);
+
+	error = errno; /* save errno from step or status */
+
+	if (bep != NULL)
+		mdb_tgt_sespec_arm_one(t, bep);
+
+	(void) set_errno(error);
+	return (status);
+}
+
+/*ARGSUSED*/
+static int
+kmt_wapt_match(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_tgt_status_t *tsp)
+{
+	return (kmdb_dpi_wapt_match(sep->se_data));
+}
+
+static const mdb_se_ops_t kmt_wapt_ops = {
+	kmt_wapt_ctor,		/* se_ctor */
+	kmt_wapt_dtor,		/* se_dtor */
+	kmt_wapt_info,		/* se_info */
+	kmt_wapt_secmp,		/* se_secmp */
+	kmt_wapt_vecmp,		/* se_vecmp */
+	kmt_wapt_arm,		/* se_arm */
+	kmt_wapt_disarm,	/* se_disarm */
+	kmt_wapt_cont,		/* se_cont */
+	kmt_wapt_match		/* se_match */
+};
+
+/*ARGSUSED*/
+static int
+kmt_trap_ctor(mdb_tgt_t *t, mdb_sespec_t *sep, void *args)
+{
+	sep->se_data = args; /* trap number */
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static char *
+kmt_trap_info(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_vespec_t *vep,
+    mdb_tgt_spec_desc_t *sp, char *buf, size_t nbytes)
+{
+	const char *name;
+	int trapnum;
+
+	if (vep != NULL)
+		trapnum = (intptr_t)vep->ve_args;
+	else
+		trapnum = (intptr_t)sep->se_data;
+
+	if (trapnum == KMT_TRAP_ALL)
+		name = "any trap";
+	else if (trapnum == KMT_TRAP_NOTENUM)
+		name = "miscellaneous trap";
+	else
+		name = kmt_trapname(trapnum);
+
+	(void) mdb_iob_snprintf(buf, nbytes, "single-step stop on %s", name);
+
+	return (buf);
+}
+
+/*ARGSUSED2*/
+static int
+kmt_trap_match(mdb_tgt_t *t, mdb_sespec_t *sep, mdb_tgt_status_t *tsp)
+{
+	int spectt = (intptr_t)sep->se_data;
+	kmt_data_t *kmt = t->t_data;
+	kreg_t tt;
+
+	(void) kmdb_dpi_get_register("tt", &tt);
+
+	switch (spectt) {
+	case KMT_TRAP_ALL:
+		return (1);
+	case KMT_TRAP_NOTENUM:
+		return (tt > kmt->kmt_trapmax ||
+		    !BT_TEST(kmt->kmt_trapmap, tt));
+	default:
+		return (tt == spectt);
+	}
+}
+
+static const mdb_se_ops_t kmt_trap_ops = {
+	kmt_trap_ctor,		/* se_ctor */
+	no_se_dtor,		/* se_dtor */
+	kmt_trap_info,		/* se_info */
+	no_se_secmp,		/* se_secmp */
+	no_se_vecmp,		/* se_vecmp */
+	no_se_arm,		/* se_arm */
+	no_se_disarm,		/* se_disarm */
+	no_se_cont,		/* se_cont */
+	kmt_trap_match		/* se_match */
+};
+
+static void
+kmt_bparg_dtor(mdb_vespec_t *vep)
+{
+	kmt_bparg_t *ka = vep->ve_args;
+
+	if (ka->ka_symbol != NULL)
+		strfree(ka->ka_symbol);
+
+	if (ka->ka_defbp != NULL)
+		kmt_defbp_delete(mdb.m_target, ka->ka_defbp);
+
+	mdb_free(ka, sizeof (kmt_bparg_t));
+}
+
+static int
+kmt_add_vbrkpt(mdb_tgt_t *t, uintptr_t addr,
+    int spec_flags, mdb_tgt_se_f *func, void *data)
+{
+	kmt_bparg_t *ka = mdb_alloc(sizeof (kmt_bparg_t), UM_SLEEP);
+
+	ka->ka_addr = addr;
+	ka->ka_symbol = NULL;
+	ka->ka_defbp = NULL;
+
+	return (mdb_tgt_vespec_insert(t, &kmt_brkpt_ops, spec_flags,
+	    func, data, ka, kmt_bparg_dtor));
+}
+
+static int
+kmt_add_sbrkpt(mdb_tgt_t *t, const char *fullname,
+    int spec_flags, mdb_tgt_se_f *func, void *data)
+{
+	kmt_bparg_t *ka;
+	kmt_defbp_t *dbp;
+	GElf_Sym sym;
+	char *tick, *objname, *symname;
+	int serrno;
+
+	if ((tick = strchr(fullname, '`')) == fullname) {
+		(void) set_errno(EMDB_NOOBJ);
+		return (0);
+	}
+
+	/*
+	 * Deferred breakpoints are always scoped.  If we didn't find a tick,
+	 * there's no scope.  We'll create a vbrkpt, but only if we can turn the
+	 * provided string into an address.
+	 */
+	if (tick == NULL) {
+		uintptr_t addr;
+
+		if (strisbasenum(fullname)) {
+			addr = mdb_strtoull(fullname); /* a bare address */
+		} else if (mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EVERY,
+		    fullname, &sym, NULL) < 0) {
+			(void) set_errno(EMDB_NOSYM);
+			return (0);
+		} else {
+			addr = (uintptr_t)sym.st_value; /* unscoped sym name */
+		}
+
+		return (kmt_add_vbrkpt(t, addr, spec_flags, func, data));
+	}
+
+	if (*(tick + 1) == '\0') {
+		(void) set_errno(EMDB_NOSYM);
+		return (0);
+	}
+
+	objname = strndup(fullname, tick - fullname);
+	symname = tick + 1;
+
+	if (mdb_tgt_lookup_by_name(t, objname, symname, NULL, NULL) < 0 &&
+	    errno != EMDB_NOOBJ) {
+		serrno = errno;
+		strfree(objname);
+
+		(void) set_errno(serrno);
+		return (0); /* errno is set for us */
+	}
+
+	dbp = kmt_defbp_create(t, objname, symname);
+	strfree(objname);
+
+	ka = mdb_alloc(sizeof (kmt_bparg_t), UM_SLEEP);
+	ka->ka_symbol = strdup(fullname);
+	ka->ka_addr = NULL;
+	ka->ka_defbp = dbp;
+
+	return (mdb_tgt_vespec_insert(t, &kmt_brkpt_ops, spec_flags,
+		    func, data, ka, kmt_bparg_dtor));
+}
+
+static int
+kmt_wparg_overlap(const kmdb_wapt_t *wp1, const kmdb_wapt_t *wp2)
+{
+	/* Assume the watchpoint spaces don't overlap */
+	if (wp1->wp_type != wp2->wp_type)
+		return (0);
+
+	if (wp2->wp_addr + wp2->wp_size <= wp1->wp_addr)
+		return (0); /* no range overlap */
+
+	if (wp1->wp_addr + wp1->wp_size <= wp2->wp_addr)
+		return (0); /* no range overlap */
+
+	return (wp1->wp_addr != wp2->wp_addr || wp1->wp_size != wp2->wp_size ||
+	    wp1->wp_wflags != wp2->wp_wflags);
+}
+
+static void
+kmt_wparg_dtor(mdb_vespec_t *vep)
+{
+	mdb_free(vep->ve_args, sizeof (kmdb_wapt_t));
+}
+
+static int
+kmt_add_wapt_common(mdb_tgt_t *t, uintptr_t addr, size_t len, uint_t wflags,
+    int spec_flags, mdb_tgt_se_f *func, void *data, int type)
+{
+	kmdb_wapt_t *wp = mdb_alloc(sizeof (kmdb_wapt_t), UM_SLEEP);
+	mdb_sespec_t *sep;
+
+	wp->wp_addr = addr;
+	wp->wp_size = len;
+	wp->wp_type = type;
+	wp->wp_wflags = wflags;
+
+	if (kmdb_dpi_wapt_validate(wp) < 0)
+		return (0); /* errno is set for us */
+
+	for (sep = mdb_list_next(&t->t_active); sep; sep = mdb_list_next(sep)) {
+		if (sep->se_ops == &kmt_wapt_ops &&
+		    mdb_list_next(&sep->se_velist) != NULL &&
+		    kmt_wparg_overlap(wp, sep->se_data))
+			goto wapt_dup;
+	}
+
+	for (sep = mdb_list_next(&t->t_idle); sep; sep = mdb_list_next(sep)) {
+		if (sep->se_ops == &kmt_wapt_ops && kmt_wparg_overlap(wp,
+		    ((mdb_vespec_t *)mdb_list_next(&sep->se_velist))->ve_args))
+			goto wapt_dup;
+	}
+
+	return (mdb_tgt_vespec_insert(t, &kmt_wapt_ops, spec_flags,
+	    func, data, wp, kmt_wparg_dtor));
+
+wapt_dup:
+	mdb_free(wp, sizeof (kmdb_wapt_t));
+	(void) set_errno(EMDB_WPDUP);
+	return (0);
+}
+
+static int
+kmt_add_pwapt(mdb_tgt_t *t, physaddr_t addr, size_t len, uint_t wflags,
+    int spec_flags, mdb_tgt_se_f *func, void *data)
+{
+	return (kmt_add_wapt_common(t, (uintptr_t)addr, len, wflags, spec_flags,
+	    func, data, DPI_WAPT_TYPE_PHYS));
+}
+
+static int
+kmt_add_vwapt(mdb_tgt_t *t, uintptr_t addr, size_t len, uint_t wflags,
+    int spec_flags, mdb_tgt_se_f *func, void *data)
+{
+	return (kmt_add_wapt_common(t, addr, len, wflags, spec_flags, func,
+	    data, DPI_WAPT_TYPE_VIRT));
+}
+
+static int
+kmt_add_iowapt(mdb_tgt_t *t, uintptr_t addr, size_t len, uint_t wflags,
+    int spec_flags, mdb_tgt_se_f *func, void *data)
+{
+	return (kmt_add_wapt_common(t, addr, len, wflags, spec_flags, func,
+	    data, DPI_WAPT_TYPE_IO));
+}
+
+static int
+kmt_add_trap(mdb_tgt_t *t, int trapnum, int spec_flags, mdb_tgt_se_f *func,
+    void *data)
+{
+	kmt_data_t *kmt = t->t_data;
+
+	if (trapnum != KMT_TRAP_ALL && trapnum != KMT_TRAP_NOTENUM) {
+		if (trapnum < 0 || trapnum > kmt->kmt_trapmax) {
+			(void) set_errno(EMDB_BADFLTNUM);
+			return (0);
+		}
+
+		BT_SET(kmt->kmt_trapmap, trapnum);
+	}
+
+	return (mdb_tgt_vespec_insert(t, &kmt_trap_ops, spec_flags, func, data,
+	    (void *)(uintptr_t)trapnum, no_ve_dtor));
+}
+
+/*ARGSUSED*/
+static uintmax_t
+kmt_cpuid_disc_get(const mdb_var_t *v)
+{
+	return (kmdb_dpi_get_master_cpuid());
+}
+
+static const mdb_nv_disc_t kmt_cpuid_disc = {
+	NULL,
+	kmt_cpuid_disc_get
+};
+
+/*
+ * This routine executes while the kernel is running.
+ */
+void
+kmt_activate(mdb_tgt_t *t)
+{
+	kmt_data_t *kmt = t->t_data;
+
+	mdb_prop_postmortem = FALSE;
+	mdb_prop_kernel = TRUE;
+
+	(void) mdb_tgt_register_dcmds(t, &kmt_dcmds[0], MDB_MOD_FORCE);
+	mdb_tgt_register_regvars(t, kmt->kmt_rds, &kmt_reg_disc, 0);
+}
+
+static void
+kmt_destroy(mdb_tgt_t *t)
+{
+	kmt_data_t *kmt = t->t_data;
+	kmt_module_t *km, *pkm;
+
+	mdb_nv_destroy(&kmt->kmt_modules);
+	for (km = mdb_list_prev(&kmt->kmt_modlist); km != NULL; km = pkm) {
+		pkm = mdb_list_prev(km);
+		mdb_free(km, sizeof (kmt_module_t));
+	}
+
+	if (!kmt_defbp_lock)
+		kmt_defbp_destroy_all();
+
+	if (kmt->kmt_trapmap != NULL)
+		mdb_free(kmt->kmt_trapmap, BT_SIZEOFMAP(kmt->kmt_trapmax));
+
+	if (kmt->kmt_cpu != NULL)
+		kmt_cpu_destroy(kmt->kmt_cpu);
+
+	if (kmt != NULL)
+		mdb_free(kmt, sizeof (kmt_data_t));
+}
+
+static const mdb_tgt_ops_t kmt_ops = {
+	kmt_setflags,				/* t_setflags */
+	(int (*)()) mdb_tgt_notsup,		/* t_setcontext */
+	kmt_activate,				/* t_activate */
+	(void (*)()) mdb_tgt_nop,		/* t_deactivate */
+	kmt_periodic,				/* t_periodic */
+	kmt_destroy,				/* t_destroy */
+	kmt_name,				/* t_name */
+	(const char *(*)()) mdb_conf_isa,	/* t_isa */
+	kmt_platform,				/* t_platform */
+	kmt_uname,				/* t_uname */
+	kmt_dmodel,				/* t_dmodel */
+	(ssize_t (*)()) mdb_tgt_notsup,		/* t_aread */
+	(ssize_t (*)()) mdb_tgt_notsup,		/* t_awrite */
+	kmt_read,				/* t_vread */
+	kmt_write,				/* t_vwrite */
+	kmt_pread,				/* t_pread */
+	kmt_pwrite,				/* t_pwrite */
+	kmt_read,				/* t_fread */
+	kmt_write,				/* t_fwrite */
+	kmt_ioread,				/* t_ioread */
+	kmt_iowrite,				/* t_iowrite */
+	kmt_vtop,				/* t_vtop */
+	kmt_lookup_by_name,			/* t_lookup_by_name */
+	kmt_lookup_by_addr,			/* t_lookup_by_addr */
+	kmt_symbol_iter,			/* t_symbol_iter */
+	kmt_mapping_iter,			/* t_mapping_iter */
+	kmt_object_iter,			/* t_object_iter */
+	kmt_addr_to_map,			/* t_addr_to_map */
+	kmt_name_to_map,			/* t_name_to_map */
+	kmt_addr_to_ctf,			/* t_addr_to_ctf */
+	kmt_name_to_ctf,			/* t_name_to_ctf */
+	kmt_status,				/* t_status */
+	(int (*)()) mdb_tgt_notsup,		/* t_run */
+	kmt_step,				/* t_step */
+	kmt_step_out,				/* t_step_out */
+	kmt_step_branch,			/* t_step_branch */
+	kmt_next,				/* t_next */
+	kmt_continue,				/* t_cont */
+	(int (*)()) mdb_tgt_notsup,		/* t_signal */
+	kmt_add_vbrkpt,				/* t_add_vbrkpt */
+	kmt_add_sbrkpt,				/* t_add_sbrkpt */
+	kmt_add_pwapt,				/* t_add_pwapt */
+	kmt_add_vwapt,				/* t_add_vwapt */
+	kmt_add_iowapt,				/* t_add_iowapt */
+	(int (*)()) mdb_tgt_null,		/* t_add_sysenter */
+	(int (*)()) mdb_tgt_null,		/* t_add_sysexit */
+	(int (*)()) mdb_tgt_null,		/* t_add_signal */
+	kmt_add_trap,				/* t_add_fault */
+	kmt_getareg,				/* t_getareg */
+	kmt_putareg,				/* t_putareg */
+	(int (*)()) mdb_tgt_nop			/* XXX t_stack_iter */
+};
+
+/*
+ * Called immediately upon resumption of the system after a step or continue.
+ * Allows us to synchronize kmt's view of the world with reality.
+ */
+/*ARGSUSED*/
+static void
+kmt_sync(mdb_tgt_t *t)
+{
+	kmt_data_t *kmt = t->t_data;
+	int symavail;
+
+	mdb_dprintf(MDB_DBG_KMOD, "synchronizing with kernel\n");
+
+	symavail = kmt->kmt_symavail;
+	kmt->kmt_symavail = FALSE;
+
+	/*
+	 * Resync our view of the world if the modules have changed, or if we
+	 * didn't have any symbols coming into this function.  The latter will
+	 * only happen on startup.
+	 */
+	if (kmdb_kdi_mods_changed() || !symavail)
+		kmt_modlist_update(t);
+
+	/*
+	 * It would be nice if we could run this less frequently, perhaps
+	 * after a dvec-initiated trigger.
+	 */
+	kmdb_module_sync();
+
+	kmt->kmt_symavail = TRUE;
+
+	mdb_dprintf(MDB_DBG_KMOD, "synchronization complete\n");
+
+	kmt_defbp_prune();
+
+	if (kmt_defbp_num > 0 && kmt_defbp_bpspec == 0 &&
+	    kmdb_kdi_dtrace_get_state() != KDI_DTSTATE_DTRACE_ACTIVE) {
+		/*
+		 * Deferred breakpoints were created while DTrace was active,
+		 * and consequently the deferred breakpoint enabling mechanism
+		 * wasn't activated.  Activate it now, and then try to activate
+		 * the deferred breakpoints.  We do this so that we can catch
+		 * the ones which may apply to modules that have been loaded
+		 * while they were waiting for DTrace to deactivate.
+		 */
+		(void) kmt_defbp_activate(t);
+		(void) mdb_tgt_sespec_activate_all(t);
+	}
+
+	if (kmt->kmt_cpu_retry && ((kmt->kmt_cpu = kmt_cpu_create(t)) !=
+	    NULL || errno != EAGAIN))
+		kmt->kmt_cpu_retry = FALSE;
+
+	(void) mdb_tgt_status(t, &t->t_status);
+}
+
+/*
+ * This routine executes while the kernel is running.
+ */
+/*ARGSUSED*/
+int
+kmdb_kvm_create(mdb_tgt_t *t, int argc, const char *argv[])
+{
+	kmt_data_t *kmt;
+
+	if (argc != 0)
+		return (set_errno(EINVAL));
+
+	kmt = mdb_zalloc(sizeof (kmt_data_t), UM_SLEEP);
+	t->t_data = kmt;
+	t->t_ops = &kmt_ops;
+	t->t_flags |= MDB_TGT_F_RDWR;	/* kmdb is always r/w */
+
+	(void) mdb_nv_insert(&mdb.m_nv, "cpuid", &kmt_cpuid_disc, 0,
+	    MDB_NV_PERSIST | MDB_NV_RDONLY);
+
+	(void) mdb_nv_create(&kmt->kmt_modules, UM_SLEEP);
+
+	kmt_init_isadep(t);
+
+	kmt->kmt_symavail = FALSE;
+	kmt->kmt_cpu_retry = TRUE;
+
+	bzero(&kmt_defbp_list, sizeof (mdb_list_t));
+
+	return (0);
+
+create_err:
+	kmt_destroy(t);
+
+	return (-1);
+}
+
+/*
+ * This routine is called once, when kmdb first has control of the world.
+ */
+void
+kmdb_kvm_startup(void)
+{
+	mdb_dprintf(MDB_DBG_KMOD, "kmdb_kvm startup\n");
+
+	kmt_sync(mdb.m_target);
+	(void) mdb_module_load_builtin(KMT_MODULE);
+	kmt_startup_isadep(mdb.m_target);
+
+	/*
+	 * This is here because we need to write the deferred breakpoint
+	 * breakpoint when the debugger starts.  Our normal r/o write routines
+	 * don't work when the kernel is running, so we have to do it during
+	 * startup.
+	 */
+	(void) mdb_tgt_sespec_activate_all(mdb.m_target);
+}
+
+/*
+ * This routine is called after kmdb has loaded its initial set of modules.
+ */
+void
+kmdb_kvm_poststartup(void)
+{
+	mdb_dprintf(MDB_DBG_KMOD, "kmdb_kvm post-startup\n");
+
+	(void) mdb_dis_select(kmt_def_dismode());
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.h b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.h
new file mode 100644
index 0000000..6b0d4dc
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.h
@@ -0,0 +1,54 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_KVM_H
+#define	_KMDB_KVM_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * External, non-constructor interfaces for kmdb_kvm.
+ */
+
+#include <sys/modctl.h>
+
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_ctf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void kmdb_kvm_startup(void);
+extern void kmdb_kvm_poststartup(void);
+extern void kmdb_kvm_defbp_process(uint_t, struct modctl *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_KVM_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_main.c b/usr/src/cmd/mdb/common/kmdb/kmdb_main.c
new file mode 100644
index 0000000..932b132
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_main.c
@@ -0,0 +1,405 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/termios.h>
+#include <sys/param.h>
+#include <sys/salib.h>
+
+#include <alloca.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <libctf.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <kmdb/kmdb_promif.h>
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kmdb_umemglue.h>
+#include <kmdb/kmdb_io.h>
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kmdb_wr.h>
+#include <kmdb/kmdb_start.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_kvm.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_set.h>
+#include <mdb/mdb.h>
+
+#ifdef __sparc
+#define	KMDB_STACK_SIZE	(384 * 1024)
+#else
+#define	KMDB_STACK_SIZE (192 * 1024)
+#endif
+
+caddr_t kmdb_main_stack;
+size_t kmdb_main_stack_size;
+
+#define	KMDB_DEF_IPATH	"internal"
+#if defined(_LP64)
+#define	KMDB_DEF_LPATH	\
+	"%r/platform/%p/kernel/kmdb/%i:" \
+	"%r/platform/%m/kernel/kmdb/%i:" \
+	"%r/kernel/kmdb/%i"
+#else
+#define	KMDB_DEF_LPATH	\
+	"%r/platform/%m/kernel/kmdb:" \
+	"%r/kernel/kmdb"
+#endif
+
+#define	MDB_DEF_PROMPT "[%<_cpuid>]> "
+
+#define	KMDB_DEF_TERM_TYPE	"vt100"
+
+/*
+ * Similar to the panic_* variables in the kernel, we keep some relevant
+ * information stored in a set of global _mdb_abort_* variables; in the
+ * event that the debugger dumps core, these will aid core dump analysis.
+ */
+const char *volatile _mdb_abort_str;	/* reason for failure */
+
+/*
+ * The kernel supplies a space-delimited list of directories
+ * (/platform/sun4u/kernel /kernel /usr/kernel ...) which we must transform into
+ * a debugger-friendly, colon-delimited module search path.  We add the kmdb
+ * module directory to each component and change the delimiter.
+ */
+static char *
+kmdb_modpath2lpath(const char *modpath)
+{
+#ifdef	_LP64
+	static const char suffix[] = "/kmdb/%i:";
+#else
+	static const char suffix[] = "/kmdb:";
+#endif
+	const char *c;
+	char *lpath, *lpend, *nlpath;
+	size_t lpsz, lpres;
+
+	if (strchr(modpath, ':') != NULL) {
+		warn("invalid kernel module path\n");
+		return (NULL);
+	}
+
+	lpres = lpsz = strlen(modpath) + MAXPATHLEN;
+	lpend = lpath = mdb_zalloc(lpsz, UM_SLEEP);
+
+	while (isspace(*modpath))
+		modpath++;
+
+	for (; *modpath != '\0'; modpath = c) {
+		size_t sz;
+
+		for (c = modpath; !isspace(*c) && *c != '\0'; c++)
+			continue;
+
+		sz = (c - modpath) + sizeof (suffix) - 1;
+		if (sz >= lpres)
+			continue;
+
+		(void) strncpy(lpend, modpath, c - modpath);
+		(void) strcpy(lpend + (c - modpath), suffix);
+
+		lpend += sz;
+		lpres -= sz;
+
+		while (isspace(*c))
+			c++;
+	}
+
+	if (lpend != lpath)
+		lpend[-1] = '\0';	/* eat trailing colon */
+
+	nlpath = strdup(lpath);
+	mdb_free(lpath, lpsz);
+	return (nlpath);
+}
+
+/*
+ * called while the kernel is running
+ */
+int
+kmdb_init(const char *execname, kmdb_auxv_t *kav)
+{
+	mdb_io_t *in_io, *out_io, *err_io, *null_io;
+	mdb_tgt_ctor_f *tgt_ctor = kmdb_kvm_create;
+	mdb_tgt_t *tgt;
+	int i;
+
+	/*
+	 * The beginnings of debugger initialization are a bit of a dance, due
+	 * to interlocking dependencies between kmdb_prom_init,
+	 * mdb_umem_startup, and mdb_create.  In particular, allocator
+	 * initialization can't begin until prom_init() is called,
+	 * kmdb_prom_init can't finish until the allocator is ready and
+	 * mdb_create has been called.  We therefore split kmdb_prom_init into
+	 * two pieces, and call thembefore and after umem initialization and
+	 * mdb_create.
+	 */
+	kmdb_prom_init_begin("kmdb", kav);
+	mdb_umem_startup(kav->kav_dseg, kav->kav_dseg_size,
+	    kav->kav_pagesize);
+	mdb_create(execname, "kmdb");
+	kmdb_prom_init_finish(kav);
+
+	mdb.m_dseg = kav->kav_dseg;
+	mdb.m_dsegsz = kav->kav_dseg_size;
+
+	out_io = kmdb_promio_create("stdout");
+	mdb.m_out = mdb_iob_create(out_io, MDB_IOB_WRONLY);
+
+	err_io = kmdb_promio_create("stderr");
+	mdb.m_err = mdb_iob_create(err_io, MDB_IOB_WRONLY);
+	mdb_iob_clrflags(mdb.m_err, MDB_IOB_AUTOWRAP);
+
+	null_io = mdb_nullio_create();
+	mdb.m_null = mdb_iob_create(null_io, MDB_IOB_WRONLY);
+
+	in_io = kmdb_promio_create("stdin");
+	mdb.m_term = NULL;
+
+	if (kav->kav_config != NULL)
+		mdb_set_config(kav->kav_config);
+
+	if (kav->kav_argv != NULL) {
+		for (i = 0; kav->kav_argv[i] != NULL; i++) {
+			if (!mdb_set_options(kav->kav_argv[i], TRUE))
+				return (-1);
+		}
+	}
+
+	if (kav->kav_flags & KMDB_AUXV_FL_NOUNLOAD)
+		mdb.m_flags |= MDB_FL_NOUNLOAD;
+
+	mdb.m_in = mdb_iob_create(in_io, MDB_IOB_RDONLY);
+	mdb_iob_setflags(mdb.m_in, MDB_IOB_TTYLIKE);
+
+	mdb_lex_reset();
+
+	kmdb_kdi_init(kav->kav_kdi, kav);
+
+	if (kmdb_dpi_init(kav) < 0) {
+		warn("Couldn't initialize kernel/PROM interface\n");
+		return (-1);
+	}
+
+	/*
+	 * Path evaluation part 1: Create the initial module path to allow
+	 * the target constructor to load a support module.  We base kmdb's
+	 * module path off the kernel's module path unless the user has
+	 * explicitly supplied one.
+	 */
+	mdb_set_ipath(KMDB_DEF_IPATH);
+	if (strlen(mdb.m_lpathstr) > 0) {
+		mdb_set_lpath(mdb.m_lpathstr);
+	} else {
+		char *lpath;
+
+		if (kav->kav_modpath != NULL && *kav->kav_modpath != '\0' &&
+		    (lpath = kmdb_modpath2lpath(kav->kav_modpath)) != NULL) {
+			mdb_set_lpath(lpath);
+			strfree(lpath);
+		} else {
+			mdb_set_lpath(KMDB_DEF_LPATH);
+		}
+	}
+
+	if (mdb_get_prompt() == NULL)
+		(void) mdb_set_prompt(MDB_DEF_PROMPT);
+
+	tgt = mdb_tgt_create(tgt_ctor, mdb.m_tgtflags, 0, NULL);
+
+	if (tgt == NULL) {
+		warn("failed to initialize target");
+		return (-1);
+	}
+
+	mdb_tgt_activate(tgt);
+
+	mdb_create_loadable_disasms();
+
+	/*
+	 * Path evaluation part 2: Re-evaluate the path now that the target
+	 * is ready (and thus we have access to the real platform string).
+	 */
+	mdb_set_ipath(mdb.m_ipathstr);
+	mdb_set_lpath(mdb.m_lpathstr);
+
+	if (!(mdb.m_flags & MDB_FL_NOMODS))
+		mdb_module_load_all(MDB_MOD_DEFER);
+
+	/* Allocate the main debugger stack */
+	kmdb_main_stack = mdb_alloc(KMDB_STACK_SIZE, UM_SLEEP);
+	kmdb_main_stack_size = KMDB_STACK_SIZE;
+
+	kmdb_kdi_end_init();
+
+	return (0);
+}
+
+/*
+ * First-time kmdb startup.  Run when kmdb has control of the machine for the
+ * first time.
+ */
+static void
+kmdb_startup(void)
+{
+	mdb_io_t *inio, *outio;
+
+	if (mdb.m_termtype == NULL) {
+		/*
+		 * The terminal type wasn't specified, so we guess.  If we're
+		 * on console, we'll get a terminal type from the PROM.  If not,
+		 * we'll use the default.
+		 */
+		if ((mdb.m_termtype = kmdb_prom_term_type()) == NULL) {
+			warn("unable to determine terminal type: assuming "
+			    "`" KMDB_DEF_TERM_TYPE "'\n");
+			mdb.m_termtype = strdup(KMDB_DEF_TERM_TYPE);
+		}
+		mdb.m_flags |= MDB_FL_TERMGUESS;
+
+	} else if (mdb.m_flags & MDB_FL_TERMGUESS) {
+		/*
+		 * The terminal type wasn't specified by the user, but a guess
+		 * was made using either $TERM or a property from the SMF.  A
+		 * terminal type from the PROM code overrides the guess, so
+		 * we'll use that if we can.
+		 */
+		char *promttype;
+
+		if ((promttype = kmdb_prom_term_type()) != NULL) {
+			strfree(mdb.m_termtype);
+			mdb.m_termtype = strdup(promttype);
+		}
+	}
+
+	inio = kmdb_promio_create("stdin");
+	outio = kmdb_promio_create("stdout");
+
+	if ((mdb.m_term = mdb_termio_create(mdb.m_termtype, inio, outio)) ==
+	    NULL && strcmp(mdb.m_termtype, KMDB_DEF_TERM_TYPE) != 0) {
+		warn("failed to set terminal type to `%s', using `"
+		    KMDB_DEF_TERM_TYPE "'\n", mdb.m_termtype);
+
+		strfree(mdb.m_termtype);
+		mdb.m_termtype = strdup(KMDB_DEF_TERM_TYPE);
+
+		if ((mdb.m_term = mdb_termio_create(mdb.m_termtype, inio,
+		    outio)) == NULL) {
+			fail("failed to set terminal type to `"
+			    KMDB_DEF_TERM_TYPE "'\n");
+		}
+	}
+
+	mdb_iob_destroy(mdb.m_in);
+	mdb.m_in = mdb_iob_create(mdb.m_term, MDB_IOB_RDONLY);
+	mdb_iob_setpager(mdb.m_out, mdb.m_term);
+	mdb_iob_setflags(mdb.m_out, MDB_IOB_PGENABLE);
+
+	kmdb_kvm_startup();
+
+	/*
+	 * kmdb_init() and kctl_activate() may have been talking to each other,
+	 * and may have left some messages for us.  The driver -> debugger
+	 * queue is normally processed during the resume path, so we have to
+	 * do it manually here if we want it to be run for first startup.
+	 */
+	kmdb_dpi_process_work_queue();
+
+	kmdb_kvm_poststartup();
+}
+
+void
+kmdb_main(void)
+{
+	int status;
+
+	kmdb_dpi_set_state(DPI_STATE_STOPPED, 0);
+	mdb_printf("\nWelcome to kmdb\n");
+	kmdb_startup();
+
+	/*
+	 * Debugger termination is a bit tricky.  For compatibility with kadb,
+	 * neither an EOF on stdin nor a normal ::quit will cause the debugger
+	 * to unload.  In both cases, they get a trip to OBP, after which the
+	 * debugger returns.
+	 *
+	 * The only supported way to cause the debugger to unload is to specify
+	 * the unload flag to ::quit, or to have the driver request it.  The
+	 * driver request is the primary exit mechanism - the ::quit flag is
+	 * provided for convenience.
+	 *
+	 * Both forms of "exit" (unqualified ::quit that won't cause an unload,
+	 * and a driver request that will) are signalled by an MDB_ERR_QUIT.  In
+	 * the latter case, however, the KDI will have the unload request.
+	 */
+	for (;;) {
+		status = mdb_run();
+
+		if (status == MDB_ERR_QUIT && kmdb_kdi_get_unload_request()) {
+			break;
+
+		} else if (status == MDB_ERR_QUIT || status == 0) {
+			kmdb_dpi_enter_mon();
+
+		} else if (status == MDB_ERR_OUTPUT) {
+			/*
+			 * If a write failed on stdout, give up.  A more
+			 * informative error message will already have been
+			 * printed by mdb_run().
+			 */
+			if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_ERR)
+				fail("write to stdout failed, exiting\n");
+
+		} else if (status != MDB_ERR_ABORT) {
+			fail("debugger exited abnormally (status = %s)\n",
+			    mdb_err2str(status));
+		}
+	}
+
+	mdb_destroy();
+
+	kmdb_dpi_resume_unload();
+
+	/*NOTREACHED*/
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_module.c b/usr/src/cmd/mdb/common/kmdb/kmdb_module.c
new file mode 100644
index 0000000..f547c8b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_module.c
@@ -0,0 +1,201 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Routines for manipulating the kmdb-specific aspects of dmods.
+ */
+
+#include <sys/param.h>
+
+#include <mdb/mdb_target_impl.h>
+#include <kmdb/kmdb_module.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+typedef struct kmod_symarg {
+	mdb_tgt_sym_f *sym_cb;		/* Caller's callback function */
+	void *sym_data;			/* Callback function argument */
+	uint_t sym_type;		/* Symbol type/binding filter */
+	mdb_syminfo_t sym_info;		/* Symbol id and table id */
+	const char *sym_obj;		/* Containing object */
+} kmod_symarg_t;
+
+void
+kmdb_module_path_set(const char **path, size_t pathlen)
+{
+	kmdb_wr_path_t *wr;
+
+	wr = mdb_zalloc(sizeof (kmdb_wr_path_t), UM_SLEEP);
+	wr->dpth_node.wn_task = WNTASK_DMOD_PATH_CHANGE;
+	wr->dpth_path = mdb_path_dup(path, pathlen, &wr->dpth_pathlen);
+
+	kmdb_wr_driver_notify(wr);
+}
+
+void
+kmdb_module_path_ack(kmdb_wr_path_t *dpth)
+{
+	if (dpth->dpth_path != NULL)
+		mdb_path_free(dpth->dpth_path, dpth->dpth_pathlen);
+	mdb_free(dpth, sizeof (kmdb_wr_path_t));
+}
+
+/*
+ * Given an address, try to match it up with a dmod symbol.
+ */
+int
+kmdb_module_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
+    size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
+{
+	kmdb_modctl_t *sym_kmc = NULL;
+	GElf_Sym sym;
+	uint_t symid;
+	mdb_var_t *v;
+	const char *name;
+
+	mdb_nv_rewind(&mdb.m_dmodctl);
+	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
+		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
+
+		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
+			continue;
+
+		if (mdb_gelf_symtab_lookup_by_addr(kmc->kmc_symtab, addr, flags,
+		    buf, nbytes, symp, &sip->sym_id) != 0 ||
+		    symp->st_value == 0)
+			continue;
+
+		if (flags & MDB_TGT_SYM_EXACT) {
+			sym_kmc = kmc;
+			goto found;
+		}
+
+		/*
+		 * If this is the first match we've found, or if this symbol is
+		 * closer to the specified address than the last one we found,
+		 * use it.
+		 */
+		if (sym_kmc == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) {
+			sym_kmc = kmc;
+			sym = *symp;
+			symid = sip->sym_id;
+		}
+	}
+
+	if (sym_kmc == NULL)
+		return (set_errno(EMDB_NOSYMADDR));
+
+	*symp = sym;
+	sip->sym_id = symid;
+
+found:
+	/*
+	 * Once we've found something, copy the final name into the caller's
+	 * buffer, prefixed with a marker identifying this as a dmod symbol.
+	 */
+	if (buf != NULL) {
+		name = mdb_gelf_sym_name(sym_kmc->kmc_symtab, symp);
+
+		(void) mdb_snprintf(buf, nbytes, "DMOD`%s`%s",
+		    sym_kmc->kmc_modname, name);
+	}
+	sip->sym_table = MDB_TGT_SYMTAB;
+
+	return (0);
+}
+
+/*
+ * Locate a given dmod symbol
+ */
+int
+kmdb_module_lookup_by_name(const char *obj, const char *name, GElf_Sym *symp,
+    mdb_syminfo_t *sip)
+{
+	kmdb_modctl_t *kmc;
+	mdb_var_t *v;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, obj)) == NULL)
+		return (set_errno(EMDB_NOSYMADDR));
+
+	kmc = MDB_NV_COOKIE(v);
+
+	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
+		return (set_errno(EMDB_NOSYMADDR));
+
+	if (mdb_gelf_symtab_lookup_by_name(kmc->kmc_symtab, name,
+	    symp, &sip->sym_id) == 0) {
+		sip->sym_table = MDB_TGT_SYMTAB;
+		return (0);
+	}
+
+	return (set_errno(EMDB_NOSYM));
+}
+
+static int
+kmdb_module_symtab_func(void *data, const GElf_Sym *sym, const char *name,
+    uint_t id)
+{
+	kmod_symarg_t *arg = data;
+
+	if (mdb_tgt_sym_match(sym, arg->sym_type)) {
+		arg->sym_info.sym_id = id;
+
+		return (arg->sym_cb(arg->sym_data, sym, name, &arg->sym_info,
+		    arg->sym_obj));
+	}
+
+	return (0);
+}
+
+int
+kmdb_module_symbol_iter(const char *obj, uint_t type, mdb_tgt_sym_f *cb,
+    void *p)
+{
+	kmdb_modctl_t *kmc;
+	kmod_symarg_t arg;
+	mdb_var_t *v;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, obj)) == NULL)
+		return (set_errno(EMDB_NOMOD));
+
+	kmc = MDB_NV_COOKIE(v);
+
+	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
+		return (set_errno(EMDB_NOMOD));
+
+	arg.sym_cb = cb;
+	arg.sym_data = p;
+	arg.sym_type = type;
+	arg.sym_info.sym_table = kmc->kmc_symtab->gst_tabid;
+	arg.sym_obj = obj;
+
+	mdb_gelf_symtab_iter(kmc->kmc_symtab, kmdb_module_symtab_func, &arg);
+
+	return (0);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_module.h b/usr/src/cmd/mdb/common/kmdb/kmdb_module.h
new file mode 100644
index 0000000..f34a4ac
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_module.h
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_MODULE_H
+#define	_KMDB_MODULE_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/modctl.h>
+#include <sys/kobj.h>
+
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_module.h>
+#include <kmdb/kmdb_wr_impl.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	KMDB_MC_STATE_LOADING	1
+#define	KMDB_MC_STATE_LOADED	2
+#define	KMDB_MC_STATE_UNLOADING	3
+
+#define	KMDB_MC_FL_NOUNLOAD	0x1
+#define	KMDB_MC_FL_CTFCOPIED	0x2
+
+/*
+ * The mdb_module_t describes the runtime attributes of dmods - things that
+ * matter after the dmod has been loaded.  kmdb needs to track information about
+ * modules before they've been loaded, and while they're in the process of
+ * being unloaded.  As such, a kmdb_modctl_t is created for each module when the
+ * load is requested, and isn't destroyed until the module has completed
+ * unloading.
+ *
+ * This description reflects the sequence of events that occur during the
+ * successful loading and unloading of a dmod.
+ *
+ * 1. Debugger requests a dmod load.
+ *
+ *    A kmdb_modctl_t is allocated.  kmc_state is set to KMDB_MC_STATE_LOADING.
+ *
+ * 2. The driver reports the successful loading of the dmod.
+ *
+ *    kmc_state is set to KMDB_MC_STATE_LOADED, and an mdb_module_t is created
+ *    by mdb_module_create.
+ *
+ * 3. Debugger requests a dmod unload.
+ *
+ *    The mdb_module_t is destroyed, and kmc_state is set to
+ *    KMDB_MC_STATE_UNLOADING.
+ *
+ * 4. The driver reports the successful unloading of the dmod.
+ *
+ *    The kmdb_modctl_t is destroyed.
+ */
+typedef struct kmdb_modctl {
+	mdb_module_t *kmc_mod;		/* common dmod state */
+	struct modctl *kmc_modctl;	/* kernel's modctl for this dmod */
+	int kmc_exported;		/* KOBJ_EXPORTED set when last seen? */
+	char *kmc_modname;		/* name of this dmod */
+	ushort_t kmc_loadmode;		/* MDB_MOD_* from load request */
+	ushort_t kmc_flags;		/* KMDB_MC_FL_* (above) */
+	int kmc_dlrefcnt;		/* Counts dlopens/dlcloses */
+	int kmc_state;			/* KMDB_MC_STATE_* (above) */
+	mdb_gelf_symtab_t *kmc_symtab;	/* This dmod's symbol table */
+	GElf_Ehdr kmc_ehdr;		/* Copy of ehdr in gelf format */
+	caddr_t kmc_ctfdata;		/* Ptr to CTF to be used for dmod */
+	size_t kmc_ctfsize;		/* Size of CTF to be used for dmod */
+} kmdb_modctl_t;
+
+extern int kmdb_module_loaded(kmdb_wr_load_t *);
+extern void kmdb_module_load_ack(kmdb_wr_load_t *);
+extern void kmdb_module_load_all_ack(kmdb_wr_t *);
+extern int kmdb_module_unloaded(kmdb_wr_unload_t *);
+extern void kmdb_module_unload_ack(kmdb_wr_unload_t *);
+
+extern void kmdb_module_path_set(const char **, size_t);
+extern void kmdb_module_path_ack(kmdb_wr_path_t *);
+
+extern int kmdb_module_lookup_by_addr(uintptr_t, uint_t, char *, size_t,
+    GElf_Sym *, mdb_syminfo_t *);
+extern int kmdb_module_lookup_by_name(const char *, const char *, GElf_Sym *,
+    mdb_syminfo_t *);
+extern int kmdb_module_symbol_iter(const char *, uint_t, mdb_tgt_sym_f *,
+    void *);
+extern void kmdb_module_sync(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_MODULE_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_module_load.c b/usr/src/cmd/mdb/common/kmdb/kmdb_module_load.c
new file mode 100644
index 0000000..c1caf37
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_module_load.c
@@ -0,0 +1,408 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/param.h>
+#include <sys/modctl.h>
+#include <sys/kobj.h>
+#include <sys/kobj_impl.h>
+#include <unistd.h>
+#include <strings.h>
+#include <dlfcn.h>
+#include <link.h>
+
+#include <kmdb/kmdb_module.h>
+#include <kmdb/kmdb_wr_impl.h>
+#include <kmdb/kmdb_kdi.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb.h>
+
+static void kmdb_module_request_unload(kmdb_modctl_t *, const char *, int);
+
+static void
+kmc_free(kmdb_modctl_t *kmc)
+{
+	if (kmc->kmc_flags & KMDB_MC_FL_CTFCOPIED)
+		mdb_free(kmc->kmc_ctfdata, kmc->kmc_ctfsize);
+
+	if (kmc->kmc_modname != NULL)
+		strfree(kmc->kmc_modname);
+	mdb_free(kmc, sizeof (kmdb_modctl_t));
+}
+
+/*
+ * Sends a request to the driver to load the module.  If/when the load has
+ * completed successfully, kmdb_module_loaded is called.
+ */
+int
+mdb_module_load(const char *fname, int mode)
+{
+	const char *modname = strbasename(fname);
+	kmdb_wr_load_t *dlr;
+	kmdb_modctl_t *kmc = NULL;
+	const char *wformat = NULL;
+	mdb_var_t *v;
+
+	if (!mdb_module_validate_name(modname, &wformat))
+		goto module_load_err;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, modname)) != NULL) {
+		kmc = MDB_NV_COOKIE(v);
+
+		if (kmc->kmc_state == KMDB_MC_STATE_LOADING)
+			wformat = "module %s is already being loaded\n";
+		else
+			wformat = "module %s is being unloaded\n";
+		goto module_load_err;
+	}
+
+	kmc = mdb_zalloc(sizeof (kmdb_modctl_t), UM_SLEEP);
+	kmc->kmc_loadmode = mode;
+	kmc->kmc_modname = strdup(modname);
+	kmc->kmc_state = KMDB_MC_STATE_LOADING;
+
+	if (mdb_nv_insert(&mdb.m_dmodctl, modname, NULL, (uintptr_t)kmc, 0) ==
+	    NULL) {
+		wformat = "module %s can't be registered for load\n";
+		kmc_free(kmc);
+		goto module_load_err;
+	}
+
+	dlr = mdb_zalloc(sizeof (kmdb_wr_load_t), UM_SLEEP);
+	dlr->dlr_node.wn_task = WNTASK_DMOD_LOAD;
+	dlr->dlr_fname = strdup(fname);
+
+	kmdb_wr_driver_notify(dlr);
+
+	if (!(mode & MDB_MOD_DEFER) &&
+	    mdb_tgt_continue(mdb.m_target, NULL) == 0)
+		return (0);
+
+	if (!(mode & MDB_MOD_SILENT))
+		mdb_printf("%s load pending (:c to complete)\n", modname);
+
+	return (0);
+
+module_load_err:
+	if (!(mode & MDB_MOD_SILENT))
+		warn(wformat, modname);
+
+	return (-1);
+}
+
+int
+kmdb_module_loaded(kmdb_wr_load_t *dlr)
+{
+	struct modctl *modp = dlr->dlr_modctl;
+	const char *modname = strbasename(dlr->dlr_fname);
+	struct module *mp;
+	kmdb_modctl_t *kmc;
+	mdb_var_t *v;
+
+	v = mdb_nv_lookup(&mdb.m_dmodctl, modname);
+
+	if (dlr->dlr_errno != 0) {
+		/*
+		 * We're somewhat limited in the diagnostics that we can
+		 * provide in the event of a failed load.  In most load-failure
+		 * cases, the driver can only send up a generic errno.  We use
+		 * EMDB_ENOMOD to signal generic errors, and supply our own
+		 * message.  This twists the meaning of EMDB_NOMOD somewhat, but
+		 * it's better than defining a new one.
+		 */
+		if (dlr->dlr_errno == EMDB_NOMOD) {
+			mdb_warn("%s does not appear to be a kmdb dmod\n",
+			    modname);
+		} else {
+			(void) set_errno(dlr->dlr_errno);
+			mdb_warn("dmod %s failed to load", modname);
+		}
+
+		if (v != NULL)
+			mdb_nv_remove(&mdb.m_dmodctl, v);
+		return (0);
+	}
+
+	if ((mp = modp->mod_mp) == NULL || mp->symhdr == NULL ||
+	    mp->strhdr == NULL || mp->symtbl == NULL || mp->strings == NULL) {
+		mdb_warn("dmod %s did not load properly\n");
+		goto module_loaded_err;
+	}
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, modname)) == NULL) {
+		kmc = mdb_zalloc(sizeof (kmdb_modctl_t), UM_SLEEP);
+		kmc->kmc_loadmode = MDB_MOD_LOCAL;
+		kmc->kmc_modname = strdup(modname);
+		kmc->kmc_state = KMDB_MC_STATE_LOADING;
+
+		(void) mdb_nv_insert(&mdb.m_dmodctl, modname, NULL,
+		    (uintptr_t)kmc, 0);
+	} else {
+		kmc = MDB_NV_COOKIE(v);
+		ASSERT(kmc->kmc_symtab == NULL);
+	}
+
+	kmc->kmc_modctl = modp;
+	kmc->kmc_exported = (mp->flags & KOBJ_EXPORTED) != 0;
+	mdb_gelf_ehdr_to_gehdr(&mp->hdr, &kmc->kmc_ehdr);
+
+	kmc->kmc_symtab = mdb_gelf_symtab_create_raw(&kmc->kmc_ehdr, mp->symhdr,
+	    mp->symtbl, mp->strhdr, mp->strings,
+	    MDB_TGT_SYMTAB);
+
+	if (mp->flags & KOBJ_PRIM) {
+		kmc->kmc_flags |= KMDB_MC_FL_NOUNLOAD;
+
+		/*
+		 * Prior to the unmapping of boot scratch memory, CTF and symbol
+		 * data for primary kernel modules is exported (copied) to vmem.
+		 * If we start using the pre-export CTF data, we'll be in for a
+		 * rude surprise when it moves.  Unfortunately, we can't simply
+		 * adapt to the moved data, as we may have already passed out
+		 * pointers (via libstandctf) into the data at the old location.
+		 * To avoid this problem, we'll copy the CTF data into Oz if
+		 * we're seeing the module before it has been exported.  This
+		 * issue only arises when we enter boot-loaded kmdb before the
+		 * completion of kdi_dvec_modavail().
+		 *
+		 * Note that this still leaves us with a migratory symbol table.
+		 * Unlike with CTF, we have the ability to deal with symbol
+		 * tables whose addresses change, so we'll just rebuild the kmc
+		 * symtab when we detect the export.
+		 */
+		if (!(mp->flags & KOBJ_EXPORTED) && mp->ctfdata != NULL) {
+			kmc->kmc_ctfdata = mdb_alloc(mp->ctfsize, UM_SLEEP);
+			kmc->kmc_ctfsize = mp->ctfsize;
+			bcopy(mp->ctfdata, kmc->kmc_ctfdata, kmc->kmc_ctfsize);
+			kmc->kmc_flags |= KMDB_MC_FL_CTFCOPIED;
+		}
+	} else {
+		kmc->kmc_ctfdata = mp->ctfdata;
+		kmc->kmc_ctfsize = mp->ctfsize;
+	}
+
+	if (mdb_module_create(modname, modp->mod_filename,
+	    kmc->kmc_loadmode, &kmc->kmc_mod) < 0)
+		goto module_loaded_err;
+
+	kmc->kmc_state = KMDB_MC_STATE_LOADED;
+
+	return (1);
+
+module_loaded_err:
+	if (kmc->kmc_symtab != NULL)
+		mdb_gelf_symtab_destroy(kmc->kmc_symtab);
+
+	kmdb_module_request_unload(kmc, kmc->kmc_modname, MDB_MOD_DEFER);
+	return (0);
+}
+
+void
+kmdb_module_load_ack(kmdb_wr_load_t *dlr)
+{
+	strfree(dlr->dlr_fname);
+	mdb_free(dlr, sizeof (kmdb_wr_load_t));
+}
+
+void
+mdb_module_load_all(int mode)
+{
+	kmdb_wr_t *wn;
+
+	ASSERT(mode & MDB_MOD_DEFER);
+
+	wn = mdb_zalloc(sizeof (kmdb_wr_t), UM_SLEEP);
+	wn->wn_task = WNTASK_DMOD_LOAD_ALL;
+
+	kmdb_wr_driver_notify(wn);
+}
+
+void
+kmdb_module_load_all_ack(kmdb_wr_t *wn)
+{
+	mdb_free(wn, sizeof (kmdb_wr_t));
+}
+
+static void
+kmdb_module_request_unload(kmdb_modctl_t *kmc, const char *modname, int mode)
+{
+	kmdb_wr_unload_t *dur = mdb_zalloc(sizeof (kmdb_wr_unload_t), UM_SLEEP);
+	dur->dur_node.wn_task = WNTASK_DMOD_UNLOAD;
+	dur->dur_modname = strdup(modname);
+	dur->dur_modctl = kmc->kmc_modctl;
+
+	kmdb_wr_driver_notify(dur);
+
+	kmc->kmc_state = KMDB_MC_STATE_UNLOADING;
+
+	if (!(mode & MDB_MOD_DEFER) &&
+	    mdb_tgt_continue(mdb.m_target, NULL) == 0)
+		return;
+
+	if (!(mode & MDB_MOD_SILENT))
+		mdb_printf("%s unload pending (:c to complete)\n", modname);
+}
+
+/*ARGSUSED*/
+int
+mdb_module_unload(const char *name, int mode)
+{
+	kmdb_modctl_t *kmc = NULL;
+	const char *basename;
+	mdb_var_t *v;
+
+	/*
+	 * We may have been called with the name from the module itself
+	 * if the caller is iterating through the module list, so we need
+	 * to make a copy of the name.  If we don't, we can't use it after
+	 * the call to unload_common(), which frees the module.
+	 */
+	name = strdup(name);
+	basename = strbasename(name);
+
+	/*
+	 * Make sure the module is in the proper state for unloading.  Modules
+	 * may only be unloaded if they have properly completed loading.
+	 */
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, basename)) != NULL) {
+		kmc = MDB_NV_COOKIE(v);
+		switch (kmc->kmc_state) {
+		case KMDB_MC_STATE_LOADING:
+			warn("%s is in the process of loading\n", basename);
+			return (set_errno(EMDB_NOMOD));
+		case KMDB_MC_STATE_UNLOADING:
+			warn("%s is already being unloaded\n", basename);
+			return (set_errno(EMDB_NOMOD));
+		default:
+			ASSERT(kmc->kmc_state == KMDB_MC_STATE_LOADED);
+		}
+
+		if (kmc->kmc_flags & KMDB_MC_FL_NOUNLOAD)
+			return (set_errno(EMDB_KMODNOUNLOAD));
+	}
+
+	if (mdb_module_unload_common(name) < 0) {
+		if (!(mode & MDB_MOD_SILENT)) {
+			mdb_dprintf(MDB_DBG_MODULE, "unload of %s failed\n",
+			    name);
+		}
+		return (-1); /* errno is set for us */
+	}
+
+	/*
+	 * Any modules legitimately not listed in dmodctl (builtins, for
+	 * example) will be handled by mdb_module_unload_common.  If any of
+	 * them get here, we've got a problem.
+	 */
+	if (v == NULL) {
+		warn("unload of unregistered module %s\n", basename);
+		return (set_errno(EMDB_NOMOD));
+	}
+
+	ASSERT(kmc->kmc_dlrefcnt == 0);
+
+	mdb_gelf_symtab_destroy(kmc->kmc_symtab);
+
+	kmdb_module_request_unload(kmc, basename, mode);
+	return (0);
+}
+
+int
+kmdb_module_unloaded(kmdb_wr_unload_t *dur)
+{
+	mdb_var_t *v;
+
+	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, dur->dur_modname)) == NULL) {
+		mdb_warn("unload for unrequested module %s\n",
+		    dur->dur_modname);
+		return (0);
+	}
+
+	if (dur->dur_errno != 0) {
+		mdb_warn("dmod %s failed to unload", dur->dur_modname);
+		return (0);
+	}
+
+	kmc_free(MDB_NV_COOKIE(v));
+	mdb_nv_remove(&mdb.m_dmodctl, v);
+
+	return (1);
+}
+
+void
+kmdb_module_unload_ack(kmdb_wr_unload_t *dur)
+{
+	if (dur->dur_modname != NULL)
+		strfree(dur->dur_modname);
+	mdb_free(dur, sizeof (kmdb_wr_unload_t));
+}
+
+/*
+ * Called by the kmdb_kvm target upon debugger reentry, this routine checks
+ * to see if the loaded dmods have changed.  Of particular interest is the
+ * exportation of dmod symbol tables, which will happen during the boot
+ * process for dmods that were loaded prior to kernel startup.  If this
+ * has occurred, we'll need to reconstruct our view of the symbol tables for
+ * the affected dmods.
+ */
+void
+kmdb_module_sync(void)
+{
+	mdb_var_t *v;
+
+	mdb_nv_rewind(&mdb.m_dmodctl);
+	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
+		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
+		struct module *mp;
+
+		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
+			continue;
+
+		mp = kmc->kmc_modctl->mod_mp;
+
+		if ((mp->flags & (KOBJ_PRIM | KOBJ_EXPORTED)) &&
+		    !kmc->kmc_exported) {
+			/*
+			 * The exporting process moves the symtab from boot
+			 * scratch memory to vmem.
+			 */
+			if (kmc->kmc_symtab != NULL)
+				mdb_gelf_symtab_destroy(kmc->kmc_symtab);
+
+			kmc->kmc_symtab = mdb_gelf_symtab_create_raw(
+			    &kmc->kmc_ehdr, mp->symhdr, mp->symtbl, mp->strhdr,
+			    mp->strings, MDB_TGT_SYMTAB);
+
+			kmc->kmc_exported = TRUE;
+		}
+	}
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
new file mode 100644
index 0000000..ef065d4
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
@@ -0,0 +1,775 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <sys/termios.h>
+#include <sys/promif.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <kmdb/kmdb_promif_impl.h>
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kmdb_dpi.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb.h>
+
+#define	KMDB_PROM_DEF_CONS_MODE	"9600,n,1,-,-"
+
+#define	KMDB_PROM_READBUF_SIZE	1024
+
+static char kmdb_prom_readbuf[KMDB_PROM_READBUF_SIZE];
+static int kmdb_prom_readbuf_head;
+static int kmdb_prom_readbuf_tail;
+
+ssize_t
+kmdb_prom_polled_read(caddr_t buf, size_t len)
+{
+	uintptr_t arg = (uintptr_t)mdb.m_pio->cons_polledio_argument;
+	uintptr_t ischar = (uintptr_t)mdb.m_pio->cons_polledio_ischar;
+	int nread = 0;
+	int c;
+
+	while ((ischar == NULL || kmdb_dpi_call(ischar, 1, &arg)) &&
+	    nread < len) {
+		c = kmdb_dpi_call((uintptr_t)mdb.m_pio->cons_polledio_getchar,
+		    1, &arg);
+
+		*buf++ = (char)c;
+		nread++;
+	}
+
+	return (nread);
+}
+
+ssize_t
+kmdb_prom_polled_write(caddr_t buf, size_t len)
+{
+	uintptr_t args[2];
+	int i;
+
+	args[0] = (uintptr_t)mdb.m_pio->cons_polledio_argument;
+
+	for (i = 0; i < len; i++) {
+		args[1] = *buf++;
+		(void) kmdb_dpi_call(
+		    (uintptr_t)mdb.m_pio->cons_polledio_putchar, 2, args);
+	}
+
+	return (len);
+}
+
+static ssize_t
+kmdb_prom_reader(caddr_t buf, size_t len)
+{
+	int nread = 0;
+	int c;
+
+	if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_getchar != NULL)
+		return (kmdb_prom_polled_read(buf, len));
+
+	while (nread < len && (c = prom_mayget()) != -1) {
+		*buf++ = (char)c;
+		nread++;
+	}
+
+	return (nread);
+}
+
+static ssize_t
+kmdb_prom_writer(caddr_t buf, size_t len)
+{
+	if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_putchar != NULL)
+		return (kmdb_prom_polled_write(buf, len));
+
+	return (kmdb_prom_obp_writer(buf, len));
+}
+
+/*
+ * Due to the nature of kmdb, we don't have signals.  This prevents us from
+ * receiving asynchronous notification when the user would like to abort
+ * active dcmds.  Whereas mdb can simply declare a SIGINT handler, we must
+ * occasionally poll the input stream, looking for pending ^C characters.  To
+ * give the illusion of asynchronous interrupt delivery, this polling is
+ * triggered from several commonly-used functions, such as kmdb_prom_write,
+ * kmdb_prom_read, and the *read and *write target ops.  When an interrupt
+ * check is triggered, we read through pending input, looking for interrupt
+ * characters.  If we find one, we deliver it.
+ *
+ * OBP doesn't have an "unget" facility.  Any character read for interrupt
+ * checking is gone forever, unless we save it.  Loss of these characters
+ * would prevent us from supporting typeahead.  We like typeahead, so we're
+ * going to save characters gathered during interrupt checking.  As with
+ * ungetc(3c), however, we can only store a finite number of characters in
+ * our typeahead buffer.  Characters read beyond that will be silently dropped
+ * after they undergo interrupt processing.
+ *
+ * The typeahead facility is implemented as a ring buffer, stored in
+ * kmdb_prom_readbuf, and has the following interfaces:
+ *
+ *   kmdb_prom_drain_readbuf - Given a buffer and a length, copy that many bytes
+ *    into the passed buffer.  This routine will not attempt to refill the ring
+ *    buffer.  The number of bytes actually copied will be returned.
+ *
+ *   kmdb_prom_fill_readbuf - Attempt to refill the ring buffer from the input
+ *    stream.  The `lossy' argument governs the action taken if the available
+ *    input will overflow the available space in the ring buffer.  If the lossy
+ *    argument is set, available input will be exhausted.  Characters that will
+ *    not fit in the ring buffer will be discarded after interrupt processing.
+ *    If this routine is invoked in lossless mode (i.e. the lossy flag is not
+ *    set), input will be read only until the buffer is full.
+ *
+ * kmdb_prom_* routines may call drain and fill directly.  Callers outside of
+ * the kmdb promif subsystem must use kmdb_prom_check_interrupt, which will
+ * invoke fill in lossy mode.
+ */
+size_t
+kmdb_prom_drain_readbuf(void *buf, size_t len)
+{
+	size_t n, tailread;
+
+	/*
+	 * If head > tail, life is easy - we can simply read as much as we need
+	 * in one gulp.
+	 */
+	if (kmdb_prom_readbuf_head > kmdb_prom_readbuf_tail) {
+		n = MIN(kmdb_prom_readbuf_head - kmdb_prom_readbuf_tail, len);
+		bcopy(kmdb_prom_readbuf + kmdb_prom_readbuf_tail, buf, n);
+		kmdb_prom_readbuf_tail += n;
+		return (n);
+
+	} else if (kmdb_prom_readbuf_head == kmdb_prom_readbuf_tail) {
+		return (0);
+	}
+
+	/*
+	 * The consumable slots wrap around zero (there are slots from tail to
+	 * zero, and from zero to head).  We have to read them in two parts.
+	 */
+	n = MIN(KMDB_PROM_READBUF_SIZE - kmdb_prom_readbuf_tail, len);
+	bcopy(kmdb_prom_readbuf + kmdb_prom_readbuf_tail, buf, n);
+	kmdb_prom_readbuf_tail = (kmdb_prom_readbuf_tail + n) %
+	    KMDB_PROM_READBUF_SIZE;
+
+	if (n == len) {
+		/*
+		 * We filled the passed buffer from the first part, so there's
+		 * no need to read the second.
+		 */
+		return (n);
+	} else {
+		tailread = n;
+	}
+
+	n = MIN(kmdb_prom_readbuf_head, len - tailread);
+	buf = (void *)((uintptr_t)buf + tailread);
+	bcopy(kmdb_prom_readbuf, buf, n);
+
+	kmdb_prom_readbuf_tail = (kmdb_prom_readbuf_tail + n) %
+	    KMDB_PROM_READBUF_SIZE;
+
+	return (tailread + n);
+}
+
+static void
+check_int(char *buf, size_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (buf[i] == ('c' & 037)) {
+			kmdb_prom_readbuf_tail = kmdb_prom_readbuf_head;
+			if (mdb.m_intr == 0)
+				longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
+			else
+				mdb.m_pend++;
+		}
+	}
+}
+
+void
+kmdb_prom_fill_readbuf(int lossy)
+{
+	int oldhead, left, n;
+
+	/*
+	 * Calculate the number of slots left before we wrap around to the
+	 * beginning again.
+	 */
+	left = KMDB_PROM_READBUF_SIZE - kmdb_prom_readbuf_head;
+	if (kmdb_prom_readbuf_tail == 0)
+		left--;
+
+	if (kmdb_prom_readbuf_head == kmdb_prom_readbuf_tail ||
+	    (kmdb_prom_readbuf_head > kmdb_prom_readbuf_tail && left > 0)) {
+		/*
+		 * head > tail, so we have to read in two parts - the slots
+		 * from head until we wrap back around to zero, and the ones
+		 * from zero to tail.  We handle the first part here, and let
+		 * the common code handle the second.
+		 */
+		if ((n = kmdb_prom_reader(kmdb_prom_readbuf +
+		    kmdb_prom_readbuf_head, left)) <= 0)
+			return;
+
+		oldhead = kmdb_prom_readbuf_head;
+		kmdb_prom_readbuf_head = (kmdb_prom_readbuf_head + n) %
+		    KMDB_PROM_READBUF_SIZE;
+
+		check_int(kmdb_prom_readbuf + oldhead, n);
+
+		if (n != left)
+			return;
+	}
+
+	left = kmdb_prom_readbuf_tail - kmdb_prom_readbuf_head - 1;
+	if (left > 0) {
+		if ((n = kmdb_prom_reader(kmdb_prom_readbuf +
+		    kmdb_prom_readbuf_head, left)) <= 0)
+			return;
+
+		oldhead = kmdb_prom_readbuf_head;
+		kmdb_prom_readbuf_head = (kmdb_prom_readbuf_head + n) %
+		    KMDB_PROM_READBUF_SIZE;
+
+		check_int(kmdb_prom_readbuf + oldhead, n);
+
+		if (n != left)
+			return;
+	}
+
+	if (lossy) {
+		char c;
+
+		while (kmdb_prom_reader(&c, 1) == 1)
+			check_int(&c, 1);
+	}
+}
+
+void
+kmdb_prom_check_interrupt(void)
+{
+	kmdb_prom_fill_readbuf(1);
+}
+
+/*
+ * OBP reads are always non-blocking.  If there are characters available,
+ * we'll return as many as we can.  If nothing is available, we'll spin
+ * until one shows up.
+ */
+ssize_t
+kmdb_prom_read(void *buf, size_t len, struct termios *tio)
+{
+	size_t totread = 0;
+	size_t thisread;
+	char *c = (char *)buf;
+
+	for (;;) {
+		kmdb_prom_fill_readbuf(0);
+		thisread = kmdb_prom_drain_readbuf(c, len);
+		len -= thisread;
+		totread += thisread;
+		c += thisread;
+
+		/* wait until something shows up */
+		if (totread == 0)
+			continue;
+
+		/*
+		 * We're done if we've exhausted available input or if we've
+		 * filled the provided buffer.
+		 */
+		if (len == 0 || thisread == 0)
+			break;
+	}
+
+	if (tio->c_iflag & ICRNL) {
+		char *cbuf = buf;
+		int i;
+
+		for (i = 0; i < totread; i++) {
+			if (cbuf[i] == '\r')
+				cbuf[i] = '\n';
+		}
+	}
+
+	if (tio->c_lflag & ECHO)
+		(void) kmdb_prom_write(buf, totread, tio);
+
+	return (totread);
+}
+
+/*ARGSUSED*/
+ssize_t
+kmdb_prom_write(const void *bufp, size_t len, struct termios *tio)
+{
+	caddr_t buf = (caddr_t)bufp;
+	size_t left = len;
+	char *nl = "\r\n";
+	char *c;
+
+	kmdb_prom_check_interrupt();
+
+	if (!(tio->c_oflag & ONLCR))
+		return (kmdb_prom_writer(buf, left));
+
+	/* translate every \n into \r\n */
+	while ((c = strnchr(buf, '\n', left)) != NULL) {
+		if (c != buf) {
+			size_t sz = (size_t)(c - buf);
+			(void) kmdb_prom_writer(buf, sz);
+			left -= sz;
+		}
+
+		buf = c + 1;
+		left--;
+
+		(void) kmdb_prom_writer(nl, 2);
+	}
+
+	if (*buf != '\0')
+		(void) kmdb_prom_writer(buf, left);
+
+	return (len);
+}
+
+static char *
+kmdb_get_ttyio_mode(kmdb_auxv_t *kav, char *devname)
+{
+	char *modepname, *modepval;
+
+	modepname = mdb_alloc(strlen(devname) + 5 + 1, UM_SLEEP);
+	strcpy(modepname, devname);
+	strcat(modepname, "-mode");
+
+	modepval = kmdb_prom_get_options_prop(kav, modepname);
+
+	strfree(modepname);
+
+	return (modepval);
+}
+
+static int
+termios_setispeed(struct termios *tip, speed_t s)
+{
+	if (s > (2 * CBAUD + 1))
+		return (-1);
+
+	if ((s << IBSHIFT) > CIBAUD) {
+		tip->c_cflag |= CIBAUDEXT;
+		s -= ((CIBAUD >> IBSHIFT) + 1);
+	} else
+		tip->c_cflag &= ~CIBAUDEXT;
+
+	tip->c_cflag = (tip->c_cflag & ~CIBAUD) | ((s << IBSHIFT) & CIBAUD);
+
+	return (0);
+}
+
+static int
+termios_setospeed(struct termios *tip, speed_t s)
+{
+	if (s > (2 * CBAUD + 1))
+		return (-1);
+
+	if (s > CBAUD) {
+		tip->c_cflag |= CBAUDEXT;
+		s -= (CBAUD + 1);
+	} else
+		tip->c_cflag &= ~CBAUDEXT;
+
+	tip->c_cflag = (tip->c_cflag & ~CBAUD) | (s & CBAUD);
+
+	return (0);
+}
+
+static int
+kmdb_parse_mode(const char *mode, struct termios *tip, int in)
+{
+	static const uint_t baudmap[] = {
+		0, 50, 75, 110, 134, 150, 200, 300, 600, 1200,
+		1800, 2400, 4800, 9600, 19200, 38400, 57600,
+		76800, 115200, 153600, 230400, 307200, 460800
+	};
+	static const uint_t bitsmap[] = { CS6, CS6, CS7, CS8 };
+	char *m = strdup(mode);
+	char *w;
+	int rc = -1;
+	speed_t speed;
+	int baud, i;
+
+	/*
+	 * termios supports different baud rates and flow control types for
+	 * input and output, but it requires character width, parity, and stop
+	 * bits to be equal in input and output.  obp allows them to be
+	 * different, but we're going to (silently) assume that nobody will use
+	 * it that way.
+	 */
+
+	/* baud rate - see baudmap above */
+	if ((w = strtok(m, ",")) == NULL)
+		goto parse_mode_bail;
+
+	baud = strtol(w, NULL, 10);
+	speed = 0;
+	for (i = 0; i < sizeof (baudmap) / sizeof (baudmap[0]); i++) {
+		if (baudmap[i] == baud) {
+			speed = i;
+			break;
+		}
+	}
+	if (speed == 0)
+		goto parse_mode_bail;
+
+	if (in == 1)
+		(void) termios_setispeed(tip, speed);
+	else
+		(void) termios_setospeed(tip, speed);
+
+	/* character width (bits) - 5, 6, 7, or 8 */
+	if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 || *w < '5' ||
+	    *w > '8')
+		goto parse_mode_bail;
+	tip->c_cflag = (tip->c_cflag & ~CSIZE) | bitsmap[*w - '5'];
+
+	/* parity - `n' (none), `e' (even), or `o' (odd) */
+	if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 ||
+	    strchr("neo", *w) == NULL)
+		goto parse_mode_bail;
+
+	tip->c_cflag = (tip->c_cflag & ~(PARENB|PARODD));
+	switch (*w) {
+	case 'n':
+		/* nothing */
+		break;
+	case 'e':
+		tip->c_cflag |= PARENB;
+		break;
+	case 'o':
+		tip->c_cflag |= PARENB|PARODD;
+		break;
+	}
+
+	/*
+	 * stop bits - 1, or 2.  obp can, in theory, support 1.5 bits,
+	 * but we can't.  how many angels can dance on half of a bit?
+	 */
+	if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 || *w < '1' ||
+	    *w > '2')
+		goto parse_mode_bail;
+
+	if (*w == '1')
+		tip->c_cflag &= ~CSTOPB;
+	else
+		tip->c_cflag |= CSTOPB;
+
+	/* flow control - `-' (none), `h' (h/w), or `s' (s/w - XON/XOFF) */
+	if ((w = strtok(NULL, ",")) == NULL || strlen(w) != 1 ||
+	    strchr("-hs", *w) == NULL)
+		goto parse_mode_bail;
+
+	tip->c_cflag &= ~(CRTSXOFF|CRTSCTS);
+	tip->c_iflag &= ~(IXON|IXANY|IXOFF);
+
+	switch (*w) {
+	case 'h':
+		tip->c_cflag |= (in == 1 ? CRTSXOFF : CRTSCTS);
+		break;
+
+	case 's':
+		tip->c_iflag |= (in == 1 ? IXOFF : IXON);
+		break;
+	}
+
+	rc = 0;
+
+parse_mode_bail:
+	strfree(m);
+
+	return (rc);
+}
+
+#ifdef __sparc
+#define	ATTACHED_TERM_TYPE	"sun"
+#else
+#define	ATTACHED_TERM_TYPE	"sun-color"
+#endif
+
+static void
+kmdb_prom_term_init(kmdb_auxv_t *kav, kmdb_promif_t *pif)
+{
+	const char ccs[NCCS] = { 0x03, 0x1c, 0x08, 0x15, 0x04, 0x00, 0x00,
+	    0x00, 0x11, 0x13, 0x1a, 0x19, 0x12, 0x0f, 0x17, 0x16 };
+	char *conin = NULL, *conout = NULL;
+
+	if (kmdb_prom_stdout_is_framebuffer(kav))
+		pif->pif_oterm = ATTACHED_TERM_TYPE;
+
+	bzero(&pif->pif_tios, sizeof (struct termios));
+
+	/* output device characteristics */
+	if ((conout = kmdb_prom_get_options_prop(kav, "output-device")) ==
+	    NULL || strcmp(conout, "screen") == 0) {
+		(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+		    &pif->pif_tios, 0);
+	} else if (*conout == '/') {
+		/*
+		 * We're not going to be able to get characteristics for a
+		 * device that's specified as a path, so don't even try.
+		 * Conveniently, this allows us to avoid chattering on
+		 * Serengetis.
+		 */
+		(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+		    &pif->pif_tios, 0);
+	} else {
+		char *mode = kmdb_get_ttyio_mode(kav, conout);
+
+#ifdef __sparc
+		/*
+		 * Some platforms (Starfire) define a value of `ttya' for
+		 * output-device, but neglect to provide a specific property
+		 * with the characteristics.  We'll provide a default value.
+		 */
+		if (mode == NULL && strcmp(conout, "ttya") == 0) {
+			(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+			    &pif->pif_tios, 0);
+		} else
+#endif
+		{
+			if (mode == NULL || kmdb_parse_mode(mode,
+			    &pif->pif_tios, 0) < 0) {
+				/*
+				 * Either we couldn't retrieve the
+				 * characteristics for this console, or they
+				 * weren't parseable.  The console hasn't been
+				 * set up yet, so we can't warn.  We'll have to
+				 * silently fall back to the default
+				 * characteristics.
+				 */
+				(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+				    &pif->pif_tios, 0);
+			}
+		}
+
+		if (mode != NULL)
+			kmdb_prom_free_options_prop(mode);
+	}
+
+	/* input device characteristics */
+	if ((conin = kmdb_prom_get_options_prop(kav, "input-device")) == NULL ||
+	    strcmp(conin, "keyboard") == 0) {
+		(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+		    &pif->pif_tios, 1);
+	} else if (*conin == '/') {
+		/* See similar case in output-device above */
+		(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+		    &pif->pif_tios, 1);
+	} else {
+		char *mode = kmdb_get_ttyio_mode(kav, conin);
+
+#ifdef __sparc
+		/*
+		 * Some platforms (Starfire) define a value of `ttya' for
+		 * input-device, but neglect to provide a specific property
+		 * with the characteristics.  We'll provide a default value.
+		 */
+		if (mode == NULL && strcmp(conin, "ttya") == 0) {
+			(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+			    &pif->pif_tios, 1);
+		} else
+#endif
+		{
+			if (mode == NULL || kmdb_parse_mode(mode,
+			    &pif->pif_tios, 1) < 0) {
+				/*
+				 * Either we couldn't retrieve the
+				 * characteristics for this console, or they
+				 * weren't parseable.  The console hasn't been
+				 * set up yet, so we can't warn.  We'll have to
+				 * silently fall back to the default
+				 * characteristics.
+				 */
+				(void) kmdb_parse_mode(KMDB_PROM_DEF_CONS_MODE,
+				    &pif->pif_tios, 1);
+			}
+		}
+
+		if (mode != NULL)
+			kmdb_prom_free_options_prop(mode);
+	}
+
+	/* various characteristics of the prom read/write interface */
+	pif->pif_tios.c_iflag |= ICRNL;
+	pif->pif_tios.c_lflag |= ECHO;
+	bcopy(ccs, &pif->pif_tios.c_cc, sizeof (ccs));
+
+	if (conin != NULL)
+		kmdb_prom_free_options_prop(conin);
+	if (conout != NULL)
+		kmdb_prom_free_options_prop(conout);
+}
+
+char *
+kmdb_prom_term_type(void)
+{
+	return (mdb.m_promif->pif_oterm);
+}
+
+int
+kmdb_prom_term_ctl(int req, void *arg)
+{
+	switch (req) {
+	case TCGETS: {
+		struct termios *ti = arg;
+		bcopy(&mdb.m_promif->pif_tios, ti, sizeof (struct termios));
+		return (0);
+	}
+	case TIOCGWINSZ:
+		/*
+		 * When kmdb is used over a serial console, we have no idea how
+		 * large the terminal window is.  When we're invoked on a local
+		 * console, however, we do, and need to share that information
+		 * with the debugger in order to contradict potentially
+		 * incorrect sizing information retrieved from the terminfo
+		 * database.  One specific case where this happens is with the
+		 * Intel console, which is 80x25.  The terminfo entry for
+		 * sun-color -- the default terminal type for local Intel
+		 * consoles -- was cloned from sun, which has a height of 34
+		 * rows.
+		 */
+		if (mdb.m_promif->pif_oterm != NULL) {
+			struct winsize *wsz = arg;
+			wsz->ws_row = KMDB_PIF_WINSIZE_ROWS;
+			wsz->ws_col = KMDB_PIF_WINSIZE_COLS;
+			wsz->ws_xpixel = wsz->ws_ypixel = 0;
+			return (0);
+		}
+
+		return (set_errno(ENOTSUP));
+	default:
+		return (set_errno(EINVAL));
+	}
+}
+
+int
+kmdb_prom_vtop(uintptr_t virt, physaddr_t *pap)
+{
+	physaddr_t pa;
+	int rc = kmdb_kdi_vtop(virt, &pa);
+
+#ifdef	__sparc
+	if (rc < 0 && errno == EAGAIN)
+		rc = kmdb_prom_translate_virt(virt, &pa);
+#endif
+
+	if (rc == 0 && pap != NULL)
+		*pap = pa;
+
+	return (rc);
+}
+
+void
+kmdb_prom_debugger_entry(void)
+{
+	/*
+	 * While kmdb_prom_debugger_entry and kmdb_prom_debugger_exit are not
+	 * guaranteed to be called an identical number of times (an intentional
+	 * debugger fault will cause an additional entry call without a matching
+	 * exit call), we must ensure that the polled I/O entry and exit calls
+	 * match.
+	 */
+	if (mdb.m_pio == NULL) {
+		mdb.m_pio = kmdb_kdi_get_polled_io();
+
+		if (mdb.m_pio != NULL &&
+		    mdb.m_pio->cons_polledio_enter != NULL) {
+			(void) kmdb_dpi_call(
+			    (uintptr_t)mdb.m_pio->cons_polledio_enter, 1,
+			    (uintptr_t *)&mdb.m_pio->cons_polledio_argument);
+		}
+	}
+}
+
+void
+kmdb_prom_debugger_exit(void)
+{
+	if (mdb.m_pio != NULL && mdb.m_pio->cons_polledio_exit != NULL) {
+		(void) kmdb_dpi_call((uintptr_t)mdb.m_pio->cons_polledio_exit,
+		    1, (uintptr_t *)&mdb.m_pio->cons_polledio_argument);
+	}
+
+	mdb.m_pio = NULL;
+}
+
+#ifdef DEBUG
+/*
+ * The prom_* files use ASSERT, which is #defined as assfail().
+ * We need to redirect that to our assert function.
+ */
+int
+kmdb_prom_assfail(const char *assertion, const char *file, int line)
+{
+	(void) mdb_dassert(assertion, file, line);
+	/*NOTREACHED*/
+	return (0);
+}
+#endif
+
+/*
+ * Begin the initialization of the debugger/PROM interface.  Initialization is
+ * performed in two steps due to interlocking dependencies between promif and
+ * both the memory allocator and mdb_create.  The first phase is performed
+ * before either of the others have been initialized, and thus must neither
+ * attempt to allocate memory nor access/write to `mdb'.
+ */
+void
+kmdb_prom_init_begin(char *pgmname, kmdb_auxv_t *kav)
+{
+	prom_init(pgmname, kav->kav_romp);
+
+	/* Initialize the interrupt ring buffer */
+	kmdb_prom_readbuf_head = kmdb_prom_readbuf_tail;
+
+#if defined(__i386) || defined(__amd64)
+	kmdb_sysp = kav->kav_romp;
+#endif
+}
+
+/*
+ * Conclude the initialization of the debugger/PROM interface.  Memory
+ * allocation and the global `mdb' object are now available.
+ */
+void
+kmdb_prom_init_finish(kmdb_auxv_t *kav)
+{
+	mdb.m_promif = mdb_zalloc(sizeof (kmdb_promif_t), UM_SLEEP);
+	kmdb_prom_term_init(kav, mdb.m_promif);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.h b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.h
new file mode 100644
index 0000000..bdbadb5
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.h
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_PROMIF_H
+#define	_KMDB_PROMIF_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/obpdefs.h>
+#include <sys/termios.h>
+
+#include <kmdb/kmdb_promif_isadep.h>
+#include <kmdb/kmdb_auxv.h>
+#include <mdb/mdb_target.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void kmdb_prom_init_begin(char *, kmdb_auxv_t *);
+extern void kmdb_prom_init_finish(kmdb_auxv_t *);
+extern ssize_t kmdb_prom_read(void *, size_t, struct termios *);
+extern ssize_t kmdb_prom_write(const void *, size_t, struct termios *);
+extern ihandle_t kmdb_prom_get_handle(char *);
+
+extern void kmdb_prom_check_interrupt(void);
+extern int kmdb_prom_vtop(uintptr_t, physaddr_t *);
+
+extern char *kmdb_prom_term_type(void);
+extern int kmdb_prom_term_ctl(int, void *);
+
+extern void kmdb_prom_debugger_entry(void);
+extern void kmdb_prom_debugger_exit(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_PROMIF_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif_impl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_promif_impl.h
new file mode 100644
index 0000000..1a50804
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif_impl.h
@@ -0,0 +1,75 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_PROMIF_IMPL_H
+#define	_KMDB_PROMIF_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/consdev.h>
+
+#include <kmdb/kmdb_promif.h>
+#include <kmdb/kmdb_auxv.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#if defined(__i386) || defined(__amd64)
+struct boot_syscalls;
+extern struct boot_syscalls *kmdb_sysp;
+#endif
+
+/*
+ * We don't have an easy way of asking for the window size, so we're going to
+ * hard-code the current values.
+ */
+#ifdef	__sparc
+#define	KMDB_PIF_WINSIZE_ROWS	34
+#else
+#define	KMDB_PIF_WINSIZE_ROWS	25
+#endif
+#define	KMDB_PIF_WINSIZE_COLS	80
+
+typedef struct kmdb_promif {
+	char *pif_oterm;	/* term type for local console (NULL if rem) */
+	struct termios pif_tios; /* derived settings for console */
+} kmdb_promif_t;
+
+extern void kmdb_prom_init_finish_isadep(kmdb_auxv_t *);
+
+extern char *kmdb_prom_get_options_prop(kmdb_auxv_t *, char *);
+extern void kmdb_prom_free_options_prop(char *);
+
+extern ssize_t kmdb_prom_obp_writer(caddr_t, size_t);
+
+extern int kmdb_prom_stdout_is_framebuffer(kmdb_auxv_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_PROMIF_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promio.c b/usr/src/cmd/mdb/common/kmdb/kmdb_promio.c
new file mode 100644
index 0000000..42e217c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promio.c
@@ -0,0 +1,213 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * PROM I/O backend
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/obpdefs.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_io_impl.h>
+#include <kmdb/kmdb_promif.h>
+#include <kmdb/kmdb_io.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+#define	PIO_FL_TIO_READ	0x001
+
+typedef struct pio_data {
+	char		pio_name[MAXPATHLEN];
+	ihandle_t	pio_fd;
+	uint_t		pio_flags;
+	struct termios	pio_ti;
+} pio_data_t;
+
+static pid_t pio_pgrp;
+
+static ssize_t
+pio_read(mdb_io_t *io, void *buf, size_t nbytes)
+{
+	pio_data_t *pdp = io->io_data;
+
+	if (io->io_next == NULL)
+		return (kmdb_prom_read(buf, nbytes, &pdp->pio_ti));
+
+	return (IOP_READ(io->io_next, buf, nbytes));
+}
+
+static ssize_t
+pio_write(mdb_io_t *io, const void *buf, size_t nbytes)
+{
+	pio_data_t *pdp = io->io_data;
+
+	if (io->io_next == NULL)
+		return (kmdb_prom_write(buf, nbytes, &pdp->pio_ti));
+
+	return (IOP_WRITE(io->io_next, buf, nbytes));
+}
+
+static off64_t
+pio_seek(mdb_io_t *io, off64_t offset, int whence)
+{
+	if (io->io_next == NULL)
+		return (set_errno(ENOTSUP));
+
+	return (IOP_SEEK(io->io_next, offset, whence));
+}
+
+static int
+pio_ctl(mdb_io_t *io, int req, void *arg)
+{
+	pio_data_t *pdp = io->io_data;
+
+	if (io->io_next != NULL)
+		return (IOP_CTL(io->io_next, req, arg));
+
+	switch (req) {
+	case TIOCGWINSZ:
+		return (kmdb_prom_term_ctl(TIOCGWINSZ, arg));
+
+	case TCGETS: {
+		struct termios *ti = arg;
+
+		if (!(pdp->pio_flags & PIO_FL_TIO_READ)) {
+			(void) kmdb_prom_term_ctl(TCGETS, &pdp->pio_ti);
+			pdp->pio_flags |= PIO_FL_TIO_READ;
+		}
+
+		bcopy(&pdp->pio_ti, ti, sizeof (struct termios));
+
+		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gets: i: 0%o o: 0%o c: "
+		    "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag,
+		    ti->c_lflag);
+		return (0);
+	}
+
+	case TCSETSW: {
+		struct termios *ti = arg;
+
+		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: setsw: i: 0%o o: 0%o c: "
+		    "0%o l: 0%o\n", ti->c_iflag, ti->c_oflag, ti->c_cflag,
+		    ti->c_lflag);
+
+		bcopy(ti, &pdp->pio_ti, sizeof (struct termios));
+
+		return (0);
+	}
+
+	case TIOCSPGRP:
+		pio_pgrp = *(pid_t *)arg;
+		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: spgrp: %ld\n",
+		    (long)pio_pgrp);
+		return (0);
+
+	case TIOCGPGRP:
+		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: gpgrp: %ld\n",
+		    (long)pio_pgrp);
+		*(pid_t *)arg = pio_pgrp;
+		return (0);
+
+	case MDB_IOC_CTTY:
+		mdb_dprintf(MDB_DBG_CMDBUF, "pio_ctl: ignoring MDB_IOC_CTTY\n");
+		return (0);
+
+	case MDB_IOC_GETFD:
+		return (set_errno(ENOTSUP));
+
+	default:
+		warn("Unknown ioctl %d\n", req);
+		return (set_errno(EINVAL));
+	}
+}
+
+void
+pio_close(mdb_io_t *io)
+{
+	pio_data_t *pdp = io->io_data;
+
+	mdb_free(pdp, sizeof (pio_data_t));
+}
+
+static const char *
+pio_name(mdb_io_t *io)
+{
+	pio_data_t *pdp = io->io_data;
+
+	if (io->io_next == NULL)
+		return (pdp->pio_name);
+
+	return (IOP_NAME(io->io_next));
+}
+
+static const mdb_io_ops_t promio_ops = {
+	pio_read,
+	pio_write,
+	pio_seek,
+	pio_ctl,
+	pio_close,
+	pio_name,
+	no_io_link,
+	no_io_unlink,
+	no_io_setattr,
+	no_io_suspend,
+	no_io_resume
+};
+
+mdb_io_t *
+kmdb_promio_create(char *name)
+{
+	mdb_io_t *io;
+	pio_data_t *pdp;
+	ihandle_t hdl = kmdb_prom_get_handle(name);
+
+	if (hdl == -1)
+		return (NULL);
+
+	io = mdb_zalloc(sizeof (mdb_io_t), UM_SLEEP);
+	pdp = mdb_zalloc(sizeof (pio_data_t), UM_SLEEP);
+
+	strlcpy(pdp->pio_name, name, MAXPATHLEN);
+	pdp->pio_fd = hdl;
+
+#ifdef __sparc
+	pdp->pio_ti.c_oflag |= ONLCR;
+	pdp->pio_ti.c_iflag |= ICRNL;
+#endif
+	pdp->pio_ti.c_lflag |= ECHO;
+
+	io->io_data = pdp;
+	io->io_ops = &promio_ops;
+
+	return (io);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_shell.c b/usr/src/cmd/mdb/common/kmdb/kmdb_shell.c
new file mode 100644
index 0000000..5d02b80
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_shell.c
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_shell.h>
+#include <mdb/mdb_lex.h>
+
+/*ARGSUSED*/
+void
+mdb_shell_exec(char *cmd)
+{
+	yyperror("shell escape facility not available in kmdb\n");
+}
+
+/*ARGSUSED*/
+void
+mdb_shell_pipe(char *cmd)
+{
+	yyperror("shell pipe facility not available in kmdb\n");
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_start.h b/usr/src/cmd/mdb/common/kmdb/kmdb_start.h
new file mode 100644
index 0000000..07bb24d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_start.h
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_START_H
+#define	_KMDB_START_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Used for initial initialization of kmdb
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern caddr_t kmdb_main_stack;
+extern size_t kmdb_main_stack_size;
+
+extern void kmdb_first_start(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_START_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_stubs.c b/usr/src/cmd/mdb/common/kmdb/kmdb_stubs.c
new file mode 100644
index 0000000..45b9637
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_stubs.c
@@ -0,0 +1,180 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Stubs for basic system services otherwise unavailable to the debugger.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <libproc.h>
+#include <sys/time.h>
+
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kmdb_promif.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb.h>
+
+/*ARGSUSED*/
+char *
+getenv(const char *name)
+{
+	/* There aren't any environment variables here */
+	return (NULL);
+}
+
+char *
+strerror(int errnum)
+{
+	static char errnostr[16];
+
+	(void) mdb_snprintf(errnostr, sizeof (errnostr), "Error %d", errnum);
+
+	return (errnostr);
+}
+
+pid_t
+getpid(void)
+{
+	return (1);
+}
+
+/*
+ * We're trying to isolate ourselves from the rest of the world as much as
+ * possible, so we can't rely on the time in the kernel proper.  For now, we
+ * just bump a counter whenever time is requested, thus guaranteeing that
+ * things with timestamps can be compared according to order of occurrance.
+ */
+hrtime_t
+gethrtime(void)
+{
+	static hrtime_t kmdb_timestamp;
+
+	return (++kmdb_timestamp);
+}
+
+/*
+ * Signal handling
+ */
+
+/*ARGSUSED*/
+int
+sigemptyset(sigset_t *set)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+sigaddset(sigset_t *set, int signo)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+sigfillset(sigset_t *set)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+sigaction(int sig, const struct sigaction *act, struct sigaction *oact)
+{
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+kill(pid_t pid, int sig)
+{
+	if (sig == SIGABRT) {
+		mdb_printf("Debugger aborted\n");
+		exit(1);
+	}
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+proc_str2flt(const char *buf, int *ptr)
+{
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+proc_str2sig(const char *buf, int *ptr)
+{
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+proc_str2sys(const char *buf, int *ptr)
+{
+	return (-1);
+}
+
+/*ARGSUSED*/
+void
+exit(int status)
+{
+#ifdef __sparc
+	kmdb_prom_exit_to_mon();
+#else
+	extern void kmdb_dpi_reboot(void) __NORETURN;
+	static int recurse = 0;
+
+	if (!recurse) {
+		char c;
+
+		recurse = 1;
+
+		mdb_iob_printf(mdb.m_out, "Press any key to reboot\n");
+		mdb_iob_flush(mdb.m_out);
+
+		while (IOP_READ(mdb.m_term, &c, 1) != 1)
+			continue;
+		mdb_iob_printf(mdb.m_out, "%c%s", c, (c == '\n' ? "" : "\n"));
+	}
+
+	kmdb_dpi_reboot();
+#endif
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_terminfo_skel.c b/usr/src/cmd/mdb/common/kmdb/kmdb_terminfo_skel.c
new file mode 100644
index 0000000..dc5f3e7
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_terminfo_skel.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ */
+/* BEGIN PROLOGUE */
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * kmdb_terminfo_skel.c is the skeleton used to generate
+ * kmdb_terminfo.c, which contains the kmdb-specific version
+ * of terminfo.
+ */
+
+#include <strings.h>
+#include <unistd.h>
+#include <curses.h>
+
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb.h>
+
+typedef enum {
+	TIO_ATTR_REQSTR,
+	TIO_ATTR_STR,
+	TIO_ATTR_BOOL,
+	TIO_ATTR_INT
+} termio_attr_type_t;
+
+typedef struct {
+	const char *ta_name;
+	termio_attr_type_t ta_type;
+	const void *ta_data;
+} termio_attr_t;
+
+typedef struct {
+	const char *td_name;
+	const termio_attr_t *td_data;
+} termio_desc_t;
+
+/* END PROLOGUE */
+
+/*
+ * tigen will insert the following definitions here:
+ *
+ *	<term>_attrs  (one per terminal type passed to tigen)
+ *	termio_db
+ */
+
+/* BEGIN EPILOGUE */
+
+static const termio_desc_t *tdp;
+
+/*ARGSUSED*/
+int
+setupterm(char *name, int fd, int *err)
+{
+	for (tdp = termio_db; tdp->td_name != NULL; tdp++) {
+		if (strcmp(tdp->td_name, name) == 0)
+			return (OK);
+	}
+
+	*err = 0;
+	return (ERR);
+}
+
+int
+restartterm(char *name, int fd, int *err)
+{
+	const termio_desc_t *otdp = tdp;
+	int status;
+
+	if ((status = setupterm(name, fd, err)) != OK)
+		tdp = otdp; /* restore old terminal settings */
+
+	return (status);
+}
+
+const char *
+tigetstr(const char *name)
+{
+	const termio_attr_t *tap;
+
+	for (tap = tdp->td_data; tap->ta_name != NULL; tap++) {
+		if (strcmp(tap->ta_name, name) == 0) {
+			if (tap->ta_type == TIO_ATTR_REQSTR ||
+			    tap->ta_type == TIO_ATTR_STR)
+				return (tap->ta_data);
+			else
+				return ((char *)-1);
+		}
+	}
+
+	return (NULL);
+}
+
+int
+tigetflag(const char *name)
+{
+	const termio_attr_t *tap;
+
+	for (tap = tdp->td_data; tap->ta_name != NULL; tap++) {
+		if (strcmp(tap->ta_name, name) == 0) {
+			if (tap->ta_type == TIO_ATTR_BOOL)
+				return ((uintptr_t)tap->ta_data);
+			else
+				return (-1);
+		}
+	}
+
+	return (0);
+}
+
+int
+tigetnum(const char *name)
+{
+	const termio_attr_t *tap;
+
+	for (tap = tdp->td_data; tap->ta_name != NULL; tap++) {
+		if (strcmp(tap->ta_name, name) == 0) {
+			if (tap->ta_type == TIO_ATTR_INT)
+				return ((uintptr_t)tap->ta_data);
+			else
+				return (-2);
+		}
+	}
+
+	return (-1);
+}
+
+/* END EPILOGUE */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.c b/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.c
new file mode 100644
index 0000000..655f216
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.c
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_io.h>
+
+#define	UMEM_STANDALONE
+#include <umem_impl.h>
+
+/*
+ * The standalone umem requires that kmdb provide some error-handling
+ * services.  These are them.
+ */
+
+/*ARGSUSED*/
+int
+__umem_assert_failed(const char *assertion, const char *file, int line)
+{
+#ifdef DEBUG
+	(void) mdb_dassert(assertion, file, line);
+	/*NOTREACHED*/
+#endif
+	return (0);
+}
+
+void
+umem_panic(const char *format, ...)
+{
+	va_list alist;
+
+	va_start(alist, format);
+	vfail(format, alist);
+	va_end(alist);
+}
+
+void
+umem_err_recoverable(const char *format, ...)
+{
+	va_list alist;
+
+	va_start(alist, format);
+	vwarn(format, alist);
+	va_end(alist);
+}
+
+int
+umem_vsnprintf(char *s, size_t n, const char *format, va_list ap)
+{
+	return (mdb_iob_vsnprintf(s, n, format, ap));
+}
+
+int
+umem_snprintf(char *s, size_t n, const char *format, ...)
+{
+	va_list ap;
+	int rc;
+
+	va_start(ap, format);
+	rc = umem_vsnprintf(s, n, format, ap);
+	va_end(ap);
+
+	return (rc);
+}
+
+/* These aren't atomic, but we're not MT, so it doesn't matter */
+uint32_t
+umem__atomic_add_32_nv(uint32_t *target, int32_t delta)
+{
+	return (*target = *target + delta);
+}
+
+void
+umem__atomic_add_64(uint64_t *target, int64_t delta)
+{
+	*target = *target + delta;
+}
+
+/*
+ * Standalone umem must be manually initialized
+ */
+void
+mdb_umem_startup(caddr_t base, size_t len, size_t pgsize)
+{
+	umem_startup(base, len, pgsize, base, base + len);
+}
+
+/*
+ * The kernel will tell us when there's more memory available for us to use.
+ * This is most common on amd64, which boots with only 4G of VA available, and
+ * later expands to the full 64-bit address space.
+ */
+int
+mdb_umem_add(caddr_t base, size_t len)
+{
+	return (umem_add(base, len));
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.h b/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.h
new file mode 100644
index 0000000..627a0f9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_umemglue.h
@@ -0,0 +1,47 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_UMEMGLUE_H
+#define	_KMDB_UMEMGLUE_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * kmdb interface to libumem
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void mdb_umem_startup(caddr_t, size_t, size_t);
+extern int mdb_umem_add(caddr_t, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_UMEMGLUE_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_wr.c b/usr/src/cmd/mdb/common/kmdb/kmdb_wr.c
new file mode 100644
index 0000000..ff62dd8
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_wr.c
@@ -0,0 +1,224 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The communication mechanism for requesting that the driver perform work on
+ * behalf of the debugger.  Messages are passed and processed in FIFO order,
+ * with no provision for high priority messages.  High priority messages, such
+ * as debugger termination requests, should be passed using a different
+ * mechanism.
+ *
+ * Two FIFO queues are used for communication - one from the debugger to the
+ * driver, known as the driver_notify queue, and one from the driver to the
+ * debugger, known as the debugger_notify queue.  Messages are added to one
+ * queue, processed by the party on the other end, and are sent back as
+ * acknowledgements on the other queue.  All messages must be acknowledged, in
+ * part because the party who sent the message is the only one who can free it.
+ *
+ * Debugger-initiated work requests are usually triggered by dcmds such as
+ * ::load.  In the case of a ::load, the debugger adds a load request to the
+ * driver_notify queue.  The driver removes the request from the queue and
+ * processes it.  When processing is complete, the message is turned into an
+ * acknowledgement, and completion status is added.  The message is then added
+ * to the debugger_notify queue.  Upon receipt, the debugger removes the
+ * message from the queue, notes the completion status, and frees it.
+ *
+ * The driver can itself initiate unsolicited work, such as the automatic
+ * loading of a dmod in response to a krtld module load notification.  In this
+ * case, the driver loads the module and creates a work-completion message.
+ * This completion is identical to the one sent in the solicited load case
+ * above, with the exception of the acknowledgement bit, which isn't be set.
+ * When the debugger receives the completion message, it notes the completion
+ * status, and sends the message back to the driver via the driver_notify queue,
+ * this time with the acknowledgement bit set.
+ */
+
+#include <sys/types.h>
+
+#include <kmdb/kmdb_asmutil.h>
+#include <kmdb/kmdb_wr_impl.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb.h>
+
+/*
+ * Called by the driver to pass a message to the debugger.  The debugger could
+ * start running at any time.  Nodes are added to the queue in FIFO order, but
+ * with links pointing in reverse order.
+ */
+void
+kmdb_wr_debugger_notify(void *arg)
+{
+	kmdb_wr_t *new = arg;
+	kmdb_wr_t *curtail;
+
+	new->wn_next = new->wn_prev = NULL;
+	membar_producer();
+
+	do {
+		if ((curtail = mdb.m_dbgwrtail) == NULL) {
+			/*
+			 * The queue is empty, because tail will only be NULL if
+			 * head is NULL too.  We're the only one who can add
+			 * to the queue, so we can blindly add our node.  The
+			 * debugger can't look at tail until head is non-NULL,
+			 * so we set tail first.
+			 */
+			mdb.m_dbgwrtail = new;
+			membar_producer();
+			mdb.m_dbgwrhead = new;
+			membar_producer();
+			break;
+		}
+
+		/*
+		 * Point the new node at the current tail.  Attempt to set tail
+		 * to point to our new node, but only as long as tail is what
+		 * we think it is.
+		 */
+		new->wn_prev = curtail;
+		membar_producer();
+	} while (cas((uintptr_t *)&mdb.m_dbgwrtail, (uintptr_t)curtail,
+	    (uintptr_t)new) != (uintptr_t)curtail);
+}
+
+/*
+ * Called by the debugger to receive messages from the driver.  The driver
+ * has added the nodes in FIFO order, but has only set the prev pointers.  We
+ * have to correct that before processing the nodes.  This routine will not
+ * be preempted.
+ */
+int
+kmdb_wr_debugger_process(int (*cb)(kmdb_wr_t *, void *), void *arg)
+{
+	kmdb_wr_t *wn, *wnn;
+	int i;
+
+	if (mdb.m_dbgwrhead == NULL)
+		return (0); /* The queue is empty, so there's nothing to do */
+
+	/* Re-establish the next links so we can traverse in FIFO order */
+	mdb.m_dbgwrtail->wn_next = NULL;
+	for (wn = mdb.m_dbgwrtail; wn->wn_prev != NULL;
+	    wn = wn->wn_prev)
+		wn->wn_prev->wn_next = wn;
+
+	/* We don't own wn after we've invoked the callback */
+	wn = mdb.m_dbgwrhead;
+	i = 0;
+	do {
+		wnn = wn->wn_next;
+		i += cb(wn, arg);
+	} while ((wn = wnn) != NULL);
+
+	mdb.m_dbgwrhead = mdb.m_dbgwrtail = NULL;
+
+	return (i);
+}
+
+/*
+ * Called by the debugger to check queue status.
+ */
+int
+kmdb_wr_debugger_notify_isempty(void)
+{
+	return (mdb.m_dbgwrhead == NULL);
+}
+
+/*
+ * Called by the debugger to pass a message to the driver.  This routine will
+ * not be preempted.
+ */
+void
+kmdb_wr_driver_notify(void *arg)
+{
+	kmdb_wr_t *new = arg;
+
+	/*
+	 * We restrict ourselves to manipulating the rear of the queue.  We
+	 * don't look at the head unless the tail is NULL.
+	 */
+	if (mdb.m_drvwrtail == NULL) {
+		new->wn_next = new->wn_prev = NULL;
+		mdb.m_drvwrhead = mdb.m_drvwrtail = new;
+	} else {
+		mdb.m_drvwrtail->wn_next = new;
+		new->wn_prev = mdb.m_drvwrtail;
+		new->wn_next = NULL;
+		mdb.m_drvwrtail = new;
+	}
+}
+
+/*
+ * Called by the driver to receive messages from the debugger.  The debugger
+ * could start running at any time.
+ *
+ * NOTE: This routine may run *after* mdb_destroy(), and may *NOT* use any MDB
+ * services.
+ */
+int
+kmdb_wr_driver_process(int (*cb)(kmdb_wr_t *, void *), void *arg)
+{
+	kmdb_wr_t *worklist, *wn, *wnn;
+	int rc, rv, i;
+
+	if ((worklist = mdb.m_drvwrhead) == NULL) {
+		return (0); /* The queue is empty, so there's nothing to do */
+	}
+
+	mdb.m_drvwrhead = NULL;
+	/* The debugger uses tail, so enqueues still work */
+	membar_producer();
+	mdb.m_drvwrtail = NULL;
+	membar_producer();
+
+	/*
+	 * The current set of messages has been removed from the queue, so
+	 * we can process them at our leisure.
+	 */
+
+	wn = worklist;
+	rc = i = 0;
+	do {
+		wnn = wn->wn_next;
+		if ((rv = cb(wn, arg)) < 0)
+			rc = -1;
+		else
+			i += rv;
+	} while ((wn = wnn) != NULL);
+
+	return (rc == 0 ? i : -1);
+}
+
+/*
+ * Called by the debugger to check queue status
+ */
+int
+kmdb_wr_driver_notify_isempty(void)
+{
+	return (mdb.m_drvwrhead == NULL);
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_wr.h b/usr/src/cmd/mdb/common/kmdb/kmdb_wr.h
new file mode 100644
index 0000000..5197393
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_wr.h
@@ -0,0 +1,51 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_WR_H
+#define	_KMDB_WR_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct kmdb_wr kmdb_wr_t;
+
+extern void kmdb_wr_debugger_notify(void *);
+extern int kmdb_wr_debugger_process(int (*)(kmdb_wr_t *, void *), void *);
+extern int kmdb_wr_debugger_notify_isempty(void);
+extern void kmdb_wr_driver_notify(void *);
+extern int kmdb_wr_driver_process(int (*)(kmdb_wr_t *, void *), void *);
+extern int kmdb_wr_driver_notify_isempty(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_WR_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_wr_impl.h b/usr/src/cmd/mdb/common/kmdb/kmdb_wr_impl.h
new file mode 100644
index 0000000..424ec3e
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_wr_impl.h
@@ -0,0 +1,119 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KMDB_WR_IMPL_H
+#define	_KMDB_WR_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kmdb_wr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	WNTASK_DMOD_LOAD	0x0001		/* Load a specific dmod */
+#define	WNTASK_DMOD_LOAD_ALL	0x0002		/* Load all dmods for kmods */
+#define	WNTASK_DMOD_UNLOAD	0x0004		/* Unload a specific dmod */
+#define	WNTASK_DMOD_PATH_CHANGE	0x0008		/* Change dmod search path */
+
+#define	WNFLAGS_NOFREE		0x0001		/* Don't free this wr on ack */
+
+#define	WNTASK_ACK		0x8000		/* Acknowledgement of req */
+
+#define	WR_ISACK(wr)		((((kmdb_wr_t *)(wr))->wn_task) & WNTASK_ACK)
+#define	WR_ACK(wr)		(((kmdb_wr_t *)(wr))->wn_task) |= WNTASK_ACK
+#define	WR_TASK(wr)		((((kmdb_wr_t *)(wr))->wn_task) & ~WNTASK_ACK)
+
+struct kmdb_wr {
+	struct kmdb_wr		*wn_next;	/* List of work requests */
+	struct kmdb_wr		*wn_prev;	/* List of work requests */
+	ushort_t		wn_task;	/* Task to be performed */
+	ushort_t		wn_flags;	/* Flags for this request */
+	uint_t			wn_errno;	/* Status for completed reqs */
+};
+
+/*
+ * Debugger-initiated loads: Debugger creates, passes to driver, driver loads
+ * the module, returns the request as an ack.  Driver-initiated loads: driver
+ * creates, loads module, passes to debugger as announcement, debugger returns
+ * as an ack.
+ */
+typedef struct kmdb_wr_load {
+	kmdb_wr_t		dlr_node;
+
+	/* Supplied by requestor */
+	char			*dlr_fname;
+
+	/* Filled in by driver upon successful completion */
+	struct modctl		*dlr_modctl;
+
+	/*
+	 * Used by the driver to track outstanding driver-initiated
+	 * notifications for leak prevention.
+	 */
+	struct kmdb_wr_load	*dlr_next;
+	struct kmdb_wr_load	*dlr_prev;
+} kmdb_wr_load_t;
+
+#define	dlr_errno	dlr_node.wn_errno
+
+/*
+ * The debugger creates a request for a module to be unloaded, and passes it
+ * to the driver.  The driver unloads the module, and returns the message to
+ * the debugger as an ack.
+ */
+typedef struct kmdb_wr_unload {
+	kmdb_wr_t		dur_node;
+
+	/* Supplied by requestor */
+	char			*dur_modname;
+	struct modctl		*dur_modctl;
+} kmdb_wr_unload_t;
+
+#define	dur_errno	dur_node.wn_errno
+
+/*
+ * The debugger creates a new path-change "request" when the dmod search path
+ * changes, and sends it to the driver.  The driver hangs onto the request
+ * until either the path changes again or the debugger is unloaded.  Either way,
+ * the state change request is passed back at that time as an ack.
+ */
+typedef struct kmdb_wr_path {
+	kmdb_wr_t		dpth_node;
+
+	/* Supplied by requestor */
+	const char		**dpth_path;
+	size_t			dpth_pathlen;
+} kmdb_wr_path_t;
+
+#define	dpth_errno	dpth_node.wn_errno
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KMDB_WR_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kvm.h b/usr/src/cmd/mdb/common/kmdb/kvm.h
new file mode 100644
index 0000000..757c7f4
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kvm.h
@@ -0,0 +1,158 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KVM_H
+#define	_KVM_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The kmdb target
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_target.h>
+#include <kmdb/kmdb_dpi.h>
+#include <kmdb/kvm_isadep.h>
+#include <kmdb/kvm_cpu.h>
+
+#include <sys/kobj.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define	KM_F_PRIMARY		1
+
+#define	KMT_TRAP_NOTENUM	-1	/* Glob for unnamed traps */
+#define	KMT_TRAP_ALL		-2	/* Glob for all traps */
+
+typedef struct kmt_module {
+	mdb_list_t	km_list;	/* List forward/back pointers */
+	char		*km_name;	/* Module name */
+	char		km_seen;
+	GElf_Ehdr	km_ehdr;
+
+	mdb_gelf_symtab_t *km_symtab;
+	Shdr		km_symtab_hdr;
+	Shdr		km_strtab_hdr;
+	const void	*km_symtab_va;
+	const void	*km_strtab_va;
+
+	uintptr_t	km_text_va;
+	size_t		km_text_size;
+	uintptr_t	km_data_va;
+	size_t		km_data_size;
+	uintptr_t	km_bss_va;
+	size_t		km_bss_size;
+	const void	*km_ctf_va;
+	size_t		km_ctf_size;
+
+	ctf_file_t	*km_ctfp;
+	struct modctl	km_modctl;
+	struct module	km_module;
+	int		km_flags;
+} kmt_module_t;
+
+typedef struct kmt_data {
+	const mdb_tgt_regdesc_t	*kmt_rds;	/* Register description table */
+	mdb_nv_t	kmt_modules;		/* Hash table of modules */
+	mdb_list_t	kmt_modlist;		/* List of mods in load order */
+	caddr_t		kmt_writemap;		/* Used to map PAs for writes */
+	size_t		kmt_writemapsz;		/* Size of same */
+	mdb_map_t	kmt_map;		/* Persistant map for callers */
+	ulong_t		*kmt_trapmap;
+	size_t		kmt_trapmax;
+	kmt_cpu_t	*kmt_cpu;		/* CPU-specific plugin */
+	int		kmt_cpu_retry;		/* Try CPU detect again? */
+	int		kmt_symavail;		/* Symbol resolution allowed */
+	uint_t		kmt_narmedbpts;		/* Number of armed brkpts */
+#if defined(__i386) || defined(__amd64)
+	struct {
+		GElf_Sym	_kmt_cmnint;
+		GElf_Sym	_kmt_cmntrap;
+		GElf_Sym	_kmt_sysenter;
+#if defined(__amd64)
+		GElf_Sym	_kmt_syscall;
+#endif
+	} kmt_intrsyms;
+#endif
+} kmt_data_t;
+
+#if defined(__i386) || defined(__amd64)
+#define	kmt_cmnint	kmt_intrsyms._kmt_cmnint
+#define	kmt_cmntrap	kmt_intrsyms._kmt_cmntrap
+#endif
+
+typedef struct kmt_defbp {
+	mdb_list_t dbp_bplist;
+	char *dbp_objname;
+	char *dbp_symname;
+	int dbp_ref;
+} kmt_defbp_t;
+
+typedef struct kmt_brkpt {
+	uintptr_t kb_addr;			/* Breakpoint address */
+	mdb_instr_t kb_oinstr;			/* Replaced instruction */
+} kmt_brkpt_t;
+
+typedef struct kmt_bparg {
+	uintptr_t ka_addr;			/* Explicit address */
+	char *ka_symbol;			/* Symbolic name */
+	kmt_defbp_t *ka_defbp;
+} kmt_bparg_t;
+
+extern void kmt_printregs(const mdb_tgt_gregset_t *gregs);
+
+extern const char *kmt_def_dismode(void);
+
+extern void kmt_init_isadep(mdb_tgt_t *);
+extern void kmt_startup_isadep(mdb_tgt_t *);
+
+extern ssize_t kmt_write(mdb_tgt_t *, const void *, size_t, uintptr_t);
+extern ssize_t kmt_pwrite(mdb_tgt_t *, const void *, size_t, physaddr_t);
+extern ssize_t kmt_rw(mdb_tgt_t *, void *, size_t, uint64_t,
+    ssize_t (*)(void *, size_t, uint64_t));
+extern ssize_t kmt_writer(void *, size_t, uint64_t);
+extern ssize_t kmt_ioread(mdb_tgt_t *, void *, size_t, uintptr_t);
+extern ssize_t kmt_iowrite(mdb_tgt_t *, const void *, size_t, uintptr_t);
+
+extern int kmt_step_out(mdb_tgt_t *, uintptr_t *);
+extern int kmt_step_branch(mdb_tgt_t *);
+extern int kmt_next(mdb_tgt_t *, uintptr_t *);
+
+extern int kmt_stack(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int kmt_stackv(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int kmt_stackr(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern int kmt_cpustack(uintptr_t, uint_t, int, const mdb_arg_t *, int, int);
+
+extern const char *kmt_trapname(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KVM_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kvm_cpu.c b/usr/src/cmd/mdb/common/kmdb/kvm_cpu.c
new file mode 100644
index 0000000..ad4ecf4
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kvm_cpu.c
@@ -0,0 +1,98 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * CPU-specific target implementation
+ *
+ * Each CPU provides a set of debugging facilities.  We have per-CPU "modules",
+ * each of which exposes a kmt_cpu_t.  When initialized, these modules will
+ * install dcmds, walkers, and the like in order to allow the user to take
+ * advantage of features specific to the CPU being used.
+ */
+
+#include <kmdb/kmdb_kdi.h>
+#include <kmdb/kvm_cpu_impl.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb.h>
+
+static kmt_cpu_ctor_f *const kmt_cpu_ctors[] = {
+#if defined(__i386) || defined(__amd64)
+	kmt_cpu_amd_create,
+	kmt_cpu_p4_create,
+#if defined(__i386)
+	kmt_cpu_p6_create,
+#endif	/* __i386 */
+#endif	/* __i386 || __amd64 */
+	NULL
+};
+
+kmt_cpu_t *
+kmt_cpu_create(mdb_tgt_t *t)
+{
+	kmt_cpu_t *cpu;
+	int retry = 0;
+	int i;
+
+	for (i = 0; kmt_cpu_ctors[i] != NULL; i++) {
+		if ((cpu = kmt_cpu_ctors[i](t)) != NULL)
+			return (cpu);
+		else if (errno == EAGAIN)
+			retry = 1;
+	}
+
+	if (retry)
+		(void) set_errno(EAGAIN);
+
+	return (NULL);
+}
+
+void
+kmt_cpu_destroy(kmt_cpu_t *cpu)
+{
+	if (cpu != NULL)
+		cpu->kmt_cpu_ops->kco_destroy(cpu);
+}
+
+int
+kmt_cpu_step_branch(mdb_tgt_t *t, kmt_cpu_t *cpu)
+{
+	if (cpu == NULL || cpu->kmt_cpu_ops->kco_step_branch == NULL)
+		return (set_errno(EMDB_TGTHWNOTSUP));
+
+	return (cpu->kmt_cpu_ops->kco_step_branch(cpu, t));
+}
+
+const char *
+kmt_cpu_name(kmt_cpu_t *cpu)
+{
+	if (cpu == NULL)
+		return ("none");
+	else
+		return (cpu->kmt_cpu_ops->kco_name(cpu));
+}
diff --git a/usr/src/cmd/mdb/common/kmdb/kvm_cpu.h b/usr/src/cmd/mdb/common/kmdb/kvm_cpu.h
new file mode 100644
index 0000000..3a315a2
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kvm_cpu.h
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KVM_CPU_H
+#define	_KVM_CPU_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * CPU-specific target implementation
+ *
+ * Each CPU provides a set of debugging facilities.  We have per-CPU "modules",
+ * each of which exposes a kmt_cpu_t.  When initialized, these modules will
+ * install dcmds, walkers, and the like in order to allow the user to take
+ * advantage of features specific to the CPU being used.
+ */
+
+#include <mdb/mdb_target.h>
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct kmt_cpu kmt_cpu_t;
+
+extern kmt_cpu_t *kmt_cpu_create(mdb_tgt_t *);
+extern void kmt_cpu_destroy(kmt_cpu_t *);
+
+extern const char *kmt_cpu_name(kmt_cpu_t *);
+
+#if defined(__i386) || defined(__amd64)
+extern int kmt_cpu_step_branch(mdb_tgt_t *, kmt_cpu_t *);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KVM_CPU_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/kvm_cpu_impl.h b/usr/src/cmd/mdb/common/kmdb/kvm_cpu_impl.h
new file mode 100644
index 0000000..a22b8c9
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/kvm_cpu_impl.h
@@ -0,0 +1,64 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _KVM_CPU_IMPL_H
+#define	_KVM_CPU_IMPL_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <kmdb/kvm_cpu.h>
+#include <mdb/mdb_target.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct kmt_cpu_ops {
+	void (*kco_destroy)(kmt_cpu_t *);
+	const char *(*kco_name)(kmt_cpu_t *);
+	int (*kco_step_branch)(kmt_cpu_t *, mdb_tgt_t *);
+} kmt_cpu_ops_t;
+
+struct kmt_cpu {
+	kmt_cpu_ops_t *kmt_cpu_ops;	/* Pointer to ops vector */
+	void *kmt_cpu_data;		/* Private storage */
+};
+
+typedef kmt_cpu_t *kmt_cpu_ctor_f(mdb_tgt_t *);
+
+#if defined(__i386) || defined(__amd64)
+extern kmt_cpu_ctor_f kmt_cpu_amd_create;
+extern kmt_cpu_ctor_f kmt_cpu_p4_create;
+#if defined(__i386)
+extern kmt_cpu_ctor_f kmt_cpu_p6_create;
+#endif	/* __i386 */
+#endif	/* __i386 || __amd64 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _KVM_CPU_IMPL_H */
diff --git a/usr/src/cmd/mdb/common/kmdb/mapfile_skel b/usr/src/cmd/mdb/common/kmdb/mapfile_skel
new file mode 100644
index 0000000..f8f70dd
--- /dev/null
+++ b/usr/src/cmd/mdb/common/kmdb/mapfile_skel
@@ -0,0 +1,56 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ */
+/* BEGIN PROLOGUE */
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This file is used to limit the symbols that are to be exported from the
+ * debugger.  This ensures that dmods follow the module API.
+ */
+
+{
+	global:
+
+/* END PROLOGUE */
+/* BEGIN EPILOGUE */
+		/*
+		 * Secret additions to the module API
+		 */
+
+		/* Implementation detail of the ctype macros */
+		__ctype;
+		/* There should be only one - ours */
+		errno;
+
+		mdb_tgt_aread;
+		mdb_dis_create;
+		mdb_dis_destroy;
+
+	local:
+		*;
+};
+/* END EPILOGUE */
diff --git a/usr/src/cmd/mdb/common/libstand/ctime.c b/usr/src/cmd/mdb/common/libstand/ctime.c
new file mode 100644
index 0000000..2b67917
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstand/ctime.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * This localtime is a modified version of offtime from libc, which does not
+ * bother to figure out the time zone from the kernel, from environment
+ * variables, or from Unix files.
+ */
+
+#include <sys/types.h>
+#include <sys/salib.h>
+#include <tzfile.h>
+#include <errno.h>
+
+static int mon_lengths[2][MONS_PER_YEAR] = {
+	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31,
+	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static int year_lengths[2] = {
+	DAYS_PER_NYEAR, DAYS_PER_LYEAR
+};
+
+struct tm *
+localtime(const time_t *clock)
+{
+	struct tm *tmp;
+	long days;
+	long rem;
+	int y;
+	int yleap;
+	int *ip;
+	static struct tm tm;
+
+	tmp = &tm;
+	days = *clock / SECS_PER_DAY;
+	rem = *clock % SECS_PER_DAY;
+	while (rem < 0) {
+		rem += SECS_PER_DAY;
+		--days;
+	}
+	while (rem >= SECS_PER_DAY) {
+		rem -= SECS_PER_DAY;
+		++days;
+	}
+	tmp->tm_hour = (int)(rem / SECS_PER_HOUR);
+	rem = rem % SECS_PER_HOUR;
+	tmp->tm_min = (int)(rem / SECS_PER_MIN);
+	tmp->tm_sec = (int)(rem % SECS_PER_MIN);
+	tmp->tm_wday = (int)((EPOCH_WDAY + days) % DAYS_PER_WEEK);
+	if (tmp->tm_wday < 0)
+		tmp->tm_wday += DAYS_PER_WEEK;
+	y = EPOCH_YEAR;
+	if (days >= 0) {
+		for (;;) {
+			yleap = isleap(y);
+			if (days < (long)year_lengths[yleap])
+				break;
+			if (++y > 9999) {
+				errno = EOVERFLOW;
+				return (NULL);
+			}
+			days = days - (long)year_lengths[yleap];
+		}
+	} else {
+		do {
+			if (--y < 0) {
+				errno = EOVERFLOW;
+				return (NULL);
+			}
+			yleap = isleap(y);
+			days = days + (long)year_lengths[yleap];
+		} while (days < 0);
+	}
+	tmp->tm_year = y - TM_YEAR_BASE;
+	tmp->tm_yday = (int)days;
+	ip = mon_lengths[yleap];
+	for (tmp->tm_mon = 0; days >= (long)ip[tmp->tm_mon]; ++(tmp->tm_mon))
+		days = days - (long)ip[tmp->tm_mon];
+	tmp->tm_mday = (int)(days + 1);
+	tmp->tm_isdst = 0;
+
+	return (tmp);
+}
+
+/*
+ * So is ctime...
+ */
+
+/*
+ * This routine converts time as follows.
+ * The epoch is 0000 Jan 1 1970 GMT.
+ * The argument time is in seconds since then.
+ * The localtime(t) entry returns a pointer to an array
+ * containing
+ *  seconds (0-59)
+ *  minutes (0-59)
+ *  hours (0-23)
+ *  day of month (1-31)
+ *  month (0-11)
+ *  year-1970
+ *  weekday (0-6, Sun is 0)
+ *  day of the year
+ *  daylight savings flag
+ *
+ * The routine corrects for daylight saving
+ * time and will work in any time zone provided
+ * "timezone" is adjusted to the difference between
+ * Greenwich and local standard time (measured in seconds).
+ * In places like Michigan "daylight" must
+ * be initialized to 0 to prevent the conversion
+ * to daylight time.
+ * There is a table which accounts for the peculiarities
+ * undergone by daylight time in 1974-1975.
+ *
+ * The routine does not work
+ * in Saudi Arabia which runs on Solar time.
+ *
+ * asctime(tvec)
+ * where tvec is produced by localtime
+ * returns a ptr to a character string
+ * that has the ascii time in the form
+ *	Thu Jan 01 00:00:00 1970\n\0
+ *	01234567890123456789012345
+ *	0	  1	    2
+ *
+ * ctime(t) just calls localtime, then asctime.
+ *
+ * tzset() looks for an environment variable named
+ * TZ.
+ * If the variable is present, it will set the external
+ * variables "timezone", "altzone", "daylight", and "tzname"
+ * appropriately. It is called by localtime, and
+ * may also be called explicitly by the user.
+ */
+
+
+
+#define	dysize(A) (((A)%4)? 365: 366)
+#define	CBUFSIZ 26
+
+/*
+ * POSIX.1c standard version of the function asctime_r.
+ * User gets it via static asctime_r from the header file.
+ */
+char *
+__posix_asctime_r(const struct tm *t, char *cbuf)
+{
+	const char *Date = "Day Mon 00 00:00:00 1900\n";
+	const char *Day  = "SunMonTueWedThuFriSat";
+	const char *Month = "JanFebMarAprMayJunJulAugSepOctNovDec";
+	static char *ct_numb();
+	const char *ncp;
+	const int *tp;
+	char *cp;
+
+	if (t == NULL)
+		return (NULL);
+
+	cp = cbuf;
+	for (ncp = Date; *cp++ = *ncp++; /* */);
+	ncp = Day + (3*t->tm_wday);
+	cp = cbuf;
+	*cp++ = *ncp++;
+	*cp++ = *ncp++;
+	*cp++ = *ncp++;
+	cp++;
+	tp = &t->tm_mon;
+	ncp = Month + ((*tp) * 3);
+	*cp++ = *ncp++;
+	*cp++ = *ncp++;
+	*cp++ = *ncp++;
+	cp = ct_numb(cp, *--tp);
+	cp = ct_numb(cp, *--tp+100);
+	cp = ct_numb(cp, *--tp+100);
+	cp = ct_numb(cp, *--tp+100);
+	if (t->tm_year > 9999) {
+		errno = EOVERFLOW;
+		return (NULL);
+	} else {
+		uint_t hun = 19 + (t->tm_year / 100);
+		cp[1] = (hun / 10) + '0';
+		cp[2] = (hun % 10) + '0';
+	}
+	cp += 2;
+	cp = ct_numb(cp, t->tm_year+100);
+	return (cbuf);
+}
+
+/*
+ * POSIX.1c Draft-6 version of the function asctime_r.
+ * It was implemented by Solaris 2.3.
+ */
+char *
+_asctime_r(const struct tm *t, char *cbuf, int buflen)
+{
+	if (buflen < CBUFSIZ) {
+		errno = ERANGE;
+		return (NULL);
+	}
+	return (__posix_asctime_r(t, cbuf));
+}
+
+char *
+ctime(const time_t *t)
+{
+	return (asctime(localtime(t)));
+}
+
+
+char *
+asctime(const struct tm *t)
+{
+	static char cbuf[CBUFSIZ];
+
+	return (_asctime_r(t, cbuf, CBUFSIZ));
+}
+
+
+static char *
+ct_numb(char *cp, int n)
+{
+	cp++;
+	if (n >= 10)
+		*cp++ = (n/10)%10 + '0';
+	else
+		*cp++ = ' ';		/* Pad with blanks */
+	*cp++ = n%10 + '0';
+	return (cp);
+}
diff --git a/usr/src/cmd/mdb/common/libstand/ctype.c b/usr/src/cmd/mdb/common/libstand/ctype.c
new file mode 100644
index 0000000..4037d0f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstand/ctype.c
@@ -0,0 +1,50 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <ctype.h>
+
+unsigned char __ctype[129] =
+{
+	0, /* EOF */
+	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,
+	_C,	_S|_C,	_S|_C,	_S|_C,	_S|_C,	_S|_C,	_C,	_C,
+	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,
+	_C,	_C,	_C,	_C,	_C,	_C,	_C,	_C,
+	_S|_B,	_P,	_P,	_P,	_P,	_P,	_P,	_P,
+	_P,	_P,	_P,	_P,	_P,	_P,	_P,	_P,
+	_N|_X,	_N|_X,	_N|_X,	_N|_X,	_N|_X,	_N|_X,	_N|_X,	_N|_X,
+	_N|_X,	_N|_X,	_P,	_P,	_P,	_P,	_P,	_P,
+	_P,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U|_X,	_U,
+	_U,	_U,	_U,	_U,	_U,	_U,	_U,	_U,
+	_U,	_U,	_U,	_U,	_U,	_U,	_U,	_U,
+	_U,	_U,	_U,	_P,	_P,	_P,	_P,	_P,
+	_P,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L|_X,	_L,
+	_L,	_L,	_L,	_L,	_L,	_L,	_L,	_L,
+	_L,	_L,	_L,	_L,	_L,	_L,	_L,	_L,
+	_L,	_L,	_L,	_P,	_P,	_P,	_P,	_C,
+};
diff --git a/usr/src/cmd/mdb/common/libstand/errno.c b/usr/src/cmd/mdb/common/libstand/errno.c
new file mode 100644
index 0000000..c7bea1b
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstand/errno.c
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/salib.h>
+#include <errno.h>
+
+int	errno;
diff --git a/usr/src/cmd/mdb/common/libstand/getopt.c b/usr/src/cmd/mdb/common/libstand/getopt.c
new file mode 100644
index 0000000..286c26e
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstand/getopt.c
@@ -0,0 +1,97 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/salib.h>
+
+#define	EOF	(-1)
+
+int opterr = 1, optind = 1, optopt = 0;
+char *optarg = NULL;
+int _sp = 1;
+
+extern void warn(const char *, ...);
+
+void
+getopt_reset(void)
+{
+	opterr = 1;
+	optind = 1;
+	optopt = 0;
+	optarg = NULL;
+	_sp = 1;
+}
+
+int
+getopt(int argc, char *const *argv, const char *opts)
+{
+	char c;
+	char *cp;
+
+	if (_sp == 1) {
+		if (optind >= argc || argv[optind][0] != '-' ||
+		    argv[optind] == NULL || argv[optind][1] == '\0')
+			return (EOF);
+		else if (strcmp(argv[optind], "--") == NULL) {
+			optind++;
+			return (EOF);
+		}
+	}
+	optopt = c = (unsigned char)argv[optind][_sp];
+	if (c == ':' || (cp = strchr(opts, c)) == NULL) {
+		if (opts[0] != ':')
+			warn("%s: illegal option -- %c\n", argv[0], c);
+		if (argv[optind][++_sp] == '\0') {
+			optind++;
+			_sp = 1;
+		}
+		return ('?');
+	}
+
+	if (*(cp + 1) == ':') {
+		if (argv[optind][_sp+1] != '\0')
+			optarg = &argv[optind++][_sp+1];
+		else if (++optind >= argc) {
+			if (opts[0] != ':') {
+				warn("%s: option requires an argument"
+				    " -- %c\n", argv[0], c);
+			}
+			_sp = 1;
+			optarg = NULL;
+			return (opts[0] == ':' ? ':' : '?');
+		} else
+			optarg = argv[optind++];
+		_sp = 1;
+	} else {
+		if (argv[optind][++_sp] == '\0') {
+			_sp = 1;
+			optind++;
+		}
+		optarg = NULL;
+	}
+	return (c);
+}
diff --git a/usr/src/cmd/mdb/common/libstand/sys/salib.h b/usr/src/cmd/mdb/common/libstand/sys/salib.h
new file mode 100644
index 0000000..c46cdab
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstand/sys/salib.h
@@ -0,0 +1,89 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_SALIB_H
+#define	_SYS_SALIB_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+#include <time.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+extern char *asctime(const struct tm *);
+extern char *ctime(const time_t *);
+
+extern int bcmp(const void *, const void *, size_t);
+extern void bcopy(const void *, void *, size_t);
+extern void *bsearch(const void *, const void *, size_t, size_t,
+    int (*)(const void *, const void *));
+extern void bzero(void *, size_t);
+
+extern int getopt(int, char *const [], const char *);
+extern void getopt_reset(void);
+
+extern void *memchr(const void *, int, size_t);
+extern int memcmp(const void *, const void *, size_t);
+extern void *memcpy(void *, const void *, size_t);
+extern void *memccpy(void *, const void *, int, size_t);
+extern void *memmove(void *, const void *, size_t);
+extern void *memset(void *, int, size_t);
+
+extern void qsort(void *, size_t, size_t, int (*)(const void *,
+    const void *));
+
+extern long strtol(const char *, char **, int);
+extern unsigned long strtoul(const char *, char **, int);
+extern char *strcat(char *, const char *);
+extern char *strchr(const char *, int);
+extern int strcmp(const char *, const char *);
+extern char *strcpy(char *, const char *);
+extern int strcasecmp(const char *, const char *);
+extern int strncasecmp(const char *, const char *, size_t);
+extern size_t strlcpy(char *, const char *, size_t);
+extern size_t strlen(const char *);
+extern char *strncat(char *, const char *, size_t);
+extern size_t strlcat(char *, const char *, size_t);
+extern int strncmp(const char *, const char *, size_t);
+extern char *strncpy(char *, const char *, size_t);
+extern char *strrchr(const char *, int);
+extern char *strstr(const char *, const char *);
+extern size_t strspn(const char *, const char *);
+extern char *strpbrk(const char *, const char *);
+extern char *strtok(char *, const char *);
+
+#ifndef	NULL
+#define	NULL	(0)
+#endif
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_SALIB_H */
diff --git a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
new file mode 100644
index 0000000..0a211fc
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
@@ -0,0 +1,158 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb.h>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/zmod.h>
+
+#include <ctf_impl.h>
+#include <stdlib.h>
+
+/*ARGSUSED*/
+void *
+ctf_zopen(int *errp)
+{
+	/* The kernel will have decompressed the buffer for us */
+	return (ctf_set_open_errno(errp, ECTF_ZMISSING));
+}
+
+/*ARGSUSED*/
+const void *
+ctf_sect_mmap(ctf_sect_t *sp, int fd)
+{
+	return (MAP_FAILED); /* we don't support this in kmdb  */
+}
+
+/*ARGSUSED*/
+void
+ctf_sect_munmap(const ctf_sect_t *sp)
+{
+	/* we don't support this in kmdb */
+}
+
+/*ARGSUSED*/
+ctf_file_t *
+ctf_fdopen(int fd, int *errp)
+{
+	return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+/*ARGSUSED*/
+ctf_file_t *
+ctf_open(const char *filename, int *errp)
+{
+	return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+int
+ctf_version(int version)
+{
+	ASSERT(version > 0 && version <= CTF_VERSION);
+
+	if (version > 0)
+		_libctf_version = MIN(CTF_VERSION, version);
+
+	return (_libctf_version);
+}
+
+void *
+ctf_data_alloc(size_t size)
+{
+	void *buf = mdb_alloc(size, UM_NOSLEEP);
+
+	if (buf == NULL)
+		return (MAP_FAILED);
+
+	return (buf);
+}
+
+void
+ctf_data_free(void *buf, size_t size)
+{
+	mdb_free(buf, size);
+}
+
+/*ARGSUSED*/
+void
+ctf_data_protect(void *buf, size_t size)
+{
+	/* Not supported in kmdb */
+}
+
+void *
+ctf_alloc(size_t size)
+{
+	return (mdb_alloc(size, UM_NOSLEEP));
+}
+
+void
+ctf_free(void *buf, size_t size)
+{
+	mdb_free(buf, size);
+}
+
+/*ARGSUSED*/
+const char *
+ctf_strerror(int err)
+{
+	return (NULL); /* Not supported in kmdb */
+}
+
+/*PRINTFLIKE1*/
+void
+ctf_dprintf(const char *format, ...)
+{
+	va_list alist;
+
+	va_start(alist, format);
+	mdb_dvprintf(MDB_DBG_CTF, format, alist);
+	va_end(alist);
+}
+
+/*ARGSUSED*/
+int
+z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
+{
+	return (Z_ERRNO);
+}
+
+/*ARGSUSED*/
+const char *
+z_strerror(int err)
+{
+	return ("zlib unsupported in kmdb");
+}
+
+int
+ctf_vsnprintf(char *buf, size_t nbytes, const char *format, va_list alist)
+{
+	return ((int)mdb_iob_vsnprintf(buf, nbytes, format, alist));
+}
diff --git a/usr/src/cmd/mdb/common/libstandctf/mapfile b/usr/src/cmd/mdb/common/libstandctf/mapfile
new file mode 100644
index 0000000..6433bdb
--- /dev/null
+++ b/usr/src/cmd/mdb/common/libstandctf/mapfile
@@ -0,0 +1,78 @@
+#
+# Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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
+#
+# ident	"%Z%%M%	%I%	%E% SMI"
+
+{
+	global:
+		ctf_add_array;
+		ctf_add_member;
+		ctf_add_pointer;
+		ctf_add_struct;
+		ctf_add_typedef;
+		ctf_add_union;
+		ctf_array_info;
+		ctf_bufopen;
+		ctf_close;
+		ctf_create;
+		ctf_discard;
+		ctf_enum_iter;
+		ctf_enum_name;
+		ctf_enum_value;
+		ctf_errmsg;
+		ctf_errno;
+		ctf_fdopen;
+		ctf_func_args;
+		ctf_func_info;
+		ctf_getmodel;
+		ctf_getspecific;
+		ctf_import;
+		ctf_label_info;
+		ctf_label_iter;
+		ctf_label_topmost;
+		ctf_lookup_by_name;
+		ctf_lookup_by_symbol;
+		ctf_member_info;
+		ctf_member_iter;
+		ctf_open;
+		ctf_parent_name;
+		ctf_setmodel;
+		ctf_setspecific;
+		ctf_type_align;
+		ctf_type_cmp;
+		ctf_type_encoding;
+		ctf_type_iter;
+		ctf_type_lname;
+		ctf_type_kind;
+		ctf_type_name;
+		ctf_type_pointer;
+		ctf_type_reference;
+		ctf_type_resolve;
+		ctf_type_size;
+		ctf_type_visit;
+		ctf_update;
+		ctf_version;
+	local:
+		*;
+};
diff --git a/usr/src/cmd/mdb/common/mdb/Makefile b/usr/src/cmd/mdb/common/mdb/Makefile
new file mode 100644
index 0000000..6d772db
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/Makefile
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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 (c) 1997-1999 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#ident	"%Z%%M%	%I%	%E% SMI"
+
+include ../../../Makefile.cmd
+
+HDRS = mdb_modapi.h
+ROOTHDRDIR = $(ROOT)/usr/include/sys
+ROOTHDRS = $(HDRS:%=$(ROOTHDRDIR)/%)
+
+$(ROOTHDRS) := FILEMODE= 0644
+
+$(ROOTHDRDIR)/%.h: %.h
+	$(INS.file)
+
+.KEEP_STATE:
+.SUFFIXES:
+
+install_h: $(ROOTHDRS)
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.c b/usr/src/cmd/mdb/common/mdb/mdb.c
new file mode 100644
index 0000000..755322c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb.c
@@ -0,0 +1,1374 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Modular Debugger (MDB)
+ *
+ * Refer to the white paper "A Modular Debugger for Solaris" for information
+ * on the design, features, and goals of MDB.  See /shared/sac/PSARC/1999/169
+ * for copies of the paper and related documentation.
+ *
+ * This file provides the basic construction and destruction of the debugger's
+ * global state, as well as the main execution loop, mdb_run().  MDB maintains
+ * a stack of execution frames (mdb_frame_t's) that keep track of its current
+ * state, including a stack of input and output buffers, walk and memory
+ * garbage collect lists, and a list of commands (mdb_cmd_t's).  As the
+ * parser consumes input, it fills in a list of commands to execute, and then
+ * invokes mdb_call(), below.  A command consists of a dcmd, telling us
+ * what function to execute, and a list of arguments and other invocation-
+ * specific data.  Each frame may have more than one command, kept on a list,
+ * when multiple commands are separated by | operators.  New frames may be
+ * stacked on old ones by nested calls to mdb_run: this occurs when, for
+ * example, in the middle of processing one input source (such as a file
+ * or the terminal), we invoke a dcmd that in turn calls mdb_eval().  mdb_eval
+ * will construct a new frame whose input source is the string passed to
+ * the eval function, and then execute this frame to completion.
+ */
+
+#include <sys/param.h>
+#include <stropts.h>
+
+#define	_MDB_PRIVATE
+#include <mdb/mdb.h>
+
+#include <mdb/mdb_context.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_signal.h>
+#include <mdb/mdb_macalias.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_callb.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_module.h>
+#endif
+
+/*
+ * Macro for testing if a dcmd's return status (x) indicates that we should
+ * abort the current loop or pipeline.
+ */
+#define	DCMD_ABORTED(x)	((x) == DCMD_USAGE || (x) == DCMD_ABORT)
+
+extern const mdb_dcmd_t mdb_dcmd_builtins[];
+extern mdb_dis_ctor_f *const mdb_dis_builtins[];
+
+/*
+ * Variable discipline for toggling MDB_FL_PSYM based on the value of the
+ * undocumented '_' variable.  Once adb(1) has been removed from the system,
+ * we should just remove this functionality and always disable PSYM for macros.
+ */
+static uintmax_t
+psym_disc_get(const mdb_var_t *v)
+{
+	int i = (mdb.m_flags & MDB_FL_PSYM) ? 1 : 0;
+	int j = (MDB_NV_VALUE(v) != 0) ? 1 : 0;
+
+	if ((i ^ j) == 0)
+		MDB_NV_VALUE((mdb_var_t *)v) = j ^ 1;
+
+	return (MDB_NV_VALUE(v));
+}
+
+static void
+psym_disc_set(mdb_var_t *v, uintmax_t value)
+{
+	if (value == 0)
+		mdb.m_flags |= MDB_FL_PSYM;
+	else
+		mdb.m_flags &= ~MDB_FL_PSYM;
+
+	MDB_NV_VALUE(v) = value;
+}
+
+/*
+ * Variable discipline for making <1 (most recent offset) behave properly.
+ */
+static uintmax_t
+roff_disc_get(const mdb_var_t *v)
+{
+	return (MDB_NV_VALUE(v));
+}
+
+static void
+roff_disc_set(mdb_var_t *v, uintmax_t value)
+{
+	mdb_nv_set_value(mdb.m_proffset, MDB_NV_VALUE(v));
+	MDB_NV_VALUE(v) = value;
+}
+
+/*
+ * Variable discipline for exporting the representative thread.
+ */
+static uintmax_t
+thr_disc_get(const mdb_var_t *v)
+{
+	mdb_tgt_status_t s;
+
+	if (mdb.m_target != NULL && mdb_tgt_status(mdb.m_target, &s) == 0)
+		return (s.st_tid);
+
+	return (MDB_NV_VALUE(v));
+}
+
+const char **
+mdb_path_alloc(const char *s, size_t *newlen)
+{
+	char *format = mdb_alloc(strlen(s) * 2 + 1, UM_NOSLEEP);
+	const char **path;
+	char *p, *q;
+
+	struct utsname uts;
+	size_t len;
+	int i;
+
+	mdb_arg_t arg_i, arg_m, arg_p, arg_r, arg_t, arg_R, arg_V;
+	mdb_argvec_t argv;
+
+	static const char *empty_path[] = { NULL };
+
+	if (format == NULL)
+		goto nomem;
+
+	while (*s == ':')
+		s++; /* strip leading delimiters */
+
+	if (*s == '\0') {
+		*newlen = 0;
+		return (empty_path);
+	}
+
+	(void) strcpy(format, s);
+	mdb_argvec_create(&argv);
+
+	/*
+	 * %i embedded in path string expands to ISA.
+	 */
+	arg_i.a_type = MDB_TYPE_STRING;
+	if (mdb.m_target != NULL)
+		arg_i.a_un.a_str = mdb_tgt_isa(mdb.m_target);
+	else
+		arg_i.a_un.a_str = mdb_conf_isa();
+
+	/*
+	 * %p embedded in path string expands to the platform name.
+	 */
+	arg_p.a_type = MDB_TYPE_STRING;
+	if (mdb.m_target != NULL)
+		arg_p.a_un.a_str = mdb_tgt_platform(mdb.m_target);
+	else
+		arg_p.a_un.a_str = mdb_conf_platform();
+
+	/*
+	 * %r embedded in path string expands to root directory, or
+	 * to the empty string if root is "/" (to avoid // in paths).
+	 */
+	arg_r.a_type = MDB_TYPE_STRING;
+	arg_r.a_un.a_str = strcmp(mdb.m_root, "/") ? mdb.m_root : "";
+
+	/*
+	 * %t embedded in path string expands to the target name.
+	 */
+	arg_t.a_type = MDB_TYPE_STRING;
+	arg_t.a_un.a_str = mdb.m_target ? mdb_tgt_name(mdb.m_target) : "none";
+
+	/*
+	 * %R and %V expand to uname -r (release) and uname -v (version).
+	 */
+	if (mdb.m_target == NULL || mdb_tgt_uname(mdb.m_target, &uts) < 0)
+		mdb_conf_uname(&uts);
+
+	arg_m.a_type = MDB_TYPE_STRING;
+	arg_m.a_un.a_str = uts.machine;
+
+	arg_R.a_type = MDB_TYPE_STRING;
+	arg_R.a_un.a_str = uts.release;
+
+	arg_V.a_type = MDB_TYPE_STRING;
+	if (mdb.m_flags & MDB_FL_LATEST)
+		arg_V.a_un.a_str = "latest";
+	else
+		arg_V.a_un.a_str = uts.version;
+
+	/*
+	 * In order to expand the buffer, we examine the format string for
+	 * our % tokens and construct an argvec, replacing each % token
+	 * with %s along the way.  If we encounter an unknown token, we
+	 * shift over the remaining format buffer and stick in %%.
+	 */
+	for (q = format; (q = strchr(q, '%')) != NULL; q++) {
+		switch (q[1]) {
+		case 'i':
+			mdb_argvec_append(&argv, &arg_i);
+			*++q = 's';
+			break;
+		case 'm':
+			mdb_argvec_append(&argv, &arg_m);
+			*++q = 's';
+			break;
+		case 'p':
+			mdb_argvec_append(&argv, &arg_p);
+			*++q = 's';
+			break;
+		case 'r':
+			mdb_argvec_append(&argv, &arg_r);
+			*++q = 's';
+			break;
+		case 't':
+			mdb_argvec_append(&argv, &arg_t);
+			*++q = 's';
+			break;
+		case 'R':
+			mdb_argvec_append(&argv, &arg_R);
+			*++q = 's';
+			break;
+		case 'V':
+			mdb_argvec_append(&argv, &arg_V);
+			*++q = 's';
+			break;
+		default:
+			bcopy(q + 1, q + 2, strlen(q));
+			*++q = '%';
+		}
+	}
+
+	/*
+	 * We're now ready to use our printf engine to format the final string.
+	 * Take one lap with a NULL buffer to determine how long the final
+	 * string will be, allocate it, and format it.
+	 */
+	len = mdb_iob_asnprintf(NULL, 0, format, argv.a_data);
+	if ((p = mdb_alloc(len + 1, UM_NOSLEEP)) != NULL)
+		(void) mdb_iob_asnprintf(p, len + 1, format, argv.a_data);
+	else
+		goto nomem;
+
+	mdb_argvec_zero(&argv);
+	mdb_argvec_destroy(&argv);
+
+	mdb_free(format, strlen(s) * 2 + 1);
+	format = NULL;
+
+	/*
+	 * Compress the string to exclude any leading delimiters.
+	 */
+	for (q = p; *q == ':'; q++)
+		continue;
+	if (q != p)
+		bcopy(q, p, strlen(q) + 1);
+
+	/*
+	 * Count up the number of delimited elements.  A sequence of
+	 * consecutive delimiters is only counted once.
+	 */
+	for (i = 1, q = p; (q = strchr(q, ':')) != NULL; i++) {
+		while (*q == ':')
+			q++;
+	}
+
+	if ((path = mdb_alloc(sizeof (char *) * (i + 1), UM_NOSLEEP)) == NULL) {
+		mdb_free(p, len + 1);
+		goto nomem;
+	}
+
+	for (i = 0, q = strtok(p, ":"); q != NULL; q = strtok(NULL, ":"))
+		path[i++] = q;
+
+	path[i] = NULL;
+	*newlen = len + 1;
+	return (path);
+
+nomem:
+	warn("failed to allocate memory for path");
+	if (format != NULL)
+		mdb_free(format, strlen(s) * 2 + 1);
+	*newlen = 0;
+	return (empty_path);
+}
+
+const char **
+mdb_path_dup(const char *path[], size_t pathlen, size_t *npathlenp)
+{
+	char **npath;
+	int i, j;
+
+	for (i = 0; path[i] != NULL; i++)
+		continue; /* count the path elements */
+
+	npath = mdb_zalloc(sizeof (char *) * (i + 1), UM_SLEEP);
+	if (pathlen > 0) {
+		npath[0] = mdb_alloc(pathlen, UM_SLEEP);
+		bcopy(path[0], npath[0], pathlen);
+	}
+
+	for (j = 1; j < i; j++)
+		npath[j] = npath[0] + (path[j] - path[0]);
+	npath[i] = NULL;
+
+	*npathlenp = pathlen;
+	return ((const char **)npath);
+}
+
+void
+mdb_path_free(const char *path[], size_t pathlen)
+{
+	int i;
+
+	for (i = 0; path[i] != NULL; i++)
+		continue; /* count the path elements */
+
+	if (i > 0) {
+		mdb_free((void *)path[0], pathlen);
+		mdb_free(path, sizeof (char *) * (i + 1));
+	}
+}
+
+/*
+ * Convert path string "s" to canonical form, expanding any %o tokens that are
+ * found within the path.  The old path string is specified by "path", a buffer
+ * of size MAXPATHLEN which is then overwritten with the new path string.
+ */
+static const char *
+path_canon(char *path, const char *s)
+{
+	char *p = path;
+	char *q = p + MAXPATHLEN - 1;
+
+	char old[MAXPATHLEN];
+	char c;
+
+	(void) strcpy(old, p);
+	*q = '\0';
+
+	while (p < q && (c = *s++) != '\0') {
+		if (c == '%') {
+			if ((c = *s++) == 'o') {
+				(void) strncpy(p, old, (size_t)(q - p));
+				p += strlen(p);
+			} else {
+				*p++ = '%';
+				if (p < q && c != '\0')
+					*p++ = c;
+				else
+					break;
+			}
+		} else
+			*p++ = c;
+	}
+
+	*p = '\0';
+	return (path);
+}
+
+void
+mdb_set_ipath(const char *path)
+{
+	if (mdb.m_ipath != NULL)
+		mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
+
+	path = path_canon(mdb.m_ipathstr, path);
+	mdb.m_ipath = mdb_path_alloc(path, &mdb.m_ipathlen);
+}
+
+void
+mdb_set_lpath(const char *path)
+{
+	if (mdb.m_lpath != NULL)
+		mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
+
+	path = path_canon(mdb.m_lpathstr, path);
+	mdb.m_lpath = mdb_path_alloc(path, &mdb.m_lpathlen);
+
+#ifdef _KMDB
+	kmdb_module_path_set(mdb.m_lpath, mdb.m_lpathlen);
+#endif
+}
+
+static void
+prompt_update(void)
+{
+	(void) mdb_snprintf(mdb.m_prompt, sizeof (mdb.m_prompt),
+	    mdb.m_promptraw);
+	mdb.m_promptlen = strlen(mdb.m_prompt);
+}
+
+const char *
+mdb_get_prompt(void)
+{
+	if (mdb.m_promptlen == 0)
+		return (NULL);
+	else
+		return (mdb.m_prompt);
+}
+
+int
+mdb_set_prompt(const char *p)
+{
+	size_t len = strlen(p);
+
+	if (len > MDB_PROMPTLEN) {
+		warn("prompt may not exceed %d characters\n", MDB_PROMPTLEN);
+		return (0);
+	}
+
+	(void) strcpy(mdb.m_promptraw, p);
+	prompt_update();
+	return (1);
+}
+
+static mdb_frame_t frame0;
+
+void
+mdb_create(const char *execname, const char *arg0)
+{
+	static const mdb_nv_disc_t psym_disc = { psym_disc_set, psym_disc_get };
+	static const mdb_nv_disc_t roff_disc = { roff_disc_set, roff_disc_get };
+	static const mdb_nv_disc_t thr_disc = { NULL, thr_disc_get };
+
+	static char rootdir[MAXPATHLEN];
+
+	const mdb_dcmd_t *dcp;
+	int i;
+
+	bzero(&mdb, sizeof (mdb_t));
+
+	mdb.m_flags = MDB_FL_PSYM | MDB_FL_PAGER | MDB_FL_BPTNOSYMSTOP |
+	    MDB_FL_READBACK;
+	mdb.m_radix = MDB_DEF_RADIX;
+	mdb.m_nargs = MDB_DEF_NARGS;
+	mdb.m_histlen = MDB_DEF_HISTLEN;
+	mdb.m_armemlim = MDB_DEF_ARRMEM;
+	mdb.m_arstrlim = MDB_DEF_ARRSTR;
+
+	mdb.m_pname = strbasename(arg0);
+	if (strcmp(mdb.m_pname, "adb") == 0) {
+		mdb.m_flags |= MDB_FL_NOMODS | MDB_FL_ADB | MDB_FL_REPLAST;
+		mdb.m_flags &= ~MDB_FL_PAGER;
+	}
+
+	mdb.m_ipathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
+	mdb.m_lpathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
+
+	(void) strncpy(rootdir, execname, sizeof (rootdir));
+	rootdir[sizeof (rootdir) - 1] = '\0';
+	(void) strdirname(rootdir);
+
+	if (strcmp(strbasename(rootdir), "sparcv9") == 0 ||
+	    strcmp(strbasename(rootdir), "sparcv7") == 0 ||
+	    strcmp(strbasename(rootdir), "amd64") == 0 ||
+	    strcmp(strbasename(rootdir), "i86") == 0)
+		(void) strdirname(rootdir);
+
+	if (strcmp(strbasename(rootdir), "bin") == 0) {
+		(void) strdirname(rootdir);
+		if (strcmp(strbasename(rootdir), "usr") == 0)
+			(void) strdirname(rootdir);
+	} else
+		(void) strcpy(rootdir, "/");
+
+	mdb.m_root = rootdir;
+
+	mdb.m_rminfo.mi_dvers = MDB_API_VERSION;
+	mdb.m_rminfo.mi_dcmds = mdb_dcmd_builtins;
+	mdb.m_rminfo.mi_walkers = NULL;
+
+	(void) mdb_nv_create(&mdb.m_rmod.mod_walkers, UM_SLEEP);
+	(void) mdb_nv_create(&mdb.m_rmod.mod_dcmds, UM_SLEEP);
+
+	mdb.m_rmod.mod_name = mdb.m_pname;
+	mdb.m_rmod.mod_info = &mdb.m_rminfo;
+
+	(void) mdb_nv_create(&mdb.m_disasms, UM_SLEEP);
+	(void) mdb_nv_create(&mdb.m_modules, UM_SLEEP);
+	(void) mdb_nv_create(&mdb.m_dcmds, UM_SLEEP);
+	(void) mdb_nv_create(&mdb.m_walkers, UM_SLEEP);
+	(void) mdb_nv_create(&mdb.m_nv, UM_SLEEP);
+
+	mdb.m_dot = mdb_nv_insert(&mdb.m_nv, ".", NULL, 0, MDB_NV_PERSIST);
+	mdb.m_rvalue = mdb_nv_insert(&mdb.m_nv, "0", NULL, 0, MDB_NV_PERSIST);
+
+	mdb.m_roffset =
+	    mdb_nv_insert(&mdb.m_nv, "1", &roff_disc, 0, MDB_NV_PERSIST);
+
+	mdb.m_proffset = mdb_nv_insert(&mdb.m_nv, "2", NULL, 0, MDB_NV_PERSIST);
+	mdb.m_rcount = mdb_nv_insert(&mdb.m_nv, "9", NULL, 0, MDB_NV_PERSIST);
+
+	(void) mdb_nv_insert(&mdb.m_nv, "b", NULL, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "d", NULL, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "e", NULL, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "m", NULL, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "t", NULL, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "_", &psym_disc, 0, MDB_NV_PERSIST);
+	(void) mdb_nv_insert(&mdb.m_nv, "hits", NULL, 0, MDB_NV_PERSIST);
+
+	(void) mdb_nv_insert(&mdb.m_nv, "thread", &thr_disc, 0,
+	    MDB_NV_PERSIST | MDB_NV_RDONLY);
+
+	mdb.m_prsym = mdb_gelf_symtab_create_mutable();
+
+	(void) mdb_nv_insert(&mdb.m_modules, mdb.m_pname, NULL,
+	    (uintptr_t)&mdb.m_rmod, MDB_NV_RDONLY);
+
+	for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
+		(void) mdb_module_add_dcmd(&mdb.m_rmod, dcp, 0);
+
+	for (i = 0; mdb_dis_builtins[i] != NULL; i++)
+		(void) mdb_dis_create(mdb_dis_builtins[i]);
+
+	mdb_macalias_create();
+
+	mdb_create_builtin_tgts();
+
+	(void) mdb_callb_add(NULL, MDB_CALLB_PROMPT, (mdb_callb_f)prompt_update,
+	    NULL);
+
+#ifdef _KMDB
+	(void) mdb_nv_create(&mdb.m_dmodctl, UM_SLEEP);
+#endif
+	mdb_lex_state_create(&frame0);
+
+	mdb_list_append(&mdb.m_flist, &frame0);
+	mdb.m_frame = &frame0;
+}
+
+void
+mdb_destroy(void)
+{
+	const mdb_dcmd_t *dcp;
+	mdb_var_t *v;
+	int unload_mode = MDB_MOD_SILENT;
+
+#ifdef _KMDB
+	unload_mode |= MDB_MOD_DEFER;
+#endif
+
+	mdb_intr_disable();
+
+	mdb_macalias_destroy();
+
+	/*
+	 * Unload modules _before_ destroying the disassemblers since a
+	 * module that installs a disassembler should try to clean up after
+	 * itself.
+	 */
+	mdb_module_unload_all(unload_mode);
+
+	mdb_nv_rewind(&mdb.m_disasms);
+	while ((v = mdb_nv_advance(&mdb.m_disasms)) != NULL)
+		mdb_dis_destroy(mdb_nv_get_cookie(v));
+
+	mdb_callb_remove_all();
+
+	if (mdb.m_defdisasm != NULL)
+		strfree(mdb.m_defdisasm);
+
+	if (mdb.m_target != NULL)
+		(void) mdb_tgt_destroy(mdb.m_target);
+
+	if (mdb.m_prsym != NULL)
+		mdb_gelf_symtab_destroy(mdb.m_prsym);
+
+	for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
+		(void) mdb_module_remove_dcmd(&mdb.m_rmod, dcp->dc_name);
+
+	mdb_nv_destroy(&mdb.m_nv);
+	mdb_nv_destroy(&mdb.m_walkers);
+	mdb_nv_destroy(&mdb.m_dcmds);
+	mdb_nv_destroy(&mdb.m_modules);
+	mdb_nv_destroy(&mdb.m_disasms);
+
+	mdb_free(mdb.m_ipathstr, MAXPATHLEN);
+	mdb_free(mdb.m_lpathstr, MAXPATHLEN);
+
+	if (mdb.m_ipath != NULL)
+		mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
+
+	if (mdb.m_lpath != NULL)
+		mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
+
+	if (mdb.m_in != NULL)
+		mdb_iob_destroy(mdb.m_in);
+
+	mdb_iob_destroy(mdb.m_out);
+	mdb.m_out = NULL;
+	mdb_iob_destroy(mdb.m_err);
+	mdb.m_err = NULL;
+
+	if (mdb.m_log != NULL)
+		mdb_io_rele(mdb.m_log);
+
+	mdb_lex_state_destroy(&frame0);
+}
+
+/*
+ * The real main loop of the debugger: create a new execution frame on the
+ * debugger stack, and while we have input available, call into the parser.
+ */
+int
+mdb_run(void)
+{
+	volatile int err;
+	mdb_frame_t f;
+
+	mdb_intr_disable();
+	mdb_frame_push(&f);
+
+	/*
+	 * This is a fresh mdb context, so ignore any pipe command we may have
+	 * inherited from the previous frame.
+	 */
+	f.f_pcmd = NULL;
+
+	if ((err = setjmp(f.f_pcb)) != 0) {
+		int pop = (mdb.m_in != NULL &&
+		    (mdb_iob_isapipe(mdb.m_in) || mdb_iob_isastr(mdb.m_in)));
+		int fromcmd = (f.f_cp != NULL);
+
+		mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught event %s\n",
+		    f.f_id, mdb_err2str(err));
+
+		/*
+		 * If a syntax error or other failure has occurred, pop all
+		 * input buffers pushed by commands executed in this frame.
+		 */
+		while (mdb_iob_stack_size(&f.f_istk) != 0) {
+			if (mdb.m_in != NULL)
+				mdb_iob_destroy(mdb.m_in);
+			mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
+			yylineno = mdb_iob_lineno(mdb.m_in);
+		}
+
+		/*
+		 * Reset standard output and the current frame to a known,
+		 * clean state, so we can continue execution.
+		 */
+		mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
+		mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+		mdb_iob_discard(mdb.m_out);
+		mdb_frame_reset(&f);
+
+		/*
+		 * If there was an error writing to output, display a warning
+		 * message if this is the topmost frame.
+		 */
+		if (err == MDB_ERR_OUTPUT && mdb.m_depth == 1 && errno != EPIPE)
+			mdb_warn("write failed");
+
+		/*
+		 * If an interrupt or quit signal is reported, we may have been
+		 * in the middle of typing or processing the command line:
+		 * print a newline and discard everything in the parser's iob.
+		 * Note that we do this after m_out has been reset, otherwise
+		 * we could trigger a pipe context switch or cause a write
+		 * to a broken pipe (in the case of a shell command) when
+		 * writing the newline.
+		 */
+		if (err == MDB_ERR_SIGINT || err == MDB_ERR_QUIT) {
+			mdb_iob_nl(mdb.m_out);
+			yydiscard();
+		}
+
+		/*
+		 * If we quit or abort using the output pager, reset the
+		 * line count on standard output back to zero.
+		 */
+		if (err == MDB_ERR_PAGER || MDB_ERR_IS_FATAL(err))
+			mdb_iob_clearlines(mdb.m_out);
+
+		/*
+		 * If the user requested the debugger quit or abort back to
+		 * the top, or if standard input is a pipe or mdb_eval("..."),
+		 * then propagate the error up the debugger stack.
+		 */
+		if (MDB_ERR_IS_FATAL(err) || pop != 0 ||
+		    (err == MDB_ERR_PAGER && mdb.m_fmark != &f) ||
+		    (err == MDB_ERR_NOMEM && !fromcmd)) {
+			mdb_frame_pop(&f, err);
+			return (err);
+		}
+
+		/*
+		 * If we've returned here from a context where signals were
+		 * blocked (e.g. a signal handler), we can now unblock them.
+		 */
+		if (err == MDB_ERR_SIGINT)
+			(void) mdb_signal_unblock(SIGINT);
+	} else
+		mdb_intr_enable();
+
+	for (;;) {
+		while (mdb.m_in != NULL && (mdb_iob_getflags(mdb.m_in) &
+		    (MDB_IOB_ERR | MDB_IOB_EOF)) == 0) {
+			if (mdb.m_depth == 1 &&
+			    mdb_iob_stack_size(&f.f_istk) == 0) {
+				mdb_iob_clearlines(mdb.m_out);
+				mdb_tgt_periodic(mdb.m_target);
+			}
+
+			(void) yyparse();
+		}
+
+		if (mdb.m_in != NULL) {
+			if (mdb_iob_err(mdb.m_in)) {
+				warn("error reading input stream %s\n",
+				    mdb_iob_name(mdb.m_in));
+			}
+			mdb_iob_destroy(mdb.m_in);
+			mdb.m_in = NULL;
+		}
+
+		if (mdb_iob_stack_size(&f.f_istk) == 0)
+			break; /* return when we're out of input */
+
+		mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
+		yylineno = mdb_iob_lineno(mdb.m_in);
+	}
+
+	mdb_frame_pop(&f, 0);
+
+	/*
+	 * The value of '.' is a per-frame attribute, to preserve it properly
+	 * when switching frames.  But in the case of calling mdb_run()
+	 * explicitly (such as through mdb_eval), we want to propagate the value
+	 * of '.' to the parent.
+	 */
+	mdb_nv_set_value(mdb.m_dot, f.f_dot);
+
+	return (0);
+}
+
+/*
+ * The read-side of the pipe executes this service routine.  We simply call
+ * mdb_run to create a new frame on the execution stack and run the MDB parser,
+ * and then propagate any error code back to the previous frame.
+ */
+static int
+runsvc(void)
+{
+	int err = mdb_run();
+
+	if (err != 0) {
+		mdb_dprintf(MDB_DBG_DSTK, "forwarding error %s from pipeline\n",
+		    mdb_err2str(err));
+		longjmp(mdb.m_frame->f_pcb, err);
+	}
+
+	return (err);
+}
+
+/*
+ * Read-side pipe service routine: if we longjmp here, just return to the read
+ * routine because now we have more data to consume.  Otherwise:
+ * (1) if ctx_data is non-NULL, longjmp to the write-side to produce more data;
+ * (2) if wriob is NULL, there is no writer but this is the first read, so we
+ *     can just execute mdb_run() to completion on the current stack;
+ * (3) if (1) and (2) are false, then there is a writer and this is the first
+ *     read, so create a co-routine context to execute mdb_run().
+ */
+/*ARGSUSED*/
+static void
+rdsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
+{
+	if (setjmp(ctx->ctx_rpcb) == 0) {
+		/*
+		 * Save the current standard input into the pipe context, and
+		 * reset m_in to point to the pipe.  We will restore it on
+		 * the way back in wrsvc() below.
+		 */
+		ctx->ctx_iob = mdb.m_in;
+		mdb.m_in = rdiob;
+
+		ctx->ctx_rptr = mdb.m_frame;
+		if (ctx->ctx_wptr != NULL)
+			mdb_frame_switch(ctx->ctx_wptr);
+
+		if (ctx->ctx_data != NULL)
+			longjmp(ctx->ctx_wpcb, 1);
+		else if (wriob == NULL)
+			(void) runsvc();
+		else if ((ctx->ctx_data = mdb_context_create(runsvc)) != NULL)
+			mdb_context_switch(ctx->ctx_data);
+		else
+			mdb_warn("failed to create pipe context");
+	}
+}
+
+/*
+ * Write-side pipe service routine: if we longjmp here, just return to the
+ * write routine because now we have free space in the pipe buffer for writing;
+ * otherwise longjmp to the read-side to consume data and create space for us.
+ */
+/*ARGSUSED*/
+static void
+wrsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
+{
+	if (setjmp(ctx->ctx_wpcb) == 0) {
+		ctx->ctx_wptr = mdb.m_frame;
+		if (ctx->ctx_rptr != NULL)
+			mdb_frame_switch(ctx->ctx_rptr);
+
+		mdb.m_in = ctx->ctx_iob;
+		longjmp(ctx->ctx_rpcb, 1);
+	}
+}
+
+/*
+ * Call the current frame's mdb command.  This entry point is used by the
+ * MDB parser to actually execute a command once it has successfully parsed
+ * a line of input.  The command is waiting for us in the current frame.
+ * We loop through each command on the list, executing its dcmd with the
+ * appropriate argument.  If the command has a successor, we know it had
+ * a | operator after it, and so we need to create a pipe and replace
+ * stdout with the pipe's output buffer.
+ */
+int
+mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
+{
+	mdb_frame_t *fp = mdb.m_frame;
+	mdb_cmd_t *cp, *ncp;
+	mdb_iob_t *iobs[2];
+	int status, err = 0;
+	jmp_buf pcb;
+
+	if (mdb_iob_isapipe(mdb.m_in))
+		yyerror("syntax error");
+
+	mdb_intr_disable();
+	fp->f_cp = mdb_list_next(&fp->f_cmds);
+
+	if (flags & DCMD_LOOP)
+		flags |= DCMD_LOOPFIRST; /* set LOOPFIRST if this is a loop */
+
+	for (cp = mdb_list_next(&fp->f_cmds); cp; cp = mdb_list_next(cp)) {
+		if (mdb_list_next(cp) != NULL) {
+			mdb_iob_pipe(iobs, rdsvc, wrsvc);
+
+			mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
+			mdb.m_in = iobs[MDB_IOB_RDIOB];
+
+			mdb_iob_stack_push(&fp->f_ostk, mdb.m_out, 0);
+			mdb.m_out = iobs[MDB_IOB_WRIOB];
+
+			ncp = mdb_list_next(cp);
+			mdb_vcb_inherit(cp, ncp);
+
+			bcopy(fp->f_pcb, pcb, sizeof (jmp_buf));
+			ASSERT(fp->f_pcmd == NULL);
+			fp->f_pcmd = ncp;
+
+			if ((err = setjmp(fp->f_pcb)) == 0) {
+				status = mdb_call_idcmd(cp->c_dcmd, addr, count,
+				    flags | DCMD_PIPE_OUT, &cp->c_argv,
+				    &cp->c_addrv, cp->c_vcbs);
+
+				ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]);
+				ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]);
+			} else {
+				mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught "
+				    "error %s from pipeline\n", fp->f_id,
+				    mdb_err2str(err));
+			}
+
+			if (err != 0 || DCMD_ABORTED(status)) {
+				mdb_iob_setflags(mdb.m_in, MDB_IOB_ERR);
+				mdb_iob_setflags(mdb.m_out, MDB_IOB_ERR);
+			} else {
+				mdb_iob_flush(mdb.m_out);
+				(void) mdb_iob_ctl(mdb.m_out, I_FLUSH,
+				    (void *)FLUSHW);
+			}
+
+			mdb_iob_destroy(mdb.m_out);
+			mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk);
+
+			if (mdb.m_in != NULL)
+				mdb_iob_destroy(mdb.m_in);
+
+			mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
+			yylineno = mdb_iob_lineno(mdb.m_in);
+
+			fp->f_pcmd = NULL;
+			bcopy(pcb, fp->f_pcb, sizeof (jmp_buf));
+
+			if (MDB_ERR_IS_FATAL(err))
+				longjmp(fp->f_pcb, err);
+
+			if (err != 0 || DCMD_ABORTED(status) ||
+			    mdb_addrvec_length(&ncp->c_addrv) == 0)
+				break;
+
+			addr = mdb_nv_get_value(mdb.m_dot);
+			count = 1;
+			flags = 0;
+
+		} else {
+			mdb_intr_enable();
+			(void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
+			    &cp->c_argv, &cp->c_addrv, cp->c_vcbs);
+			mdb_intr_disable();
+		}
+
+		fp->f_cp = mdb_list_next(cp);
+		mdb_cmd_reset(cp);
+	}
+
+	/*
+	 * If our last-command list is non-empty, destroy it.  Then copy the
+	 * current frame's cmd list to the m_lastc list and reset the frame.
+	 */
+	while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
+		mdb_list_delete(&mdb.m_lastc, cp);
+		mdb_cmd_destroy(cp);
+	}
+
+	mdb_list_move(&fp->f_cmds, &mdb.m_lastc);
+	mdb_frame_reset(fp);
+	mdb_intr_enable();
+	return (err == 0);
+}
+
+uintmax_t
+mdb_dot_incr(const char *op)
+{
+	uintmax_t odot, ndot;
+
+	odot = mdb_nv_get_value(mdb.m_dot);
+	ndot = odot + mdb.m_incr;
+
+	if ((odot ^ ndot) & 0x8000000000000000ull)
+		yyerror("'%s' would cause '.' to overflow\n", op);
+
+	return (ndot);
+}
+
+uintmax_t
+mdb_dot_decr(const char *op)
+{
+	uintmax_t odot, ndot;
+
+	odot = mdb_nv_get_value(mdb.m_dot);
+	ndot = odot - mdb.m_incr;
+
+	if (ndot > odot)
+		yyerror("'%s' would cause '.' to underflow\n", op);
+
+	return (ndot);
+}
+
+mdb_iwalker_t *
+mdb_walker_lookup(const char *s)
+{
+	const char *p = strchr(s, '`');
+	mdb_var_t *v;
+
+	if (p != NULL) {
+		size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
+		char mname[MDB_NV_NAMELEN];
+		mdb_module_t *mod;
+
+		(void) strncpy(mname, s, nbytes);
+		mname[nbytes] = '\0';
+
+		if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
+			(void) set_errno(EMDB_NOMOD);
+			return (NULL);
+		}
+
+		mod = mdb_nv_get_cookie(v);
+
+		if ((v = mdb_nv_lookup(&mod->mod_walkers, ++p)) != NULL)
+			return (mdb_nv_get_cookie(v));
+
+	} else if ((v = mdb_nv_lookup(&mdb.m_walkers, s)) != NULL)
+		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+	(void) set_errno(EMDB_NOWALK);
+	return (NULL);
+}
+
+mdb_idcmd_t *
+mdb_dcmd_lookup(const char *s)
+{
+	const char *p = strchr(s, '`');
+	mdb_var_t *v;
+
+	if (p != NULL) {
+		size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
+		char mname[MDB_NV_NAMELEN];
+		mdb_module_t *mod;
+
+		(void) strncpy(mname, s, nbytes);
+		mname[nbytes] = '\0';
+
+		if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
+			(void) set_errno(EMDB_NOMOD);
+			return (NULL);
+		}
+
+		mod = mdb_nv_get_cookie(v);
+
+		if ((v = mdb_nv_lookup(&mod->mod_dcmds, ++p)) != NULL)
+			return (mdb_nv_get_cookie(v));
+
+	} else if ((v = mdb_nv_lookup(&mdb.m_dcmds, s)) != NULL)
+		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+	(void) set_errno(EMDB_NODCMD);
+	return (NULL);
+}
+
+void
+mdb_dcmd_usage(const mdb_idcmd_t *idcp, mdb_iob_t *iob)
+{
+	const char *prefix = "", *usage = "";
+	char name0 = idcp->idc_name[0];
+
+	if (idcp->idc_usage != NULL) {
+		if (idcp->idc_usage[0] == ':') {
+			if (name0 != ':' && name0 != '$')
+				prefix = "address::";
+			else
+				prefix = "address";
+			usage = &idcp->idc_usage[1];
+
+		} else if (idcp->idc_usage[0] == '?') {
+			if (name0 != ':' && name0 != '$')
+				prefix = "[address]::";
+			else
+				prefix = "[address]";
+			usage = &idcp->idc_usage[1];
+
+		} else
+			usage = idcp->idc_usage;
+	}
+
+	mdb_iob_printf(iob, "Usage: %s%s %s\n", prefix, idcp->idc_name, usage);
+
+	if (idcp->idc_help != NULL) {
+		mdb_iob_printf(iob, "%s: try '::help %s' for more "
+		    "information\n", mdb.m_pname, idcp->idc_name);
+	}
+}
+
+static mdb_idcmd_t *
+dcmd_ndef(const mdb_idcmd_t *idcp)
+{
+	mdb_var_t *v = mdb_nv_get_ndef(idcp->idc_var);
+
+	if (v != NULL)
+		return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
+
+	return (NULL);
+}
+
+static int
+dcmd_invoke(mdb_idcmd_t *idcp, uintptr_t addr, uint_t flags,
+    int argc, const mdb_arg_t *argv, const mdb_vcb_t *vcbs)
+{
+	int status;
+
+	mdb_dprintf(MDB_DBG_DCMD, "dcmd %s`%s dot = %lr incr = %llr\n",
+	    idcp->idc_modp->mod_name, idcp->idc_name, addr, mdb.m_incr);
+
+	if ((status = idcp->idc_funcp(addr, flags, argc, argv)) == DCMD_USAGE) {
+		mdb_dcmd_usage(idcp, mdb.m_err);
+		goto done;
+	}
+
+	while (status == DCMD_NEXT && (idcp = dcmd_ndef(idcp)) != NULL)
+		status = idcp->idc_funcp(addr, flags, argc, argv);
+
+	if (status == DCMD_USAGE)
+		mdb_dcmd_usage(idcp, mdb.m_err);
+
+	if (status == DCMD_NEXT)
+		status = DCMD_OK;
+done:
+	/*
+	 * If standard output is a pipe and there are vcbs active, we need to
+	 * flush standard out and the write-side of the pipe.  The reasons for
+	 * this are explained in more detail in mdb_vcb.c.
+	 */
+	if ((flags & DCMD_PIPE_OUT) && (vcbs != NULL)) {
+		mdb_iob_flush(mdb.m_out);
+		(void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW);
+	}
+
+	return (status);
+}
+
+/*
+ * Call an internal dcmd directly: this code is used by module API functions
+ * that need to execute dcmds, and by mdb_call() above.
+ */
+int
+mdb_call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
+    uint_t flags, mdb_argvec_t *avp, mdb_addrvec_t *adp, mdb_vcb_t *vcbs)
+{
+	int is_exec = (strcmp(idcp->idc_name, "$<") == 0);
+	mdb_arg_t *argv;
+	int argc;
+	uintmax_t i;
+	int status;
+
+	/*
+	 * Update the values of dot and the most recent address and count
+	 * to the values of our input parameters.
+	 */
+	mdb_nv_set_value(mdb.m_dot, addr);
+	mdb.m_raddr = addr;
+	mdb.m_dcount = count;
+
+	/*
+	 * Here the adb(1) man page lies: '9' is only set to count
+	 * when the command is $<, not when it's $<<.
+	 */
+	if (is_exec)
+		mdb_nv_set_value(mdb.m_rcount, count);
+
+	/*
+	 * We can now return if the repeat count is zero.
+	 */
+	if (count == 0)
+		return (DCMD_OK);
+
+	/*
+	 * To guard against bad dcmds, we avoid passing the actual argv that
+	 * we will use to free argument strings directly to the dcmd.  Instead,
+	 * we pass a copy that will be garbage collected automatically.
+	 */
+	argc = avp->a_nelems;
+	argv = mdb_alloc(sizeof (mdb_arg_t) * argc, UM_SLEEP | UM_GC);
+	bcopy(avp->a_data, argv, sizeof (mdb_arg_t) * argc);
+
+	if (mdb_addrvec_length(adp) != 0) {
+		flags |= DCMD_PIPE | DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
+		addr = mdb_addrvec_shift(adp);
+		mdb_nv_set_value(mdb.m_dot, addr);
+		mdb_vcb_propagate(vcbs);
+		count = 1;
+	}
+
+	status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+	if (DCMD_ABORTED(status))
+		goto done;
+
+	/*
+	 * If the command is $< and we're not receiving input from a pipe, we
+	 * ignore the repeat count and just return since the macro file is now
+	 * pushed on to the input stack.
+	 */
+	if (is_exec && mdb_addrvec_length(adp) == 0)
+		goto done;
+
+	/*
+	 * If we're going to loop, we've already executed the dcmd once,
+	 * so clear the LOOPFIRST flag before proceeding.
+	 */
+	if (flags & DCMD_LOOP)
+		flags &= ~DCMD_LOOPFIRST;
+
+	for (i = 1; i < count; i++) {
+		addr = mdb_dot_incr(",");
+		mdb_nv_set_value(mdb.m_dot, addr);
+		status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+		if (DCMD_ABORTED(status))
+			goto done;
+	}
+
+	while (mdb_addrvec_length(adp) != 0) {
+		addr = mdb_addrvec_shift(adp);
+		mdb_nv_set_value(mdb.m_dot, addr);
+		mdb_vcb_propagate(vcbs);
+		status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
+		if (DCMD_ABORTED(status))
+			goto done;
+	}
+done:
+	mdb_iob_nlflush(mdb.m_out);
+	return (status);
+}
+
+void
+mdb_intr_enable(void)
+{
+	ASSERT(mdb.m_intr >= 1);
+	if (mdb.m_intr == 1 && mdb.m_pend != 0) {
+		(void) mdb_signal_block(SIGINT);
+		mdb.m_intr = mdb.m_pend = 0;
+		mdb_dprintf(MDB_DBG_DSTK, "delivering pending INT\n");
+		longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
+	} else
+		mdb.m_intr--;
+}
+
+void
+mdb_intr_disable(void)
+{
+	mdb.m_intr++;
+	ASSERT(mdb.m_intr >= 1);
+}
+
+/*
+ * Create an encoded string representing the internal user-modifiable
+ * configuration of the debugger and return a pointer to it.  The string can be
+ * used to initialize another instance of the debugger with the same
+ * configuration as this one.
+ */
+char *
+mdb_get_config(void)
+{
+	size_t r, n = 0;
+	char *s = NULL;
+
+	while ((r = mdb_snprintf(s, n,
+	    "%x;%x;%x;%x;%x;%x;%lx;%x;%x;%s;%s;%s;%s;%s",
+	    mdb.m_tgtflags, mdb.m_flags, mdb.m_debug, mdb.m_radix, mdb.m_nargs,
+	    mdb.m_histlen, (ulong_t)mdb.m_symdist, mdb.m_execmode,
+	    mdb.m_forkmode, mdb.m_root, mdb.m_termtype, mdb.m_ipathstr,
+	    mdb.m_lpathstr, mdb.m_prompt)) > n) {
+
+		mdb_free(s, n);
+		n = r + 1;
+		s = mdb_alloc(r + 1, UM_SLEEP);
+	}
+
+	return (s);
+}
+
+/*
+ * Decode a configuration string created with mdb_get_config() and reset the
+ * appropriate parts of the global mdb_t accordingly.
+ */
+void
+mdb_set_config(const char *s)
+{
+	const char *p;
+	size_t len;
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_tgtflags = strntoul(s, (size_t)(p - s), 16);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_flags = strntoul(s, (size_t)(p - s), 16);
+		mdb.m_flags &= ~(MDB_FL_LOG | MDB_FL_LATEST);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_debug = strntoul(s, (size_t)(p - s), 16);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_radix = (int)strntoul(s, (size_t)(p - s), 16);
+		if (mdb.m_radix < 2 || mdb.m_radix > 16)
+			mdb.m_radix = MDB_DEF_RADIX;
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_nargs = (int)strntoul(s, (size_t)(p - s), 16);
+		mdb.m_nargs = MAX(mdb.m_nargs, 0);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_histlen = (int)strntoul(s, (size_t)(p - s), 16);
+		mdb.m_histlen = MAX(mdb.m_histlen, 1);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_symdist = strntoul(s, (size_t)(p - s), 16);
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_execmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
+		if (mdb.m_execmode > MDB_EM_FOLLOW)
+			mdb.m_execmode = MDB_EM_ASK;
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_forkmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
+		if (mdb.m_forkmode > MDB_FM_CHILD)
+			mdb.m_forkmode = MDB_FM_ASK;
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_root = strndup(s, (size_t)(p - s));
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		mdb.m_termtype = strndup(s, (size_t)(p - s));
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		size_t len = MIN(sizeof (mdb.m_ipathstr) - 1, p - s);
+		strncpy(mdb.m_ipathstr, s, len);
+		mdb.m_ipathstr[len] = '\0';
+		s = p + 1;
+	}
+
+	if ((p = strchr(s, ';')) != NULL) {
+		size_t len = MIN(sizeof (mdb.m_lpathstr) - 1, p - s);
+		strncpy(mdb.m_lpathstr, s, len);
+		mdb.m_lpathstr[len] = '\0';
+		s = p + 1;
+	}
+
+	p = s + strlen(s);
+	len = MIN(MDB_PROMPTLEN, (size_t)(p - s));
+	(void) strncpy(mdb.m_prompt, s, len);
+	mdb.m_prompt[len] = '\0';
+	mdb.m_promptlen = len;
+}
+
+mdb_module_t *
+mdb_get_module(void)
+{
+	if (mdb.m_lmod)
+		return (mdb.m_lmod);
+
+	if (mdb.m_frame && mdb.m_frame->f_cp && mdb.m_frame->f_cp->c_dcmd)
+		return (mdb.m_frame->f_cp->c_dcmd->idc_modp);
+
+	return (NULL);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb.h b/usr/src/cmd/mdb/common/mdb/mdb.h
new file mode 100644
index 0000000..9af8354
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb.h
@@ -0,0 +1,247 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_H
+#define	_MDB_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_io.h>
+#include <mdb/mdb_gelf.h>
+#include <mdb/mdb_addrvec.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_disasm.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_vcb.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_wr.h>
+#endif
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	MDB_ERR_PARSE	1	/* Error occurred in lexer or parser */
+#define	MDB_ERR_NOMEM	2	/* Failed to allocate needed memory */
+#define	MDB_ERR_PAGER	3	/* User quit current command from pager */
+#define	MDB_ERR_SIGINT	4	/* User interrupt: abort current command */
+#define	MDB_ERR_QUIT	5	/* User request: quit debugger */
+#define	MDB_ERR_ASSERT	6	/* Assertion failure: abort current command */
+#define	MDB_ERR_API	7	/* API function error: abort current command */
+#define	MDB_ERR_ABORT	8	/* User abort or resume: abort to top level */
+#define	MDB_ERR_OUTPUT	9	/* Write to m_out failed: abort to top level */
+
+#define	MDB_ERR_IS_FATAL(err)	\
+	((err) == MDB_ERR_QUIT || (err) == MDB_ERR_ABORT || \
+	(err) == MDB_ERR_OUTPUT)
+
+#define	MDB_DEF_RADIX	16	/* Default output radix */
+#define	MDB_DEF_NARGS	6	/* Default # of arguments in stack trace */
+#define	MDB_DEF_HISTLEN	128	/* Default length of command history */
+#define	MDB_DEF_SYMDIST	0x8000	/* Default symbol distance for addresses */
+#define	MDB_DEF_ARRMEM	32	/* Default number of array members to print */
+#define	MDB_DEF_ARRSTR	1024	/* Default number of array chars to print */
+
+#define	MDB_ARR_NOLIMIT	-1UL	/* No limit on number of array elements */
+
+#define	MDB_FL_PSYM	0x00001	/* Print dot as symbol + offset when possible */
+#define	MDB_FL_LOG	0x00002	/* Logging is enabled */
+#define	MDB_FL_NOMODS	0x00004	/* Skip automatic mdb module loading */
+#define	MDB_FL_USECUP	0x00008	/* Use terminal cup initialization sequences */
+#define	MDB_FL_ADB	0x00010	/* Enable stricter adb(1) compatibility */
+#define	MDB_FL_SHOWLMID	0x00020	/* Always show link map id with symbol names */
+#define	MDB_FL_IGNEOF	0x00040	/* Ignore EOF as a synonym for ::quit */
+#define	MDB_FL_REPLAST	0x00080	/* Naked newline repeats previous command */
+#define	MDB_FL_PAGER	0x00100	/* Enable pager by default */
+#define	MDB_FL_LATEST	0x00200	/* Replace version string with "latest" */
+#define	MDB_FL_VCREATE	0x00400	/* Victim process was created by debugger */
+#define	MDB_FL_JOBCTL	0x00800	/* Victim process jobctl stopped on same tty */
+#define	MDB_FL_DEMANGLE	0x01000	/* Demangle symbols as part of %a processing */
+#define	MDB_FL_EXEC	0x02000	/* Debugger exec'd by a previous instance */
+#define	MDB_FL_NOCTF	0x04000	/* Skip automatic CTF data loading */
+#define	MDB_FL_BPTNOSYMSTOP 0x08000 /* Stop on deferred bkpts for unk symbols */
+#define	MDB_FL_TERMGUESS 0x10000 /* m_termtype derived from userland */
+#define	MDB_FL_READBACK	0x20000	/* Read value back after write */
+#ifdef _KMDB
+#define	MDB_FL_NOUNLOAD	0x40000	/* Don't allow debugger unload */
+#endif
+
+#define	MDB_FL_VOLATILE	0x0001	/* Mask of all volatile flags to save/restore */
+
+#define	MDB_EM_ASK	0	/* Ask what to do on an exec */
+#define	MDB_EM_STOP	1	/* Stop after an exec */
+#define	MDB_EM_FOLLOW	2	/* Follow an exec */
+
+#define	MDB_FM_ASK	0	/* Ask what to do on a fork */
+#define	MDB_FM_PARENT	1	/* Follow parent process on a fork */
+#define	MDB_FM_CHILD	2	/* Follow child process on a fork */
+
+#define	MDB_PROMPTLEN	35	/* Maximum prompt length */
+
+struct kmdb_promif;
+
+typedef struct mdb {
+	uint_t m_tgtflags;	/* Target open flags (see mdb_target.h) */
+	uint_t m_flags;		/* Miscellaneous flags (see above) */
+	uint_t m_debug;		/* Debugging flags (see mdb_debug.h) */
+	int m_radix;		/* Default radix for output formatting */
+	int m_nargs;		/* Default number of arguments in stack trace */
+	int m_histlen;		/* Length of command history */
+	size_t m_symdist;	/* Distance from sym for addr match (0=smart) */
+	const char *m_pname;	/* Program basename from argv[0] */
+	char m_promptraw[MDB_PROMPTLEN + 1]; /* Un-expanded prompt */
+	char m_prompt[MDB_PROMPTLEN + 1]; /* Prompt for interactive mode */
+	size_t m_promptlen;	/* Length of prompt in bytes */
+	const char *m_shell;	/* Shell for ! commands and pipelines */
+	char *m_root;		/* Root for path construction */
+	char *m_ipathstr;	/* Path string for include path */
+	char *m_lpathstr;	/* Path string for library path */
+	const char **m_ipath;	/* Path for $< and $<< macro files */
+	size_t m_ipathlen;	/* Length of underlying ipath buffer */
+	const char **m_lpath;	/* Path for :: loadable modules */
+	size_t m_lpathlen;	/* Length of underlying lpath buffer */
+	mdb_modinfo_t m_rminfo;	/* Root debugger module information */
+	mdb_module_t m_rmod;	/* Root debugger module (builtins) */
+	mdb_module_t *m_mhead;	/* Head of module list (in load order) */
+	mdb_module_t *m_mtail;	/* Tail of module list (in load order) */
+	mdb_list_t m_tgtlist;	/* List of active target backends */
+	mdb_tgt_t *m_target;	/* Current debugger target backend */
+	mdb_nv_t m_disasms;	/* Hash of available disassemblers */
+	mdb_disasm_t *m_disasm;	/* Current disassembler backend */
+	char *m_defdisasm;	/* Deferred diassembler selection */
+	mdb_nv_t m_modules;	/* Name/value hash for loadable modules */
+	mdb_nv_t m_dcmds;	/* Name/value hash for extended commands */
+	mdb_nv_t m_walkers;	/* Name/value hash for walk operations */
+	mdb_nv_t m_nv;		/* Name/value hash for named variables */
+	mdb_var_t *m_dot;	/* Variable reference for '.' */
+	uintmax_t m_incr;	/* Current increment */
+	uintmax_t m_raddr;	/* Most recent address specified to a dcmd */
+	uintmax_t m_dcount;	/* Most recent count specified to a dcmd */
+	mdb_var_t *m_rvalue;	/* Most recent value printed */
+	mdb_var_t *m_roffset;	/* Most recent offset from an instruction */
+	mdb_var_t *m_proffset;	/* Previous value of m_roffset */
+	mdb_var_t *m_rcount;	/* Most recent count on $< dcmd */
+	mdb_iob_t *m_in;	/* Input stream */
+	mdb_iob_t *m_out;	/* Output stream */
+	mdb_iob_t *m_err;	/* Error stream */
+	mdb_iob_t *m_null;	/* Null stream */
+	char *m_termtype;	/* Interactive mode terminal type */
+	mdb_io_t *m_term;	/* Terminal for interactive mode */
+	mdb_io_t *m_log;	/* Log file i/o backend (NULL if not logging) */
+	mdb_module_t *m_lmod;	/* Pointer to loading module, if in load */
+	mdb_list_t m_lastc;	/* Last executed command list */
+	mdb_gelf_symtab_t *m_prsym;   /* Private symbol table */
+	mdb_demangler_t *m_demangler; /* Demangler (see <mdb/mdb_demangle.h>) */
+	mdb_list_t m_flist;	/* Stack of execution frames */
+	struct mdb_frame *volatile m_frame; /* Current stack frame */
+	struct mdb_frame *volatile m_fmark; /* Stack marker for pager */
+	uint_t m_fid;		/* Next frame identifier number to assign */
+	uint_t m_depth;		/* Depth of m_frame stack */
+	volatile uint_t m_intr;	/* Don't allow SIGINT if set */
+	volatile uint_t m_pend;	/* Pending SIGINT count */
+	pid_t m_pgid;		/* Debugger process group id */
+	uint_t m_rdvers;	/* Librtld_db version number */
+	uint_t m_ctfvers;	/* Libctf version number */
+	ulong_t m_armemlim;	/* Limit on number of array members to print */
+	ulong_t m_arstrlim;	/* Limit on number of array chars to print */
+	uchar_t m_execmode;	/* Follow exec behavior */
+	uchar_t m_forkmode;	/* Follow fork behavior */
+	char **m_env;		/* Current environment */
+	mdb_list_t m_cblist;	/* List of callbacks */
+	mdb_nv_t m_macaliases;	/* Name/value hash of ADB macro aliases */
+#ifdef _KMDB
+	struct dpi_ops *m_dpi;	/* DPI ops vector */
+	struct kdi *m_kdi;	/* KDI ops vector */
+	size_t m_pagesize;	/* Base page size for this machine */
+	caddr_t m_dseg;		/* Debugger segment address */
+	size_t m_dsegsz;	/* Debugger segment size */
+	mdb_nv_t m_dmodctl;	/* dmod name -> kmdb_modctl hash */
+	kmdb_wr_t *m_drvwrhead;	/* Driver work request queue */
+	kmdb_wr_t *m_drvwrtail;	/* Driver work request queue */
+	kmdb_wr_t *m_dbgwrhead;	/* Debugger request queue */
+	kmdb_wr_t *m_dbgwrtail;	/* Debugger request queue */
+	struct cons_polledio *m_pio; /* Polled I/O struct from kernel */
+	struct kmdb_promif *m_promif; /* Debugger/PROM interface state */
+#endif
+} mdb_t;
+
+#ifdef _MDB_PRIVATE
+mdb_t mdb;
+#else
+extern mdb_t mdb;
+#endif
+
+#ifdef _MDB
+
+#define	MDB_CONFIG_ENV_VAR "_MDB_CONFIG"
+
+extern void mdb_create(const char *, const char *);
+extern void mdb_destroy(void);
+
+extern int mdb_call_idcmd(mdb_idcmd_t *, uintmax_t, uintmax_t, uint_t,
+    mdb_argvec_t *, mdb_addrvec_t *, mdb_vcb_t *);
+
+extern int mdb_call(uintmax_t, uintmax_t, uint_t);
+extern int mdb_run(void);
+
+extern const char *mdb_get_prompt(void);
+extern int mdb_set_prompt(const char *);
+extern void mdb_set_ipath(const char *);
+extern void mdb_set_lpath(const char *);
+
+extern const char **mdb_path_alloc(const char *, size_t *);
+extern const char **mdb_path_dup(const char *[], size_t, size_t *);
+extern void mdb_path_free(const char *[], size_t);
+
+extern uintmax_t mdb_dot_incr(const char *);
+extern uintmax_t mdb_dot_decr(const char *);
+
+extern mdb_iwalker_t *mdb_walker_lookup(const char *);
+extern mdb_idcmd_t *mdb_dcmd_lookup(const char *);
+extern void mdb_dcmd_usage(const mdb_idcmd_t *, mdb_iob_t *);
+
+extern void mdb_pservice_init(void);
+
+extern void mdb_intr_enable(void);
+extern void mdb_intr_disable(void);
+
+extern char *mdb_get_config(void);
+extern void mdb_set_config(const char *);
+
+extern mdb_module_t *mdb_get_module(void);
+
+#endif /* _MDB */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c
new file mode 100644
index 0000000..052aa3d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.c
@@ -0,0 +1,86 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_addrvec.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_modapi.h>
+
+#include <strings.h>
+
+#define	AD_INIT	16	/* initial size of addrvec array */
+#define	AD_GROW	2	/* array growth multiplier */
+
+void
+mdb_addrvec_create(mdb_addrvec_t *adp)
+{
+	bzero(adp, sizeof (mdb_addrvec_t));
+}
+
+void
+mdb_addrvec_destroy(mdb_addrvec_t *adp)
+{
+	mdb_free(adp->ad_data, sizeof (uintptr_t) * adp->ad_size);
+	bzero(adp, sizeof (mdb_addrvec_t));
+}
+
+void
+mdb_addrvec_unshift(mdb_addrvec_t *adp, uintptr_t value)
+{
+	if (adp->ad_nelems >= adp->ad_size) {
+		size_t size = adp->ad_size ? adp->ad_size * AD_GROW : AD_INIT;
+		void *data = mdb_alloc(sizeof (uintptr_t) * size, UM_SLEEP);
+
+		bcopy(adp->ad_data, data, sizeof (uintptr_t) * adp->ad_size);
+		mdb_free(adp->ad_data, sizeof (uintptr_t) * adp->ad_size);
+
+		adp->ad_data = data;
+		adp->ad_size = size;
+	}
+
+	adp->ad_data[adp->ad_nelems++] = value;
+}
+
+uintptr_t
+mdb_addrvec_shift(mdb_addrvec_t *adp)
+{
+	if (adp->ad_ndx < adp->ad_nelems)
+		return (adp->ad_data[adp->ad_ndx++]);
+
+	return ((uintptr_t)-1L);
+}
+
+size_t
+mdb_addrvec_length(mdb_addrvec_t *adp)
+{
+	if (adp != NULL) {
+		ASSERT(adp->ad_nelems >= adp->ad_ndx);
+		return (adp->ad_nelems - adp->ad_ndx);
+	}
+
+	return (0); /* convenience for callers */
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h
new file mode 100644
index 0000000..936177f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_addrvec.h
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 (c) 1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef	_MDB_ADDRVEC_H
+#define	_MDB_ADDRVEC_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_addrvec {
+	uintptr_t *ad_data;		/* Array of addresses */
+	size_t ad_nelems;		/* Number of valid elements */
+	size_t ad_size;			/* Array size */
+	size_t ad_ndx;			/* Array index */
+} mdb_addrvec_t;
+
+#ifdef _MDB
+
+extern void mdb_addrvec_create(mdb_addrvec_t *);
+extern void mdb_addrvec_destroy(mdb_addrvec_t *);
+
+extern uintptr_t mdb_addrvec_shift(mdb_addrvec_t *);
+extern void mdb_addrvec_unshift(mdb_addrvec_t *, uintptr_t);
+extern size_t mdb_addrvec_length(mdb_addrvec_t *);
+
+#endif	/* _MDB */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_ADDRVEC_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_argvec.c b/usr/src/cmd/mdb/common/mdb/mdb_argvec.c
new file mode 100644
index 0000000..3505906
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_argvec.c
@@ -0,0 +1,415 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_types.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_stdlib.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb.h>
+
+#include <alloca.h>
+
+#define	AV_DEFSZ	16	/* Initial size of argument vector */
+#define	AV_GROW		2	/* Multiplier for growing argument vector */
+
+void
+mdb_argvec_create(mdb_argvec_t *vec)
+{
+	vec->a_data = NULL;
+	vec->a_nelems = 0;
+	vec->a_size = 0;
+}
+
+void
+mdb_argvec_destroy(mdb_argvec_t *vec)
+{
+	if (vec->a_data != NULL) {
+		mdb_argvec_reset(vec);
+		mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
+	}
+}
+
+void
+mdb_argvec_append(mdb_argvec_t *vec, const mdb_arg_t *arg)
+{
+	if (vec->a_nelems >= vec->a_size) {
+		size_t size = vec->a_size ? vec->a_size * AV_GROW : AV_DEFSZ;
+		void *data = mdb_alloc(sizeof (mdb_arg_t) * size, UM_NOSLEEP);
+
+		if (data == NULL) {
+			warn("failed to grow argument vector");
+			longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
+		}
+
+		bcopy(vec->a_data, data, sizeof (mdb_arg_t) * vec->a_size);
+		mdb_free(vec->a_data, sizeof (mdb_arg_t) * vec->a_size);
+
+		vec->a_data = data;
+		vec->a_size = size;
+	}
+
+	bcopy(arg, &vec->a_data[vec->a_nelems++], sizeof (mdb_arg_t));
+}
+
+void
+mdb_argvec_reset(mdb_argvec_t *vec)
+{
+	size_t nelems = vec->a_nelems;
+	mdb_arg_t *arg;
+
+	for (arg = vec->a_data; nelems != 0; nelems--, arg++) {
+		if (arg->a_type == MDB_TYPE_STRING && arg->a_un.a_str != NULL)
+			strfree((char *)arg->a_un.a_str);
+	}
+
+	vec->a_nelems = 0;
+}
+
+void
+mdb_argvec_zero(mdb_argvec_t *vec)
+{
+#ifdef DEBUG
+	size_t i;
+
+	for (i = 0; i < vec->a_size; i++) {
+		vec->a_data[i].a_type = UMEM_UNINITIALIZED_PATTERN;
+		vec->a_data[i].a_un.a_val =
+		    ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN << 32) |
+		    ((u_longlong_t)UMEM_UNINITIALIZED_PATTERN);
+	}
+#endif
+	vec->a_nelems = 0;
+}
+
+void
+mdb_argvec_copy(mdb_argvec_t *dst, const mdb_argvec_t *src)
+{
+	if (src->a_nelems > dst->a_size) {
+		mdb_arg_t *data =
+		    mdb_alloc(sizeof (mdb_arg_t) * src->a_nelems, UM_NOSLEEP);
+
+		if (data == NULL) {
+			warn("failed to grow argument vector");
+			longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM);
+		}
+
+		if (dst->a_data != NULL)
+			mdb_free(dst->a_data, sizeof (mdb_arg_t) * dst->a_size);
+
+		dst->a_data = data;
+		dst->a_size = src->a_nelems;
+	}
+
+	bcopy(src->a_data, dst->a_data, sizeof (mdb_arg_t) * src->a_nelems);
+	dst->a_nelems = src->a_nelems;
+}
+
+static int
+argvec_process_subopt(const mdb_opt_t *opt, const mdb_arg_t *arg)
+{
+	mdb_subopt_t *sop;
+	const char *start;
+	const char *next;
+	char error[32];
+	size_t len;
+	uint_t value = 0;
+	uint_t i;
+
+	start = arg->a_un.a_str;
+
+	for (i = 0; ; i++) {
+		next = strchr(start, ',');
+
+		if (next == NULL)
+			len = strlen(start);
+		else
+			len = next - start;
+
+		/*
+		 * Record the index of the subopt if a match if found.
+		 */
+		for (sop = opt->opt_subopts; sop->sop_flag; sop++) {
+			if (strlen(sop->sop_str) == len &&
+			    strncmp(sop->sop_str, start, len) == 0) {
+				value |= sop->sop_flag;
+				sop->sop_index = i;
+				goto found;
+			}
+		}
+		(void) mdb_snprintf(error, len + 1, "%s", start);
+		warn("invalid option for -%c: \"%s\"\n", opt->opt_char, error);
+
+		return (-1);
+
+found:
+		if (next == NULL)
+			break;
+		start = next + 1;
+	}
+
+	*((uint_t *)opt->opt_valp) = value;
+
+	return (0);
+}
+
+
+static int
+argvec_process_opt(const mdb_opt_t *opt, const mdb_arg_t *arg)
+{
+	uint64_t ui64;
+	uintptr_t uip;
+
+	switch (opt->opt_type) {
+	case MDB_OPT_SETBITS:
+		*((uint_t *)opt->opt_valp) |= opt->opt_bits;
+		break;
+
+	case MDB_OPT_CLRBITS:
+		*((uint_t *)opt->opt_valp) &= ~opt->opt_bits;
+		break;
+
+	case MDB_OPT_STR:
+		if (arg->a_type != MDB_TYPE_STRING) {
+			warn("string argument required for -%c\n",
+			    opt->opt_char);
+			return (-1);
+		}
+		*((const char **)opt->opt_valp) = arg->a_un.a_str;
+		break;
+
+	case MDB_OPT_UINTPTR_SET:
+		*opt->opt_flag = TRUE;
+		/* FALLTHROUGH */
+	case MDB_OPT_UINTPTR:
+		if (arg->a_type == MDB_TYPE_STRING)
+			uip = (uintptr_t)mdb_strtoull(arg->a_un.a_str);
+		else
+			uip = (uintptr_t)arg->a_un.a_val;
+		*((uintptr_t *)opt->opt_valp) = uip;
+		break;
+
+	case MDB_OPT_UINT64:
+		if (arg->a_type == MDB_TYPE_STRING)
+			ui64 = mdb_strtoull(arg->a_un.a_str);
+		else
+			ui64 = arg->a_un.a_val;
+		*((uint64_t *)opt->opt_valp) = ui64;
+		break;
+
+	case MDB_OPT_SUBOPTS:
+		if (arg->a_type != MDB_TYPE_STRING) {
+			warn("string argument required for -%c\n",
+			    opt->opt_char);
+			return (-1);
+		}
+		return (argvec_process_subopt(opt, arg));
+
+	default:
+		warn("internal: bad opt=%p type=%hx\n",
+		    (void *)opt, opt->opt_type);
+		return (-1);
+	}
+
+	return (0);
+}
+
+static const mdb_opt_t *
+argvec_findopt(const mdb_opt_t *opts, char c)
+{
+	const mdb_opt_t *optp;
+
+	for (optp = opts; optp->opt_char != 0; optp++) {
+		if (optp->opt_char == c)
+			return (optp);
+	}
+
+	return (NULL);
+}
+
+static int
+argvec_getopts(const mdb_opt_t *opts, const mdb_arg_t *argv, int argc)
+{
+	const mdb_opt_t *optp;
+	const mdb_arg_t *argp;
+
+	mdb_arg_t arg;
+
+	const char *p;
+	int i;
+	int nargs;	/* Number of arguments consumed in an iteration */
+
+	for (i = 0; i < argc; i++, argv++) {
+		/*
+		 * Each option must begin with a string argument whose first
+		 * character is '-' and has additional characters afterward.
+		 */
+		if (argv->a_type != MDB_TYPE_STRING ||
+		    argv->a_un.a_str[0] != '-' || argv->a_un.a_str[1] == '\0')
+			return (i);
+
+		/*
+		 * The special prefix '--' ends option processing.
+		 */
+		if (strncmp(argv->a_un.a_str, "--", 2) == 0)
+			return (i);
+
+		for (p = &argv->a_un.a_str[1]; *p != '\0'; p++) {
+			/*
+			 * Locate an option struct whose opt_char field
+			 * matches the current option letter.
+			 */
+			if ((optp = argvec_findopt(opts, *p)) == NULL) {
+				warn("illegal option -- %c\n", *p);
+				return (i);
+			}
+
+			/*
+			 * Require an argument for strings, immediate
+			 * values, subopt-lists and callback functions
+			 * which require arguments.
+			 */
+			if (optp->opt_type == MDB_OPT_STR ||
+			    optp->opt_type == MDB_OPT_UINTPTR ||
+			    optp->opt_type == MDB_OPT_UINTPTR_SET ||
+			    optp->opt_type == MDB_OPT_SUBOPTS ||
+			    optp->opt_type == MDB_OPT_UINT64) {
+				/*
+				 * More text after the option letter:
+				 * forge a string argument from remainder.
+				 */
+				if (p[1] != '\0') {
+					arg.a_type = MDB_TYPE_STRING;
+					arg.a_un.a_str = ++p;
+					argp = &arg;
+					p += strlen(p) - 1;
+
+					nargs = 0;
+				/*
+				 * Otherwise use the next argv element as
+				 * the argument if there is one.
+				 */
+				} else if (++i == argc) {
+					warn("option requires an "
+					    "argument -- %c\n", *p);
+					return (i - 1);
+				} else {
+					argp = ++argv;
+					nargs = 1;
+				}
+			} else {
+				argp = NULL;
+				nargs = 0;
+			}
+
+			/*
+			 * Perform type-specific handling for this option.
+			 */
+			if (argvec_process_opt(optp, argp) == -1)
+				return (i - nargs);
+		}
+	}
+
+	return (i);
+}
+
+int
+mdb_getopts(int argc, const mdb_arg_t *argv, ...)
+{
+	/*
+	 * For simplicity just declare enough options on the stack to handle
+	 * a-z and A-Z and an extra terminator.
+	 */
+	mdb_opt_t opts[53], *op = &opts[0];
+	va_list alist;
+	int c, i = 0;
+	mdb_subopt_t *sop;
+
+	va_start(alist, argv);
+
+	for (i = 0; i < (sizeof (opts) / sizeof (opts[0]) - 1); i++, op++) {
+		if ((c = va_arg(alist, int)) == 0)
+			break; /* end of options */
+
+		op->opt_char = (char)c;
+		op->opt_type = va_arg(alist, uint_t);
+
+		if (op->opt_type == MDB_OPT_SETBITS ||
+		    op->opt_type == MDB_OPT_CLRBITS) {
+			op->opt_bits = va_arg(alist, uint_t);
+		} else if (op->opt_type == MDB_OPT_UINTPTR_SET) {
+			op->opt_flag = va_arg(alist, boolean_t *);
+		} else if (op->opt_type == MDB_OPT_SUBOPTS) {
+			op->opt_subopts = va_arg(alist, mdb_subopt_t *);
+
+			for (sop = op->opt_subopts; sop->sop_flag; sop++)
+				sop->sop_index = -1;
+		}
+
+		op->opt_valp = va_arg(alist, void *);
+	}
+
+	bzero(&opts[i], sizeof (mdb_opt_t));
+	va_end(alist);
+
+	return (argvec_getopts(opts, argv, argc));
+}
+
+/*
+ * The old adb breakpoint and watchpoint routines did not accept any arguments;
+ * all characters after the verb were concatenated to form the string callback.
+ * This utility function concatenates all arguments in argv[] into a single
+ * string to simplify the implementation of these legacy routines.
+ */
+char *
+mdb_argv_to_str(int argc, const mdb_arg_t *argv)
+{
+	char *s = NULL;
+	size_t n = 0;
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (argv[i].a_type == MDB_TYPE_STRING)
+			n += strlen(argv[i].a_un.a_str);
+	}
+
+	if (n != 0) {
+		s = mdb_zalloc(n + argc, UM_SLEEP);
+
+		for (i = 0; i < argc - 1; i++, argv++) {
+			(void) strcat(s, argv->a_un.a_str);
+			(void) strcat(s, " ");
+		}
+
+		(void) strcat(s, argv->a_un.a_str);
+	}
+
+	return (s);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_argvec.h b/usr/src/cmd/mdb/common/mdb/mdb_argvec.h
new file mode 100644
index 0000000..d76280d
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_argvec.h
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_ARGVEC_H
+#define	_MDB_ARGVEC_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+struct mdb_arg;
+
+typedef struct mdb_argvec {
+	struct mdb_arg *a_data;		/* Array of arguments */
+	size_t a_nelems;		/* Number of valid elements */
+	size_t a_size;			/* Array size */
+} mdb_argvec_t;
+
+/* see mdb_modapi.h for 1-6 */
+#define	MDB_OPT_SUBOPTS	7		/* Option requires a mdb_subopt_t */
+					/* list and a value argument */
+
+typedef struct mdb_subopt {
+	uint_t sop_flag;		/* Option flag */
+	const char *sop_str;		/* Sub-option name */
+	int sop_index;			/* Index of subopt in argument */
+} mdb_subopt_t;
+
+typedef struct mdb_opt {
+	char opt_char;			/* Option name */
+	void *opt_valp;			/* Value storage pointer */
+	uint_t opt_bits;		/* Bits to set or clear for booleans */
+	boolean_t *opt_flag;		/* pointer to flag (uintptr_set) */
+	mdb_subopt_t *opt_subopts;	/* List of mdb_subopt_t */
+	uint_t opt_type;		/* Option type (see above) */
+} mdb_opt_t;
+
+#ifdef _MDB
+
+#ifdef	_BIG_ENDIAN
+#ifdef	_LP64
+#define	MDB_INIT_CHAR(x)	((const char *)((uintptr_t)(uchar_t)(x) << 56))
+#else	/* _LP64 */
+#define	MDB_INIT_CHAR(x)	((const char *)((uintptr_t)(uchar_t)(x) << 24))
+#endif	/* _LP64 */
+#else	/* _BIG_ENDIAN */
+#define	MDB_INIT_CHAR(x)	((const char *)(uchar_t)(x))
+#endif	/* _BIG_ENDIAN */
+#define	MDB_INIT_STRING(x)	((const char *)(x))
+
+extern void mdb_argvec_create(mdb_argvec_t *);
+extern void mdb_argvec_destroy(mdb_argvec_t *);
+extern void mdb_argvec_append(mdb_argvec_t *, const struct mdb_arg *);
+extern void mdb_argvec_reset(mdb_argvec_t *);
+extern void mdb_argvec_zero(mdb_argvec_t *);
+extern void mdb_argvec_copy(mdb_argvec_t *, const mdb_argvec_t *);
+
+extern char *mdb_argv_to_str(int, const struct mdb_arg *);
+
+#endif	/* _MDB */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_ARGVEC_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_callb.c b/usr/src/cmd/mdb/common/mdb/mdb_callb.c
new file mode 100644
index 0000000..308eb48
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_callb.c
@@ -0,0 +1,115 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * Callback facility designed to allow interested parties (dmods, targets, or
+ * even the core debugger framework) to register for notification when certain
+ * "interesting" events occur.
+ */
+
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_callb.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb.h>
+
+mdb_callb_t *
+mdb_callb_add(mdb_module_t *m, int class, mdb_callb_f fp, void *arg)
+{
+	mdb_callb_t *new = mdb_zalloc(sizeof (mdb_callb_t), UM_SLEEP);
+
+	ASSERT(class == MDB_CALLB_STCHG || class == MDB_CALLB_PROMPT);
+
+	new->cb_mod = m;
+	new->cb_class = class;
+	new->cb_func = fp;
+	new->cb_arg = arg;
+
+	if (m == NULL) {
+		mdb_list_prepend(&mdb.m_cblist, new);
+	} else {
+		mdb_list_insert(&mdb.m_cblist, m->mod_cb, new);
+		if (m->mod_cb == NULL)
+			m->mod_cb = new;
+	}
+
+	return (new);
+}
+
+void
+mdb_callb_remove(mdb_callb_t *cb)
+{
+	if (cb->cb_mod != NULL) {
+		mdb_callb_t *next = mdb_list_next(cb);
+		mdb_module_t *mod = cb->cb_mod;
+
+		if (mod->mod_cb == cb) {
+			if (next == NULL || next->cb_mod != mod)
+				mod->mod_cb = NULL;
+			else
+				mod->mod_cb = next;
+		}
+	}
+
+	mdb_list_delete(&mdb.m_cblist, cb);
+
+	mdb_free(cb, sizeof (mdb_callb_t));
+}
+
+void
+mdb_callb_remove_by_mod(mdb_module_t *m)
+{
+	while (m->mod_cb != NULL)
+		mdb_callb_remove(m->mod_cb);
+}
+
+void
+mdb_callb_remove_all(void)
+{
+	mdb_callb_t *cb;
+
+	while ((cb = mdb_list_next(&mdb.m_cblist)) != NULL)
+		mdb_callb_remove(cb);
+}
+
+void
+mdb_callb_fire(int class)
+{
+	mdb_callb_t *cb, *next;
+
+	ASSERT(class == MDB_CALLB_STCHG || class == MDB_CALLB_PROMPT);
+
+	mdb_dprintf(MDB_DBG_CALLB, "invoking %s callbacks\n",
+	    (class == MDB_CALLB_STCHG ? "state change" : "prompt"));
+
+	for (cb = mdb_list_next(&mdb.m_cblist); cb != NULL; cb = next) {
+		next = mdb_list_next(cb);
+		if (cb->cb_class == class)
+			cb->cb_func(cb->cb_arg);
+	}
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_callb.h b/usr/src/cmd/mdb/common/mdb/mdb_callb.h
new file mode 100644
index 0000000..1c12e4c
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_callb.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MDB_CALLB_H
+#define	_MDB_CALLB_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <mdb/mdb_list.h>
+#include <mdb/mdb_module.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Callback facility designed to allow interested parties (dmods, targets, or
+ * even the core debugger framework) to register for notification when certain
+ * "interesting" events occur.
+ */
+
+/*
+ * Callback classes:
+ * (MDB_CALLBACK_* definitions in the module API need to be in sync with these)
+ */
+#define	MDB_CALLB_STCHG		1	/* System execution state change */
+#define	MDB_CALLB_PROMPT	2	/* Before printing the prompt */
+
+typedef void (*mdb_callb_f)(void *);
+
+typedef struct mdb_callb {
+	mdb_list_t	cb_list;	/* List of callbacks */
+	mdb_module_t	*cb_mod;	/* Requesting module (if any) */
+	int		cb_class;	/* When to notify */
+	mdb_callb_f	cb_func;	/* Function to invoke */
+	void		*cb_arg;	/* Argument for cb_func */
+} mdb_callb_t;
+
+extern mdb_callb_t *mdb_callb_add(mdb_module_t *, int, mdb_callb_f, void *);
+extern void mdb_callb_remove(mdb_callb_t *);
+extern void mdb_callb_remove_by_mod(mdb_module_t *);
+extern void mdb_callb_remove_all(void);
+extern void mdb_callb_fire(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDB_CALLB_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c
new file mode 100644
index 0000000..b317462
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.c
@@ -0,0 +1,484 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+/*
+ * The MDB command buffer is a simple structure that keeps track of the
+ * command history list, and provides operations to manipulate the current
+ * buffer according to the various emacs editing options.  The terminal
+ * code uses this code to keep track of the actual contents of the command
+ * line, and then uses this content to perform redraw operations.
+ */
+
+#include <strings.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_cmdbuf.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb.h>
+
+#define	CMDBUF_LINELEN	BUFSIZ		/* Length of each buffer line */
+#define	CMDBUF_TABLEN	8		/* Length of a tab in spaces */
+
+static void
+cmdbuf_shiftr(mdb_cmdbuf_t *cmd, size_t nbytes)
+{
+	bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
+	    &cmd->cmd_buf[cmd->cmd_bufidx + nbytes],
+	    cmd->cmd_buflen - cmd->cmd_bufidx);
+}
+
+void
+mdb_cmdbuf_create(mdb_cmdbuf_t *cmd)
+{
+	size_t i;
+
+	/*
+	 * This is pretty weak, but good enough for the moment: just allocate
+	 * BUFSIZ-sized chunks in advance for every history element.  Later
+	 * it would be nice to replace this with either code that allocates
+	 * space for the history list on-the-fly so as not to waste so much
+	 * memory, or that keeps a mapped history file like the shell.
+	 */
+	cmd->cmd_history = mdb_alloc(mdb.m_histlen * sizeof (char *), UM_SLEEP);
+	cmd->cmd_linebuf = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
+
+	for (i = 0; i < mdb.m_histlen; i++)
+		cmd->cmd_history[i] = mdb_alloc(CMDBUF_LINELEN, UM_SLEEP);
+
+	cmd->cmd_buf = cmd->cmd_history[0];
+	cmd->cmd_linelen = CMDBUF_LINELEN;
+	cmd->cmd_histlen = mdb.m_histlen;
+	cmd->cmd_buflen = 0;
+	cmd->cmd_bufidx = 0;
+	cmd->cmd_hold = 0;
+	cmd->cmd_hnew = 0;
+	cmd->cmd_hcur = 0;
+	cmd->cmd_hlen = 0;
+}
+
+void
+mdb_cmdbuf_destroy(mdb_cmdbuf_t *cmd)
+{
+	size_t i;
+
+	for (i = 0; i < cmd->cmd_histlen; i++)
+		mdb_free(cmd->cmd_history[i], CMDBUF_LINELEN);
+
+	mdb_free(cmd->cmd_linebuf, CMDBUF_LINELEN);
+	mdb_free(cmd->cmd_history, cmd->cmd_histlen * sizeof (char *));
+}
+
+int
+mdb_cmdbuf_caninsert(mdb_cmdbuf_t *cmd, size_t nbytes)
+{
+	return (cmd->cmd_buflen + nbytes < cmd->cmd_linelen);
+}
+
+int
+mdb_cmdbuf_atstart(mdb_cmdbuf_t *cmd)
+{
+	return (cmd->cmd_bufidx == 0);
+}
+
+int
+mdb_cmdbuf_atend(mdb_cmdbuf_t *cmd)
+{
+	return (cmd->cmd_bufidx == cmd->cmd_buflen);
+}
+
+int
+mdb_cmdbuf_insert(mdb_cmdbuf_t *cmd, int c)
+{
+	if (c == '\t') {
+		if (cmd->cmd_buflen + CMDBUF_TABLEN < cmd->cmd_linelen) {
+			int i;
+
+			if (cmd->cmd_buflen != cmd->cmd_bufidx)
+				cmdbuf_shiftr(cmd, CMDBUF_TABLEN);
+
+			for (i = 0; i < CMDBUF_TABLEN; i++)
+				cmd->cmd_buf[cmd->cmd_bufidx++] = ' ';
+
+			cmd->cmd_buflen += CMDBUF_TABLEN;
+			return (0);
+		}
+
+		return (-1);
+	}
+
+	if (c < ' ' || c > '~')
+		return (-1);
+
+	if (cmd->cmd_buflen < cmd->cmd_linelen) {
+		if (cmd->cmd_buflen != cmd->cmd_bufidx)
+			cmdbuf_shiftr(cmd, 1);
+
+		cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
+		cmd->cmd_buflen++;
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+const char *
+mdb_cmdbuf_accept(mdb_cmdbuf_t *cmd)
+{
+	if (cmd->cmd_bufidx < cmd->cmd_linelen) {
+		cmd->cmd_buf[cmd->cmd_buflen++] = '\0';
+		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+
+		/*
+		 * Don't bother inserting empty buffers into the history ring.
+		 */
+		if (cmd->cmd_buflen > 1) {
+			cmd->cmd_hnew = (cmd->cmd_hnew + 1) % cmd->cmd_histlen;
+			cmd->cmd_buf = cmd->cmd_history[cmd->cmd_hnew];
+			cmd->cmd_hcur = cmd->cmd_hnew;
+
+			if (cmd->cmd_hlen + 1 == cmd->cmd_histlen)
+				cmd->cmd_hold =
+				    (cmd->cmd_hold + 1) % cmd->cmd_histlen;
+			else
+				cmd->cmd_hlen++;
+		}
+
+		cmd->cmd_bufidx = 0;
+		cmd->cmd_buflen = 0;
+
+		return ((const char *)cmd->cmd_linebuf);
+	}
+
+	return (NULL);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backspace(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx > 0) {
+		if (cmd->cmd_buflen != cmd->cmd_bufidx) {
+			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx],
+			    &cmd->cmd_buf[cmd->cmd_bufidx - 1],
+			    cmd->cmd_buflen - cmd->cmd_bufidx);
+		}
+
+		cmd->cmd_bufidx--;
+		cmd->cmd_buflen--;
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_delchar(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
+		if (cmd->cmd_bufidx < --cmd->cmd_buflen) {
+			bcopy(&cmd->cmd_buf[cmd->cmd_bufidx + 1],
+			    &cmd->cmd_buf[cmd->cmd_bufidx],
+			    cmd->cmd_buflen - cmd->cmd_bufidx);
+		}
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx < cmd->cmd_buflen) {
+		cmd->cmd_bufidx++;
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backchar(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx > 0) {
+		cmd->cmd_bufidx--;
+		return (0);
+	}
+
+	return (-1);
+}
+
+int
+mdb_cmdbuf_transpose(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx > 0 && cmd->cmd_buflen > 1) {
+		c = cmd->cmd_buf[cmd->cmd_bufidx - 1];
+
+		if (cmd->cmd_bufidx == cmd->cmd_buflen) {
+			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
+			    cmd->cmd_buf[cmd->cmd_bufidx - 2];
+			cmd->cmd_buf[cmd->cmd_bufidx - 2] = (char)c;
+		} else {
+			cmd->cmd_buf[cmd->cmd_bufidx - 1] =
+			    cmd->cmd_buf[cmd->cmd_bufidx];
+			cmd->cmd_buf[cmd->cmd_bufidx++] = (char)c;
+		}
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_home(mdb_cmdbuf_t *cmd, int c)
+{
+	cmd->cmd_bufidx = 0;
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_end(mdb_cmdbuf_t *cmd, int c)
+{
+	cmd->cmd_bufidx = cmd->cmd_buflen;
+	return (0);
+}
+
+static size_t
+fwdword_index(mdb_cmdbuf_t *cmd)
+{
+	size_t i = cmd->cmd_bufidx + 1;
+
+	ASSERT(cmd->cmd_bufidx < cmd->cmd_buflen);
+
+	while (i < cmd->cmd_buflen && isspace(cmd->cmd_buf[i]))
+		i++;
+
+	while (i < cmd->cmd_buflen && !isspace(cmd->cmd_buf[i]) &&
+	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
+		i++;
+
+	while (i < cmd->cmd_buflen &&
+	    (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
+		i++;
+
+	return (i);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_fwdword(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx == cmd->cmd_buflen)
+		return (-1);
+
+	cmd->cmd_bufidx = fwdword_index(cmd);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *cmd, int c)
+{
+	size_t i;
+
+	if (cmd->cmd_bufidx == cmd->cmd_buflen)
+		return (-1);
+
+	i = fwdword_index(cmd);
+
+	bcopy(&cmd->cmd_buf[i], &cmd->cmd_buf[cmd->cmd_bufidx],
+	    cmd->cmd_buflen - i);
+
+	cmd->cmd_buflen -= i - cmd->cmd_bufidx;
+
+	return (0);
+}
+
+static size_t
+backword_index(mdb_cmdbuf_t *cmd)
+{
+	size_t i = cmd->cmd_bufidx - 1;
+
+	ASSERT(cmd->cmd_bufidx != 0);
+
+	while (i != 0 && isspace(cmd->cmd_buf[i]))
+		i--;
+
+	while (i != 0 && !isspace(cmd->cmd_buf[i]) &&
+	    !isalnum(cmd->cmd_buf[i]) && cmd->cmd_buf[i] != '_')
+		i--;
+
+	while (i != 0 && (isalnum(cmd->cmd_buf[i]) || cmd->cmd_buf[i] == '_'))
+		i--;
+
+	if (i != 0)
+		i++;
+
+	return (i);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_backword(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_bufidx == 0)
+		return (-1);
+
+	cmd->cmd_bufidx = backword_index(cmd);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_killbackword(mdb_cmdbuf_t *cmd, int c)
+{
+	size_t i;
+
+	if (cmd->cmd_bufidx == 0)
+		return (-1);
+
+	i = backword_index(cmd);
+
+	bcopy(&cmd->cmd_buf[cmd->cmd_bufidx], &cmd->cmd_buf[i],
+	    cmd->cmd_buflen - cmd->cmd_bufidx);
+
+	cmd->cmd_buflen -= cmd->cmd_bufidx - i;
+	cmd->cmd_bufidx = i;
+
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_kill(mdb_cmdbuf_t *cmd, int c)
+{
+	cmd->cmd_buflen = cmd->cmd_bufidx;
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_reset(mdb_cmdbuf_t *cmd, int c)
+{
+	cmd->cmd_buflen = 0;
+	cmd->cmd_bufidx = 0;
+	return (0);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_prevhist(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_hcur != cmd->cmd_hold) {
+		if (cmd->cmd_hcur-- == cmd->cmd_hnew) {
+			cmd->cmd_buf[cmd->cmd_buflen] = 0;
+			(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+		}
+
+		if (cmd->cmd_hcur < 0)
+			cmd->cmd_hcur = cmd->cmd_histlen - 1;
+
+		(void) strcpy(cmd->cmd_buf, cmd->cmd_history[cmd->cmd_hcur]);
+		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+		cmd->cmd_buflen = cmd->cmd_bufidx;
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_nexthist(mdb_cmdbuf_t *cmd, int c)
+{
+	if (cmd->cmd_hcur != cmd->cmd_hnew) {
+		cmd->cmd_hcur = (cmd->cmd_hcur + 1) % cmd->cmd_histlen;
+
+		if (cmd->cmd_hcur == cmd->cmd_hnew) {
+			(void) strcpy(cmd->cmd_buf, cmd->cmd_linebuf);
+		} else {
+			(void) strcpy(cmd->cmd_buf,
+			    cmd->cmd_history[cmd->cmd_hcur]);
+		}
+
+		cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+		cmd->cmd_buflen = cmd->cmd_bufidx;
+
+		return (0);
+	}
+
+	return (-1);
+}
+
+/*ARGSUSED*/
+int
+mdb_cmdbuf_findhist(mdb_cmdbuf_t *cmd, int c)
+{
+	ssize_t i, n;
+
+	if (cmd->cmd_buflen != 0) {
+		cmd->cmd_hcur = cmd->cmd_hnew;
+		cmd->cmd_buf[cmd->cmd_buflen] = 0;
+		(void) strcpy(cmd->cmd_linebuf, cmd->cmd_buf);
+	}
+
+	for (i = cmd->cmd_hcur, n = 0; n < cmd->cmd_hlen; n++) {
+		if (--i < 0)
+			i = cmd->cmd_histlen - 1;
+
+		if (strstr(cmd->cmd_history[i], cmd->cmd_linebuf) != NULL) {
+			(void) strcpy(cmd->cmd_buf, cmd->cmd_history[i]);
+			cmd->cmd_bufidx = strlen(cmd->cmd_buf);
+			cmd->cmd_buflen = cmd->cmd_bufidx;
+			cmd->cmd_hcur = i;
+
+			return (0);
+		}
+	}
+
+	cmd->cmd_hcur = cmd->cmd_hnew;
+
+	cmd->cmd_bufidx = 0;
+	cmd->cmd_buflen = 0;
+
+	return (-1);
+}
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h
new file mode 100644
index 0000000..ae49042
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmdbuf.h
@@ -0,0 +1,87 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 1997-2002 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_MDB_CMDBUF_H
+#define	_MDB_CMDBUF_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/types.h>
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+typedef struct mdb_cmdbuf {
+	char **cmd_history;	/* Circular array of history buffers */
+	char *cmd_linebuf;	/* Temporary history for current buffer */
+	char *cmd_buf;		/* Current line buffer */
+	size_t cmd_linelen;	/* Maximum line length */
+	size_t cmd_histlen;	/* Maximum history entries */
+	size_t cmd_buflen;	/* Number of bytes in current line buffer */
+	size_t cmd_bufidx;	/* Byte position in current line buffer */
+	ssize_t cmd_hold;	/* Oldest history entry index */
+	ssize_t cmd_hnew;	/* Newest history entry index */
+	ssize_t cmd_hcur;	/* Current history entry index */
+	ssize_t cmd_hlen;	/* Number of valid history buffers */
+} mdb_cmdbuf_t;
+
+#ifdef _MDB
+
+extern void mdb_cmdbuf_create(mdb_cmdbuf_t *);
+extern void mdb_cmdbuf_destroy(mdb_cmdbuf_t *);
+
+extern const char *mdb_cmdbuf_accept(mdb_cmdbuf_t *);
+
+extern int mdb_cmdbuf_caninsert(mdb_cmdbuf_t *, size_t);
+extern int mdb_cmdbuf_atstart(mdb_cmdbuf_t *);
+extern int mdb_cmdbuf_atend(mdb_cmdbuf_t *);
+
+extern int mdb_cmdbuf_insert(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backspace(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_delchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_fwdchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backchar(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_transpose(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_home(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_end(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_fwdword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_backword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_killfwdword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_killbackword(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_kill(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_reset(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_prevhist(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_nexthist(mdb_cmdbuf_t *, int);
+extern int mdb_cmdbuf_findhist(mdb_cmdbuf_t *, int);
+
+#endif /* _MDB */
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _MDB_CMDBUF_H */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
new file mode 100644
index 0000000..46a1c6f
--- /dev/null
+++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
@@ -0,0 +1,2991 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#include <sys/elf.h>
+#include <sys/elf_SPARC.h>
+
+#include <libproc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <alloca.h>
+#include <libctf.h>
+#include <ctype.h>
+
+#include <mdb/mdb_string.h>
+#include <mdb/mdb_argvec.h>
+#include <mdb/mdb_nv.h>
+#include <mdb/mdb_fmt.h>
+#include <mdb/mdb_target.h>
+#include <mdb/mdb_err.h>
+#include <mdb/mdb_debug.h>
+#include <mdb/mdb_conf.h>
+#include <mdb/mdb_module.h>
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_stdlib.h>
+#include <mdb/mdb_lex.h>
+#include <mdb/mdb_io_impl.h>
+#include <mdb/mdb_help.h>
+#include <mdb/mdb_disasm.h>
+#include <mdb/mdb_frame.h>
+#include <mdb/mdb_evset.h>
+#include <mdb/mdb_print.h>
+#include <mdb/mdb_nm.h>
+#include <mdb/mdb_set.h>
+#include <mdb/mdb_demangle.h>
+#include <mdb/mdb_ctf.h>
+#include <mdb/mdb_macalias.h>
+#ifdef _KMDB
+#include <kmdb/kmdb_kdi.h>
+#endif
+#include <mdb/mdb.h>
+
+#ifdef __sparc
+#define	SETHI_MASK	0xc1c00000
+#define	SETHI_VALUE	0x01000000
+
+#define	IS_SETHI(machcode)	(((machcode) & SETHI_MASK) == SETHI_VALUE)
+
+#define	OP(machcode)	((machcode) >> 30)
+#define	OP3(machcode)	(((machcode) >> 19) & 0x3f)
+#define	RD(machcode)	(((machcode) >> 25) & 0x1f)
+#define	RS1(machcode)	(((machcode) >> 14) & 0x1f)
+#define	I(machcode)	(((machcode) >> 13) & 0x01)
+
+#define	IMM13(machcode)	((machcode) & 0x1fff)
+#define	IMM22(machcode)	((machcode) & 0x3fffff)
+
+#define	OP_ARITH_MEM_MASK	0x2
+#define	OP_ARITH		0x2
+#define	OP_MEM			0x3
+
+#define	OP3_CC_MASK		0x10
+#define	OP3_COMPLEX_MASK	0x20
+
+#define	OP3_ADD			0x00
+#define	OP3_OR			0x02
+#define	OP3_XOR			0x03
+
+#ifndef	R_O7
+#define	R_O7	0xf
+#endif
+#endif /* __sparc */
+
+static mdb_tgt_addr_t
+write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+	uint8_t o, n = (uint8_t)ull;
+
+	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+	    addr) == -1)
+		return (addr);
+
+	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+		return (addr);
+
+	if (rdback) {
+		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+			return (addr);
+
+		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
+		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
+	}
+
+	return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+	uint16_t o, n = (uint16_t)ull;
+
+	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+	    addr) == -1)
+		return (addr);
+
+	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+		return (addr);
+
+	if (rdback) {
+		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+			return (addr);
+
+		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
+		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
+	}
+
+	return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
+{
+	uint32_t o, n = (uint32_t)ull;
+
+	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+	    addr) == -1)
+		return (addr);
+
+	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+		return (addr);
+
+	if (rdback) {
+		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+			return (addr);
+
+		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
+		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
+	}
+
+	return (addr + sizeof (n));
+}
+
+static mdb_tgt_addr_t
+write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
+{
+	uint64_t o;
+
+	if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
+	    addr) == -1)
+		return (addr);
+
+	if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+		return (addr);
+
+	if (rdback) {
+		if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
+			return (addr);
+
+		mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
+		    mdb_iob_getmargin(mdb.m_out), addr, o, n);
+	}
+
+	return (addr + sizeof (n));
+}
+
+static int
+write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
+    int argc, const mdb_arg_t *argv)
+{
+	mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
+	    uint64_t, uint_t);
+	mdb_tgt_addr_t naddr;
+	uintmax_t value;
+	int rdback = mdb.m_flags & MDB_FL_READBACK;
+	size_t i;
+
+	if (argc == 1) {
+		mdb_warn("expected value to write following %c\n",
+		    argv->a_un.a_char);
+		return (DCMD_ERR);
+	}
+
+	switch (argv->a_un.a_char) {
+	case 'v':
+		write_value = write_uint8;
+		break;
+	case 'w':
+		write_value = write_uint16;
+		break;
+	case 'W':
+		write_value = write_uint32;
+		break;
+	case 'Z':
+		write_value = write_uint64;
+		break;
+	}
+
+	for (argv++, i = 1; i < argc; i++, argv++) {
+		if (argv->a_type == MDB_TYPE_CHAR) {
+			mdb_warn("expected immediate value instead of '%c'\n",
+			    argv->a_un.a_char);
+			return (DCMD_ERR);
+		}
+
+		if (argv->a_type == MDB_TYPE_STRING) {
+			if (mdb_eval(argv->a_un.a_str) == -1) {
+				mdb_warn("failed to write \"%s\"",
+				    argv->a_un.a_str);
+				return (DCMD_ERR);
+			}
+			value = mdb_nv_get_value(mdb.m_dot);
+		} else
+			value = argv->a_un.a_val;
+
+		mdb_nv_set_value(mdb.m_dot, addr);
+
+		if ((naddr = write_value(as, addr, value, rdback)) == addr) {
+			mdb_warn("failed to write %llr at address 0x%llx",
+			    value, addr);
+			mdb.m_incr = 0;
+			break;
+		}
+
+		mdb.m_incr = naddr - addr;
+		addr = naddr;
+	}
+
+	return (DCMD_OK);
+}
+
+static mdb_tgt_addr_t
+match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
+{
+	uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
+
+	for (; mdb_tgt_aread(mdb.m_target, as, &x,
+	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+		if ((x & mask) == val) {
+			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+			break;
+		}
+	}
+	return (addr);
+}
+
+static mdb_tgt_addr_t
+match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
+{
+	uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
+
+	for (; mdb_tgt_aread(mdb.m_target, as, &x,
+	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+		if ((x & mask) == val) {
+			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+			break;
+		}
+	}
+	return (addr);
+}
+
+static mdb_tgt_addr_t
+match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
+{
+	uint64_t x;
+
+	for (; mdb_tgt_aread(mdb.m_target, as, &x,
+	    sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
+
+		if ((x & mask) == val) {
+			mdb_iob_printf(mdb.m_out, "%lla\n", addr);
+			break;
+		}
+	}
+	return (addr);
+}
+
+static int
+match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
+    int argc, const mdb_arg_t *argv)
+{
+	mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
+	    uint64_t, uint64_t);
+
+	uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
+	size_t i;
+
+	if (argc < 2) {
+		mdb_warn("expected value following %c\n", argv->a_un.a_char);
+		return (DCMD_ERR);
+	}
+
+	if (argc > 3) {
+		mdb_warn("only value and mask may follow %c\n",
+		    argv->a_un.a_char);
+		return (DCMD_ERR);
+	}
+
+	switch (argv->a_un.a_char) {
+	case 'l':
+		match_value = match_uint16;
+		break;
+	case 'L':
+		match_value = match_uint32;
+		break;
+	case 'M':
+		match_value = match_uint64;
+		break;
+	}
+
+	for (argv++, i = 1; i < argc; i++, argv++) {
+		if (argv->a_type == MDB_TYPE_CHAR) {
+			mdb_warn("expected immediate value instead of '%c'\n",
+			    argv->a_un.a_char);
+			return (DCMD_ERR);
+		}
+
+		if (argv->a_type == MDB_TYPE_STRING) {
+			if (mdb_eval(argv->a_un.a_str) == -1) {
+				mdb_warn("failed to evaluate \"%s\"",
+				    argv->a_un.a_str);
+				return (DCMD_ERR);
+			}
+			args[i - 1] = mdb_nv_get_value(mdb.m_dot);
+		} else
+			args[i - 1] = argv->a_un.a_val;
+	}
+
+	addr = match_value(as, addr, args[0], args[1]);
+	mdb_nv_set_value(mdb.m_dot, addr);
+
+	/*
+	 * In adb(1), the match operators ignore any repeat count that has
+	 * been applied to them.  We emulate this undocumented property
+	 * by returning DCMD_ABORT if our input is not a pipeline.
+	 */
+	return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
+}
+
+static int
+argncmp(int argc, const mdb_arg_t *argv, const char *s)
+{
+	for (; *s != '\0'; s++, argc--, argv++) {
+		if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
+			return (FALSE);
+		if (argv->a_un.a_char != *s)
+			return (FALSE);
+	}
+	return (TRUE);
+}
+
+static int
+print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
+    int argc, const mdb_arg_t *argv)
+{
+	char buf[MDB_TGT_SYM_NAMLEN];
+	mdb_tgt_addr_t oaddr = addr;
+	mdb_tgt_addr_t naddr;
+	GElf_Sym sym;
+	size_t i, n;
+
+	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
+		const char *fmt;
+		int is_dis;
+		/*
+		 * This is nasty, but necessary for precise adb compatibility.
+		 * Detect disassembly format by looking for "ai" or "ia":
+		 */
+		if (argncmp(argc, argv, "ai")) {
+			fmt = "%-#*lla\n";
+			is_dis = TRUE;
+		} else if (argncmp(argc, argv, "ia")) {
+			fmt = "%-#*lla";
+			is_dis = TRUE;
+		} else {
+			fmt = "%-#*lla%16T";
+			is_dis = FALSE;
+		}
+
+		/*
+		 * If symbolic decoding is on, disassembly is off, and the
+		 * address exactly matches a symbol, print the symbol name:
+		 */
+		if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
+		    (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
+		    mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
+		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
+			mdb_iob_printf(mdb.m_out, "%s:\n", buf);
+
+		/*
+		 * If this is a virtual address, cast it so that it reflects
+		 * only the valid component of the address.
+		 */
+		if (as == MDB_TGT_AS_VIRT)
+			addr = (uintptr_t)addr;
+
+		mdb_iob_printf(mdb.m_out, fmt,
+		    (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
+	}
+
+	if (argc == 0) {
+		/*
+		 * Yes, for you trivia buffs: if you use a format verb and give
+		 * no format string, you get: X^"= "i ... note that in adb the
+		 * the '=' verb once had 'z' as its default, but then 'z' was
+		 * deleted (it was once an alias for 'i') and so =\n now calls
+		 * scanform("z") and produces a 'bad modifier' message.
+		 */
+		static const mdb_arg_t def_argv[] = {
+			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
+			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
+			{ MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
+			{ MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
+		};
+
+		argc = sizeof (def_argv) / sizeof (mdb_arg_t);
+		argv = def_argv;
+	}
+
+	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
+
+	for (i = 0, n = 1; i < argc; i++, argv++) {
+		switch (argv->a_type) {
+		case MDB_TYPE_CHAR:
+			naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
+			    argv->a_un.a_char);
+			mdb.m_incr = naddr - addr;
+			addr = naddr;
+			n = 1;
+			break;
+
+		case MDB_TYPE_IMMEDIATE:
+			n = argv->a_un.a_val;
+			break;
+
+		case MDB_TYPE_STRING:
+			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
+			n = 1;
+			break;
+		}
+	}
+
+	mdb.m_incr = addr - oaddr;
+	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+	return (DCMD_OK);
+}
+
+static int
+print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
+
+	if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
+		if (strchr("vwWZ", argv->a_un.a_char))
+			return (write_arglist(as, addr, argc, argv));
+		if (strchr("lLM", argv->a_un.a_char))
+			return (match_arglist(as, flags, addr, argc, argv));
+	}
+
+	return (print_arglist(as, addr, flags, argc, argv));
+}
+
+/*ARGSUSED*/
+static int
+cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
+}
+
+/*ARGSUSED*/
+static int
+cmd_print_value(uintptr_t addr, uint_t flags,
+	int argc, const mdb_arg_t *argv)
+{
+	uintmax_t ndot, dot = mdb_get_dot();
+	const char *tgt_argv[1];
+	mdb_tgt_t *t;
+	size_t i, n;
+
+	if (argc == 0) {
+		mdb_warn("expected one or more format characters "
+		    "following '='\n");
+		return (DCMD_ERR);
+	}
+
+	tgt_argv[0] = (const char *)&dot;
+	t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
+	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
+
+	for (i = 0, n = 1; i < argc; i++, argv++) {
+		switch (argv->a_type) {
+		case MDB_TYPE_CHAR:
+			ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
+			    dot, n, argv->a_un.a_char);
+			if (argv->a_un.a_char == '+' ||
+			    argv->a_un.a_char == '-')
+				dot = ndot;
+			n = 1;
+			break;
+
+		case MDB_TYPE_IMMEDIATE:
+			n = argv->a_un.a_val;
+			break;
+
+		case MDB_TYPE_STRING:
+			mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
+			n = 1;
+			break;
+		}
+	}
+
+	mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
+	mdb_nv_set_value(mdb.m_dot, dot);
+	mdb.m_incr = 0;
+
+	mdb_tgt_destroy(t);
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_assign_variable(uintptr_t addr, uint_t flags,
+    int argc, const mdb_arg_t *argv)
+{
+	uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
+	const char *p;
+	mdb_var_t *v;
+
+	if (argc == 2) {
+		if (argv->a_type != MDB_TYPE_CHAR) {
+			mdb_warn("improper arguments following '>' operator\n");
+			return (DCMD_ERR);
+		}
+
+		switch (argv->a_un.a_char) {
+		case 'c':
+			addr = *((uchar_t *)&addr);
+			break;
+		case 's':
+			addr = *((ushort_t *)&addr);
+			break;
+		case 'i':
+			addr = *((uint_t *)&addr);
+			break;
+		case 'l':
+			addr = *((ulong_t *)&addr);
+			break;
+		default:
+			mdb_warn("%c is not a valid // modifier\n",
+			    argv->a_un.a_char);
+			return (DCMD_ERR);
+		}
+
+		dot = addr;
+		argv++;
+		argc--;
+	}
+
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
+		mdb_warn("expected single variable name following '>'\n");
+		return (DCMD_ERR);
+	}
+
+	if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
+		mdb_warn("variable names may not exceed %d characters\n",
+		    MDB_NV_NAMELEN - 1);
+		return (DCMD_ERR);
+	}
+
+	if ((p = strbadid(argv->a_un.a_str)) != NULL) {
+		mdb_warn("'%c' may not be used in a variable name\n", *p);
+		return (DCMD_ERR);
+	}
+
+	if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
+		(void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
+	else
+		mdb_nv_set_value(v, dot);
+
+	mdb.m_incr = 0;
+	return (DCMD_OK);
+}
+
+static int
+print_soutype(const char *sou, uintptr_t addr, uint_t flags)
+{
+	static const char *prefixes[] = { "struct ", "union " };
+	size_t namesz = 7 + strlen(sou) + 1;
+	char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
+	mdb_ctf_id_t id;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		(void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
+
+		if (mdb_ctf_lookup_by_name(name, &id) == 0) {
+			mdb_arg_t v;
+			int rv;
+
+			v.a_type = MDB_TYPE_STRING;
+			v.a_un.a_str = name;
+
+			rv = mdb_call_dcmd("print", addr, flags, 1, &v);
+			return (rv);
+		}
+	}
+
+	return (DCMD_ERR);
+}
+
+static int
+print_type(const char *name, uintptr_t addr, uint_t flags)
+{
+	mdb_ctf_id_t id;
+	char *sname;
+	size_t snamesz;
+	int rv;
+
+	if (!(flags & DCMD_ADDRSPEC)) {
+		addr = mdb_get_dot();
+		flags |= DCMD_ADDRSPEC;
+	}
+
+	if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
+		return (rv);
+
+	snamesz = strlen(name) + 3;
+	sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
+	(void) mdb_snprintf(sname, snamesz, "%s_t", name);
+
+	if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
+		mdb_arg_t v;
+		int rv;
+
+		v.a_type = MDB_TYPE_STRING;
+		v.a_un.a_str = sname;
+
+		rv = mdb_call_dcmd("print", addr, flags, 1, &v);
+		return (rv);
+	}
+
+	sname[snamesz - 2] = 's';
+	rv = print_soutype(sname, addr, flags);
+	return (rv);
+}
+
+static int
+exec_alias(const char *fname, uintptr_t addr, uint_t flags)
+{
+	const char *alias;
+	int rv;
+
+	if ((alias = mdb_macalias_lookup(fname)) == NULL)
+		return (DCMD_ERR);
+
+	if (flags & DCMD_ADDRSPEC) {
+		size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
+		char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
+		(void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
+		rv = mdb_eval(addralias);
+	} else {
+		rv = mdb_eval(alias);
+	}
+
+	return (rv == -1 ? DCMD_ABORT : DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	const char *fname;
+	mdb_io_t *fio;
+	int rv;
+
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	fname = argv->a_un.a_str;
+
+	if (flags & DCMD_PIPE_OUT) {
+		mdb_warn("macro files cannot be used as input to a pipeline\n");
+		return (DCMD_ABORT);
+	}
+
+	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
+	    O_RDONLY, 0)) != NULL) {
+		mdb_frame_t *fp = mdb.m_frame;
+		int err;
+
+		mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
+		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
+		err = mdb_run();
+
+		ASSERT(fp == mdb.m_frame);
+		mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
+		yylineno = mdb_iob_lineno(mdb.m_in);
+
+		if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
+			longjmp(fp->f_pcb, err);
+
+		if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
+		    err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
+			longjmp(fp->f_pcb, err);
+
+		return (DCMD_OK);
+	}
+
+	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
+	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
+		return (rv);
+
+	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
+	return (DCMD_ABORT);
+}
+
+static int
+cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	const char *fname;
+	mdb_io_t *fio;
+	int rv;
+
+	/*
+	 * The syntax [expr[,count]]$< with no trailing macro file name is
+	 * magic in that if count is zero, this command won't be called and
+	 * the expression is thus a no-op.  If count is non-zero, we get
+	 * invoked with argc == 0, and this means abort the current macro.
+	 * If our debugger stack depth is greater than one, we may be using
+	 * $< from within a previous $<<, so in that case we set m_in to
+	 * NULL to force this entire frame to be popped.
+	 */
+	if (argc == 0) {
+		if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
+			mdb_iob_destroy(mdb.m_in);
+			mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
+		} else if (mdb.m_depth > 1) {
+			mdb_iob_destroy(mdb.m_in);
+			mdb.m_in = NULL;
+		} else
+			mdb_warn("input stack is empty\n");
+		return (DCMD_OK);
+	}
+
+	if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
+		return (cmd_src_file(addr, flags, argc, argv));
+
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	fname = argv->a_un.a_str;
+
+	if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
+	    O_RDONLY, 0)) != NULL) {
+		mdb_iob_destroy(mdb.m_in);
+		mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
+		return (DCMD_OK);
+	}
+
+	if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
+	    (rv = print_type(fname, addr, flags)) != DCMD_ERR)
+		return (rv);
+
+	mdb_warn("failed to open %s (see ::help '$<')\n", fname);
+	return (DCMD_ABORT);
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int status = DCMD_OK;
+	char buf[BUFSIZ];
+	mdb_iob_t *iob;
+	mdb_io_t *fio;
+
+	if (flags & DCMD_ADDRSPEC)
+		return (DCMD_USAGE);
+
+	for (; argc-- != 0; argv++) {
+		if (argv->a_type != MDB_TYPE_STRING) {
+			mdb_warn("expected string argument\n");
+			status = DCMD_ERR;
+			continue;
+		}
+
+		if ((fio = mdb_fdio_create_path(NULL,
+		    argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
+			mdb_warn("failed to open %s", argv->a_un.a_str);
+			status = DCMD_ERR;
+			continue;
+		}
+
+		iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
+
+		while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
+			ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
+			if (len > 0) {
+				if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
+					if (errno != EPIPE)
+						mdb_warn("write failed");
+					status = DCMD_ERR;
+					break;
+				}
+			}
+		}
+
+		if (mdb_iob_err(iob))
+			mdb_warn("error while reading %s", mdb_iob_name(iob));
+
+		mdb_iob_destroy(iob);
+	}
+
+	return (status);
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	if (mdb_eval(argv->a_un.a_str) == -1)
+		return (DCMD_ABORT);
+
+	if (mdb_get_dot() != 0)
+		mdb_printf("%lr\n", addr);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	if (mdb_eval(argv->a_un.a_str) == -1)
+		return (DCMD_ABORT);
+
+	mdb_printf("%llr\n", mdb_get_dot());
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_warn("command is not supported by current target\n");
+	return (DCMD_ERR);
+}
+
+/*ARGSUSED*/
+static int
+cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+#ifdef _KMDB
+	uint_t opt_u = FALSE;
+
+	if (mdb_getopts(argc, argv,
+	    'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (opt_u) {
+		if (mdb.m_flags & MDB_FL_NOUNLOAD) {
+			warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
+			return (DCMD_ERR);
+		}
+
+		kmdb_kdi_set_unload_request();
+	}
+#endif
+
+	longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
+	/*NOTREACHED*/
+	return (DCMD_ERR);
+}
+
+#ifdef _KMDB
+static void
+quit_help(void)
+{
+	mdb_printf(
+	    "-u    unload the debugger (if not loaded at boot)\n");
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
+	mdb_var_t *v;
+
+	if (mdb_getopts(argc, argv,
+	    'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
+	    'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
+	    't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
+		return (DCMD_USAGE);
+
+	mdb_nv_rewind(&mdb.m_nv);
+
+	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
+		if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
+		    (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
+			if (opt_prt) {
+				mdb_printf("%#llr>%s\n",
+				    mdb_nv_get_value(v), mdb_nv_get_name(v));
+			} else {
+				mdb_printf("%s = %llr\n",
+				    mdb_nv_get_name(v), mdb_nv_get_value(v));
+			}
+		}
+	}
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uintmax_t value;
+	mdb_var_t *v;
+
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	mdb_nv_rewind(&mdb.m_nv);
+
+	while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
+		if ((value = mdb_nv_get_value(v)) != 0)
+			mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
+	}
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	if (flags & DCMD_ADDRSPEC) {
+		if (addr < 2 || addr > 16) {
+			mdb_warn("expected radix from 2 to 16\n");
+			return (DCMD_ERR);
+		}
+		mdb.m_radix = (int)addr;
+	}
+
+	mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	if (flags & DCMD_ADDRSPEC)
+		mdb.m_symdist = addr;
+
+	mdb_printf("symbol matching distance = %lr (%s)\n",
+	    mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	if (flags & DCMD_ADDRSPEC)
+		mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
+
+	mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
+		mdb_warn("failed to re-open target for writing");
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
+{
+	mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0 || (flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	(void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_var_t *v;
+	size_t i;
+
+	for (i = 0; i < argc; i++) {
+		if (argv[i].a_type != MDB_TYPE_STRING) {
+			mdb_warn("bad option: arg %lu is not a string\n",
+			    (ulong_t)i + 1);
+			return (DCMD_USAGE);
+		}
+	}
+
+	for (i = 0; i < argc; i++, argv++) {
+		if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
+			mdb_warn("variable '%s' not defined\n",
+			    argv->a_un.a_str);
+		else
+			mdb_nv_remove(&mdb.m_nv, v);
+	}
+
+	return (DCMD_OK);
+}
+
+#ifndef _KMDB
+/*ARGSUSED*/
+static int
+cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uint_t opt_e = FALSE, opt_d = FALSE;
+	const char *filename = NULL;
+	int i;
+
+	i = mdb_getopts(argc, argv,
+	    'd', MDB_OPT_SETBITS, TRUE, &opt_d,
+	    'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
+
+	if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
+	    (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
+	    (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
+		return (DCMD_USAGE);
+
+	if (mdb.m_depth != 1) {
+		mdb_warn("log may not be manipulated in this context\n");
+		return (DCMD_ABORT);
+	}
+
+	if (i != argc)
+		filename = argv[i].a_un.a_str;
+
+	/*
+	 * If no arguments were specified, print the log file name (if any)
+	 * and report whether the log is enabled or disabled.
+	 */
+	if (argc == 0) {
+		if (mdb.m_log) {
+			mdb_printf("%s: logging to \"%s\" is currently %s\n",
+			    mdb.m_pname, IOP_NAME(mdb.m_log),
+			    mdb.m_flags & MDB_FL_LOG ?  "enabled" : "disabled");
+		} else
+			mdb_printf("%s: no log is active\n", mdb.m_pname);
+		return (DCMD_OK);
+	}
+
+	/*
+	 * If the -d option was specified, pop the log i/o object off the
+	 * i/o stack of stdin, stdout, and stderr.
+	 */
+	if (opt_d) {
+		if (mdb.m_flags & MDB_FL_LOG) {
+			(void) mdb_iob_pop_io(mdb.m_in);
+			(void) mdb_iob_pop_io(mdb.m_out);
+			(void) mdb_iob_pop_io(mdb.m_err);
+			mdb.m_flags &= ~MDB_FL_LOG;
+		} else
+			mdb_warn("logging is already disabled\n");
+		return (DCMD_OK);
+	}
+
+	/*
+	 * The -e option is the default: (re-)enable logging by pushing
+	 * the log i/o object on to stdin, stdout, and stderr.  If we have
+	 * a previous log file, we need to pop it and close it.  If we have
+	 * no new log file, push the previous one back on.
+	 */
+	if (filename != NULL) {
+		if (mdb.m_log != NULL) {
+			if (mdb.m_flags & MDB_FL_LOG) {
+				(void) mdb_iob_pop_io(mdb.m_in);
+				(void) mdb_iob_pop_io(mdb.m_out);
+				(void) mdb_iob_pop_io(mdb.m_err);
+				mdb.m_flags &= ~MDB_FL_LOG;
+			}
+			mdb_io_rele(mdb.m_log);
+		}
+
+		mdb.m_log = mdb_fdio_create_path(NULL, filename,
+		    O_CREAT | O_APPEND | O_WRONLY, 0666);
+
+		if (mdb.m_log == NULL) {
+			mdb_warn("failed to open %s", filename);
+			return (DCMD_ERR);
+		}
+	}
+
+	if (mdb.m_log != NULL) {
+		mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
+		mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
+		mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
+
+		mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
+		mdb.m_log = mdb_io_hold(mdb.m_log);
+		mdb.m_flags |= MDB_FL_LOG;
+
+		return (DCMD_OK);
+	}
+
+	mdb_warn("no log file has been selected\n");
+	return (DCMD_ERR);
+}
+
+static int
+cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc == 0) {
+		mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
+		return (cmd_log(addr, flags, 1, &arg));
+	}
+
+	return (cmd_log(addr, flags, argc, argv));
+}
+#endif
+
+/*ARGSUSED*/
+static int
+cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int i, mode = MDB_MOD_LOCAL;
+
+	i = mdb_getopts(argc, argv,
+#ifdef _KMDB
+	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
+#endif
+	    'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
+	    'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
+	    's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
+	    NULL);
+
+	argc -= i;
+	argv += i;
+
+	if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
+	    argv->a_type != MDB_TYPE_STRING ||
+	    strchr("+-", argv->a_un.a_str[0]) != NULL)
+		return (DCMD_USAGE);
+
+	if (mdb_module_load(argv->a_un.a_str, mode) < 0)
+		return (DCMD_ERR);
+
+	return (DCMD_OK);
+}
+
+static void
+load_help(void)
+{
+	mdb_printf(
+#ifdef _KMDB
+	    "-d    defer load until next continue\n"
+#endif
+	    "-s    load module silently\n");
+}
+
+/*ARGSUSED*/
+static int
+cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int mode = 0;
+	int i;
+
+	i = mdb_getopts(argc, argv,
+#ifdef _KMDB
+	    'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
+#endif
+	    NULL);
+
+	argc -= i;
+	argv += i;
+
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
+		mdb_warn("failed to unload %s", argv->a_un.a_str);
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+#ifdef _KMDB
+static void
+unload_help(void)
+{
+	mdb_printf(
+	    "-d    defer unload until next continue\n");
+}
+#endif
+
+static int
+cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
+		return (DCMD_USAGE);
+
+	if (argc != 0) {
+		if (argv->a_type != MDB_TYPE_STRING)
+			return (DCMD_USAGE);
+		if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
+			mdb_dmode(addr);
+	} else if (flags & DCMD_ADDRSPEC)
+		mdb_dmode(addr);
+
+	mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+#ifdef DEBUG
+	mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
+#else
+	mdb_printf("\r%s\n", mdb_conf_version());
+#endif
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (mdb.m_flags & MDB_FL_ADB)
+		mdb_printf("No algol 68 here\n");
+	else
+		mdb_printf("No adb here\n");
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (mdb.m_flags & MDB_FL_ADB)
+		mdb_printf("CHAPTER 1\n");
+	else
+		mdb_printf("No Language H here\n");
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_global(void *data, const GElf_Sym *sym, const char *name,
+    const mdb_syminfo_t *sip, const char *obj)
+{
+	uintptr_t value;
+
+	if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
+	    (uintptr_t)sym->st_value) == sizeof (value))
+		mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
+	else
+		mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 0)
+		return (DCMD_USAGE);
+
+	(void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
+	    MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
+	    MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
+
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
+		return (DCMD_USAGE);
+
+	if (mdb_eval(argv->a_un.a_str) == -1)
+		return (DCMD_ABORT);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_file(void *data, const GElf_Sym *sym, const char *name,
+    const mdb_syminfo_t *sip, const char *obj)
+{
+	int i = *((int *)data);
+
+	mdb_printf("%d\t%s\n", i++, name);
+	*((int *)data) = i;
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	int i = 1;
+	const char *obj = MDB_TGT_OBJ_EVERY;
+
+	if ((flags & DCMD_ADDRSPEC) || argc > 1)
+		return (DCMD_USAGE);
+
+	if (argc == 1) {
+		if (argv->a_type != MDB_TYPE_STRING)
+			return (DCMD_USAGE);
+
+		obj = argv->a_un.a_str;
+	}
+
+	(void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
+	    MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+print_map(void *ignored, const mdb_map_t *map, const char *name)
+{
+	if (name == NULL || *name == '\0') {
+		if (map->map_flags & MDB_TGT_MAP_SHMEM)
+			name = "[ shmem ]";
+		else if (map->map_flags & MDB_TGT_MAP_STACK)
+			name = "[ stack ]";
+		else if (map->map_flags & MDB_TGT_MAP_HEAP)
+			name = "[ heap ]";
+		else if (map->map_flags & MDB_TGT_MAP_ANON)
+			name = "[ anon ]";
+	}
+
+	mdb_printf("%?p %?p %?lx %s\n", map->map_base, map->map_base +
+	    map->map_size, map->map_size, name ? name : map->map_name);
+	return (0);
+}
+
+static int
+cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	const mdb_map_t *m;
+
+	if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
+		return (DCMD_USAGE);
+
+	mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
+	    "BASE", "LIMIT", "SIZE", "NAME");
+
+	if (flags & DCMD_ADDRSPEC) {
+		if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
+			mdb_warn("failed to obtain mapping");
+		else
+			(void) print_map(NULL, m, NULL);
+
+	} else if (argc != 0) {
+		if (argv->a_type == MDB_TYPE_STRING)
+			m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
+		else
+			m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
+
+		if (m == NULL)
+			mdb_warn("failed to obtain mapping");
+		else
+			(void) print_map(NULL, m, NULL);
+
+	} else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
+		mdb_warn("failed to iterate over mappings");
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
+{
+	ctf_file_t *ctfp;
+	const char *version;
+
+	ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
+	if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
+		version = "Unknown";
+
+	mdb_printf("%-28s %s\n", name, version);
+	return (0);
+}
+
+/*ARGSUSED*/
+static int
+cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uint_t opt_v = FALSE;
+	mdb_tgt_map_f *cb;
+
+	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
+	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (opt_v) {
+		cb = objects_printversion;
+		mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
+	} else {
+		cb = print_map;
+		mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
+		    "BASE", "LIMIT", "SIZE", "NAME");
+	}
+
+	if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
+		mdb_warn("failed to iterate over objects");
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
+{
+	ctf_file_t *ctfp;
+	const char *version = NULL;
+	char *objname;
+
+	objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
+	(void) strcpy(objname, object);
+
+	if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
+		version = ctf_label_topmost(ctfp);
+
+	/*
+	 * Not all objects have CTF and label data, so set version to "Unknown".
+	 */
+	if (version == NULL)
+		version = "Unknown";
+
+	/*
+	 * The hash table implementation in OVERLOAD mode limits the version
+	 * name to 31 characters because we cannot specify an external name.
+	 * The full version name is available via the ::objects dcmd if needed.
+	 */
+	(void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
+	    MDB_NV_OVERLOAD);
+
+	return (0);
+}
+
+static int
+showrev_ispatch(const char *s)
+{
+	if (s == NULL)
+		return (0);
+
+	if (*s == 'T')
+		s++; /* skip T for T-patch */
+
+	for (; *s != '\0'; s++) {
+		if ((*s < '0' || *s > '9') && *s != '-')
+			return (0);
+	}
+
+	return (1);
+}
+
+/*ARGSUSED*/
+static int
+showrev_printobject(mdb_var_t *v, void *ignored)
+{
+	mdb_printf("%s ", MDB_NV_COOKIE(v));
+	return (0);
+}
+
+static int
+showrev_printversion(mdb_var_t *v, void *showall)
+{
+	const char *version = mdb_nv_get_name(v);
+	int patch;
+
+	patch = showrev_ispatch(version);
+	if (patch || (uintptr_t)showall) {
+		mdb_printf("%s: %s  Objects: ",
+		    (patch ? "Patch" : "Version"), version);
+		(void) mdb_inc_indent(2);
+
+		mdb_nv_defn_iter(v, showrev_printobject, NULL);
+
+		(void) mdb_dec_indent(2);
+		mdb_printf("\n");
+	}
+
+	return (0);
+}
+
+/*
+ * Display version information for each object in the system.
+ * Print information about patches only, unless showall is TRUE.
+ */
+static int
+showrev_objectversions(int showall)
+{
+	mdb_nv_t vers_nv;
+
+	(void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
+	if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
+	    &vers_nv) == -1) {
+		mdb_warn("failed to iterate over objects");
+		return (DCMD_ERR);
+	}
+
+	mdb_nv_sort_iter(&vers_nv, showrev_printversion,
+	    (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
+	return (DCMD_OK);
+}
+
+/*
+ * Display information similar to what showrev(1M) displays when invoked
+ * with no arguments.
+ */
+static int
+showrev_sysinfo(void)
+{
+	const char *s;
+	int rc;
+	struct utsname u;
+
+	if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
+		mdb_printf("Hostname: %s\n", u.nodename);
+		mdb_printf("Release: %s\n", u.release);
+		mdb_printf("Kernel architecture: %s\n", u.machine);
+	}
+
+	/*
+	 * Match the order of the showrev(1M) output and put "Application
+	 * architecture" before "Kernel version"
+	 */
+	if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
+		mdb_printf("Application architecture: %s\n", s);
+
+	if (rc != -1)
+		mdb_printf("Kernel version: %s %s %s %s\n",
+		    u.sysname, u.release, u.machine, u.version);
+
+	if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
+		mdb_printf("Platform: %s\n", s);
+
+	return (DCMD_OK);
+}
+
+/*ARGSUSED*/
+static int
+cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uint_t opt_p = FALSE, opt_v = FALSE;
+
+	if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
+	    'p', MDB_OPT_SETBITS, TRUE, &opt_p,
+	    'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
+		return (DCMD_USAGE);
+
+	if (opt_p || opt_v)
+		return (showrev_objectversions(opt_v));
+	else
+		return (showrev_sysinfo());
+}
+
+#ifdef __sparc
+static void
+findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
+{
+	uintptr_t	*symbolp;
+
+	for (symbolp = symlist; *symbolp; symbolp++)
+		if (value == *symbolp)
+			mdb_printf("found %a at %a\n", value, location);
+}
+
+/*ARGSUSED*/
+static int
+findsym_cb(void *data, const GElf_Sym *sym, const char *name,
+    const mdb_syminfo_t *sip, const char *obj)
+{
+	uint32_t	*text;
+	int		len;
+	int		i;
+	int		j;
+	uint8_t		rd;
+	uintptr_t	value;
+	int32_t		imm13;
+	uint8_t		op;
+	uint8_t		op3;
+	uintptr_t	*symlist = data;
+	size_t		size = sym->st_size;
+
+	/*
+	 * if the size of the symbol is 0, then this symbol must be for an
+	 * alternate entry point or just some global label. We will,
+	 * therefore, get back to the text that follows this symbol in
+	 * some other symbol
+	 */
+	if (size == 0)
+		return (0);
+
+	if (sym->st_shndx == SHN_UNDEF)
+		return (0);
+
+	text = alloca(size);
+
+	if (mdb_vread(text, size, sym->st_value) == -1) {
+		mdb_warn("failed to read text for %s", name);
+		return (0);
+	}
+
+	len = size / 4;
+	for (i = 0; i < len; i++) {
+		if (!IS_SETHI(text[i]))
+			continue;
+
+		rd = RD(text[i]);
+		value = IMM22(text[i]) << 10;
+
+		/*
+		 * see if we already have a match with just the sethi
+		 */
+		findsym_output(symlist, value, sym->st_value + i * 4);
+
+		/*
+		 * search from the sethi on until we hit a relevent instr
+		 */
+		for (j = i + 1; j < len; j++) {
+			if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
+				op3 = OP3(text[j]);
+
+				if (RS1(text[j]) != rd)
+					goto instr_end;
+
+				/*
+				 * This is a simple tool; we only deal
+				 * with operations which take immediates
+				 */
+				if (I(text[j]) == 0)
+					goto instr_end;
+
+				/*
+				 * sign extend the immediate value
+				 */
+				imm13 = IMM13(text[j]);
+				imm13 <<= 19;
+				imm13 >>= 19;
+
+				if (op == OP_ARITH) {
+					/* arithmetic operations */
+					if (op3 & OP3_COMPLEX_MASK)
+						goto instr_end;
+
+					switch (op3 & ~OP3_CC_MASK) {
+					case OP3_OR:
+						value |= imm13;
+						break;
+					case OP3_ADD:
+						value += imm13;
+						break;
+					case OP3_XOR:
+						value ^= imm13;
+						break;
+					default:
+						goto instr_end;
+					}
+				} else {
+					/* loads and stores */
+					/* op3 == OP_MEM */
+
+					value += imm13;
+				}
+
+				findsym_output(symlist, value,
+				    sym->st_value + j * 4);
+instr_end:
+				/*
+				 * if we're clobbering rd, break
+				 */
+				if (RD(text[j]) == rd)
+					break;
+			} else if (IS_SETHI(text[j])) {
+				if (RD(text[j]) == rd)
+					break;
+			} else if (OP(text[j]) == 1) {
+				/*
+				 * see if a call clobbers an %o or %g
+				 */
+				if (rd <= R_O7)
+					break;
+			}
+		}
+	}
+
+	return (0);
+}
+
+static int
+cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	uintptr_t *symlist;
+	uint_t optg = FALSE;
+	uint_t type;
+	int len, i;
+
+	i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
+
+	argc -= i;
+	argv += i;
+
+	len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
+
+	if (len <= 1)
+		return (DCMD_USAGE);
+
+	/*
+	 * Set up a NULL-terminated symbol list, and then iterate over the
+	 * symbol table, scanning each function for references to these symbols.
+	 */
+	symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
+	len = 0;
+
+	for (i = 0; i < argc; i++, argv++) {
+		const char *str = argv->a_un.a_str;
+		uintptr_t value;
+		GElf_Sym sym;
+
+		if (argv->a_type == MDB_TYPE_STRING) {
+			if (strchr("+-", str[0]) != NULL)
+				return (DCMD_USAGE);
+			else if (str[0] >= '0' && str[0] <= '9')
+				value = mdb_strtoull(str);
+			else if (mdb_lookup_by_name(str, &sym) != 0) {
+				mdb_warn("symbol '%s' not found", str);
+				return (DCMD_USAGE);
+			} else
+				value = sym.st_value;
+		} else
+			value = argv[i].a_un.a_val;
+
+		if (value != NULL)
+			symlist[len++] = value;
+	}
+
+	if (flags & DCMD_ADDRSPEC)
+		symlist[len++] = addr;
+
+	symlist[len] = NULL;
+
+	if (optg)
+		type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
+	else
+		type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
+
+	if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
+	    MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
+		mdb_warn("failed to iterate over symbol table");
+		return (DCMD_ERR);
+	}
+
+	return (DCMD_OK);
+}
+#endif /* __sparc */
+
+static int
+dis_str2addr(const char *s, uintptr_t *addr)
+{
+	GElf_Sym sym;
+
+	if (s[0] >= '0' && s[0] <= '9') {
+		*addr = (uintptr_t)mdb_strtoull(s);
+		return (0);
+	}
+
+	if (mdb_tgt_lookup_by_name(mdb.m_target,
+	    MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
+		mdb_warn("symbol '%s' not found\n", s);
+		return (-1);
+	}
+
+	*addr = (uintptr_t)sym.st_value;
+	return (0);
+}
+
+static int
+cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+	mdb_tgt_t *tgt = mdb.m_target;
+	mdb_disasm_t *dis = mdb.m_disasm;
+
+	uintptr_t oaddr, naddr;
+	mdb_tgt_as_t as;
+	mdb_tgt_status_t st;
+	char buf[BUFSIZ];
+	GElf_Sym sym;
+	int i;
+
+	uint_t opt_f = FALSE;		/* File-mode off by default */
+	uint_t opt_w = FALSE;		/* Window mode off by default */
+	uint_t opt_a = FALSE;		/* Raw-address mode off by default */
+	uintptr_t n = -1UL;		/* Length of window in instructions */
+	uintptr_t eaddr = 0;		/* Ending address; 0 if limited by n */
+
+	i = mdb_getopts(argc, argv,
+	    'f', MDB_OPT_SETBITS, TRUE, &opt_f,
+	    'w', MDB_OPT_SETBITS, TRUE, &opt_w,
+	    'a', MDB_OPT_SETBITS, TRUE, &opt_a,
+	    'n', MDB_OPT_UINTPTR, &n, NULL);
+
+	/*
+	 * Disgusting argument post-processing ... basically the idea is to get
+	 * the target address into addr, which we do by using the specified
+	 * expression value, looking up a string as a symbol name, or by
+	 * using the address specified as dot.
+	 */
+	if (i != argc) {
+		if (argc != 0 && (argc - i) == 1) {
+			if (argv[i].a_type == MDB_TYPE_STRING) {
+				if (argv[i].a_un.a_str[0] == '-')
+					return (DCMD_USAGE);
+
+				if (dis_str2addr(argv[i].a_un.a_str, &addr))
+					return (DCMD_ERR);
+			} else
+				addr = argv[i].a_un.a_val;
+		} else
+			return (DCMD_USAGE);
+	}
+
+	/*
+	 * If we're not in window mode yet, and some type of arguments were
+	 * specified, see if the address corresponds nicely to a function.
+	 * If not, turn on window mode; otherwise disassemble the function.
+	 */
+	if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
+		if (mdb_tgt_lookup_by_addr(tgt, addr,
+		    MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
+		    GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
+			/*
+			 * If the symbol has a size then set our end address to
+			 * be the end of the function symbol we just located.
+			 */
+			if (sym.st_size != 0)
+				eaddr = addr + (uintptr_t)sym.st_size;