view usr/src/cmd/mdb/common/modules/genunix/ldi.c @ 4:1a15d5aaf794

synchronized with onnv_86 (6202) in onnv-gate
author Koji Uno <koji.uno@sun.com>
date Mon, 31 Aug 2009 14:38:03 +0900
parents c9caec207d52
children
line wrap: on
line source

/*
 * 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/sysmacros.h>
#include <sys/dditypes.h>
#include <sys/ddi_impldefs.h>
#include <sys/ddipropdefs.h>
#include <sys/modctl.h>
#include <sys/file.h>
#include <sys/sunldi_impl.h>

#include <mdb/mdb_modapi.h>
#include <mdb/mdb_ks.h>

#include "ldi.h"

/*
 * ldi handle walker structure
 */
typedef struct lh_walk {
	struct ldi_handle	**hash;	/* current bucket pointer 	*/
	struct ldi_handle	*lhp;	/* ldi handle pointer		*/
	size_t 			index;	/* hash table index		*/
	struct ldi_handle	buf;	/* buffer used for handle reads */
} lh_walk_t;

/*
 * ldi identifier walker structure
 */
typedef struct li_walk {
	struct ldi_ident	**hash;	/* current bucket pointer 	*/
	struct ldi_ident	*lip;	/* ldi handle pointer		*/
	size_t 			index;	/* hash table index		*/
	struct ldi_ident	buf;	/* buffer used for ident reads */
} li_walk_t;

/*
 * Options for ldi_handles dcmd
 */
#define	LH_IDENTINFO	0x1

/*
 * LDI walkers
 */
int
ldi_handle_walk_init(mdb_walk_state_t *wsp)
{
	lh_walk_t	*lhwp;
	GElf_Sym	sym;

	/* get the address of the hash table */
	if (mdb_lookup_by_name("ldi_handle_hash", &sym) == -1) {
		mdb_warn("couldn't find ldi_handle_hash");
		return (WALK_ERR);
	}

	lhwp = mdb_alloc(sizeof (lh_walk_t), UM_SLEEP|UM_GC);
	lhwp->hash = (struct ldi_handle **)(uintptr_t)sym.st_value;
	lhwp->index = 0;

	/* get the address of the first element in the first hash bucket */
	if ((mdb_vread(&lhwp->lhp, sizeof (struct ldi_handle *),
	    (uintptr_t)lhwp->hash)) == -1) {
		mdb_warn("couldn't read ldi handle hash at %p", lhwp->hash);
		return (WALK_ERR);
	}

	wsp->walk_addr = (uintptr_t)lhwp->lhp;
	wsp->walk_data = lhwp;

	return (WALK_NEXT);
}

int
ldi_handle_walk_step(mdb_walk_state_t *wsp)
{
	lh_walk_t 	*lhwp = (lh_walk_t *)wsp->walk_data;
	int		status;

	/* check if we need to go to the next hash bucket */
	while (wsp->walk_addr == NULL) {

		/* advance to the next bucket */
		if (++(lhwp->index) >= LH_HASH_SZ)
			return (WALK_DONE);

		/* get handle address from the hash bucket */
		if ((mdb_vread(&lhwp->lhp, sizeof (struct ldi_handle *),
		    (uintptr_t)(lhwp->hash + lhwp->index))) == -1) {
			mdb_warn("couldn't read ldi handle hash at %p",
			    (uintptr_t)lhwp->hash + lhwp->index);
			return (WALK_ERR);
		}

		wsp->walk_addr = (uintptr_t)lhwp->lhp;
	}

	/* invoke the walker callback for this hash element */
	status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
	if (status != WALK_NEXT)
		return (status);

	/* get a pointer to the next hash element */
	if (mdb_vread(&lhwp->buf, sizeof (struct ldi_handle),
	    wsp->walk_addr) == -1) {
		mdb_warn("couldn't read ldi handle at %p", wsp->walk_addr);
		return (WALK_ERR);
	}
	wsp->walk_addr = (uintptr_t)lhwp->buf.lh_next;
	return (WALK_NEXT);
}

