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 *)˙
+ 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;