/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
*/
/*
* For machines that support the openprom, fetch and print the list
* of devices that the kernel has fetched from the prom or conjured up.
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <ctype.h>
#include <strings.h>
#include <unistd.h>
#include <stropts.h>
#include <sys/openpromio.h>
#include <zone.h>
#include <libnvpair.h>
#include <pcidb.h>
#include "prtconf.h"
typedef char *(*dump_propname_t)(void *);
typedef int (*dump_proptype_t)(void *);
typedef int (*dump_propints_t)(void *, int **);
typedef int (*dump_propstrings_t)(void *, char **);
typedef struct dumpops_common {
}, pathprop_common_dumpops = {
};
typedef void *(*dump_nextprop_t)(void *, void *);
typedef struct dumpops {
} dumpops_t;
typedef struct di_args {
} di_arg_t;
}, globprop_dumpops = {
}, drvprop_dumpops = {
}, hwprop_dumpops = {
}, pathprop_dumpops = {
};
#define NUM_ELEMENTS(A) (sizeof (A) / sizeof (A[0]))
static int prop_type_guess(const dumpops_t *, void *, void **, int *);
static int dump_prop_list(const dumpops_t *, const char *,
int, void *, dev_t, int *);
static int _error(const char *, ...);
static int is_openprom();
char *, int);
const char *, uchar_t **);
static int dump_compatible(char *, int, di_node_t);
static void dump_pathing_data(int, di_node_t);
static int print_composite_string(const char *, char *, int);
static int unprintable(char *, int);
static int promopen(int);
static void promclose();
static void node_display_set(di_node_t);
void
prtconf_devinfo(void)
{
char *rootpath;
/* determine what info we need to get from kernel */
flag = DINFOSUBTREE;
rootpath = "/";
}
}
if (opts.o_forcecache) {
if (dbg.d_forceload) {
}
}
flag = DINFOCACHE;
}
if (dbg.d_forceload) {
flag |= DINFOFORCE;
}
/* get devlink (aka aliases) data */
} else
if (root_node == DI_NODE_NIL) {
/* not an error if this isn't the global zone */
if (getzoneid() == GLOBAL_ZONEID)
exit(-1);
else
exit(0);
}
"continuing anyways");
}
/*
* ...and walk all nodes to report them out...
*/
if (dbg.d_bydriver) {
if (prom_hdl != DI_PROM_HANDLE_NIL)
if (devlink_hdl != NULL)
(void) di_devlink_fini(&devlink_hdl);
return;
}
if (target_node == DI_NODE_NIL) {
"invalid device path specified\n",
exit(1);
}
/* mark the target node so we display it */
if (opts.o_ancestors) {
/*
* mark the ancestors of this node so we display
* them as well
*/
node = target_node;
} else {
/*
* when we display device tree nodes the indentation
* level is based off of tree depth.
*
* here we increment o_target to reflect the
* depth of the target node in the tree. we do
* this so that when we calculate the indentation
* level we can subtract o_target so that the
* target node starts with an indentation of zero.
*/
node = target_node;
}
if (opts.o_children) {
/*
* mark the children of this node so we display
* them as well
*/
(void *)1,
(int (*)(di_node_t, void *))
}
}
if (prom_hdl != DI_PROM_HANDLE_NIL)
if (devlink_hdl != NULL)
(void) di_devlink_fini(&devlink_hdl);
}
/*
* utility routines
*/
static int
{
char *path;
return (DI_WALK_TERMINATE);
}
return (DI_WALK_TERMINATE);
}
}
} else {
/* we should never get here */
}
return (DI_WALK_CONTINUE);
}
static di_node_t
{
/* special case to allow displaying of the root node */
return (root_node);
return (root_node);
}
return (target);
}
static long
{
return (data & NODE_DISPLAY);
}
static void
{
data |= NODE_DISPLAY;
}
static long
{
return (data & LNODE_DISPLAYED);
}
static void
{
data |= LNODE_DISPLAYED;
}
static void
{
data &= ~LNODE_DISPLAYED;
}
static long
{
return (data & MINOR_DISPLAYED);
}
static void
{
data |= MINOR_DISPLAYED;
}
static void
{
data &= ~MINOR_DISPLAYED;
}
static void *
{
}
static void
{
}
/*
* In this comment typed properties are those of type DI_PROP_TYPE_UNDEF_IT,
* DI_PROP_TYPE_BOOLEAN, DI_PROP_TYPE_INT, DI_PROP_TYPE_INT64,
* DI_PROP_TYPE_BYTE, and DI_PROP_TYPE_STRING.
*
* The guessing algorithm is:
* 1. If the property is typed and the type is consistent with the value of
* the property, then the property is of that type. If the type is not
* consistent with value of the property, then the type is treated as
* alien to prtconf.
* 2. If the property is of type DI_PROP_TYPE_UNKNOWN the following steps
* are carried out.
* a. If the value of the property is consistent with a string property,
* the type of the property is DI_PROP_TYPE_STRING.
* b. Otherwise, if the value of the property is consistent with an integer
* property, the type of the property is DI_PROP_TYPE_INT.
* c. Otherwise, the property type is treated as alien to prtconf.
* 3. If the property type is alien to prtconf, then the property value is
* read by the appropriate routine for untyped properties and the following
* steps are carried out.
* a. If the length that the property routine returned is zero, the
* property is of type DI_PROP_TYPE_BOOLEAN.
* b. Otherwise, if the length that the property routine returned is
* positive, then the property value is treated as raw data of type
* DI_PROP_TYPE_UNKNOWN.
* c. Otherwise, if the length that the property routine returned is
* negative, then there is some internal inconsistency and this is
* treated as an error and no type is determined.
*/
static int
int *prop_type)
{
switch (type) {
case DI_PROP_TYPE_UNDEF_IT:
case DI_PROP_TYPE_BOOLEAN:
return (0);
case DI_PROP_TYPE_INT:
break;
case DI_PROP_TYPE_INT64:
break;
case DI_PROP_TYPE_BYTE:
break;
case DI_PROP_TYPE_STRING:
break;
case DI_PROP_TYPE_UNKNOWN:
return (len);
}
break;
default:
len = -1;
}
if (len > 0) {
return (len);
}
if (len < 0) {
return (-1);
} else if (len == 0) {
return (0);
}
return (len);
}
/*
* Returns 0 if nothing is printed, 1 otherwise
*/
static int
{
char *p;
int nprop = 0;
if (compat_printed)
*compat_printed = 0;
/* Skip properties a dev_t oriented caller is not requesting */
if (dev == DDI_DEV_T_ANY) {
/*
* Caller requesting print all properties
*/
goto print;
} else if (dev == DDI_DEV_T_NONE) {
/*
* Caller requesting print of properties
* associated with devinfo (not minor).
*/
if ((pdev == DDI_DEV_T_ANY) ||
(pdev == DDI_DEV_T_NONE))
goto print;
/*
* Property has a minor association, see if
* we have a minor with this dev_t. If there
* is no such minor we print the property now
* so it gets displayed.
*/
minor)) != DI_MINOR_NIL) {
break;
}
if (minor == DI_MINOR_NIL)
goto print;
/*
* Caller requesting print of properties
* associated with a specific matching minor
* node.
*/
goto print;
}
/* otherwise skip print */
continue;
}
if (nitems < 0)
continue;
if (nprop == 0) {
if (name) {
}
ilev++;
}
nprop++;
/* report 'compatible' as processed */
if (compat_printed &&
*compat_printed = 1;
switch (prop_type) {
case DI_PROP_TYPE_UNDEF_IT:
(void) printf("undef");
break;
case DI_PROP_TYPE_BOOLEAN:
(void) printf("boolean");
break;
case DI_PROP_TYPE_INT:
(void) printf("int");
break;
case DI_PROP_TYPE_INT64:
(void) printf("int64");
break;
case DI_PROP_TYPE_BYTE:
(void) printf("byte");
break;
case DI_PROP_TYPE_STRING:
(void) printf("string");
break;
case DI_PROP_TYPE_UNKNOWN:
(void) printf("unknown");
break;
default:
/* Should never be here */
}
if (nitems != 0)
/* print the major and minor numbers for a device property */
if ((pdev == DDI_DEV_T_NONE) ||
(pdev == DDI_DEV_T_ANY)) {
(void) printf(" dev=none");
} else {
(void) printf(" dev=(%u,%u)",
}
}
(void) putchar('\n');
if (nitems == 0)
continue;
(void) printf(" value=");
switch (prop_type) {
case DI_PROP_TYPE_INT:
for (i = 0; i < nitems - 1; i++)
break;
case DI_PROP_TYPE_INT64:
for (i = 0; i < nitems - 1; i++)
(void) printf("%16.16llx.",
((long long *)prop_data)[i]);
break;
case DI_PROP_TYPE_STRING:
p = (char *)prop_data;
for (i = 0; i < nitems - 1; i++) {
(void) printf("'%s' + ", p);
p += strlen(p) + 1;
}
(void) printf("'%s'", p);
break;
default:
for (i = 0; i < nitems - 1; i++)
(void) printf("%2.2x.",
}
(void) putchar('\n');
}
return (nprop ? 1 : 0);
}
/*
* walk_driver is a debugging facility.
*/
static void
{
while (node != DI_NODE_NIL) {
}
}
/*
* print out information about this node, returns appropriate code.
*/
/*ARGSUSED1*/
static int
{
char *driver_name;
int compat_printed;
int printed;
}
if (dbg.d_bydriver) {
ilev = 1;
} else {
/* figure out indentation level */
ilev++;
}
}
/*
* if we're only displaying certain nodes and this one
* isn't flagged, skip it.
*/
return (DI_WALK_CONTINUE);
}
/*
* if this node does not have an instance number or is the
* root node (1229946), we don't print an instance number
*/
if (opts.o_drv_name) {
if (driver_name != NULL)
} else if (di_retired(node)) {
(void) printf(" (retired)");
(void) printf(" (driver not attached)");
(void) printf("\n");
} else {
(void) dump_prop_list(&globprop_dumpops,
}
/* Ensure that 'compatible' is printed under Hardware header */
if (!compat_printed)
/* Ensure that pci id information is printed under Hardware */
}
return (DI_WALK_CONTINUE);
return (DI_WALK_PRUNECHILD);
return (DI_WALK_CONTINUE);
}
/* _error([no_perror, ] fmt [, arg ...]) */
static int
{
int saved_errno;
int no_perror = 0;
const char *fmt;
saved_errno = errno;
if (opt_noperror == NULL) {
no_perror = 1;
} else
fmt = opt_noperror;
if (no_perror)
else {
errno = saved_errno;
perror("");
}
return (-1);
}
/*
* The rest of the routines handle printing the raw prom devinfo (-p option).
*
* 128 is the size of the largest (currently) property name
* 16k - MAXNAMESZ - sizeof (int) is the size of the largest
* (currently) property value that is allowed.
* the sizeof (uint_t) is from struct openpromio
*/
typedef union {
} Oppbuf;
static int prom_fd;
static int
is_openprom(void)
{
unsigned int i;
i = (unsigned int)((unsigned char)opp->oprom_array[0]);
return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
}
int
do_prominfo(void)
{
}
if (is_openprom() == 0) {
"support this option of this command.\n");
return (1);
}
/* OPROMSNAPSHOT returns size in arg */
if (arg == 0)
return (1);
/* copy out the snapshot for printing */
/*LINTED*/
promclose();
/* print out information */
return (0);
}
static void
{
int error;
/* Expand to an nvlist */
/* print current node */
/* print children */
return; /* no child exists */
continue;
}
if (type != DATA_TYPE_BYTE_ARRAY) {
dprintf("unexpected nvpair type %d, not byte array \n",
type);
continue;
}
(void) nvpair_value_byte_array(child,
}
}
/*
* Print all properties and values
*/
static void
{
int id = 0;
(void) printf("Node");
(void) printf("data not available");
else
(void) putchar('\n');
return;
}
if (name[0] == '@')
continue;
}
(void) putchar('\n');
}
static const char *
{
switch (st) {
case DI_PATH_STATE_ONLINE:
return ("online");
case DI_PATH_STATE_STANDBY:
return ("standby");
case DI_PATH_STATE_OFFLINE:
return ("offline");
case DI_PATH_STATE_FAULT:
return ("faulted");
}
return ("unknown");
}
/*
* Print all phci's each client is connected to.
*/
static void
{
char *phci_path;
int path_instance;
if (node == DI_PATH_NIL)
return;
/* It is not really a path if we failed to capture the pHCI */
if (phci_node == DI_NODE_NIL)
continue;
/* Print header for the first path */
if (firsttime) {
firsttime = 0;
ilev++;
(void) printf("Paths from multipath bus adapters:\n");
}
/*
* Print the path instance and full "pathinfo" path, which is
* the same as the /devices devifo path had the device been
* enumerated under pHCI.
*/
if (phci_path) {
if (path_instance > 0) {
(void) printf("Path %d: %s/%s@%s\n",
}
}
/* print phci driver, instance, and path state information */
}
}
static int
{
return (DI_WALK_CONTINUE);
}
static void
{
int spec_type;
/* get the path to the device and the minor node name */
/* display the path to this minor node */
if (devlink_hdl != NULL) {
/* get the device minor node information */
switch (di_minor_type(minor)) {
case DDM_MINOR:
type = "minor";
break;
case DDM_ALIAS:
type = "alias";
break;
case DDM_DEFAULT:
type = "default";
break;
case DDM_INTERNAL_PATH:
type = "internal";
break;
default:
type = "unknown";
break;
}
/* display the device minor node information */
(void) printf("spectype=%s type=%s\n",
/* display all the devlinks for this device minor node */
}
}
static void
{
int major;
/* if there are no minor nodes, bail */
return;
/*
* here we want to create lists of minor nodes with the same
* dev_t. to do this we first sort all the minor nodes by devt.
*
* the algorithm used here is a bubble sort, so performance sucks.
* but it's probably ok here because most device instances don't
* have that many minor nodes. also we're doing this as we're
* displaying each node so it doesn't look like we're pausing
* output for a long time.
*/
continue;
if (minor_head == DI_MINOR_NIL) {
/* this is the first minor node we're looking at */
continue;
}
/*
* if the new dev is less than the old dev, update minor_head
* so it points to the beginning of the list. ie it points
* to the node with the lowest dev value
*/
minor_head = minor;
continue;
}
while ((minor_walk != DI_MINOR_NIL) &&
}
if (minor_walk == NULL)
minor_tail = minor;
}
if (minor_head == DI_MINOR_NIL)
return;
/*
* now that we have a list of minor nodes sorted by devt
* we walk through the list and break apart the entire list
* to create circular lists of minor nodes with matching devts.
*/
while (minor_walk != DI_MINOR_NIL) {
}
}
}
static void
{
/*
* if we're displaying the source of a link, we should display
* the target access mode. (either block or char.)
*/
if (endpoint == DI_LINK_SRC)
(void) printf(" accesstype=%s",
/*
* check if the lnode is bound to a specific device
* minor node (i.e. if it's bound to a dev_t) and
* if so display the dev_t value and any possible
* minor node pathing information.
*/
displayed_path = 0;
(void) printf(" dev=(%u,%u)\n",
/* display paths to the src devt minor node */
continue;
if ((endpoint == DI_LINK_TGT) &&
continue;
displayed_path = 1;
}
} else {
(void) printf("\n");
}
if (displayed_path)
return;
/*
* This device lnode is not did not have any minor node
* pathing information so display the path to device node.
*/
}
static void
{
link = DI_LINK_NIL;
continue;
continue;
if (first) {
first = 0;
(void) printf("Device Minor Layered Under:\n");
}
/* displayed this lnode */
}
link = DI_LINK_NIL;
continue;
continue;
if (first) {
first = 0;
(void) printf("Device Minor Layered Over:\n");
}
/* displayed this lnode */
}
}
static void
{
/*
* first go through and mark all lnodes and minor nodes for this
* node as undisplayed
*/
}
/*
* when we display the minor nodes we want to coalesce nodes
* that have the same dev_t. we do this by creating circular
* lists of minor nodes with the same devt.
*/
/* now we display the driver defined minor nodes */
/*
* these are only created for DLPIv2 network devices.
* since these minor nodes are associated with a driver
* and are only bound to a device instance after they
* are opened and attached we don't print them out
* here.
*/
continue;
/* skip nodes that may have already been displayed */
if (minor_displayed(minor))
continue;
if (firstminor) {
firstminor = 0;
indent_to_level(ilev++);
(void) printf("Device Minor Nodes:\n");
}
/* display the device minor node information */
(void) printf("dev=(%u,%u)\n",
minor_next = minor;
do {
/* display device minor node path info */
/* get a pointer to the next node */
} while (minor_next != minor);
/* display who has this device minor node open */
/* display properties associated with this devt */
}
/*
* now go through all the target lnodes for this node and
* if they haven't yet been displayed, display them now.
*
* this happens in the case of clone opens when an "official"
* minor node does not exist for the opened devt
*/
link = DI_LINK_NIL;
/* if we've already displayed this target lnode, skip it */
if (lnode_displayed(lnode))
continue;
if (firstminor) {
firstminor = 0;
indent_to_level(ilev++);
(void) printf("Device Minor Nodes:\n");
}
/* display the device minor node information */
(void) printf("dev=(%u,%u)\n",
(void) printf("dev_path=<clone>\n");
/* display who has this cloned device minor node open */
/* mark node as displayed */
}
}
static void
{
link = DI_LINK_NIL;
/*
* here we only want to print out layering information
* if we are the source and our source lnode is not
* associated with any particular dev_t. (which means
* we won't display this link while dumping minor node
* info.)
*/
continue;
if (first) {
first = 0;
(void) printf("Device Layered Over:\n");
}
/* displayed this lnode */
}
}
/*
* certain 'known' property names may contain 'composite' strings.
* Handle them here, and print them as 'string1' + 'string2' ...
*/
static int
{
char *p, *q;
char *firstp;
return (0); /* Not a known composite string */
/*
* Verify that each string in the composite string is non-NULL,
* is within the bounds of the property length, and contains
* printable characters or white space. Otherwise let the
* caller deal with it.
*/
if (strlen(p) == 0)
return (0); /* NULL string */
for (q = p; *q; q++) {
return (0); /* Not printable or space */
}
return (0); /* Out of bounds */
}
if (p == firstp)
(void) printf("'%s'", p);
else
(void) printf(" + '%s'", p);
}
(void) putchar('\n');
return (1);
}
/*
* Print one property and its value. Handle the verbose case.
*/
static void
{
int i;
int endswap = 0;
char *value;
switch (nvpair_type(nvp)) {
case DATA_TYPE_BOOLEAN:
(void) printf(" \n");
return;
case DATA_TYPE_BYTE_ARRAY:
&valsize)) {
(void) printf("data not available.\n");
return;
}
valsize--; /* take out null added by driver */
/*
* Do not print valsize > MAXVALSIZE, to be compatible
* with old behavior. E.g. intel's eisa-nvram property
* has a size of 65 K.
*/
if (valsize > MAXVALSIZE) {
(void) printf(" \n");
return;
}
break;
default:
(void) printf("data type unexpected.\n");
return;
}
/*
* Handle printing verbosely
*/
return;
}
return;
}
(void) printf(" ");
#ifdef __x86
/*
* Due to backwards compatibility constraints x86 int
* properties are not in big-endian (ieee 1275) byte order.
* If we have a property that is a multiple of 4 bytes,
* let's assume it is an array of ints and print the bytes
* in little endian order to make things look nicer for
* the user.
*/
#endif /* __x86 */
for (i = 0; i < valsize; i++) {
int out;
if (i && (i % 4 == 0))
(void) putchar('.');
if (endswap)
else
}
(void) putchar('\n');
}
static int
{
int i;
/*
* Is this just a zero?
*/
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 < size; ++i) {
if (value[i] == '\0')
return (i != (size - 1));
return (1);
}
return (0);
}
static int
{
for (;;) {
(void) sleep(5);
continue;
}
return (-1);
if (getzoneid() == GLOBAL_ZONEID) {
}
/* not an error if this isn't the global zone */
exit(0);
} else
return (0);
}
}
static void
promclose(void)
{
}
/*
* Get and print the name of the frame buffer device.
*/
int
do_fbname(void)
{
int retval;
if (retval == 0) {
} else {
"Error copying fb path to userland\n");
} else {
"Console output device is not a frame buffer\n");
}
return (1);
}
return (0);
}
/*
* Get and print the PROM version.
*/
int
do_promversion(void)
{
return (1);
}
promclose();
return (0);
}
int
do_prom_version64(void)
{
#ifdef sparc
/*LINTED*/
static const char msg[] =
"NOTICE: The firmware on this system does not support the "
"64-bit OS.\n"
"\tPlease upgrade to at least the following version:\n"
"\t\t%s\n\n";
return (-1);
}
if (opr->return_code == 0)
return (0);
promclose();
return (opr->return_code);
#else
return (0);
#endif
}
int
do_productinfo(void)
{
"compatible" };
"compatible", "idprom" };
if (root == DI_NODE_NIL) {
return (1);
}
promh = di_prom_init();
if (promh == DI_PROM_HANDLE_NIL) {
return (1);
}
/* Get model and version properties under node "openprom" */
if (next_node != DI_NODE_NIL)
} else
return (0);
}
char *node_name)
{
int len;
return (next_node);
}
return (DI_NODE_NIL);
}
int
{
int len;
if (len != -1) {
}
return (len);
}
static void
{
&prop_valp);
if (len != -1) {
if (print_composite_string((const char *)
continue;
}
continue;
}
(void) printf(" ");
#ifdef __x86
#endif /* __x86 */
(void) putchar('.');
if (endswap)
else
}
(void) putchar('\n');
}
}
}
static int
{
int ncompat;
char *compat_array;
char *p, *q;
int i;
if (node == DI_PATH_NIL)
return (0);
if (ncompat <= 0)
return (0); /* no 'compatible' available */
/* verify integrety of compat_array */
if (strlen(p) == 0)
return (0); /* NULL string */
for (q = p; *q; q++) {
return (0); /* Not printable or space */
}
}
/* If name is non-NULL, produce header */
if (name) {
}
ilev++;
/* process like a string array property */
(void) printf(" value=");
i++, p += strlen(p) + 1)
(void) printf("'%s' + ", p);
(void) printf("'%s'", p);
(void) putchar('\n');
return (1);
}
static int
{
char *t = NULL;
return (0);
"device_type", &t) <= 0)
return (0);
strcmp(t, "pciex") != 0))
return (0);
/*
* All devices should have a vendor and device id, if we fail to find
* one, then we're going to return right here and not print anything.
*
* We're going to also check for the subsystem-vendor-id and
* subsystem-id. If we don't find one of them, we're going to assume
* that this device does not have one. In that case, we will never
* attempt to try and print anything related to that. If it does have
* both, then we are going to look them up and print the appropriate
* string if we find it or not.
*/
return (0);
return (0);
"subsystem-id", &sdid) <= 0) {
}
goto print;
goto print;
goto print;
}
/* If name is non-NULL, produce header */
if (name) {
}
ilev++;
/* These are all going to be single string properties */
(void) printf("name='vendor-name' type=string items=1\n");
(void) printf("name='device-name' type=string items=1\n");
(void) printf("name='subsystem-name' type=string items=1\n");
}
return (0);
}