/*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <sys/openpromio.h>
#include <libintl.h>
#include "pdevinfo.h"
#include "display.h"
#include "pdevinfo_sun4u.h"
/*
* For machines that support the openprom, fetch and print the list
* of devices that the kernel has fetched from the prom or conjured up.
*
*/
static int prom_fd;
extern char *progname;
extern char *promdev;
extern void getppdata();
extern void printppdata();
/*
* Define DPRINT for run-time debugging printf's...
* #define DPRINT 1
*/
#ifdef DPRINT
static char vdebug_flag = 1;
#define dprintf if (vdebug_flag) printf
static void dprint_dev_info(caddr_t, dev_info_t *);
#endif /* DPRINT */
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
/*VARARGS1*/
int
_error(char *fmt, ...)
{
int saved_errno;
va_list ap;
extern int errno;
saved_errno = errno;
if (progname)
(void) fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
(void) vfprintf(stderr, fmt, ap);
va_end(ap);
(void) fprintf(stderr, ": ");
errno = saved_errno;
perror("");
return (2);
}
int
is_openprom(void)
{
Oppbuf oppbuf;
register struct openpromio *opp = &(oppbuf.opp);
register unsigned int i;
opp->oprom_size = MAXVALSIZE;
if (ioctl(prom_fd, OPROMGETCONS, opp) < 0)
exit(_error("OPROMGETCONS"));
i = (unsigned int)((unsigned char)opp->oprom_array[0]);
return ((i & OPROMCONS_OPENPROM) == OPROMCONS_OPENPROM);
}
/*
* Read all properties and values from nodes.
* Copy the properties read into the prom_node passsed in.
*/
void
dump_node(Prom_node *node)
{
Oppbuf oppbuf;
register struct openpromio *opp = &oppbuf.opp;
Prop *prop = NULL; /* tail of properties list */
StaticProp *temp;
/* clear out pointers in pnode */
node->props = NULL;
/* get first prop by asking for null string */
(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
/* allocate space for the property */
if ((temp = malloc(sizeof (StaticProp))) == NULL) {
perror("malloc");
exit(1);
}
opp->oprom_size = MAXPROPSIZE;
while (opp->oprom_size != 0) {
Prop *new;
int i;
char *tempp, *newp;
/*
* get property
*/
opp->oprom_size = MAXPROPSIZE;
if (ioctl(prom_fd, OPROMNXTPROP, opp) < 0)
exit(_error("OPROMNXTPROP"));
if (opp->oprom_size != 0) {
temp->name.opp.oprom_size = opp->oprom_size;
(void) strcpy(temp->name.opp.oprom_array,
opp->oprom_array);
(void) strcpy(temp->value.opp.oprom_array,
temp->name.opp.oprom_array);
getpropval(&temp->value.opp);
temp->size = temp->value.opp.oprom_size;
/* Now copy over temp's data to new. */
if ((new = malloc(sizeof (Prop))) == NULL) {
perror("malloc");
exit(1);
}
/*
* First copy over temp->name's data. The
* temp->name.opp.opio_u union always contains char[]
* (as opposed to an int or int []).
*/
new->name.opp.oprom_size = temp->name.opp.oprom_size;
if ((new->name.opp.oprom_array =
malloc(new->name.opp.oprom_size)) == NULL) {
perror("malloc");
exit(1);
}
(void) strcpy(new->name.opp.oprom_array,
temp->name.opp.oprom_array);
new->name.opp.holds_array = 1;
/*
* Then copy over temp->value's data.
* temp->value.opp.opio_u could contain char[], int or
* int []. If *(temp->value.opp.oprom_array) is '\0',
* this indicates int or int []. int is the norm, but
* to be safe we assume int [] and copy over
* OPROM_NODE_SIZE int elements.
*/
new->value.opp.oprom_size = temp->value.opp.oprom_size;
if (*(temp->value.opp.oprom_array) == '\0') {
for (i = 0; i < OPROM_NODE_SIZE; i++)
new->value.opp.oprom_node[i] =
*(&temp->value.opp.oprom_node+i);
new->value.opp.holds_array = 0;
} else {
if ((new->value.opp.oprom_array =
malloc(new->value.opp.oprom_size))
== NULL) {
perror("malloc");
exit(1);
}
/*
* temp->value.opp.oprom_array can contain one
* or more embedded NULLs. These trip-up the
* standard string copying functions, so we do
* the copy by hand. temp->value.opp.oprom_array
* will be NULL-terminated. oprom_size includes
* this terminating NULL.
*/
newp = new->value.opp.oprom_array;
tempp = temp->value.opp.oprom_array;
for (i = new->value.opp.oprom_size; i > 0; i--)
*newp++ = *tempp++;
new->value.opp.holds_array = 1;
}
new->size = temp->size;
/* everything worked so link the property list */
if (node->props == NULL)
node->props = new;
else if (prop != NULL)
prop->next = new;
prop = new;
prop->next = NULL;
}
}
free(temp);
}
int
promopen(int oflag)
{
/*CONSTCOND*/
while (1) {
if ((prom_fd = open(promdev, oflag)) < 0) {
if (errno == EAGAIN) {
(void) sleep(5);
continue;
}
if (errno == ENXIO)
return (-1);
exit(_error(dgettext(TEXT_DOMAIN, "cannot open %s"),
promdev));
} else
return (0);
}
/*NOTREACHED*/
}
void
promclose(void)
{
if (close(prom_fd) < 0)
exit(_error(dgettext(TEXT_DOMAIN, "close error on %s"),
promdev));
}
/*
* Read the value of the property from the PROM device tree
*/
void
getpropval(struct openpromio *opp)
{
opp->oprom_size = MAXVALSIZE;
if (ioctl(prom_fd, OPROMGETPROP, opp) < 0)
exit(_error("OPROMGETPROP"));
}
int
next(int id)
{
Oppbuf oppbuf;
register struct openpromio *opp = &(oppbuf.opp);
/* LINTED */
int *ip = (int *)(opp->oprom_array);
(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
opp->oprom_size = MAXVALSIZE;
*ip = id;
if (ioctl(prom_fd, OPROMNEXT, opp) < 0)
return (_error("OPROMNEXT"));
/* LINTED */
return (*(int *)opp->oprom_array);
}
int
child(int id)
{
Oppbuf oppbuf;
register struct openpromio *opp = &(oppbuf.opp);
/* LINTED */
int *ip = (int *)(opp->oprom_array);
(void) memset((void *) oppbuf.buf, 0, BUFSIZE);
opp->oprom_size = MAXVALSIZE;
*ip = id;
if (ioctl(prom_fd, OPROMCHILD, opp) < 0)
return (_error("OPROMCHILD"));
/* LINTED */
return (*(int *)opp->oprom_array);
}
/*
* Check if the Prom node passed in contains a property called
* "board#".
*/
int
has_board_num(Prom_node *node)
{
Prop *prop = node->props;
/*
* walk thru all properties in this PROM node and look for
* board# prop
*/
while (prop != NULL) {
if (strcmp(prop->name.opp.oprom_array, "board#") == 0)
return (1);
prop = prop->next;
}
return (0);
} /* end of has_board_num() */
/*
* Retrieve the value of the board number property from this Prom
* node. It has the type of int.
*/
int
get_board_num(Prom_node *node)
{
Prop *prop = node->props;
/*
* walk thru all properties in this PROM node and look for
* board# prop
*/
while (prop != NULL) {
if (strcmp(prop->name.opp.oprom_array, "board#") == 0)
return (prop->value.opp.oprom_node[0]);
prop = prop->next;
}
return (-1);
} /* end of get_board_num() */
/*
* Find the requested board struct in the system device tree.
*/
Board_node *
find_board(Sys_tree *root, int board)
{
Board_node *bnode = root->bd_list;
while ((bnode != NULL) && (board != bnode->board_num))
bnode = bnode->next;
return (bnode);
} /* end of find_board() */
/*
* Add a board to the system list in order. Initialize all pointer
* fields to NULL.
*/
Board_node *
insert_board(Sys_tree *root, int board)
{
Board_node *bnode;
Board_node *temp = root->bd_list;
if ((bnode = (Board_node *) malloc(sizeof (Board_node))) == NULL) {
perror("malloc");
exit(1);
}
bnode->nodes = NULL;
bnode->next = NULL;
bnode->board_num = board;
if (temp == NULL)
root->bd_list = bnode;
else if (temp->board_num > board) {
bnode->next = temp;
root->bd_list = bnode;
} else {
while ((temp->next != NULL) && (board > temp->next->board_num))
temp = temp->next;
bnode->next = temp->next;
temp->next = bnode;
}
root->board_cnt++;
return (bnode);
} /* end of insert_board() */
/*
* This function searches through the properties of the node passed in
* and returns a pointer to the value of the name property.
*/
char *
get_node_name(Prom_node *pnode)
{
Prop *prop;
if (pnode == NULL) {
return (NULL);
}
prop = pnode->props;
while (prop != NULL) {
if (strcmp("name", prop->name.opp.oprom_array) == 0)
return (prop->value.opp.oprom_array);
prop = prop->next;
}
return (NULL);
} /* end of get_node_name() */
/*
* This function searches through the properties of the node passed in
* and returns a pointer to the value of the name property.
*/
char *
get_node_type(Prom_node *pnode)
{
Prop *prop;
if (pnode == NULL) {
return (NULL);
}
prop = pnode->props;
while (prop != NULL) {
if (strcmp("device_type", prop->name.opp.oprom_array) == 0)
return (prop->value.opp.oprom_array);
prop = prop->next;
}
return (NULL);
} /* end of get_node_type() */
/*
* Do a depth-first walk of a device tree and
* return the first node with the name matching.
*/
Prom_node *
dev_find_node(Prom_node *root, char *name)
{
Prom_node *node;
node = dev_find_node_by_type(root, "name", name);
return (node);
}
Prom_node *
dev_next_node(Prom_node *root, char *name)
{
Prom_node *node;
node = dev_next_node_by_type(root, "name", name);
return (node);
}
/*
* Search for and return a node of the required type. If no node is found,
* then return NULL.
*/
Prom_node *
dev_find_type(Prom_node *root, char *type)
{
Prom_node *node;
node = dev_find_node_by_type(root, "device_type", type);
return (node); /* not found */
}
/*
* Start from the current node and return the next node besides the
* current one which has the requested type property.
*/
Prom_node *
dev_next_type(Prom_node *root, char *type)
{
Prom_node *node;
node = dev_next_node_by_type(root, "device_type", type);
return (node); /* not found */
}
/*
* Search a device tree and return the first failed node that is found.
* (has a 'status' property)
*/
Prom_node *
find_failed_node(Prom_node * root)
{
Prom_node *pnode;
if (root == NULL)
return (NULL);
if (node_failed(root)) {
return (root);
}
/* search the child */
if ((pnode = find_failed_node(root->child)) != NULL)
return (pnode);
/* search the siblings */
if ((pnode = find_failed_node(root->sibling)) != NULL)
return (pnode);
return (NULL);
} /* end of find_failed_node() */
/*
* Start from the current node and return the next node besides
* the current one which is failed. (has a 'status' property)
*/
Prom_node *
next_failed_node(Prom_node * root)
{
Prom_node *pnode;
Prom_node *parent;
if (root == NULL)
return (NULL);
/* search the child */
if ((pnode = find_failed_node(root->child)) != NULL) {
return (pnode);
}
/* search the siblings */
if ((pnode = find_failed_node(root->sibling)) != NULL) {
return (pnode);
}
/* backtracking the search up through parents' siblings */
parent = root->parent;
while (parent != NULL) {
if ((pnode = find_failed_node(parent->sibling)) != NULL)
return (pnode);
else
parent = parent->parent;
}
return (NULL);
} /* end of find_failed_node() */
/*
* node_failed
*
* This function determines if the current Prom node is failed. This
* is defined by having a status property containing the token 'fail'.
*/
int
node_failed(Prom_node *node)
{
return (node_status(node, "fail"));
}
int
node_status(Prom_node *node, char *status)
{
void *value;
if (status == NULL)
return (0);
/* search the local node */
if ((value = get_prop_val(find_prop(node, "status"))) != NULL) {
if ((value != NULL) && strstr((char *)value, status))
return (1);
}
return (0);
}
/*
* Get a property's value. Must be void * since the property can
* be any data type. Caller must know the *PROPER* way to use this
* data.
*/
void *
get_prop_val(Prop *prop)
{
if (prop == NULL)
return (NULL);
if (prop->value.opp.holds_array)
return ((void *)(prop->value.opp.oprom_array));
else
return ((void *)(&prop->value.opp.oprom_node[0]));
} /* end of get_prop_val() */
/*
* Search a Prom node and retrieve the property with the correct
* name.
*/
Prop *
find_prop(Prom_node *pnode, char *name)
{
Prop *prop;
if (pnode == NULL) {
return (NULL);
}
if (pnode->props == NULL) {
(void) printf("%s", dgettext(TEXT_DOMAIN, "Prom node has "
"no properties\n"));
return (NULL);
}
prop = pnode->props;
while ((prop != NULL) && (strcmp(prop->name.opp.oprom_array, name)))
prop = prop->next;
return (prop);
}
/*
* This function adds a board node to the board structure where that
* that node's physical component lives.
*/
void
add_node(Sys_tree *root, Prom_node *pnode)
{
int board;
Board_node *bnode;
Prom_node *p;
/* add this node to the Board list of the appropriate board */
if ((board = get_board_num(pnode)) == -1) {
/* board is 0 if not on Sunfire */
board = 0;
}
/* find the node with the same board number */
if ((bnode = find_board(root, board)) == NULL) {
bnode = insert_board(root, board);
bnode->board_type = UNKNOWN_BOARD;
}
/* now attach this prom node to the board list */
/* Insert this node at the end of the list */
pnode->sibling = NULL;
if (bnode->nodes == NULL)
bnode->nodes = pnode;
else {
p = bnode->nodes;
while (p->sibling != NULL)
p = p->sibling;
p->sibling = pnode;
}
}
/*
* Find the device on the current board with the requested device ID
* and name. If this rountine is passed a NULL pointer, it simply returns
* NULL.
*/
Prom_node *
find_device(Board_node *board, int id, char *name)
{
Prom_node *pnode;
int mask;
/* find the first cpu node */
pnode = dev_find_node(board->nodes, name);
mask = 0x1F;
while (pnode != NULL) {
if ((get_id(pnode) & mask) == id)
return (pnode);
pnode = dev_next_node(pnode, name);
}
return (NULL);
}
Prom_node *
dev_find_node_by_type(Prom_node *root, char *type, char *property)
{
Prom_node *node;
char *type_prop;
if (root == NULL || property == NULL)
return (NULL);
type_prop = (char *)get_prop_val(find_prop(root, type));
if (type_prop != NULL) {
if (strcmp(type_prop, property) == 0) {
return (root);
}
}
/* look at your children first */
if ((node = dev_find_node_by_type(root->child, type,
property)) != NULL)
return (node);
/* now look at your siblings */
if ((node = dev_find_node_by_type(root->sibling, type,
property)) != NULL)
return (node);
return (NULL); /* not found */
}
Prom_node *
dev_next_node_by_type(Prom_node *root, char *type, char *property)
{
Prom_node *node;
if (root == NULL || property == NULL)
return (NULL);
/* look at your children first */
if ((node = dev_find_node_by_type(root->child, type,
property)) != NULL)
return (node);
/* now look at your siblings */
if ((node = dev_find_node_by_type(root->sibling, type,
property)) != NULL)
return (node);
/* now look at papa's siblings */
if ((node = dev_find_node_by_type(root->parent->sibling,
type, property)) != NULL)
return (node);
return (NULL); /* not found */
}
/*
* Do a depth-first walk of a device tree and
* return the first node with the matching compatible.
*/
Prom_node *
dev_find_node_by_compatible(Prom_node *root, char *compatible)
{
Prom_node *node;
Prop *prop;
char *compatible_array;
int size, nbytes;
if (root == NULL || compatible == NULL)
return (NULL);
if ((prop = find_prop(root, "compatible")) != NULL &&
(compatible_array = (char *)get_prop_val(prop)) != NULL) {
/*
* The Prop structure returned by find_prop() is supposed
* to contain an indication of how big the value of the
* compatible property is. Since it is an array of strings
* this is our only means of determining just how many
* strings might be in this property. However, this size
* is often left as zero even though there is at least one
* string present. When this is the case, all we can do
* is examine the first string in the compatible property.
*/
for (size = prop->size; size >= 0; size -= nbytes) {
if (strcmp(compatible_array, compatible) == 0)
return (root); /* found a match */
nbytes = strlen(compatible_array) + 1;
compatible_array += nbytes;
}
}
node = dev_find_node_by_compatible(root->child, compatible);
if (node != NULL)
return (node);
/*
* Note the very deliberate use of tail recursion here. A good
* compiler (such as Sun's) will recognize this and generate code
* that does not allocate another stack frame. Instead, it will
* overlay the existing stack frame with the new one, the only change
* having been to replace the original root with its sibling.
* This has the potential to create some confusion for anyone
* trying to debug this code from a core dump, since the stack
* trace will not reveal recursion on siblings, only on children.
*/
return (dev_find_node_by_compatible(root->sibling, compatible));
}
/*
* Start from the current node and return the next node besides
* the current one which has the requested compatible property.
*/
Prom_node *
dev_next_node_by_compatible(Prom_node *root, char *compatible)
{
Prom_node *node;
if (root == NULL || compatible == NULL)
return (NULL);
node = dev_find_node_by_compatible(root->child, compatible);
if (node != NULL)
return (node);
/*
* More tail recursion. Even though it is a different function,
* this will overlay the current stack frame. Caveat exterminator.
*/
node = dev_find_node_by_compatible(root->sibling, compatible);
if (node != NULL)
return (node);
return (dev_find_node_by_compatible(root->parent->sibling, compatible));
}