/*
* 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
* 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 (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Subroutines used by various components of the Sun4v PI enumerator
*/
#include <sys/types.h>
#include <sys/systeminfo.h>
#include <sys/utsname.h>
#include <string.h>
#include <strings.h>
#include <sys/fm/protocol.h>
#include <fm/topo_mod.h>
#include <fm/topo_hc.h>
#include <sys/mdesc.h>
#include <libnvpair.h>
#include "pi_impl.h"
static const topo_pgroup_info_t sys_pgroup = {
TOPO_PGROUP_SYSTEM,
TOPO_STABILITY_PRIVATE,
TOPO_STABILITY_PRIVATE,
1
};
static const topo_pgroup_info_t auth_pgroup = {
FM_FMRI_AUTHORITY,
TOPO_STABILITY_PRIVATE,
TOPO_STABILITY_PRIVATE,
1
};
/*
* Search the PRI for MDE nodes using md_scan_dag. Using this routine
* consolodates similar searches used in a few places within the sun4vpi
* enumerator.
*
* The routine returns the number of nodes found, or -1. If the node array
* is non-NULL on return, then it must be freed:
* topo_mod_free(mod, nodes, nsize);
*
*/
int
pi_find_mdenodes(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_start,
char *type_str, char *arc_str, mde_cookie_t **nodes, size_t *nsize)
{
int result;
int total_mdenodes;
mde_str_cookie_t start_cookie;
mde_str_cookie_t arc_cookie;
/* Prepare to scan the PRI using the start string and given arc */
total_mdenodes = md_node_count(mdp);
start_cookie = md_find_name(mdp, type_str);
arc_cookie = md_find_name(mdp, arc_str);
/* Allocate an array to hold the results of the scan */
*nsize = sizeof (mde_cookie_t) * total_mdenodes;
*nodes = topo_mod_zalloc(mod, *nsize);
if (*nodes == NULL) {
/* We have no memory. Set an error code and return failure */
*nsize = 0;
(void) topo_mod_seterrno(mod, EMOD_NOMEM);
return (-1);
}
result = md_scan_dag(mdp, mde_start, start_cookie, arc_cookie, *nodes);
if (result <= 0) {
/* No nodes found. Free the node array before returning */
topo_mod_free(mod, *nodes, *nsize);
*nodes = NULL;
*nsize = 0;
}
return (result);
}
/*
* Determine if this node should be skipped by finding the topo-skip property.
*/
int
pi_skip_node(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
uint64_t skip;
if (mod == NULL || mdp == NULL) {
/*
* These parameters are required. Tell the caller to skip
* all nodes.
*/
return (1);
}
skip = 0; /* do not skip by default */
result = md_get_prop_val(mdp, mde_node, MD_STR_TOPO_SKIP, &skip);
if (result != 0) {
/*
* There is no topo-skip property. Assume we are not skipping
* the mde node.
*/
skip = 0;
}
/*
* If skip is present and non-zero we want to skip this node. We
* return 1 to indicate this.
*/
if (skip != 0) {
return (1);
}
return (0);
}
/*
* Build a device path without device names (PRI like) from a devinfo
* node. Return the PRI like path.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_dipath(topo_mod_t *mod, di_node_t p_dnode)
{
int i, j, rv;
int depth = 0;
char **bus_addr = NULL;
char **dev_path = NULL;
char *path = NULL;
char *di_path = NULL;
size_t path_len = 0;
boolean_t depth_found = B_FALSE;
di_node_t dnode = p_dnode;
again:
/* loop through collecting bus addresses */
do {
/* stop at '/' */
di_path = di_devfs_path(dnode);
if (strcmp(di_path, "/") == 0) {
di_devfs_path_free(di_path);
break;
}
di_devfs_path_free(di_path);
if (depth_found) {
bus_addr[depth] = topo_mod_strdup(mod,
di_bus_addr(dnode));
}
++depth;
} while ((dnode = di_parent_node(dnode)) != DI_NODE_NIL);
if (!depth_found) {
bus_addr = (char **)topo_mod_zalloc(mod,
sizeof (char *) * depth);
dev_path = (char **)topo_mod_zalloc(mod,
sizeof (char *) * depth);
if (bus_addr == NULL || dev_path == NULL) {
topo_mod_dprintf(mod, "pi_get_dipath: no memory\n");
goto out;
}
depth_found = B_TRUE;
dnode = p_dnode;
depth = 0;
goto again;
}
/* prepend '/@' to each bus address */
for (i = (depth - 1), j = 0; i >= 0; --i, j++) {
int len = strlen(bus_addr[i]) + strlen("/@") + 1;
path_len += len;
dev_path[j] = (char *)topo_mod_alloc(mod, len);
rv = snprintf(dev_path[j], len, "/@%s", bus_addr[i]);
if (rv < 0) {
topo_mod_dprintf(mod,
"pi_get_dipath: snprintf failed\n");
goto out;
}
}
/*
* Build the path from the bus addresses.
*/
path_len -= (depth - 1); /* leave room for one null char */
path = (char *)topo_mod_alloc(mod, path_len);
if (path == NULL) {
topo_mod_dprintf(mod, "pi_get_dipath: no memory for path\n");
goto out;
}
path = strcpy(path, dev_path[0]);
for (i = 1; i < depth; i++) {
path = strncat(path, dev_path[i], strlen(dev_path[i]) + 1);
}
topo_mod_dprintf(mod, "pi_get_dipath: path (%s)\n", path);
out:
for (i = 0; i < depth; i++) {
if (bus_addr[i] != NULL) {
topo_mod_strfree(mod, bus_addr[i]);
}
if (dev_path[i] != NULL) {
topo_mod_strfree(mod, dev_path[i]);
}
}
if (bus_addr != NULL) {
topo_mod_free(mod, bus_addr, sizeof (char *) * depth);
}
if (dev_path != NULL) {
topo_mod_free(mod, dev_path, sizeof (char *) * depth);
}
return (path);
}
/*
* Get the product serial number (the ID as far as the topo authority is
* concerned) either from the current node, if it is of type 'product', or
* search for a product node in the PRI.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_productsn(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
int idx;
int num_nodes;
char *id = NULL;
char *type;
size_t size;
mde_cookie_t *nodes = NULL;
topo_mod_dprintf(mod, "pi_get_productsn: enter\n");
result = md_get_prop_str(mdp, mde_node, MD_STR_TYPE, &type);
if (result == 0 && strcmp(type, MD_STR_PRODUCT) == 0) {
/*
* This is a product node. We need only search for the serial
* number property on this node to return the ID.
*/
result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER,
&id);
if (result != 0 || id == NULL || strlen(id) == 0)
return (NULL);
topo_mod_dprintf(mod, "pi_get_productsn: product-sn = %s\n",
id);
return (topo_mod_strdup(mod, id));
}
/*
* Search the PRI for nodes of type MD_STR_COMPONENT and find the
* first element with type of MD_STR_PRODUCT. This node
* will contain the MD_STR_SERIAL_NUMBER property to use as the
* product-sn.
*/
num_nodes = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
MD_STR_COMPONENT, MD_STR_FWD, &nodes, &size);
if (num_nodes <= 0 || nodes == NULL) {
/* We did not find any component nodes */
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_productsn: found %d %s nodes\n",
num_nodes, MD_STR_COMPONENT);
idx = 0;
while (id == NULL && idx < num_nodes) {
result = md_get_prop_str(mdp, nodes[idx], MD_STR_TYPE, &type);
if (result == 0 && strcmp(type, MD_STR_PRODUCT) == 0) {
/*
* This is a product node. Get the serial number
* property from the node.
*/
result = md_get_prop_str(mdp, nodes[idx],
MD_STR_SERIAL_NUMBER, &id);
if (result != 0)
topo_mod_dprintf(mod, "pi_get_productsn: "
"failed to read %s from node_0x%llx\n",
MD_STR_SERIAL_NUMBER,
(uint64_t)nodes[idx]);
}
/* Search the next node, if necessary */
idx++;
}
topo_mod_free(mod, nodes, size);
/* Everything is freed up and it's time to return the product-sn */
if (result != 0 || id == NULL || strlen(id) == 0) {
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_productsn: product-sn %s\n", id);
return (topo_mod_strdup(mod, id));
}
/*
* Get the chassis serial number (the ID as far as the topo authority is
* concerned) either from the current node, if it is of type 'chassis', or
* search for a chassis node in the PRI.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_chassisid(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
int idx;
int num_nodes;
char *id = NULL;
char *hc_name = NULL;
size_t chassis_size;
mde_cookie_t *chassis_nodes = NULL;
topo_mod_dprintf(mod, "pi_get_chassis: enter\n");
hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
if (hc_name != NULL && strcmp(hc_name, MD_STR_CHASSIS) == 0) {
topo_mod_strfree(mod, hc_name);
/*
* This is a chassis node. We need only search for the serial
* number property on this node to return the ID.
*/
result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER,
&id);
if (result != 0 || id == NULL || strlen(id) == 0) {
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_chassis: chassis-id = %s\n", id);
return (topo_mod_strdup(mod, id));
}
if (hc_name != NULL) {
topo_mod_strfree(mod, hc_name);
}
/*
* Search the PRI for nodes of type MD_STR_COMPONENT and find the
* first element with topo-hc-type of MD_STR_CHASSIS. This node
* will contain the MD_STR_SERIAL_NUMBER property to use as the
* chassis-id.
*/
num_nodes = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
MD_STR_COMPONENT, MD_STR_FWD, &chassis_nodes, &chassis_size);
if (num_nodes <= 0 || chassis_nodes == NULL) {
/* We did not find any chassis nodes */
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_chassisid: found %d %s nodes\n",
num_nodes, MD_STR_COMPONENT);
idx = 0;
while (id == NULL && idx < num_nodes) {
hc_name = pi_get_topo_hc_name(mod, mdp, chassis_nodes[idx]);
if (hc_name != NULL && strcmp(hc_name, MD_STR_CHASSIS) == 0) {
/*
* This is a chassis node. Get the serial number
* property from the node.
*/
result = md_get_prop_str(mdp, chassis_nodes[idx],
MD_STR_SERIAL_NUMBER, &id);
if (result != 0) {
topo_mod_dprintf(mod, "pi_get_chassisid: "
"failed to read %s from node_0x%llx\n",
MD_STR_SERIAL_NUMBER,
(uint64_t)chassis_nodes[idx]);
}
}
topo_mod_strfree(mod, hc_name);
/* Search the next node, if necessary */
idx++;
}
topo_mod_free(mod, chassis_nodes, chassis_size);
/* Everything is freed up and it's time to return the chassis-sn */
if (result != 0 || id == NULL || strlen(id) == 0) {
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_chassis: chassis-id %s\n", id);
return (topo_mod_strdup(mod, id));
}
/*
* Determine if the node is a FRU by checking for the existance and non-zero
* value of the 'fru' property on the mde node.
*/
int
pi_get_fru(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, int *is_fru)
{
int result;
uint64_t fru;
if (mod == NULL || mdp == NULL || is_fru == NULL) {
return (-1);
}
fru = 0;
*is_fru = 0;
result = md_get_prop_val(mdp, mde_node, MD_STR_FRU, &fru);
if (result != 0) {
/* The node is not a FRU. */
return (-1);
}
if (fru != 0) {
*is_fru = 1;
}
return (0);
}
/*
* Get the id property value from the given PRI node
*/
int
pi_get_instance(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
topo_instance_t *ip)
{
int result;
uint64_t id;
id = 0;
result = md_get_prop_val(mdp, mde_node, MD_STR_ID, &id);
if (result != 0) {
/*
* There is no id property.
*/
topo_mod_dprintf(mod, "node_0x%llx has no id property\n",
(uint64_t)mde_node);
return (-1);
}
*ip = id;
return (0);
}
/*
* If the given MDE node is a FRU return the 'nac' property, if it exists,
* to use as the label.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_label(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
int is_fru;
char *lp = NULL;
char *hc_name = pi_get_topo_hc_name(mod, mdp, mde_node);
/*
* The disk enumerator will set the "bay" node as a FRU and
* expect the label from the PRI. The "fru" property can not
* be set because hostconfig has no way to set the S/N, P/N,
* etc.. in the PRI.
*/
if (strncmp(hc_name, BAY, strlen(BAY)) != 0) {
result = pi_get_fru(mod, mdp, mde_node, &is_fru);
if (result != 0 || is_fru == 0) {
/* This node is not a FRU. It has no label */
topo_mod_strfree(mod, hc_name);
return (NULL);
}
}
topo_mod_strfree(mod, hc_name);
/*
* The node is a FRU. Get the NAC name to use as a label.
*/
result = md_get_prop_str(mdp, mde_node, MD_STR_NAC, &lp);
if (result != 0 || lp == NULL || strlen(lp) == 0) {
/* No NAC label. Return NULL */
return (NULL);
}
/* Return a copy of the label */
return (topo_mod_strdup(mod, lp));
}
/*
* Return the "lun" property.
*/
int
pi_get_lun(topo_mod_t *mod, di_node_t node)
{
int lun;
int *buf;
unsigned char *chbuf;
di_prop_t di_prop = DI_PROP_NIL;
di_path_t dpath = DI_PATH_NIL;
di_path_prop_t di_path_prop = DI_PROP_NIL;
/* look for pathinfo property */
while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
while ((di_path_prop =
di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
if (strcmp("lun",
di_path_prop_name(di_path_prop)) == 0) {
(void) di_path_prop_ints(di_path_prop, &buf);
bcopy(buf, &lun, sizeof (int));
goto found;
}
}
}
/* look for devinfo property */
for (di_prop = di_prop_next(node, DI_PROP_NIL);
di_prop != DI_PROP_NIL;
di_prop = di_prop_next(node, di_prop)) {
if (strncmp("lun", di_prop_name(di_prop),
strlen(di_prop_name(di_prop))) == 0) {
if (di_prop_bytes(di_prop, &chbuf) < sizeof (uint_t)) {
continue;
}
bcopy(chbuf, &lun, sizeof (uint_t));
goto found;
}
}
if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
di_path_prop == DI_PROP_NIL)) {
return (-1);
}
found:
topo_mod_dprintf(mod, "pi_get_lun: lun = (%d)\n", lun);
return (lun);
}
/*
* Return the complete part number string to the caller. The complete part
* number is made up of the part number attribute concatenated with the dash
* number attribute of the mde node.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_part(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
size_t bufsize;
char *buf = NULL;
char *part = NULL;
char *dash = NULL;
result = md_get_prop_str(mdp, mde_node, MD_STR_PART_NUMBER, &part);
if (result != 0) {
part = NULL;
}
result = md_get_prop_str(mdp, mde_node, MD_STR_DASH_NUMBER, &dash);
if (result != 0) {
dash = NULL;
}
bufsize = 1 + (part ? strlen(part) : 0) + (dash ? strlen(dash) : 0);
if (bufsize == 1) {
return (NULL);
}
/* Construct the part number from the part and dash values */
buf = topo_mod_alloc(mod, bufsize);
if (buf != NULL) {
(void) snprintf(buf, bufsize, "%s%s", (part ? part : ""),
(dash ? dash : ""));
}
return (buf);
}
/*
* Return the path string to the caller.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_path(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
int i = 0;
size_t max_addrs = 0;
size_t path_len = 0;
size_t buf_len;
char *propbuf = NULL;
char *buf = NULL;
char *buffree = NULL;
char *path = NULL;
char *token;
char **dev_addr = NULL;
char **dev_path = NULL;
char *lastp;
/*
* Get the path property from PRI; should look something
* like "/@600/@0".
*/
result = md_get_prop_str(mdp, mde_node, MD_STR_PATH, &propbuf);
if (result != 0 || propbuf == NULL || strlen(propbuf) == 0) {
topo_mod_dprintf(mod, "pi_get_path: failed to get path\n");
goto out;
}
buf_len = strlen(propbuf) + 1;
buf = topo_mod_alloc(mod, buf_len);
if (buf == NULL) {
topo_mod_dprintf(mod, "pi_get_path: no memory\n");
goto out;
}
(void) strcpy(buf, propbuf);
/* count the path depth */
if ((token = strtok_r(buf, "/@", &lastp)) != NULL) {
++max_addrs;
while ((token = strtok_r(NULL, "/@", &lastp)) != NULL) {
++max_addrs;
}
}
if (max_addrs == 0) {
topo_mod_dprintf(mod, "pi_get_path: bad path\n");
goto out;
}
/* allocate pointers */
dev_addr = (char **)topo_mod_zalloc(mod, sizeof (char *) * max_addrs);
dev_path = (char **)topo_mod_zalloc(mod, sizeof (char *) * max_addrs);
if (dev_addr == NULL || dev_path == NULL) {
topo_mod_dprintf(mod, "pi_get_path: no path memory\n");
goto out;
}
/*
* Grab the address(es) from the path property.
*/
(void) strcpy(buf, propbuf);
buffree = buf; /* strtok_r is destructive */
if ((token = strtok_r(buf, "/@", &lastp)) != NULL) {
dev_addr[i] = topo_mod_strdup(mod, token);
while ((token = strtok_r(NULL, "/@", &lastp)) != NULL) {
++i;
dev_addr[i] = topo_mod_strdup(mod, token);
}
} else {
topo_mod_dprintf(mod, "pi_get_path: path wrong\n");
goto out;
}
/*
* Construct the path to look something like "/pci@600/pci@0".
*/
for (i = 0; i < max_addrs; i++) {
int len = strlen(dev_addr[i]) + strlen("/pci@") + 1;
path_len += len;
dev_path[i] = (char *)topo_mod_alloc(mod, len);
if (dev_path[i] == NULL) {
topo_mod_dprintf(mod,
"pi_get_path: no memory for dev_path[%d]\n", i);
goto out;
}
result = snprintf(dev_path[i], len, "/pci@%s", dev_addr[i]);
if (result < 0) {
topo_mod_dprintf(mod,
"pi_get_path: snprintf failed\n");
goto out;
}
}
path_len -= (i - 1); /* leave room for one null char */
path = (char *)topo_mod_alloc(mod, path_len);
path = strcpy(path, dev_path[0]);
/* put the parts together */
for (i = 1; i < max_addrs; i++) {
path = strncat(path, dev_path[i], strlen(dev_path[i]) + 1);
}
topo_mod_dprintf(mod, "pi_get_path: path = (%s)\n", path);
out:
/*
* Cleanup
*/
for (i = 0; i < max_addrs; i++) {
if (dev_addr[i] != NULL) {
topo_mod_free(mod, dev_addr[i],
strlen(dev_addr[i]) + 1);
}
if (dev_path[i] != NULL) {
topo_mod_free(mod, dev_path[i],
strlen(dev_path[i]) + 1);
}
}
if (dev_addr != NULL) {
topo_mod_free(mod, dev_addr, sizeof (char *) * max_addrs);
}
if (dev_path != NULL) {
topo_mod_free(mod, dev_path, sizeof (char *) * max_addrs);
}
if (buffree != NULL) {
topo_mod_free(mod, buffree, buf_len);
}
return (path);
}
/*
* Get the product ID and serial number from the 'platform' node in the PRI
*
* Strings must be freed with topo_mod_strfree()
*/
void
pi_get_platform(topo_mod_t *mod, md_t *mdp, char **name, char **serial)
{
int result;
char *id = NULL;
size_t platform_size;
mde_cookie_t *platform_nodes = NULL;
topo_mod_dprintf(mod, "pi_get_platform: enter\n");
/*
* Search the PRI for nodes of type MD_STR_PLATFORM, which contains
* the product-id in it's MD_STR_NAME property.
*/
result = pi_find_mdenodes(mod, mdp, MDE_INVAL_ELEM_COOKIE,
MD_STR_PLATFORM, MD_STR_FWD, &platform_nodes, &platform_size);
if (result <= 0 || platform_nodes == NULL) {
/* We did not find any platform nodes */
topo_mod_dprintf(mod, "pi_get_platform: no platform node\n");
return;
}
topo_mod_dprintf(mod, "pi_get_platform: found %d platform nodes\n",
result);
/*
* There should only be 1 platform node, so we will always
* use the first if we find any at all.
*/
result = md_get_prop_str(mdp, platform_nodes[0], MD_STR_NAME, &id);
if (result == 0 && id != NULL && strlen(id) != 0) {
*name = topo_mod_strdup(mod, id);
} else {
*name = NULL;
}
*serial = pi_get_serial(mod, mdp, platform_nodes[0]);
topo_mod_dprintf(mod, "pi_get_platform: name %s serial %s\n",
*name ? *name : "NULL", *serial ? *serial : "NULL");
/* free platform node(s) */
topo_mod_free(mod, platform_nodes, platform_size);
}
/*
* If the phy pointer is NULL just return the number of 'phy_number' properties
* from the PRI; otherwise pass the 'phy_number' property values back to the
* caller.
*
* The caller is responsible for managing allocated memory.
*/
int
pi_get_priphy(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, uint8_t *phyp)
{
int result;
uint8_t *phy_num;
int nphy;
result = md_get_prop_data(mdp, mde_node, MD_STR_PHY_NUMBER,
&phy_num, &nphy);
if (result != 0) {
/* no phy_number property */
topo_mod_dprintf(mod,
"node_0x%llx has no phy_number property\n",
(uint64_t)mde_node);
return (-1);
}
if (phyp != NULL) {
bcopy(phy_num, phyp, nphy);
}
return (nphy);
}
/*
* Return "phy-num" devinfo/pathinfo property.
*/
int
pi_get_phynum(topo_mod_t *mod, di_node_t node)
{
int phy;
int *buf;
unsigned char *chbuf;
di_prop_t di_prop = DI_PROP_NIL;
di_path_t dpath = DI_PATH_NIL;
di_path_prop_t di_path_prop = DI_PROP_NIL;
/* look for pathinfo property */
while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
while ((di_path_prop =
di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
if (strcmp("phy-num",
di_path_prop_name(di_path_prop)) == 0) {
(void) di_path_prop_ints(di_path_prop, &buf);
bcopy(buf, &phy, sizeof (int));
goto found;
}
}
}
/* look for devinfo property */
for (di_prop = di_prop_next(node, DI_PROP_NIL);
di_prop != DI_PROP_NIL;
di_prop = di_prop_next(node, di_prop)) {
if (strncmp("phy-num", di_prop_name(di_prop),
strlen("phy-num")) == 0) {
if (di_prop_bytes(di_prop, &chbuf) < sizeof (uint_t)) {
continue;
}
bcopy(chbuf, &phy, sizeof (uint_t));
goto found;
}
}
if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
di_path_prop == DI_PROP_NIL)) {
return (-1);
}
found:
topo_mod_dprintf(mod, "pi_get_phynum: phy = %d\n", phy);
return (phy);
}
/*
* Return the revision string to the caller.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_revision(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
char *rp = NULL;
result = md_get_prop_str(mdp, mde_node, MD_STR_REVISION_NUMBER, &rp);
if (result != 0 || rp == NULL || strlen(rp) == 0) {
return (NULL);
}
return (topo_mod_strdup(mod, rp));
}
/*
* Return the serial number string to the caller.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_serial(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
uint64_t sn;
char *sp = NULL;
char buf[MAXNAMELEN];
result = md_get_prop_str(mdp, mde_node, MD_STR_SERIAL_NUMBER, &sp);
if (result != 0 || sp == NULL || strlen(sp) == 0) {
/* Is this a uint64_t type serial number? */
result = md_get_prop_val(mdp, mde_node, MD_STR_SERIAL_NUMBER,
&sn);
if (result != 0) {
/* try "serial#" */
result = md_get_prop_val(mdp, mde_node,
"serial#", &sn);
if (result != 0) {
/* failed to find a serial number */
return (NULL);
}
}
topo_mod_dprintf(mod, "pi_get_serial: node_0x%llx numeric "
"serial number %llx\n", (uint64_t)mde_node, sn);
/* Convert the acquired value to a string */
result = snprintf(buf, sizeof (buf), "%llu", sn);
if (result < 0) {
return (NULL);
}
sp = buf;
}
topo_mod_dprintf(mod, "pi_get_serial: node_0x%llx = %s\n",
(uint64_t)mde_node, (sp == NULL ? "NULL" : sp));
return (topo_mod_strdup(mod, sp));
}
/*
* Get the server hostname (the ID as far as the topo authority is
* concerned) from sysinfo and return a copy to the caller.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_serverid(topo_mod_t *mod)
{
int result;
char hostname[MAXNAMELEN];
topo_mod_dprintf(mod, "pi_get_serverid: enter\n");
result = sysinfo(SI_HOSTNAME, hostname, sizeof (hostname));
/* Everything is freed up and it's time to return the platform-id */
if (result == -1) {
return (NULL);
}
topo_mod_dprintf(mod, "pi_get_serverid: hostname = %s\n", hostname);
return (topo_mod_strdup(mod, hostname));
}
/*
* Return the "target-port" property.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_target_port(topo_mod_t *mod, di_node_t node)
{
char *tport;
di_prop_t di_prop = DI_PROP_NIL;
di_path_t dpath = DI_PATH_NIL;
di_path_prop_t di_path_prop = DI_PROP_NIL;
/* look for pathinfo property */
while ((dpath = di_path_phci_next_path(node, dpath)) != DI_PATH_NIL) {
while ((di_path_prop =
di_path_prop_next(dpath, di_path_prop)) != DI_PROP_NIL) {
if (strcmp("target-port",
di_path_prop_name(di_path_prop)) == 0) {
(void) di_path_prop_strings(di_path_prop,
&tport);
goto found;
}
}
}
/* look for devinfo property */
for (di_prop = di_prop_next(node, DI_PROP_NIL);
di_prop != DI_PROP_NIL;
di_prop = di_prop_next(node, di_prop)) {
if (strcmp("target-port", di_prop_name(di_prop)) == 0) {
if (di_prop_strings(di_prop, &tport) < 0) {
continue;
}
goto found;
}
}
if (di_prop == DI_PROP_NIL && (dpath == DI_PATH_NIL ||
di_path_prop == DI_PROP_NIL)) {
return (NULL);
}
found:
topo_mod_dprintf(mod, "pi_get_target_port: 'target-port' = (%s)\n",
tport);
return (topo_mod_strdup(mod, tport));
}
/*
* Get the hc scheme name for the given node.
*
* The string must be freed with topo_mod_strfree()
*/
char *
pi_get_topo_hc_name(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node)
{
int result;
char *hc_name;
/*
* Request the hc name from the node.
*/
hc_name = NULL;
result = md_get_prop_str(mdp, mde_node, MD_STR_TOPO_HC_NAME, &hc_name);
if (result != 0 || hc_name == NULL) {
topo_mod_dprintf(mod,
"failed to get property %s from node_0x%llx\n",
MD_STR_TOPO_HC_NAME, (uint64_t)mde_node);
return (NULL);
}
/* Return a copy of the type string */
return (topo_mod_strdup(mod, hc_name));
}
/*
* Calculate the authority information for a node. Inherit the data if
* possible, but always create an appropriate property group.
*/
int
pi_set_auth(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
tnode_t *t_parent, tnode_t *t_node)
{
int result;
int err;
nvlist_t *auth;
if (mod == NULL || mdp == NULL || t_parent == NULL || t_node == NULL) {
return (-1);
}
if ((topo_pgroup_create(t_node, &auth_pgroup, &err) != 0) &&
(err != ETOPO_PROP_DEFD)) {
/*
* We failed to create the property group and it was not
* already defined. Set the err code and return failure.
*/
(void) topo_mod_seterrno(mod, err);
return (-1);
}
/*
* Set the authority data.
*
* Continue even if some authority information is not available
* to enumerate as much as possible.
*/
auth = topo_mod_auth(mod, t_parent);
result = topo_node_set_auth_chassis(mod, auth, t_node);
if (result != 0) {
topo_mod_dprintf(mod,
"pi_set_auth: failed to set chassis authority "
"for node_0x%llx\n", (uint64_t)mde_node);
}
nvlist_free(auth);
return (0);
}
/*
* Calculate a generic FRU for the given node. If the node is not a FRU,
* then inherit the FRU data from the nodes parent.
*/
int
pi_set_frufmri(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
const char *name, topo_instance_t inst, tnode_t *t_parent, tnode_t *t_node)
{
int result;
int err;
int is_fru;
char *part;
char *rev;
char *serial;
nvlist_t *auth = NULL;
nvlist_t *frufmri = NULL;
if (t_node == NULL || mod == NULL || mdp == NULL) {
return (-1);
}
/*
* Determine if this node is a FRU
*/
result = pi_get_fru(mod, mdp, mde_node, &is_fru);
if (result != 0 || is_fru == 0) {
/* This node is not a FRU. Inherit from parent and return */
(void) topo_node_fru_set(t_node, NULL, 0, &result);
return (0);
}
/*
* This node is a FRU. Create an FMRI.
*/
part = pi_get_part(mod, mdp, mde_node);
rev = pi_get_revision(mod, mdp, mde_node);
serial = pi_get_serial(mod, mdp, mde_node);
auth = topo_mod_auth(mod, t_parent);
frufmri = topo_mod_hcfmri(mod, t_parent, FM_HC_SCHEME_VERSION, name,
inst, NULL, auth, part, rev, serial);
if (frufmri == NULL) {
topo_mod_dprintf(mod, "failed to create FRU: %s\n",
topo_strerror(topo_mod_errno(mod)));
}
nvlist_free(auth);
topo_mod_strfree(mod, part);
topo_mod_strfree(mod, rev);
topo_mod_strfree(mod, serial);
/* Set the FRU, whether NULL or not */
result = topo_node_fru_set(t_node, frufmri, 0, &err);
if (result != 0) {
(void) topo_mod_seterrno(mod, err);
}
nvlist_free(frufmri);
return (result);
}
int
pi_set_label(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node, tnode_t *t_node)
{
int result;
int err;
char *label;
if (mod == NULL || mdp == NULL) {
return (-1);
}
/*
* Get the label, if any, from the mde node and apply it as the label
* for this topology node. Note that a NULL label will inherit the
* label from topology node's parent.
*/
label = pi_get_label(mod, mdp, mde_node);
result = topo_node_label_set(t_node, label, &err);
topo_mod_strfree(mod, label);
if (result != 0) {
(void) topo_mod_seterrno(mod, err);
topo_mod_dprintf(mod, "pi_set_label: failed with label %s "
"on node_0x%llx: %s\n", (label == NULL ? "NULL" : label),
(uint64_t)mde_node, topo_strerror(err));
}
return (result);
}
/*
* Calculate the system information for a node. Inherit the data if
* possible, but always create an appropriate property group.
*/
int
pi_set_system(topo_mod_t *mod, tnode_t *t_node)
{
int result;
int err;
struct utsname uts;
char isa[MAXNAMELEN];
if (mod == NULL || t_node == NULL) {
return (-1);
}
result = topo_pgroup_create(t_node, &sys_pgroup, &err);
if (result != 0 && err != ETOPO_PROP_DEFD) {
/*
* We failed to create the property group and it was not
* already defined. Set the err code and return failure.
*/
(void) topo_mod_seterrno(mod, err);
return (-1);
}
result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
&err);
if (result != 0 && err != ETOPO_PROP_DEFD) {
isa[0] = '\0';
result = sysinfo(SI_ARCHITECTURE, isa, sizeof (isa));
if (result == -1) {
/* Preserve the error and continue */
topo_mod_dprintf(mod, "pi_set_system: failed to "
"read SI_ARCHITECTURE: %d\n", errno);
}
if (strnlen(isa, MAXNAMELEN) > 0) {
result = topo_prop_set_string(t_node,
TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA,
TOPO_PROP_IMMUTABLE, isa, &err);
if (result != 0) {
/* Preserve the error and continue */
(void) topo_mod_seterrno(mod, err);
topo_mod_dprintf(mod, "pi_set_auth: failed to "
"set property %s (%d) : %s\n",
TOPO_PROP_ISA, err, topo_strerror(err));
}
}
}
result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM,
TOPO_PROP_MACHINE, &err);
if (result != 0 && err != ETOPO_PROP_DEFD) {
result = uname(&uts);
if (result == -1) {
/* Preserve the error and continue */
(void) topo_mod_seterrno(mod, errno);
topo_mod_dprintf(mod, "pi_set_system: failed to "
"read uname: %d\n", errno);
}
if (strnlen(uts.machine, sizeof (uts.machine)) > 0) {
result = topo_prop_set_string(t_node,
TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE,
TOPO_PROP_IMMUTABLE, uts.machine, &err);
if (result != 0) {
/* Preserve the error and continue */
(void) topo_mod_seterrno(mod, err);
topo_mod_dprintf(mod, "pi_set_auth: failed to "
"set property %s (%d) : %s\n",
TOPO_PROP_MACHINE, err, topo_strerror(err));
}
}
}
return (0);
}
/*
* Obtain high level authority from PRI "component" nodes, create nvlist
* which is held in the topo snapshot handle.
*
* system authority - component node, type="product"
* component System authority - component node, type="system"
* chassis authority - component node, type="chassis"
*/
void
pi_set_topo_auth_impl(topo_mod_t *mod, md_t *mdp, mde_cookie_t cookie,
nvlist_t *nvl, int auth_type)
{
int rv;
char *mfg = NULL;
char *name = NULL;
char *part = NULL;
char *serial = NULL;
/* Manufacturer */
rv = md_get_prop_str(mdp, cookie, MD_STR_MANUFACTURER, &mfg);
if (rv != 0) {
topo_mod_dprintf(mod, "pi_set_topo_auth_impl: failed to read "
"%s from node_0x%llx\n", MD_STR_MANUFACTURER, cookie);
}
/* name */
rv = md_get_prop_str(mdp, cookie, MD_STR_NAME, &name);
if (rv != 0) {
topo_mod_dprintf(mod, "pi_set_topo_auth_impl: failed to read "
"%s from node_0x%llx\n", MD_STR_NAME, cookie);
}
/* part number */
rv = md_get_prop_str(mdp, cookie, MD_STR_PART_NUMBER, &part);
if (rv != 0) {
topo_mod_dprintf(mod, "pi_set_topo_auth_impl: failed to read "
"%s from node_0x%llx\n", MD_STR_PART_NUMBER, cookie);
}
/* serial number */
rv = md_get_prop_str(mdp, cookie, MD_STR_SERIAL_NUMBER,
&serial);
if (rv != 0) {
topo_mod_dprintf(mod, "pi_set_topo_auth_impl: failed to read "
"%s from node_0x%llx\n", MD_STR_SERIAL_NUMBER, cookie);
}
/* set no values to default values */
if (mfg == NULL || *mfg == '\0')
mfg = TOPO_AUTH_UNKNOWN;
if (name == NULL || *name == '\0')
name = TOPO_AUTH_UNKNOWN;
if (part == NULL || *part == '\0')
part = TOPO_AUTH_UNKNOWN;
if (serial == NULL || *serial == '\0')
serial = TOPO_AUTH_UNKNOWN;
topo_mod_dprintf(mod, "pi_set_topo_auth_impl: auth type(%d) "
"manufacturer(%s) name(%s) part(%s) serial(%s)\n", auth_type,
mfg ? mfg : "NULL", name ? name : "NULL",
part ? part : "NULL", serial ? serial : "NULL");
topo_mod_auth_set_nvl(mod, &nvl, auth_type, mfg, name, part, serial);
}
/*
* Set the product/system/chassis identity information in the topo handle
* structures. The values are taken from the PRI.
*/
int
pi_set_topo_auth(topo_mod_t *mod, uint32_t version)
{
int i;
int rv;
int ncomp;
int sys_done = 0;
int sc_done = 0;
int ch_done = 0;
size_t csize;
nvlist_t *nvl;
pi_enum_t pi;
char *type = NULL;
mde_cookie_t *component;
/* get PRI */
rv = pi_ldompri_open(mod, &pi, version);
if (rv != 0) {
topo_mod_dprintf(mod,
"pi_set_topo_auth: failed to get PRI\n");
return (-1);
}
/* find the "component" nodes */
ncomp = pi_find_mdenodes(mod, pi.mdp, MDE_INVAL_ELEM_COOKIE,
MD_STR_COMPONENT, MD_STR_FWD, &component, &csize);
if (ncomp <= 0 || component == NULL) {
/* no "component" PRI nodes found */
topo_mod_dprintf(mod,
"pi_set_topo_auth: no components PRI nodes\n");
pi_ldompri_close(mod, &pi);
return (-1);
}
/* allocate the topo authority nvlist */
rv = topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME);
if (rv != 0) {
topo_mod_dprintf(mod, "i_set_topo_auth: "
"failed to xalloc topo auth nvlist\n");
topo_mod_free(mod, component, csize);
pi_ldompri_close(mod, &pi);
return (-1);
}
rv = nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
if (rv != 0) {
topo_mod_dprintf(mod, "i_set_topo_auth: "
"failed to set FM_VERSION\n");
nvlist_free(nvl);
topo_mod_free(mod, component, csize);
pi_ldompri_close(mod, &pi);
return (-1);
}
/*
* Search through the PRI for 'component' "type=product",
* "type=system", and "type=chassis" nodes which contain the
* identity strings.
*/
for (i = 0; i < ncomp; i++) {
rv = md_get_prop_str(pi.mdp, component[i], MD_STR_TYPE, &type);
if (rv == 0) {
if (strcmp(type, MD_STR_PRODUCT) == 0) {
pi_set_topo_auth_impl(mod, pi.mdp, component[i],
nvl, FM_FMRI_AUTH_TYPE_SYSTEM);
++sys_done;
} else if (strcmp(type, MD_STR_SYSTEM) == 0) {
pi_set_topo_auth_impl(mod, pi.mdp, component[i],
nvl, FM_FMRI_AUTH_TYPE_SYS_COMP);
++sc_done;
} else if (strcmp(type, MD_STR_CHASSIS) == 0) {
pi_set_topo_auth_impl(mod, pi.mdp, component[i],
nvl, FM_FMRI_AUTH_TYPE_CHASSIS);
++ch_done;
}
} else {
topo_mod_dprintf(mod, "pi_set_topo_auth: failed to "
"read %s from node_0x%llx\n", MD_STR_TYPE,
component[i]);
}
}
topo_mod_free(mod, component, csize);
/*
* Older PRIs don't contain the product and/or system component
* nodes; do it the old fashion way.
*
* Since the required PRI nodes to differentiate system and component
* system data are absent - set them to the same values.
*/
if (!sys_done || !sc_done) {
char *prod_name = NULL, *prod_sn = NULL;
char *str = NULL, *s1 = NULL, *s2 = NULL;
if (sys_done) {
/* use product name/sn from the product node */
if ((nvlist_lookup_string(nvl,
FM_FMRI_AUTH_V1_SYSTEM_NM, &s1) == 0) &&
s1 != NULL &&
(strncmp(s1, TOPO_AUTH_UNKNOWN,
strlen(TOPO_AUTH_UNKNOWN)) != 0))
prod_name = topo_mod_strdup(mod, s1);
if ((nvlist_lookup_string(nvl,
FM_FMRI_AUTH_V1_SYSTEM_SN,
&s2) == 0) && s2 != NULL &&
(strncmp(s2, TOPO_AUTH_UNKNOWN,
strlen(TOPO_AUTH_UNKNOWN)) != 0))
prod_sn = topo_mod_strdup(mod, s2);
}
if (prod_name == NULL || prod_sn == NULL) {
char *s1 = NULL, *s2 = NULL;
/* use product name/sn from the platform node */
pi_get_platform(mod, pi.mdp, &s1, &s2);
if (prod_name == NULL)
prod_name = topo_mod_strdup(mod, s1);
if (prod_sn == NULL)
prod_sn = topo_mod_strdup(mod, s2);
if (s1 != NULL)
topo_mod_strfree(mod, s1);
if (s2 != NULL)
topo_mod_strfree(mod, s2);
}
topo_mod_dprintf(mod, "pi_set_topo_auth: set sys/comp "
"from platform node name(%s) serial(%s)\n",
prod_name ? prod_name : "NULL",
prod_sn ? prod_sn : "NULL");
topo_mod_auth_set_nvl(mod, &nvl, FM_FMRI_AUTH_TYPE_SYSTEM,
TOPO_AUTH_UNKNOWN, prod_name, TOPO_AUTH_UNKNOWN, prod_sn);
topo_mod_auth_set_nvl(mod, &nvl, FM_FMRI_AUTH_TYPE_SYS_COMP,
TOPO_AUTH_UNKNOWN, prod_name, TOPO_AUTH_UNKNOWN, prod_sn);
/* if chassis name is "unknown" set to the product name */
if ((nvlist_lookup_string(nvl, FM_FMRI_AUTH_V1_CHASSIS_NM,
&str) == 0) && ((str == NULL) ||
(strcmp(str, TOPO_AUTH_UNKNOWN) == 0))) {
topo_mod_dprintf(mod,
"pi_set_topo_auth: set %s from prod_name (%s)\n",
FM_FMRI_AUTH_V1_CHASSIS_NM, prod_name);
(void) nvlist_add_string(nvl,
FM_FMRI_AUTH_V1_CHASSIS_NM, prod_name);
}
if (prod_name != NULL) {
topo_mod_strfree(mod, prod_name);
}
if (prod_sn != NULL) {
topo_mod_strfree(mod, prod_sn);
}
}
/* copy the nvlist to the topo handle */
topo_mod_auth_set_th(mod, nvl);
/* free the nvlist */
nvlist_free(nvl);
/* close up the PRI connection */
pi_ldompri_close(mod, &pi);
return (0);
}
tnode_t *
pi_node_bind(topo_mod_t *mod, md_t *mdp, mde_cookie_t mde_node,
tnode_t *t_parent, const char *hc_name, topo_instance_t inst,
nvlist_t *fmri)
{
int result;
tnode_t *t_node;
if (t_parent == NULL) {
topo_mod_dprintf(mod,
"cannot bind for node_0x%llx instance %d type %s\n",
(uint64_t)mde_node, inst, hc_name);
return (NULL);
}
/* Bind this node to the parent */
t_node = topo_node_bind(mod, t_parent, hc_name, inst, fmri);
if (t_node == NULL) {
topo_mod_dprintf(mod,
"failed to bind node_0x%llx instance %d: %s\n",
(uint64_t)mde_node, (uint32_t)inst,
topo_strerror(topo_mod_errno(mod)));
return (NULL);
}
topo_mod_dprintf(mod, "bound node_0x%llx instance %d type %s\n",
(uint64_t)mde_node, inst, hc_name);
/*
* We have bound the node. Now decorate it with an appropriate
* FRU and label (which may be inherited from the parent).
*
* The disk enumerator requires that 'bay' nodes not set their
* fru property.
*/
if (strncmp(hc_name, BAY, strlen(BAY)) != 0) {
result = pi_set_frufmri(mod, mdp, mde_node, hc_name, inst,
t_parent, t_node);
if (result != 0) {
/*
* Though we have failed to set the FRU FMRI we still
* continue. The module errno is set by the called
* routine, so we report the problem and move on.
*/
topo_mod_dprintf(mod,
"failed to set FRU FMRI for node_0x%llx\n",
(uint64_t)mde_node);
}
}
result = pi_set_label(mod, mdp, mde_node, t_node);
if (result != 0) {
/*
* Though we have failed to set the label, we still continue.
* The module errno is set by the called routine, so we report
* the problem and move on.
*/
topo_mod_dprintf(mod, "failed to set label for node_0x%llx\n",
(uint64_t)mde_node);
}
result = pi_set_auth(mod, mdp, mde_node, t_parent, t_node);
if (result != 0) {
/*
* Though we have failed to set the authority, we still
* continue. The module errno is set by the called routine, so
* we report the problem and move on.
*/
topo_mod_dprintf(mod, "failed to set authority for "
"node_0x%llx\n", (uint64_t)mde_node);
}
result = pi_set_system(mod, t_node);
if (result != 0) {
/*
* Though we have failed to set the system group, we still
* continue. The module errno is set by the called routine, so
* we report the problem and move on.
*/
topo_mod_dprintf(mod, "failed to set system for node_0x%llx\n",
(uint64_t)mde_node);
}
return (t_node);
}