int
ldi_ident_walk_init(mdb_walk_state_t *wsp)
{
	li_walk_t	*liwp;
	GElf_Sym	sym;

	/* get the address of the hash table */
	if (mdb_lookup_by_name("ldi_ident_hash", &sym) == -1) {
		mdb_warn("couldn't find ldi_ident_hash");
		return (WALK_ERR);
	}

	liwp = mdb_alloc(sizeof (li_walk_t), UM_SLEEP|UM_GC);
	liwp->hash = (struct ldi_ident **)(uintptr_t)sym.st_value;
	liwp->index = 0;

	/* get the address of the first element in the first hash bucket */
	if ((mdb_vread(&liwp->lip, sizeof (struct ldi_ident *),
	    (uintptr_t)liwp->hash)) == -1) {
		mdb_warn("couldn't read ldi ident hash at %p", liwp->hash);
		return (WALK_ERR);
	}

	wsp->walk_addr = (uintptr_t)liwp->lip;
	wsp->walk_data = liwp;

	return (WALK_NEXT);
}

int
ldi_ident_walk_step(mdb_walk_state_t *wsp)
{
	li_walk_t 	*liwp = (li_walk_t *)wsp->walk_data;
	int		status;

	/* check if we need to go to the next hash bucket */
	while (wsp->walk_addr == NULL) {

		/* advance to the next bucket */
		if (++(liwp->index) >= LI_HASH_SZ)
			return (WALK_DONE);

		/* get handle address from the hash bucket */
		if ((mdb_vread(&liwp->lip, sizeof (struct ldi_ident *),
		    (uintptr_t)(liwp->hash + liwp->index))) == -1) {
			mdb_warn("couldn't read ldi ident hash at %p",
			    (uintptr_t)liwp->hash + liwp->index);
			return (WALK_ERR);
		}

		wsp->walk_addr = (uintptr_t)liwp->lip;
	}

	/* invoke the walker callback for this hash element */
	status = wsp->walk_callback(wsp->walk_addr, NULL, wsp->walk_cbdata);
	if (status != WALK_NEXT)
		return (status);

	/* get a pointer to the next hash element */
	if (mdb_vread(&liwp->buf, sizeof (struct ldi_ident),
	    wsp->walk_addr) == -1) {
		mdb_warn("couldn't read ldi ident at %p", wsp->walk_addr);
		return (WALK_ERR);
	}
	wsp->walk_addr = (uintptr_t)liwp->buf.li_next;
	return (WALK_NEXT);
}

/*
 * LDI dcmds
 */
static void
ldi_ident_header(int start, int refs)
{
	if (start) {
		mdb_printf("%-?s ", "IDENT");
	} else {
		mdb_printf("%?s ", "IDENT");
	}

	if (refs)
		mdb_printf("%4s ", "REFS");

	mdb_printf("%?s %5s %5s %s\n", "DIP", "MINOR", "MODID", "MODULE NAME");
}

static int
ldi_ident_print(uintptr_t addr, int refs)
{
	struct ldi_ident	li;

	/* read the ldi ident */
	if (mdb_vread(&li, sizeof (struct ldi_ident), addr) == -1) {
		mdb_warn("couldn't read ldi ident at %p", addr);
		return (1);
	}

	/* display the ident address */
	mdb_printf("%0?p ", addr);

	/* display the ref count */
	if (refs)
		mdb_printf("%4u ", li.li_ref);

	/* display the dip (if any) */
	if (li.li_dip != NULL) {
		mdb_printf("%0?p ", li.li_dip);
	} else {
		mdb_printf("%?s ", "-");
	}

	/* display the minor node (if any) */
	if (li.li_dev != DDI_DEV_T_NONE) {
		mdb_printf("%5u ", getminor(li.li_dev));
	} else {
		mdb_printf("%5s ", "-");
	}

	/* display the module info */
	mdb_printf("%5d %s\n", li.li_modid, li.li_modname);

	return (0);
}

