2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A#include <unistd.h>
2N/A#include <sys/systeminfo.h>
2N/A#include <sys/utsname.h>
2N/A#include <sys/openpromio.h>
2N/A#include <libdevinfo.h>
2N/A
2N/A#include "pdevinfo.h"
2N/A#include "pdevinfo_sun4u.h"
2N/A#include "display.h"
2N/A#include "display_sun4u.h"
2N/A#include "libprtdiag.h"
2N/A
2N/A/*
2N/A * This file contains the functions that are to be called when
2N/A * a platform wants to use libdevinfo for it's device information
2N/A * instead of OBP. This will allow prtdiag to support hot-plug
2N/A * events on platforms whose OBP doesn't get updated to reflect
2N/A * the hot-plug changes to the system.
2N/A */
2N/A
2N/Aint do_devinfo(int syserrlog, char *pgname, int log_flag,
2N/A int prt_flag);
2N/Astatic void dump_di_node(Prom_node *pnode, di_node_t di_node);
2N/Astatic Prom_node *walk_di_tree(Sys_tree *tree, Prom_node *root,
2N/A di_node_t di_node);
2N/Astatic int match_compatible_name(char *, int, char *);
2N/A
2N/A/*
2N/A * Global variables
2N/A */
2N/Adi_prom_handle_t ph; /* Handle for using di_prom interface */
2N/Aextern char *progname;
2N/A
2N/A
2N/A
2N/A/*
2N/A * Used instead of the walk() function when a platform wants to
2N/A * walk libdevinfo's device tree instead of walking OBP's
2N/A * device tree.
2N/A */
2N/Astatic Prom_node*
2N/Awalk_di_tree(Sys_tree *tree, Prom_node *root, di_node_t di_node)
2N/A{
2N/A di_node_t curnode;
2N/A Prom_node *pnode;
2N/A char *name, *type, *model, *compatible_array;
2N/A int board_node = 0;
2N/A int *int_val;
2N/A int portid;
2N/A int is_schizo = 0, n_names;
2N/A
2N/A /* allocate a node for this level */
2N/A if ((pnode = (Prom_node *) malloc(sizeof (struct prom_node))) ==
2N/A NULL) {
2N/A perror("malloc");
2N/A exit(2);
2N/A }
2N/A
2N/A /* assign parent Prom_node */
2N/A pnode->parent = root;
2N/A pnode->sibling = NULL;
2N/A pnode->child = NULL;
2N/A
2N/A /* read properties for this node */
2N/A dump_di_node(pnode, di_node);
2N/A
2N/A name = get_node_name(pnode);
2N/A type = get_node_type(pnode);
2N/A if (type == NULL)
2N/A type = "";
2N/A model = (char *)get_prop_val(find_prop(pnode, "model"));
2N/A if (model == NULL)
2N/A model = "";
2N/A
2N/A /*
2N/A * For identifying Schizo nodes we need to check if the
2N/A * compatible property contains the string 'pci108e,8001'.
2N/A * This property contains an array of strings so we need
2N/A * search all strings.
2N/A */
2N/A if ((n_names = di_compatible_names(di_node, &compatible_array)) > 0) {
2N/A if (match_compatible_name(compatible_array, n_names,
2N/A "pci108e,8001"))
2N/A is_schizo = 1;
2N/A }
2N/A
2N/A if (int_val = (int *)get_prop_val(find_prop(pnode, "portid")))
2N/A portid = *int_val;
2N/A else if ((strcmp(type, "cpu") == 0) &&
2N/A (int_val = (int *)get_prop_val(find_prop(pnode->parent, "portid"))))
2N/A portid = *int_val;
2N/A else
2N/A portid = -1;
2N/A
2N/A#ifdef DEBUG
2N/A if (name != NULL)
2N/A printf("name=%s\n", name);
2N/A if (type != NULL)
2N/A printf("type=%s\n", type);
2N/A if (model != NULL)
2N/A printf("model=%s\n", model);
2N/A printf("portid=%d\n", portid);
2N/A#endif
2N/A
2N/A if (name != NULL) {
2N/A if (has_board_num(pnode)) {
2N/A add_node(tree, pnode);
2N/A board_node = 1;
2N/A D_PRINTF("\n---\nnodename = %s [ %s ] \n",
2N/A di_node_name(di_node), di_devfs_path(di_node));
2N/A D_PRINTF("ADDED BOARD name=%s type=%s model=%s "
2N/A "portid =%d\n", name, type, model, portid);
2N/A } else if ((strcmp(name, FFB_NAME) == 0) ||
2N/A (strcmp(type, "cpu") == 0) ||
2N/A
2N/A ((strcmp(type, "memory-controller") == 0) &&
2N/A (strcmp(name, "ac") != 0)) ||
2N/A
2N/A ((strcmp(name, "pci") == 0) &&
2N/A (strcmp(model, "SUNW,psycho") == 0)) ||
2N/A
2N/A ((strcmp(name, "pci") == 0) &&
2N/A (strcmp(model, "SUNW,sabre") == 0)) ||
2N/A
2N/A ((strcmp(name, "pci") == 0) && (is_schizo)) ||
2N/A
2N/A (strcmp(name, "counter-timer") == 0) ||
2N/A (strcmp(name, "sbus") == 0)) {
2N/A add_node(tree, pnode);
2N/A board_node = 1;
2N/A D_PRINTF("\n---\nnodename = %s [ %s ] \n",
2N/A di_node_name(di_node), di_devfs_path(di_node));
2N/A D_PRINTF("ADDED BOARD name=%s type=%s model=%s\n",
2N/A name, type, model);
2N/A }
2N/A } else {
2N/A D_PRINTF("node not added: type=%s portid =%d\n", type, portid);
2N/A }
2N/A
2N/A if (curnode = di_child_node(di_node)) {
2N/A pnode->child = walk_di_tree(tree, pnode, curnode);
2N/A }
2N/A
2N/A if (curnode = di_sibling_node(di_node)) {
2N/A if (board_node) {
2N/A return (walk_di_tree(tree, root, curnode));
2N/A } else {
2N/A pnode->sibling = walk_di_tree(tree, root, curnode);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * This check is needed in case the "board node" occurs at the
2N/A * end of the sibling chain as opposed to the middle or front.
2N/A */
2N/A if (board_node)
2N/A return (NULL);
2N/A
2N/A return (pnode);
2N/A}
2N/A
2N/A/*
2N/A * Dump all the devinfo properties and then the obp properties for
2N/A * the specified devinfo node into the Prom_node structure.
2N/A */
2N/Astatic void
2N/Adump_di_node(Prom_node *pnode, di_node_t di_node)
2N/A{
2N/A Prop *prop = NULL; /* tail of properties list */
2N/A
2N/A Prop *temp; /* newly allocated property */
2N/A di_prop_t di_prop;
2N/A di_prom_prop_t p_prop;
2N/A int retval = 0;
2N/A int i;
2N/A
2N/A /* clear out pointers in pnode */
2N/A pnode->props = NULL;
2N/A
2N/A D_PRINTF("\n\n ------- Dumping devinfo properties for node ------\n");
2N/A
2N/A /*
2N/A * get all the devinfo properties first
2N/A */
2N/A for (di_prop = di_prop_next(di_node, DI_PROP_NIL);
2N/A di_prop != DI_PROP_NIL;
2N/A di_prop = di_prop_next(di_node, di_prop)) {
2N/A
2N/A char *di_name;
2N/A void *di_data;
2N/A int di_ptype;
2N/A
2N/A di_name = di_prop_name(di_prop);
2N/A if (di_name == (char *)NULL)
2N/A continue;
2N/A
2N/A di_ptype = di_prop_type(di_prop);
2N/A D_PRINTF("DEVINFO Properties %s: ", di_name);
2N/A
2N/A switch (di_ptype) {
2N/A int *int_val;
2N/A char *char_val;
2N/A case DI_PROP_TYPE_INT:
2N/A retval = di_prop_lookup_ints(DDI_DEV_T_ANY,
2N/A di_node, di_name, (int **)&di_data);
2N/A if (retval > 0) {
2N/A int_val = (int *)di_data;
2N/A D_PRINTF("0x%x\n", *int_val);
2N/A }
2N/A break;
2N/A case DI_PROP_TYPE_STRING:
2N/A retval = di_prop_lookup_strings(DDI_DEV_T_ANY,
2N/A di_node, di_name, (char **)&di_data);
2N/A if (retval > 0) {
2N/A char_val = (char *)di_data;
2N/A D_PRINTF("%s\n", char_val);
2N/A }
2N/A break;
2N/A case DI_PROP_TYPE_BYTE:
2N/A retval = di_prop_lookup_bytes(DDI_DEV_T_ANY,
2N/A di_node, di_name, (uchar_t **)&di_data);
2N/A if (retval > 0) {
2N/A char_val = (char *)di_data;
2N/A D_PRINTF("%s\n", char_val);
2N/A }
2N/A break;
2N/A case DI_PROP_TYPE_UNKNOWN:
2N/A retval = di_prop_lookup_bytes(DDI_DEV_T_ANY,
2N/A di_node, di_name, (uchar_t **)&di_data);
2N/A if (retval > 0) {
2N/A char_val = (char *)di_data;
2N/A D_PRINTF("%s\n", char_val);
2N/A }
2N/A break;
2N/A case DI_PROP_TYPE_BOOLEAN:
2N/A di_data = NULL;
2N/A retval = 1;
2N/A break;
2N/A default:
2N/A D_PRINTF(" Skipping property\n");
2N/A retval = -1;
2N/A }
2N/A
2N/A if (retval <= 0)
2N/A continue;
2N/A
2N/A /* allocate space for the property */
2N/A if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) {
2N/A perror("malloc");
2N/A exit(1);
2N/A }
2N/A
2N/A /*
2N/A * Given that we're using libdevinfo rather than OBP,
2N/A * the chances are that future accesses to di_name and
2N/A * di_data will be via temp->name.val_ptr and
2N/A * temp->value.val_ptr respectively. However, this may
2N/A * not be the case, so we have to suitably fill in
2N/A * temp->name.opp and temp->value.opp.
2N/A *
2N/A * di_name is char * and non-NULL if we've made it to
2N/A * here, so we can simply point
2N/A * temp->name.opp.oprom_array to temp->name.val_ptr.
2N/A *
2N/A * di_data could be NULL, char * or int * at this point.
2N/A * If it's non-NULL, a 1st char of '\0' indicates int *.
2N/A * We thus set temp->value.opp.oprom_node[] (although
2N/A * interest in any element other than 0 is rare, all
2N/A * elements must be set to ensure compatibility with
2N/A * OBP), and holds_array is set to 0.
2N/A *
2N/A * If di_data is NULL, or the 1st char is not '\0', we set
2N/A * temp->value.opp.oprom_array. If di_ptype is
2N/A * DI_PROP_TYPE_BOOLEAN, holds_array is set to 0, else it
2N/A * is set to 1.
2N/A */
2N/A temp->name.val_ptr = (void *)di_name;
2N/A temp->name.opp.oprom_array = temp->name.val_ptr;
2N/A temp->name.opp.holds_array = 1;
2N/A
2N/A temp->value.val_ptr = (void *)di_data;
2N/A if ((di_data != NULL) && (*((char *)di_data) == '\0')) {
2N/A for (i = 0; i < OPROM_NODE_SIZE; i++)
2N/A temp->value.opp.oprom_node[i] = *((int *)di_data+i);
2N/A
2N/A temp->value.opp.holds_array = 0;
2N/A } else {
2N/A temp->value.opp.oprom_array = temp->value.val_ptr;
2N/A if (di_ptype == DI_PROP_TYPE_BOOLEAN)
2N/A temp->value.opp.holds_array = 0;
2N/A else
2N/A temp->value.opp.holds_array = 1;
2N/A }
2N/A
2N/A temp->size = retval;
2N/A
2N/A /* everything worked so link the property list */
2N/A if (pnode->props == NULL)
2N/A pnode->props = temp;
2N/A else if (prop != NULL)
2N/A prop->next = temp;
2N/A prop = temp;
2N/A prop->next = NULL;
2N/A }
2N/A
2N/A /*
2N/A * Then get all the OBP properties.
2N/A */
2N/A for (p_prop = di_prom_prop_next(ph, di_node, DI_PROM_PROP_NIL);
2N/A p_prop != DI_PROM_PROP_NIL;
2N/A p_prop = di_prom_prop_next(ph, di_node, p_prop)) {
2N/A
2N/A char *p_name;
2N/A unsigned char *p_data;
2N/A
2N/A p_name = di_prom_prop_name(p_prop);
2N/A if (p_name == (char *)NULL)
2N/A retval = -1;
2N/A else
2N/A retval = di_prom_prop_data(p_prop, &p_data);
2N/A
2N/A if (retval <= 0)
2N/A continue;
2N/A
2N/A /* allocate space for the property */
2N/A if ((temp = (Prop *) malloc(sizeof (Prop))) == NULL) {
2N/A perror("malloc");
2N/A exit(1);
2N/A }
2N/A
2N/A /*
2N/A * As above, p_name is char * and non-NULL if we've made
2N/A * it to here, so we can simply point
2N/A * temp->name.opp.oprom_array to temp->name.val_ptr.
2N/A *
2N/A * p_data could be NULL, a character or a number at this
2N/A * point. If it's non-NULL, a 1st char of '\0' indicates a
2N/A * number, so we set temp->value.opp.oprom_node[] (again
2N/A * setting every element to ensure OBP compatibility).
2N/A * These assignments create a lint error, hence the LINTED
2N/A * comment.
2N/A *
2N/A * If p_data is NULL, or the 1st char is not '\0', we set
2N/A * temp->value.opp.oprom_array.
2N/A */
2N/A temp->name.val_ptr = (void *)p_name;
2N/A temp->name.opp.oprom_array = temp->name.val_ptr;
2N/A temp->name.opp.holds_array = 1;
2N/A
2N/A temp->value.val_ptr = (void *)p_data;
2N/A if ((p_data != NULL) && (*p_data == '\0')) {
2N/A for (i = 0; i < OPROM_NODE_SIZE; i++)
2N/A /* LINTED */
2N/A temp->value.opp.oprom_node[i] = *((int *)p_data+i);
2N/A
2N/A temp->value.opp.holds_array = 0;
2N/A } else {
2N/A temp->value.opp.oprom_array = temp->value.val_ptr;
2N/A temp->value.opp.holds_array = 1;
2N/A }
2N/A
2N/A temp->size = retval;
2N/A
2N/A /* everything worked so link the property list */
2N/A if (pnode->props == NULL) {
2N/A pnode->props = temp;
2N/A } else if (prop != NULL) {
2N/A prop->next = temp;
2N/A }
2N/A prop = temp;
2N/A prop->next = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Used in place of do_prominfo() when a platform wants to use
2N/A * libdevinfo for getting the device tree instead of OBP.
2N/A */
2N/Aint
2N/Ado_devinfo(int syserrlog, char *pgname, int log_flag, int prt_flag)
2N/A{
2N/A Sys_tree sys_tree; /* system information */
2N/A Prom_node *root_node; /* root node of OBP device tree */
2N/A di_node_t di_root_node; /* root of the devinfo tree */
2N/A struct system_kstat_data sys_kstat; /* kstats for non-OBP data */
2N/A int retval = -1;
2N/A
2N/A /* set the global flags */
2N/A progname = pgname;
2N/A logging = log_flag;
2N/A print_flag = prt_flag;
2N/A
2N/A /* set the the system tree fields */
2N/A sys_tree.sys_mem = NULL;
2N/A sys_tree.boards = NULL;
2N/A sys_tree.bd_list = NULL;
2N/A sys_tree.board_cnt = 0;
2N/A
2N/A /*
2N/A * create a snapshot of the kernel device tree
2N/A * and return a handle to it.
2N/A */
2N/A if ((di_root_node = di_init("/", DINFOCPYALL)) == DI_NODE_NIL) {
2N/A exit(_error("di_init() failed"));
2N/A }
2N/A
2N/A /*
2N/A * create a handle to the PROM device tree.
2N/A */
2N/A if ((ph = di_prom_init()) == NULL) {
2N/A exit(_error("di_prom_init() failed"));
2N/A }
2N/A
2N/A /*
2N/A * walk the devinfo tree and build up a list of all
2N/A * nodes and properties.
2N/A */
2N/A root_node = walk_di_tree(&sys_tree, NULL, di_root_node);
2N/A
2N/A /* resolve the board types now */
2N/A resolve_board_types(&sys_tree);
2N/A
2N/A read_sun4u_kstats(&sys_tree, &sys_kstat);
2N/A retval = display(&sys_tree, root_node, &sys_kstat, syserrlog);
2N/A
2N/A di_fini(di_root_node);
2N/A di_prom_fini(ph);
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * check to see if the name shows up in the compatible array
2N/A */
2N/Astatic int
2N/Amatch_compatible_name(char *compatible_array, int n_names, char *name)
2N/A{
2N/A int i, ret = 0;
2N/A
2N/A /* parse the compatible list */
2N/A for (i = 0; i < n_names; i++) {
2N/A if (strcmp(compatible_array, name) == 0) {
2N/A ret = 1;
2N/A break;
2N/A }
2N/A compatible_array += strlen(compatible_array) + 1;
2N/A }
2N/A return (ret);
2N/A}