575 lines
12 KiB
C
575 lines
12 KiB
C
/*
|
|
* Copyright (C) 1989-1990 Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#) devinfo.c 1.1 94/10/31";
|
|
#endif lint
|
|
|
|
/*
|
|
* usage: devinfo [-fpv] [ promdev ]
|
|
* compilation: cc -o -o devinfo devinfo.c -lkvm
|
|
*
|
|
* For machines that support the new openprom, fetch and print the list
|
|
* of devices that the kernel has fetched from the prom or conjured up.
|
|
* Or, with -p, print the prom's original devinfo list.
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <kvm.h>
|
|
#include <nlist.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <varargs.h>
|
|
#include <sun/openprom.h>
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sundev/openpromio.h>
|
|
|
|
|
|
/*
|
|
* function declarations
|
|
*/
|
|
|
|
extern char *malloc();
|
|
static void build_devs(), walk_devs(), dump_devs(), wrongarch(), setprogname();
|
|
static char *getkname();
|
|
static int _error();
|
|
|
|
/*
|
|
* local data
|
|
*/
|
|
static kvm_t *kd;
|
|
static char *mfail = "malloc";
|
|
static char *progname;
|
|
static char *promdev = "/dev/openprom";
|
|
static int prominfo = 0;
|
|
static char *usage = "%s [ -fpv ] [ promdev ]\n";
|
|
static int verbose = 0;
|
|
|
|
#define DSIZE (sizeof (struct dev_info))
|
|
#define P_DSIZE (sizeof (struct dev_info *))
|
|
#define NO_PERROR ((char *) 0)
|
|
|
|
|
|
main(a, v)
|
|
char **v;
|
|
{
|
|
auto struct dev_info root_node, *rnodep;
|
|
int c;
|
|
static struct nlist list[] = { { "_top_devinfo" }, 0 };
|
|
extern char *optarg;
|
|
extern int optind;
|
|
|
|
setprogname(v[0]);
|
|
while ((c = getopt(a, v, "f:pv")) != -1)
|
|
switch (c) {
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
case 'p':
|
|
prominfo++;
|
|
break;
|
|
case 'f':
|
|
promdev = optarg;
|
|
break;
|
|
default:
|
|
(void)fprintf(stderr, usage, progname);
|
|
exit(1);
|
|
}
|
|
if (prominfo)
|
|
exit(do_prominfo());
|
|
|
|
if ((kd = kvm_open((char *)0, (char *)0, (char *)0, O_RDONLY, progname))
|
|
== NULL) {
|
|
exit(_error("kvm_open failed"));
|
|
} else if ((kvm_nlist(kd, &list[0])) != 0) {
|
|
wrongarch();
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* first, build the root node...
|
|
*/
|
|
|
|
if (kvm_read(kd, list[0].n_value, (char *)&rnodep, P_DSIZE)
|
|
!= P_DSIZE) {
|
|
exit(_error("kvm_read of root node pointer fails"));
|
|
}
|
|
|
|
if (kvm_read(kd, (u_long)rnodep, (char *)&root_node, DSIZE) != DSIZE) {
|
|
exit(_error("kvm_read of root node fails"));
|
|
}
|
|
|
|
/*
|
|
* call build_devs to fetch and construct a user space copy
|
|
* of the dev_info tree.....
|
|
*/
|
|
|
|
build_devs(&root_node);
|
|
|
|
/*
|
|
* ...and call walk_devs to report it out...
|
|
*/
|
|
walk_devs (&root_node);
|
|
}
|
|
|
|
/*
|
|
* build_devs copies all appropriate information out of the kernel
|
|
* and gets it into a user addrssable place. the argument is a
|
|
* pointer to a just-copied out to user space dev_info structure.
|
|
* all pointer quantities that we might de-reference now have to
|
|
* be replaced with pointers to user addressable data.
|
|
*/
|
|
|
|
static void
|
|
build_devs(dp)
|
|
register struct dev_info *dp;
|
|
{
|
|
char *tptr;
|
|
unsigned amt;
|
|
|
|
if (dp->devi_name)
|
|
dp->devi_name = getkname(dp->devi_name);
|
|
|
|
if (dp->devi_nreg) {
|
|
amt = dp->devi_nreg * sizeof (struct dev_reg);
|
|
tptr = malloc(amt);
|
|
if (!tptr) {
|
|
exit(_error(mfail));
|
|
}
|
|
if (kvm_read(kd, (u_long)dp->devi_reg, tptr, amt) != amt) {
|
|
exit(_error("kvm_read of devi_reg"));
|
|
}
|
|
dp->devi_reg = (struct dev_reg *) tptr;
|
|
}
|
|
|
|
if (dp->devi_nintr) {
|
|
amt = dp->devi_nintr * sizeof (struct dev_intr);
|
|
tptr = malloc(amt);
|
|
if (!tptr) {
|
|
exit(_error(mfail));
|
|
}
|
|
if (kvm_read(kd, (u_long)dp->devi_intr, tptr, amt) != amt) {
|
|
exit(_error("kvm_read of devi_intr"));
|
|
}
|
|
dp->devi_intr = (struct dev_intr *) tptr;
|
|
}
|
|
|
|
if (dp->devi_nrng) {
|
|
amt = dp->devi_nrng * sizeof (struct dev_rng);
|
|
tptr = malloc(amt);
|
|
if (!tptr) {
|
|
exit(_error(mfail));
|
|
}
|
|
if (kvm_read(kd, (u_long)dp->devi_rng, tptr, amt) != amt) {
|
|
exit(_error("kvm_read of devi_rng"));
|
|
}
|
|
dp->devi_rng = (struct dev_rng *) tptr;
|
|
}
|
|
|
|
if (dp->devi_slaves) {
|
|
if (!(tptr = malloc(DSIZE))) {
|
|
exit(_error(mfail));
|
|
}
|
|
if (kvm_read(kd, (u_long)dp->devi_slaves, tptr, DSIZE)
|
|
!= DSIZE) {
|
|
exit(_error("kvm_read of devi_slaves"));
|
|
}
|
|
dp->devi_slaves = (struct dev_info *) tptr;
|
|
build_devs(dp->devi_slaves);
|
|
}
|
|
|
|
if (dp->devi_next) {
|
|
if (!(tptr = malloc(DSIZE))) {
|
|
exit(_error(mfail));
|
|
}
|
|
if (kvm_read(kd, (u_long)dp->devi_next, tptr, DSIZE) != DSIZE) {
|
|
exit(_error("kvm_read of devi_next"));
|
|
}
|
|
dp->devi_next = (struct dev_info *) tptr;
|
|
build_devs(dp->devi_next);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* print out information about this node, descend to children, then
|
|
* go to siblings
|
|
*/
|
|
|
|
static void
|
|
walk_devs(dp)
|
|
register struct dev_info *dp;
|
|
{
|
|
static int indent_level = 0;
|
|
register i;
|
|
|
|
for (i = 0; i < indent_level; i++)
|
|
(void)putchar('\t');
|
|
|
|
(void)printf("Node '%s', unit #%d", dp->devi_name, dp->devi_unit);
|
|
if (dp->devi_driver == 0)
|
|
(void)printf(" (no driver)");
|
|
(void)putchar('\n');
|
|
if (verbose) {
|
|
dump_devs(dp, indent_level+1);
|
|
}
|
|
if (dp->devi_slaves) {
|
|
indent_level++;
|
|
walk_devs(dp->devi_slaves);
|
|
indent_level--;
|
|
}
|
|
if (dp->devi_next) {
|
|
walk_devs(dp->devi_next);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* utility routines
|
|
*/
|
|
|
|
static void
|
|
setprogname(name)
|
|
char *name;
|
|
{
|
|
register char *p;
|
|
|
|
if (p = strrchr(name, '/'))
|
|
progname = p + 1;
|
|
else
|
|
progname = name;
|
|
}
|
|
|
|
static void
|
|
dump_devs(dp, ilev)
|
|
register struct dev_info *dp;
|
|
{
|
|
register i, j;
|
|
|
|
if (dp->devi_nreg) {
|
|
struct dev_reg *rp = dp->devi_reg;
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(" Register specifications:\n");
|
|
for (j = 0; j < dp->devi_nreg; j++, rp++) {
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(
|
|
" Bus Type=0x%x, Address=0x%x, Size=%x\n",
|
|
rp->reg_bustype, rp->reg_addr, rp->reg_size);
|
|
}
|
|
}
|
|
if (dp->devi_nrng) {
|
|
struct dev_rng *rp = dp->devi_rng;
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(" Range specifications:\n");
|
|
for (j = 0; j < dp->devi_nrng; j++, rp++) {
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(" Ch %.2x.%.8x:",
|
|
rp->rng_cbustype, rp->rng_coffset);
|
|
(void)printf( " Pa %.2x.%.8x",
|
|
rp->rng_bustype, rp->rng_offset);
|
|
(void)printf( " Sz 0x%x\n",
|
|
rp->rng_size);
|
|
}
|
|
}
|
|
if (dp->devi_nintr) {
|
|
struct dev_intr *ip = dp->devi_intr;
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(" Interrupt specifications:\n");
|
|
for (j = 0; j < dp->devi_nintr; j++, ip++) {
|
|
for (i = 0; i < ilev; i++)
|
|
(void)putchar('\t');
|
|
(void)printf(" Interrupt Priority=%d",
|
|
ip->int_pri, ip->int_pri);
|
|
if (ip->int_vec) {
|
|
(void)printf(", vector=0x%x (%d)",
|
|
ip->int_vec, ip->int_vec);
|
|
}
|
|
(void)putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
wrongarch()
|
|
{
|
|
static char buf[32] = "????\n";
|
|
FILE *ip = popen("/bin/arch -k", "r");
|
|
if (ip) {
|
|
(void)fgets(buf, 32, ip);
|
|
(void)pclose(ip);
|
|
}
|
|
(void)_error(NO_PERROR,
|
|
"%s not available on kernel architecture %s (yet).", progname, buf);
|
|
}
|
|
|
|
static char *
|
|
getkname(kaddr)
|
|
char *kaddr;
|
|
{
|
|
auto char buf[32], *rv;
|
|
register i = 0;
|
|
char c;
|
|
|
|
if (kaddr == (char *) 0) {
|
|
(void)strcpy(buf, "<null>");
|
|
i = 7;
|
|
} else {
|
|
while (i < 31) {
|
|
if (kvm_read(kd, (u_long)kaddr++, (char *)&c, 1) != 1) {
|
|
exit(_error("kvm_read of name string"));
|
|
}
|
|
if ((buf[i++] = c) == (char) 0)
|
|
break;
|
|
}
|
|
buf[i] = 0;
|
|
}
|
|
if ((rv = malloc((unsigned)i)) == 0) {
|
|
exit(_error(mfail));
|
|
}
|
|
bcopy(buf, rv, i);
|
|
return (rv);
|
|
}
|
|
|
|
/*
|
|
* The rest of the routines handle printing the raw prom devinfo (-p option).
|
|
*
|
|
* 32 is the size of the largest (currently) property name
|
|
* The buffer size is limited by the openprom driver to OPROMMAXPARAM bytes.
|
|
*/
|
|
|
|
#define MAXNAMESIZE 32
|
|
#define MAXVALSIZE (OPROMMAXPARAM - MAXNAMESIZE - sizeof (u_int))
|
|
#define BUFSIZE OPROMMAXPARAM
|
|
|
|
typedef union {
|
|
char buf[BUFSIZE];
|
|
struct openpromio opp;
|
|
} Oppbuf;
|
|
|
|
static void dump_node(), print_one(), promclose(), promopen(), walk();
|
|
static int child(), getpropval(), next(), unprintable();
|
|
|
|
static int prom_fd;
|
|
|
|
int
|
|
do_prominfo()
|
|
{
|
|
promopen(O_RDONLY);
|
|
if (next(0) == 0)
|
|
return (1);
|
|
walk(next(0), 0);
|
|
promclose();
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
walk(id, level)
|
|
int id, level;
|
|
{
|
|
register int curnode;
|
|
|
|
dump_node(id, level);
|
|
if (curnode = child(id))
|
|
walk(curnode, level+1);
|
|
if (curnode = next(id))
|
|
walk(curnode, level);
|
|
}
|
|
|
|
/*
|
|
* Print all properties and values
|
|
*/
|
|
static void
|
|
dump_node(id, level)
|
|
{
|
|
Oppbuf oppbuf;
|
|
register struct openpromio *opp = &(oppbuf.opp);
|
|
register int i = level;
|
|
|
|
while (i--)
|
|
(void)putchar('\t');
|
|
(void)printf("Node");
|
|
if (verbose)
|
|
(void)printf(" %#08lx\n", id);
|
|
|
|
/* get first prop by asking for null string */
|
|
bzero(oppbuf.buf, BUFSIZE);
|
|
while (1) {
|
|
/*
|
|
* get property
|
|
*/
|
|
opp->oprom_size = MAXNAMESIZE;
|
|
|
|
if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0)
|
|
exit(_error("OPROMNXTPROP"));
|
|
|
|
if (opp->oprom_size == 0) {
|
|
break;
|
|
}
|
|
print_one(opp->oprom_array, level+1);
|
|
}
|
|
(void)putchar('\n');
|
|
}
|
|
|
|
/*
|
|
* Print one property and its value.
|
|
*/
|
|
static void
|
|
print_one(var, level)
|
|
char *var;
|
|
int level;
|
|
{
|
|
Oppbuf oppbuf;
|
|
register struct openpromio *opp = &(oppbuf.opp);
|
|
register int i;
|
|
|
|
while (verbose && level--)
|
|
(void)putchar('\t');
|
|
if (verbose)
|
|
(void)printf("%s: ", var);
|
|
(void)strcpy(opp->oprom_array, var);
|
|
if (getpropval(opp) || opp->oprom_size == -1) {
|
|
(void)printf("data not available.\n");
|
|
return;
|
|
}
|
|
if (verbose && unprintable(opp)) {
|
|
(void)printf(" ");
|
|
for (i = 0; i < opp->oprom_size; ++i) {
|
|
if (i && (i % 4 == 0))
|
|
(void)putchar('.');
|
|
(void)printf("%02x",
|
|
opp->oprom_array[i] & 0xff);
|
|
}
|
|
(void)putchar('\n');
|
|
} else if (verbose) {
|
|
(void)printf(" '%s'\n", opp->oprom_array);
|
|
} else if (strcmp(var, "name") == 0)
|
|
(void)printf(" '%s'", opp->oprom_array);
|
|
}
|
|
|
|
static int
|
|
unprintable(opp)
|
|
struct openpromio *opp;
|
|
{
|
|
register int i;
|
|
|
|
/*
|
|
* Is this just a zero?
|
|
*/
|
|
if (opp->oprom_size == 0 || opp->oprom_array[0] == '\0')
|
|
return (1);
|
|
/*
|
|
* If any character is unprintable, or if a null appears
|
|
* anywhere except at the end of a string, the whole
|
|
* property is "unprintable".
|
|
*/
|
|
for (i = 0; i < opp->oprom_size; ++i) {
|
|
if (opp->oprom_array[i] == '\0')
|
|
return (i != (opp->oprom_size - 1));
|
|
if (!isascii(opp->oprom_array[i]) ||
|
|
iscntrl(opp->oprom_array[i]))
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
promopen(oflag)
|
|
register int oflag;
|
|
{
|
|
if ((prom_fd = open(promdev, oflag)) < 0)
|
|
exit(_error("cannot open %s", promdev));
|
|
}
|
|
|
|
static void
|
|
promclose()
|
|
{
|
|
if (close(prom_fd) < 0)
|
|
exit(_error("close error on %s", promdev));
|
|
}
|
|
|
|
static
|
|
getpropval(opp)
|
|
register struct openpromio *opp;
|
|
{
|
|
opp->oprom_size = MAXVALSIZE;
|
|
|
|
if (ioctl(prom_fd, OPROMGETPROP, opp) < 0)
|
|
return (_error("OPROMGETPROP"));
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
next(id)
|
|
int id;
|
|
{
|
|
Oppbuf oppbuf;
|
|
register struct openpromio *opp = &(oppbuf.opp);
|
|
int *ip = (int *)(opp->oprom_array);
|
|
|
|
bzero(oppbuf.buf, BUFSIZE);
|
|
opp->oprom_size = MAXVALSIZE;
|
|
*ip = id;
|
|
if (ioctl(prom_fd, OPROMNEXT, opp) < 0)
|
|
return (_error("OPROMNEXT"));
|
|
return (*(int *)opp->oprom_array);
|
|
}
|
|
|
|
static int
|
|
child(id)
|
|
int id;
|
|
{
|
|
Oppbuf oppbuf;
|
|
register struct openpromio *opp = &(oppbuf.opp);
|
|
int *ip = (int *)(opp->oprom_array);
|
|
|
|
bzero(oppbuf.buf, BUFSIZE);
|
|
opp->oprom_size = MAXVALSIZE;
|
|
*ip = id;
|
|
if (ioctl(prom_fd, OPROMCHILD, opp) < 0)
|
|
return (_error("OPROMCHILD"));
|
|
return (*(int *)opp->oprom_array);
|
|
}
|
|
|
|
/* _error([no_perror, ] fmt [, arg ...]) */
|
|
/*VARARGS*/
|
|
static int
|
|
_error(va_alist)
|
|
va_dcl
|
|
{
|
|
int saved_errno;
|
|
va_list ap;
|
|
int no_perror = 0;
|
|
char *fmt;
|
|
extern int errno, _doprnt();
|
|
|
|
saved_errno = errno;
|
|
|
|
if (progname)
|
|
(void) fprintf(stderr, "%s: ", progname);
|
|
|
|
va_start(ap);
|
|
if ((fmt = va_arg(ap, char *)) == 0) {
|
|
no_perror = 1;
|
|
fmt = va_arg(ap, char *);
|
|
}
|
|
(void) _doprnt(fmt, ap, stderr);
|
|
va_end(ap);
|
|
|
|
if (no_perror)
|
|
(void) fprintf(stderr, "\n");
|
|
else {
|
|
(void) fprintf(stderr, ": ");
|
|
errno = saved_errno;
|
|
perror("");
|
|
}
|
|
|
|
return (1);
|
|
}
|