int
ldi_ident(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	int	start = 1;
	int	refs = 1;

	/* Determine if there is an ldi identifier address */
	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_walk_dcmd("ldi_ident", "ldi_ident",
		    argc, argv) == -1) {
			mdb_warn("can't walk ldi idents");
			return (DCMD_ERR);
		}
		return (DCMD_OK);
	}

	/* display the header line */
	if (DCMD_HDRSPEC(flags))
		ldi_ident_header(start, refs);

	/* display the ldi ident */
	if (ldi_ident_print(addr, refs))
		return (DCMD_ERR);

	return (DCMD_OK);
}

static void
ldi_handle_header(int refs, int ident) {
	mdb_printf("%-?s ", "HANDLE");

	if (refs)
		mdb_printf("%4s ", "REFS");

	mdb_printf("%?s %10s %5s %?s ", "VNODE", "DRV", "MINOR", "EVENTS");

	if (!ident) {
		mdb_printf("%?s\n", "IDENT");
	} else {
		ldi_ident_header(0, 0);
	}
}

static int
ldi_handle_print(uintptr_t addr, int ident, int refs)
{
	vnode_t			vnode;
	struct ldi_handle	lh;
	const char		*name;

	/* read in the ldi handle */
	if (mdb_vread(&lh, sizeof (struct ldi_handle), addr) == -1) {
		mdb_warn("couldn't read ldi handle at %p", addr);
		return (DCMD_ERR);
	}

	/* display the handle address */
	mdb_printf("%0?p ", addr);

	/* display the ref count */
	if (refs)
		mdb_printf("%4u ", lh.lh_ref);

	/* display the vnode */
	mdb_printf("%0?p ", lh.lh_vp);

	/* read in the vnode associated with the handle */
	addr = (uintptr_t)lh.lh_vp;
	if (mdb_vread(&vnode, sizeof (vnode_t), addr) == -1) {
		mdb_warn("couldn't read vnode at %p", addr);
		return (1);
	}

	/* display the driver name */
	if ((name = mdb_major_to_name(getmajor(vnode.v_rdev))) == NULL) {
		mdb_warn("failed to convert major number to name\n");
		return (1);
	}
	mdb_printf("%10s ", name);

	/* display the minor number */
	mdb_printf("%5d ", getminor(vnode.v_rdev));

	/* display the event pointer (if any) */
	if (lh.lh_events != NULL) {
		mdb_printf("%0?p ", lh.lh_events);
	} else {
		mdb_printf("%?s ", "-");
	}

	if (!ident) {
		/* display the ident address */
		mdb_printf("%0?p\n", lh.lh_ident);
		return (0);
	}

	/* display the entire ident  */
	return (ldi_ident_print((uintptr_t)lh.lh_ident, refs));
}

int
ldi_handle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
	int			ident = 0;
	int			refs = 1;

	if (mdb_getopts(argc, argv,
	    'i', MDB_OPT_SETBITS, TRUE, &ident) != argc)
		return (DCMD_USAGE);

	if (ident)
		refs = 0;

	/* Determine if there is an ldi handle address */
	if (!(flags & DCMD_ADDRSPEC)) {
		if (mdb_walk_dcmd("ldi_handle", "ldi_handle",
		    argc, argv) == -1) {
			mdb_warn("can't walk ldi handles");
			return (DCMD_ERR);
		} return (DCMD_OK);
	}

	/* display the header line */
	if (DCMD_HDRSPEC(flags))
		ldi_handle_header(refs, ident);

	/* display the ldi handle */
	if (ldi_handle_print(addr, ident, refs))
		return (DCMD_ERR);

	return (DCMD_OK);
}

void
ldi_ident_help(void)
{
	mdb_printf("Displays an ldi identifier.\n"
	    "Without the address of an \"ldi_ident_t\", "
	    "print all identifiers.\n"
	    "With an address, print the specified identifier.\n");
}

void
ldi_handle_help(void)
{
	mdb_printf("Displays an ldi handle.\n"
	    "Without the address of an \"ldi_handle_t\", "
	    "print all handles.\n"
	    "With an address, print the specified handle.\n\n"
	    "Switches:\n"
	    "  -i  print the module identifier information\n");
}