/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Mdb kernel support module. This module is loaded automatically when the
* kvm target is initialized. Any global functions declared here are exported
* for the resolution of symbols in subsequently loaded modules.
*
* WARNING: Do not assume that static variables in mdb_ks will be initialized
* to zero.
*/
#include <mdb/mdb_target.h>
#include <mdb/mdb_param.h>
#include <mdb/mdb_modapi.h>
#include <sys/autoconf.h>
#include <sys/ddi_impldefs.h>
#include <sys/refstr_impl.h>
#include <sys/clock_impl.h>
#include <errno.h>
typedef struct mdb_path {
} mdb_path_t;
/*
*/
unsigned long _mdb_ks_pagesize;
unsigned int _mdb_ks_pageshift;
unsigned long _mdb_ks_pageoffset;
unsigned long long _mdb_ks_pagemask;
unsigned long _mdb_ks_mmu_pagesize;
unsigned int _mdb_ks_mmu_pageshift;
unsigned long _mdb_ks_mmu_pageoffset;
unsigned long _mdb_ks_mmu_pagemask;
unsigned long _mdb_ks_msg_bsize;
unsigned long _mdb_ks_defaultstksz;
int _mdb_ks_ncpu;
int _mdb_ks_ncpu_log2;
int _mdb_ks_ncpu_p2;
/*
* In-core copy of DNLC information:
*/
/*
* copy of page_hash-related data
*/
static int page_hash_loaded;
static long mdb_page_hashsz;
/*
* This will be the location of the vnodeops pointer for "autofs_vnodeops"
* The pointer still needs to be read with mdb_vread() to get the location
* of the vnodeops structure for autofs.
*/
/*
* STREAMS queue registrations:
*/
typedef struct mdb_qinfo {
} mdb_qinfo_t;
/*
* Device naming callback structure:
*/
typedef struct nm_query {
} nm_query_t;
/*
* Address-to-modctl callback structure:
*/
typedef struct a2m_query {
} a2m_query_t;
/*
* Segment-to-mdb_map callback structure:
*/
typedef struct {
} asmap_arg_t;
static void
dnlc_free(void)
{
int i;
return;
}
/*
* Free up current dnlc entries
*/
for (i = 0; i < MDB_DNLC_HSIZE; i++) {
}
}
}
" - continuing with the rest\n";
static int
dnlc_load(void)
{
int i; /* hash index */
int retry_cnt = 0;
int skip_bad_chains = 0;
/*
* If we've already cached the DNLC and we're looking at a dump,
* our cache is good forever, so don't bother re-loading.
*/
if (dnlc_hash && mdb_prop_postmortem) {
return (0);
}
/*
* For a core dump, retries wont help.
* Just print and skip any bad chains.
*/
if (mdb_prop_postmortem) {
skip_bad_chains = 1;
}
if (retry_cnt++ >= MDB_DNLC_MAX_RETRY) {
/*
* Give up retrying the rapidly changing dnlc.
* Just print and skip any bad chains
*/
skip_bad_chains = 1;
}
dnlc_free(); /* Free up the mdb hashed dnlc - if any */
/*
* Although nc_hashsz and the location of nc_hash doesn't currently
* change, it may do in the future with a more dynamic dnlc.
* So always read these values afresh.
*/
mdb_warn("failed to read nc_hashsz");
return (-1);
}
mdb_warn("failed to read nc_hash");
return (-1);
}
/*
* Allocate the mdb dnlc hash array
*/
/* for each kernel hash chain */
/*
* We read each element of the nc_hash array individually
* just before we process the entries in its chain. This is
* because the chain can change so rapidly on a running system.
*/
mdb_warn("failed to read nc_hash chain header %d", i);
dnlc_free();
return (-1);
}
/* for each entry in the chain */
/*
* The size of the ncache entries varies
* because the name is appended to the structure.
* So we read in the structure then re-read
* for the structure plus name.
*/
if (skip_bad_chains) {
break;
}
goto retry;
}
if (skip_bad_chains) {
break;
}
goto retry;
}
/*
* Check for chain consistency
*/
if (skip_bad_chains) {
break;
}
goto retry;
}
/*
* Terminate the new name with a null.
* Note, we allowed space for this null when
* allocating space for the entry.
*/
/*
* Validate new entry by re-hashing using the
* kernel dnlc hash function and comparing the hash
*/
if (skip_bad_chains) {
break;
}
goto retry;
}
/*
* Finally put the validated entry into the mdb
* hash chains. Reuse the kernel next hash field
* for the mdb hash chain pointer.
*/
}
}
return (0);
}
/*ARGSUSED*/
int
{
int i;
return (DCMD_USAGE);
if (dnlc_load() == -1)
return (DCMD_ERR);
for (i = 0; i < MDB_DNLC_HSIZE; i++) {
mdb_printf("%0?p %0?p %s\n",
}
}
return (DCMD_OK);
}
static int
{
char *s = buf;
int i;
if (len < sizeof ("/..."))
return (-1);
if (!path->mdp_complete) {
(void) strcpy(s, "??");
s += 2;
return (-1);
}
(void) strcpy(s, "/");
return (0);
}
/*
* Number of bytes left is the distance from where we
* are to the end, minus 2 for '/' and '\0'
*/
if (left <= 0)
break;
*s++ = '/';
s += strlen(s);
break;
}
if (i >= 0)
return (0);
}
static int
{
/*
* "autofs_vnops_ptr" is the address of the pointer to the vnodeops
* structure for autofs. We want to read it each time we access
* it since autofs could (in theory) be unloaded and reloaded.
*/
return (-1);
return (-1);
return (-1);
return (-1);
for (;;) {
char *c, *p;
if (elem == MDB_PATH_NELEM) {
return (-1);
}
return (-1);
}
}
break;
}
else
}
return (0);
}
int
{
/*
* Check to see if we have a cached value for this vnode
*/
return (0);
if (dnlc_load() == -1)
return (-1);
mdb_warn("failed to read 'rootdir'");
return (-1);
}
/*
* 0 elems && complete tells sprintpath to just print "/"
*/
goto out;
}
goto out;
}
continue;
mdb_warn("path exceeded maximum expected "
"elements\n");
return (-1);
}
goto again;
}
}
out:
}
{
return (NULL);
return (NULL);
return (NULL);
return (NULL);
while (paddr != 0) {
return (NULL);
return (NULL);
return (procp);
}
}
return (NULL);
}
int
{
return (-1);
}
int
{
size_t i;
goto out;
for (i = 0; i < nr_words; i++) {
size_t j;
ulong_t m;
if (cpuset[i] & m) {
goto out;
}
}
}
out:
return (cpu);
}
static int
page_hash_load(void)
{
if (page_hash_loaded) {
return (1);
}
mdb_warn("unable to read page_hashsz");
return (0);
}
mdb_warn("unable to read page_hashsz_shift");
return (0);
}
mdb_warn("unable to read page_hash");
return (0);
}
return (1);
}
{
if (!page_hash_loaded && !page_hash_load()) {
return (NULL);
}
return (NULL);
}
long nndx;
return (NULL);
}
return (pp);
/*
* Double check that the pages actually hash to the
* bucket we're searching. If not, our version of
* PAGE_HASH_FUNC() doesn't match the kernel's, and we're
* not going to be able to find the page. The most
* likely reason for this that mdb_ks doesn't match the
* kernel we're running against.
*/
mdb_warn("mdb_page_lookup: mdb_ks PAGE_HASH_FUNC() "
"mismatch: in bucket %ld, but page %p hashes to "
return (NULL);
}
}
return (NULL);
}
char
{
static const char vttab[] = {
' ', /* VNON */
' ', /* VREG */
'/', /* VDIR */
' ', /* VBLK */
' ', /* VCHR */
'@', /* VLNK */
'|', /* VFIFO */
'>', /* VDOOR */
' ', /* VPROC */
'=', /* VSOCK */
' ', /* VBAD */
};
return ('?');
return ('*');
}
struct pfn2page {
};
/*ARGSUSED*/
static int
{
return (WALK_DONE);
}
return (WALK_NEXT);
}
{
mdb_warn("pfn2page: can't walk memsegs");
return (0);
}
mdb_warn("pfn2page: unable to find page_t for pfn %lx\n",
pfn);
return (0);
}
return (0);
}
mdb_warn("pfn2page: page_t 0x%p should have PFN 0x%lx, "
return (0);
}
}
{
return ((pfn_t)(-1));
}
}
static int
{
return (0);
return (0);
}
goto found;
goto found;
return (0);
return (-1);
}
{
}
static mdb_qinfo_t *
{
return (qip);
}
return (NULL);
}
void
{
return;
}
}
void
{
else
return;
}
}
}
char *
{
goto err;
}
goto err;
}
goto err;
}
return (buf);
err:
return (buf);
}
void
{
buf[0] = '\0';
}
{
return (NULL);
}
{
return (NULL);
}
{
}
{
}
/*
* The following three routines borrowed from modsubr.c
*/
static int
{
char c;
int hash = 0;
hash ^= c;
return (hash & MOD_BIND_HASHMASK);
}
static uintptr_t
{
int hashndx;
while (mb) {
return (NULL);
}
mdb_warn("failed to read node name string at %p",
return (NULL);
}
break;
}
return (mb);
}
int
{
mdb_warn("failed to read symbol 'mb_hashtab'");
return (-1);
}
-1) {
return (-1);
}
return (0);
}
return (-1);
}
const char *
{
return (NULL);
return (NULL);
return (NULL);
return ((const char *)name);
}
/*
* Return the name of the driver attached to the dip in drivername.
*/
int
{
const char *namestr;
return (-1);
}
mdb_warn("failed to read binding name at %p",
return (-1);
}
/*
* Many->one relation: various names to one major number
*/
mdb_warn("failed to translate bind name to major number\n");
return (-1);
}
/*
* One->one relation: one major number corresponds to one driver
*/
return (-1);
}
return (0);
}
/*
* Find the name of the driver attached to this dip (if any), given:
* - the address of a dip (in core)
* - the NAME of the global pointer to the driver's i_ddi_soft_state struct
* - pointer to a pointer to receive the address
*/
int
{
return (-1);
}
}
/*
* Returns a pointer to the top of the soft state struct for the instance
* specified (in state_addr), given the address of the global soft state
* pointer and size of the struct. Also fills in the buffer pointed to by
* state_buf_p (if non-NULL) with the contents of the state struct.
*/
int
{
void *statep;
return (-1);
return (-1);
return (-1);
if (state_addr != NULL)
return (-1);
}
if (state_buf_p != NULL) {
/* Read the state struct into the buffer in local space. */
return (-1);
}
return (0);
}
/*
* Returns a pointer to the top of the soft state struct for the instance
* specified (in state_addr), given the name of the global soft state pointer
* and size of the struct. Also fills in the buffer pointed to by
* state_buf_p (if non-NULL) with the contents of the state struct.
*/
int
{
return (-1);
}
{ NULL }
};
/*ARGSUSED*/
static void
{
else
page_hash_loaded = 0; /* invalidate cached page_hash state */
}
const mdb_modinfo_t *
_mdb_init(void)
{
/*
* When used with mdb, mdb_ks is a separate dmod. With kmdb, however,
* mdb_ks is compiled into the debugger module. kmdb cannot
* automatically modunload itself when it exits. If it restarts after
* debugger fault, static variables may not be initialized to zero.
* They must be manually reinitialized here.
*/
return (&modinfo);
}
void
_mdb_fini(void)
{
dnlc_free();
}
}
/*
* Interface between MDB kproc target and mdb_ks. The kproc target relies
* on looking up and invoking these functions in mdb_ks so that dependencies
* on the current kernel implementation are isolated in mdb_ks.
*/
/*
* Given the address of a proc_t, return the p.p_as pointer; return NULL
* if we were unable to read a proc structure from the given address.
*/
{
proc_t p;
return (NULL);
}
/*
* Given the address of a proc_t, return the p.p_model value; return
* PR_MODEL_UNKNOWN if we were unable to read a proc structure or if
* the model value does not match one of the two known values.
*/
{
proc_t p;
switch (p.p_model) {
case DATAMODEL_ILP32:
return (PR_MODEL_ILP32);
case DATAMODEL_LP64:
return (PR_MODEL_LP64);
}
}
return (PR_MODEL_UNKNOWN);
}
/*
* Callback function for walking process's segment list. For each segment,
* we fill in an mdb_map_t describing its properties, and then invoke
* the callback function provided by the kproc target.
*/
static int
{
MDB_TGT_MAPSZ) != 0) {
}
} else
} else {
"[ seg %p ]", addr);
}
return (WALK_NEXT);
}
/*
* Given a process address space, walk its segment list using the seg walker,
* convert the segment data to an mdb_map_t, and pass this information
* back to the kproc target via the given callback function.
*/
int
{
arg.asm_cbdata = p;
}
/*
* Copy the auxv array from the given process's u-area into the provided
* buffer. If the buffer is NULL, only return the size of the auxv array
* so the caller knows how much space will be required.
*/
int
{
proc_t p;
return (-1);
sizeof (auxv_t) * __KERN_NAUXV_IMPL);
}
return (__KERN_NAUXV_IMPL);
}
/*
* Given a process address, return the PID.
*/
{
proc_t p;
return (-1);
}
/*
* Interface between the MDB kvm target and mdb_ks. The kvm target relies
* on looking up and invoking these functions in mdb_ks so that dependencies
* on the current kernel implementation are isolated in mdb_ks.
*/
/*
* Determine whether or not the thread that panicked the given kernel was a
* kernel thread (panic_thread->t_procp == &p0).
*/
void
{
int expcont = 0;
int actcont;
mdb_printf("dump content: all kernel and user pages\n");
return;
} else if (actcont == DF_CURPROC) {
mdb_printf("dump content: kernel pages and pages from "
"PID %d", content);
return;
}
mdb_printf("dump content: kernel pages only\n");
if (!(expcont & DF_CURPROC))
return;
goto kthreadpanic_err;
goto kthreadpanic_err;
goto kthreadpanic_err;
mdb_printf(" (curproc requested, but a kernel thread "
"panicked)\n");
} else {
mdb_printf(" (curproc requested, but the process that "
"panicked could not be dumped)\n");
}
return;
mdb_printf(" (curproc requested, but the process that panicked could "
"not be found)\n");
}
/*
* Determine the process that was saved in a `curproc' dump. This process will
* be recorded as the first element in dump_pids[].
*/
int
mdb_dump_find_curproc(void)
{
pid > 0)
return (pid);
else
return (-1);
}
/*
* Following three funcs extracted from sunddi.c
*/
/*
* Return core address of root node of devinfo tree
*/
static uintptr_t
mdb_ddi_root_node(void)
{
/* return (top_devinfo); */
mdb_warn("failed to read top_devinfo");
return (NULL);
}
return (top_devinfo_addr);
}
/*
* Return the name of the devinfo node pointed at by 'dip_addr' in the buffer
* pointed at by 'name.'
*
* - dip_addr is a pointer to a dev_info struct in core.
*/
static char *
{
if (dip_addr == mdb_ddi_root_node()) {
if (name_size < 1) {
mdb_warn("failed to get node name: buf too small\n");
return (NULL);
}
*name = '\0';
return (name);
}
if (name_size < 2) {
mdb_warn("failed to get node name: buf too small\n");
return (NULL);
}
local_namep = name;
*local_namep++ = '/';
*local_namep = '\0';
mdb_warn("failed to read devinfo struct");
}
if (length == -1) {
mdb_warn("failed to read node name");
return (NULL);
}
local_namep += length;
if (local_name_size < 2) {
mdb_warn("not enough room for node address string");
return (name);
}
*local_namep++ = '@';
*local_namep = '\0';
if (length == -1) {
mdb_warn("failed to read name");
return (NULL);
}
}
return (name);
}
/*
* Generate the full path under the /devices dir to the device entry.
*
* dip is a pointer to a devinfo struct in core (not in local memory).
*/
char *
{
char *bp;
if (dip_addr == mdb_ddi_root_node()) {
*path = '\0';
return (path);
}
mdb_warn("failed to read devinfo struct");
}
return (path);
}
/*
* Read in the string value of a refstr, which is appended to the end of
* the structure.
*/
{
}
/*
* Chase an mblk list by b_next and return the length.
*/
int
{
int count;
return (0);
count = 1;
count++;
-1)
break;
}
return (count);
}
/*
* Write the given MAC address as a printable string in the usual colon-
* separated format. Assumes that buflen is at least 2.
*/
void
{
int slen;
return;
}
for (;;) {
/*
* If there are more MAC address bytes available, but we won't
* have any room to print them, then add "..." to the string
* instead. See below for the 'magic number' explanation.
*/
break;
}
if (--alen == 0)
break;
*buf++ = ':';
/*
* At this point, based on the first 'if' statement above,
* either alen == 1 and buflen >= 3, or alen > 1 and
* buflen >= 4. The first case leaves room for the final "xx"
* number and trailing NUL byte. The second leaves room for at
* least "...". Thus the apparently 'magic' numbers chosen for
* that statement.
*/
}
}
/*
* Produce a string that represents a DLPI primitive, or NULL if no such string
* is possible.
*/
const char *
{
switch (prim) {
case DL_INFO_REQ: return ("DL_INFO_REQ");
case DL_INFO_ACK: return ("DL_INFO_ACK");
case DL_ATTACH_REQ: return ("DL_ATTACH_REQ");
case DL_DETACH_REQ: return ("DL_DETACH_REQ");
case DL_BIND_REQ: return ("DL_BIND_REQ");
case DL_BIND_ACK: return ("DL_BIND_ACK");
case DL_UNBIND_REQ: return ("DL_UNBIND_REQ");
case DL_OK_ACK: return ("DL_OK_ACK");
case DL_ERROR_ACK: return ("DL_ERROR_ACK");
case DL_ENABMULTI_REQ: return ("DL_ENABMULTI_REQ");
case DL_DISABMULTI_REQ: return ("DL_DISABMULTI_REQ");
case DL_PROMISCON_REQ: return ("DL_PROMISCON_REQ");
case DL_PROMISCOFF_REQ: return ("DL_PROMISCOFF_REQ");
case DL_UNITDATA_REQ: return ("DL_UNITDATA_REQ");
case DL_UNITDATA_IND: return ("DL_UNITDATA_IND");
case DL_UDERROR_IND: return ("DL_UDERROR_IND");
case DL_PHYS_ADDR_REQ: return ("DL_PHYS_ADDR_REQ");
case DL_PHYS_ADDR_ACK: return ("DL_PHYS_ADDR_ACK");
case DL_SET_PHYS_ADDR_REQ: return ("DL_SET_PHYS_ADDR_REQ");
case DL_NOTIFY_REQ: return ("DL_NOTIFY_REQ");
case DL_NOTIFY_ACK: return ("DL_NOTIFY_ACK");
case DL_NOTIFY_IND: return ("DL_NOTIFY_IND");
case DL_NOTIFY_CONF: return ("DL_NOTIFY_CONF");
case DL_CAPABILITY_REQ: return ("DL_CAPABILITY_REQ");
case DL_CAPABILITY_ACK: return ("DL_CAPABILITY_ACK");
case DL_CONTROL_REQ: return ("DL_CONTROL_REQ");
case DL_CONTROL_ACK: return ("DL_CONTROL_ACK");
case DL_PASSIVE_REQ: return ("DL_PASSIVE_REQ");
default: return (NULL);
}
}
/*
* mdb_gethrtime() returns the hires system time. This will be the timestamp at
* which we dropped into, if called from, kmdb(1); the core dump's hires time
* if inspecting one; or the running system's hires time if we're inspecting
* a live kernel.
*/
mdb_gethrtime(void)
{
/*
* We first check whether the lbolt info structure has been allocated
* and initialized. If not, lbolt_hybrid will be pointing at
* lbolt_bootstrap.
*/
return (0);
return (0);
return (0);
#ifdef _KMDB
return (0);
sizeof (lbolt_info_t))
return (0);
#else
if (mdb_prop_postmortem) {
return (0);
sizeof (lbolt_info_t))
return (0);
} else {
}
#endif
return (ts);
}
/*
* mdb_get_lbolt() returns the number of clock ticks since system boot.
* Depending on the context in which it's called, the value will be derived
* from different sources per mdb_gethrtime(). If inspecting a panicked
* system, the routine returns the 'panic_lbolt64' variable from the core file.
*/
mdb_get_lbolt(void)
{
int nsec;
return (pl);
/*
* mdb_gethrtime() will return zero if the lbolt info structure hasn't
* been allocated and initialized yet, or if it fails to read it.
*/
if ((ts = mdb_gethrtime()) <= 0)
return (0);
/*
* Load the time spent in kmdb, if any.
*/
return (0);
sizeof (lbolt_info_t))
return (0);
mdb_warn("failed to read 'nsec_per_tick'");
return (-1);
}
}