/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This plugin-in creates the FRU Hierarchy for the
* SUNW,Netra-T12 platform and manages the environmental sensors
* on the platform.
*/
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <strings.h>
#include <libintl.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <picl.h>
#include <picltree.h>
#include <libnvpair.h>
#include <kstat.h>
#include <config_admin.h>
#include <sys/sbd_ioctl.h>
#include <sys/sgfrutree.h>
#include <pthread.h>
#include "libdevice.h"
#include "picldefs.h"
#define NDEBUG
#include <assert.h>
/*
* Plugin registration entry points
*/
static void piclfrutree_register(void);
static void piclfrutree_init(void);
static void piclfrutree_fini(void);
#pragma init(piclfrutree_register)
"SUNW_Netra-T12_frutree",
};
/*
* Log message texts
*/
#define DEVCTL_DEVICE_ACQUIRE_FAILED \
gettext("frutree: devctl_device_acquire() failed: %s")
/*
* PICL property values
*/
/*
* PICL property names
*/
/*
* Local defines
*/
/*
* Calculate safari address to put in CPU_DEV/MEMORY_DEV string based on
*/
/*
* work out type of fru based on name
*/
/*
* rename sgfru driver's node_t to sgfrunode_t to avoid confusion
*/
/*
* disk_led data
*/
/*
* 'struct lw8_disk' contains the per-disk metadata needed to
* manage the current state of one of the internal disks.
*
* 'lw8_disks[]' is an array that contains the metadata
* for N_DISKS disks.
*
* The d_fruname field of 'struct lw8_disk' is static.
* d_plat_path and d_devices_path are aliases for device-paths
* to the disk. They are logically static, as they are computed
* when the disk_leds_thread() thread does its initialization.
*
* d_state is the most interesting field, as it changes
* dynamically, based on whether the associated disk
* is currently Configured or Unconfigured (by DR). d_state
* is an optimization that minimizes per-disk actions such
* as setting of LEDs and updating the FRU Tree.
*
* A disk starts in a d_state of DISK_STATE_NOT_INIT
* and moves to DISK_STATE_READY when the disk is
* Configured (by DR) and it moves to DISK_STATE_NOT_READY
* when it is Unconfigured (by DR).
*/
typedef enum {
} disk_state_t;
struct lw8_disk {
};
/* Duration of inactivity within disk_leds_thread() */
static void disk_leds_init(void);
static void disk_leds_fini(void);
static void *disk_leds_thread(void *args);
/*
* Tables to convert sgenv information
*/
"IB", "PS", "ID"};
"PCIB", "PS", "ID"};
"ar", "cbh", "dx", "cheetah", "1.5vdc", "3.3vdc",
"5vdc", "12vdc", "output", "current", "board", "sc-app",
"schizo", "fan", "input"};
"temp", "cooling", "1.5vdc", "1.8vdc", "3.3vdc", "5vdc",
"failed", "unusable"};
/*
* variables set up in init
*/
static int init_complete;
static int pcix_io = 0;
/*
* forward reference
*/
static int add_all_nodes(void);
char *slot_name);
char *tbl_name);
/*
* This function is executed as part of .init when the plugin is
* dlopen()ed
*/
static void
piclfrutree_register(void)
{
(void) picld_plugin_register(&my_reg_info);
}
/*
* This function is the init entry point of the plugin.
* It initializes the /frutree tree
*/
static void
piclfrutree_init(void)
{
int err;
init_complete = 0;
err = add_all_nodes();
init_complete = 1;
if (err != PICL_SUCCESS) {
}
}
/*
* This function is the fini entry point of the plugin.
*/
static void
piclfrutree_fini(void)
{
(void) remove_subtree(frutreeh);
}
/*
* called from piclfrutree_init() to initialise picl frutree
*/
static int
add_all_nodes(void)
{
int err;
/* Get the root node of the PICL tree */
if (err != PICL_SUCCESS) {
return (err);
}
/* find sc node so we can create sensor nodes under it */
if (err != PICL_SUCCESS) {
/*
* a different path for the the bootbus controller.
*/
if (err == PICL_SUCCESS)
pcix_io = 1;
}
if (err != PICL_SUCCESS) {
return (err);
}
/* Create and add the root node of the FRU subtree */
if (err != PICL_SUCCESS) {
return (err);
}
/* Recursively query the SC and add frutree nodes */
}
/*
* Recursive routine to add picl nodes to the frutree. Called from
* add_all_nodes() for the whole frutree at initialisation, and from
* frudr_evhandler() for portions of the frutree on DR insert events
*/
static int
{
int err, i;
int num_children;
/* find children of the parent node */
if (fruchildren == NULL)
return (PICL_FAILURE);
/* for each child, add a new picl node */
/*
* Add the appropriate PICL class
*/
childh = 0;
if (err == PICL_NOTNODE)
continue;
if (err != PICL_SUCCESS) {
return (err);
}
/*
* Recursively call this function based on has_children hint
*/
if (err != PICL_SUCCESS) {
return (err);
}
}
}
return (PICL_SUCCESS);
}
/*
* Recursive routine to remove picl nodes to the frutree. Called from
* piclfrutree_fini() for the whole frutree at termination, and from
* frudr_completion_handler() for portions of the frutree on DR remove events
*/
static int
{
for (;;) {
sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
return (PICL_FAILURE);
} else {
return (remove_picl_node(parh));
}
}
/* NOTREACHED */
}
/*
* Add fru and location nodes with SC_handle property
* (aka, container handle, for frus).
* Return picl_nodehdl of created node in *childp.
*/
static int
{
case PSEUDO_FRU_CLASS:
case FRU_CLASS:
case LOCATION_CLASS:
default:
return (PICL_NOTNODE);
}
}
/*
* create chassis node
*/
static int
{
int err;
PICL_CLASS_FRU, &childh);
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS)
return (err);
/*
* add devices table to chassis node (may need references
* to led devices)
*/
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (pcix_io)
else
if (err != PICL_SUCCESS) {
} else {
"disk-slot", NULL);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
if (err != PICL_SUCCESS)
return (err);
if (pcix_io)
else
if (err != PICL_SUCCESS) {
} else {
"disk-slot", NULL);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
if (err != PICL_SUCCESS)
return (err);
if (pcix_io)
else
if (err != PICL_SUCCESS) {
} else {
"tape-slot", NULL);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
if (err != PICL_SUCCESS)
return (err);
if (pcix_io)
else
if (err != PICL_SUCCESS) {
} else {
"dvd-slot", NULL);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
if (err != PICL_SUCCESS)
return (err);
if (pcix_io) {
/*
*/
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
} else {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
return (PICL_SUCCESS);
}
/*
* create fru node, based on sgfru node "sgfrunode" under parent parh. Return
* picl_nodehdl of created node in *childp.
*/
static int
{
int err;
/*
* if sgfrunode already there, then just carry on own the tree
*/
/*
* for frus other than dimms and ecaches, update environmental
* sensors and board status if necessary
*/
if (IS_ECACHE_NODE(nodename)) {
return (PICL_SUCCESS);
}
if (IS_DIMM_NODE(nodename)) {
/*
* for dimms we just want status
*/
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
/*
* create requested fru node
*/
&childh);
if (err != PICL_SUCCESS) {
return (err);
}
/*
* if sgfru has sent us a valid handle, then there is fruid information.
* create the SC_handle, and FRUDateAvailable properties for FRUID.
*/
if (handle != -1ULL) {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
/*
* post fru added event to fru data plugin if this was due to
* a dr event - ie post-initialisation
*/
if (init_complete)
/*
* Create empty Devices table - we'll add lines to it as we go along
*/
if (err != PICL_SUCCESS)
return (err);
/*
* Ecache nodes don't have sensors - just set up FRUType
*/
if (IS_ECACHE_NODE(nodename)) {
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
/*
* Dimm nodes don't have sensors - just set up FRUType and
* also reference properties to memory module nodes and OpStatus
*/
if (IS_DIMM_NODE(nodename)) {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
/*
* not a Dimm or Ecache node - set up environmental info,
* board status and led info
*/
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
/*
* create location node, based on sgfru node "sgfrunode" under parent parh.
* Return picl_nodehdl of created node in *childp.
*/
static int
{
int err;
char *labelp;
char *ptr;
/*
* strip "N0/" off the label if present (hang-over from wildcat)
*/
LABEL_PREAMBLE_LEN) == 0)
LABEL_PREAMBLE_LEN], sizeof (label));
else
sizeof (label));
/*
* some of the locations returned by sgfru are actually of the form
* these.
*/
/*
* null end of this section of label
*/
*ptr = '\0';
/*
* add intermediate nodes - parh will point to the created node
*/
if (IS_PROC_NODE(labelp)) {
"cpu", "PROC");
} else {
}
if (err != PICL_SUCCESS)
return (err);
/*
* if processor node, then create links to associated cpu node
* and OpStatus property
*/
if (IS_PROC_NODE(labelp)) {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
/*
* set back to "/"
*/
*ptr = '/';
}
/*
* if node already there, then just carry on down the tree
*/
return (PICL_SUCCESS);
}
/*
* now just have the final level of the node left. First create it.
*/
&childh);
if (err != PICL_SUCCESS) {
return (err);
}
/*
* if sgfru has sent us a valid handle, then there is fruid information.
* create the SC_handle property for FRUID.
*/
if (handle != -1ULL) {
if (err != PICL_SUCCESS)
return (err);
}
/* create label property for location class */
if (err != PICL_SUCCESS)
return (err);
/* create SlotType property where appropriate */
"ecache", PICL_PROP_SLOT_TYPE);
/*
* For Ecache, don't need to add environmental info
* so return here
*/
return (err);
/*
* For Dimm, don't need to add environmental info
* so return here
*/
return (err);
}
if (err != PICL_SUCCESS)
return (err);
/*
* add devices table to location node (may need
* references to led devices)
*/
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
return (PICL_SUCCESS);
}
/*
* remove an individual picl node - called from remove_subtree()
* also removes any sensor nodes pointed at by Devices table
*/
static int
{
int err;
/*
* first scan Devices table so we can find any sensor nodes
* we need to delete as well
*/
/*
* If Devices table present, then read first column.
* Devices table may be empty so don't treat this as an error
*/
if (err == PICL_SUCCESS &&
/* find second column */
if (err != PICL_SUCCESS) {
return (err);
}
/*
* walk down second column (ref ptr)
* deleting the referenced nodes
*/
while (err == PICL_SUCCESS) {
sizeof (refprop));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* don't delete memory-module nodes
* or cpu nodes (they weren't created
* by this plugin)
*/
if (err == PICL_STALEHANDLE) {
/*
* if another plugin has already deleted the
* node for us then that is ok
*/
&nextprop);
continue;
}
if (err != PICL_SUCCESS) {
return (err);
}
/*
* but - do need to remove _fru_parent
* property and Environment table (for cpu)
*/
if (err != PICL_SUCCESS)
return (err);
} else {
/*
* sensor node - need to delete it
*/
if (err != PICL_SUCCESS) {
return (err);
}
(void) ptree_destroy_node(refprop);
}
}
}
/*
* now we can remove the frutree node
*/
if (err != PICL_SUCCESS) {
return (err);
}
(void) ptree_destroy_node(nodeh);
return (PICL_SUCCESS);
}
static int
{
sizeof (childnodeh)) != PICL_SUCCESS) {
return (PICL_SUCCESS);
}
for (;;) {
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS) {
err = PICL_SUCCESS;
break;
}
}
return (err);
}
static int
char *slot_name)
{
int err;
int got_one = 0;
/*
* search for any device nodes whose BUS_ADDR or UNIT_ADDRESS
* are appropriate for this pci slot
*/
sizeof (devnodeh)) == PICL_SUCCESS) {
while (!got_one) {
got_one = 1;
break;
}
got_one = 1;
break;
}
if (err != PICL_SUCCESS)
break;
}
}
if (got_one == 0) {
/*
* no devnodes for this slot. Create location node but
* no fru node (empty slot)
*/
}
/*
* we've got the first devnode for this slot. Create the fru node
* then walk along other nodes looking for further devnodes
*/
if (err != PICL_SUCCESS)
return (err);
for (;;) {
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
if (err != PICL_SUCCESS)
break;
}
if (err != PICL_SUCCESS) {
err = PICL_SUCCESS;
break;
}
}
return (err);
}
/*
* add intermediate location into frutree (ie a location that we know
* exists but sgfru doesn't)
*/
static int
{
int err;
&intermediate);
if (err != PICL_SUCCESS) {
return (err);
}
/*
* create label property for location class
*/
if (err != PICL_SUCCESS)
return (err);
/*
* add devices table to location node (may need references to led
* devices)
*/
if (err != PICL_SUCCESS)
return (err);
/*
* scapp knows FANs 0 and 1 on IB as FAN8 and FAN9
*/
sizeof (parent_name));
if (err != PICL_SUCCESS)
return (err);
else
if (err != PICL_SUCCESS)
return (err);
if (slot_name) {
if (err != PICL_SUCCESS)
return (err);
}
*nodep = intermediate;
return (PICL_SUCCESS);
}
/*
*/
static int
{
int err;
/*
* create intermediate location node (unless it has already been
* created)
*/
if (intermediate == NULL) {
intermediate = *nodep;
if (err != PICL_SUCCESS) {
return (err);
}
}
/*
* create intermediate fru node (unless it has already been
* created)
*/
if (intermediate2 == NULL) {
/*
* need to create intermediate fru node node
*/
if (err != PICL_SUCCESS) {
return (err);
}
/*
* Create empty Devices table
*/
if (err != PICL_SUCCESS)
return (err);
if (fru_name) {
if (err != PICL_SUCCESS)
return (err);
}
} else {
if (err != PICL_SUCCESS)
return (err);
}
*nodep = intermediate2;
return (PICL_SUCCESS);
}
/*
* need to remove _fru_parent property and Environment table (for cpu)
*/
static int
{
int err;
&platprop);
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
(void) ptree_destroy_prop(platprop);
if (err != PICL_SUCCESS) {
/*
* multi-core cpu is setup with only one cpu having
* env table so ignore PICL_PROPNOTFOUND error.
*/
if (err == PICL_PROPNOTFOUND) {
return (PICL_SUCCESS);
}
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
(void) ptree_destroy_prop(platprop);
}
return (PICL_SUCCESS);
}
/*
* subroutine for various functions. Finds immediate child of parh with
* requested name if present. Otherwise returns NULL.
*/
static picl_nodehdl_t
{
int err;
&nodeh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (NULL);
for (;;) {
sizeof (nodename));
if (err != PICL_SUCCESS)
return (NULL);
return (nodeh);
}
&nodeh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (NULL);
}
}
static int
{
int err;
int id;
/*
* create reference properties for memory nodes
*/
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
&parentloch, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
&parentfruh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
&parentloch, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
&parentfruh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (sbname));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* ok - we've now got name of system board node in sbname and
* name of processor node in pname.
* Now find corresponding memory-controller node if present
*/
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
/*
* now find corresponding memory-module-group node if present
*/
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
/*
* check if this is the right bank - if not move on to sibling
*/
&id, sizeof (int));
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
&memgrphdl, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
&id, sizeof (int));
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
return (PICL_SUCCESS);
}
/*
* now find corresponding memory-module node if present
*/
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
/*
* for each DIMM set up links with matching memory-module node
*/
for (;;) {
&id, sizeof (int));
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
&memhdl, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
break;
}
return (PICL_SUCCESS);
}
static int
{
int err;
&parentloch, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
&parentfruh, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (sbname));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* Find corresponding cpu node if present. Note, this code will
* attempt to find a corresponding cpu node, by searching for devices
* any such device, we return PICL_SUCCESS such that we
* continue the construction of the remaining part of the
* tree. We first check for UltraSPARC-III. If we do not
* find such a device we check for UltraSPARC-III+. If
* we are unsuccesful again we try one of the jaguar cores
* first one, there's no point in continuing and we just
* return PICL_SUCCESS. Similarly if we find one core
* but not the other, something must be wrong, so we
* again just return PICL_SUCCESS without creating any
* references.
*/
if (err != PICL_SUCCESS) {
if (err != PICL_SUCCESS) {
/* check for jaguar cores */
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
/* add fru parent reference for the second core */
if (err != PICL_SUCCESS) {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
if (err != PICL_SUCCESS)
return (PICL_SUCCESS);
}
}
/*
* now create reference properties
*/
if (err != PICL_SUCCESS) {
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
}
/*
* create Environment table on cpu node - with Die and Ambient
* temperature sensors if present. If already there, delete and start
* again
*/
if (err == PICL_SUCCESS) {
if (err != PICL_SUCCESS)
return (err);
(void) ptree_destroy_prop(prophdl);
}
if (err != PICL_SUCCESS)
return (err);
if (pcix_io)
else
if (err == PICL_SUCCESS) {
if (err != PICL_SUCCESS)
return (err);
}
if (pcix_io)
else
if (err == PICL_SUCCESS) {
}
return (PICL_SUCCESS);
}
/*
* subroutine of add_subtree - get a list of children of a parent node
*/
static sgfrunode_t *
{
int max_children, i;
int frufd;
/*
* Open the sgfru pseudo dev
*/
return (NULL);
}
for (i = 1; i <= MAX_TRIES; i++) {
max_children = i * MAX_NODE_CHILDREN;
sizeof (sgfrunode_t))) == NULL) {
return (NULL);
}
/*
* got them - return success
*/
return (fruchildren);
}
/*
* if ENOMEM, need to calloc more space - so go round loop again
* otherwise fail
*/
return (NULL);
}
}
return (NULL);
}
/* Creates an unsigned longlong property for a given PICL node */
static int
{
int err;
PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (unsigned long long),
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* Creates a void property for a given PICL node */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* Creates a reference property for a given PICL node */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* Creates an integer property for a given PICL node */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* Creates an integer property for a given PICL node */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* Creates a charstring property for a given PICL node */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/* create an entry in the specified table */
static int
{
int err;
/* first column is class */
if (err != PICL_SUCCESS) {
return (err);
}
/* second column is refernce property */
if (err != PICL_SUCCESS) {
return (err);
}
/* add row to table */
if (err != PICL_SUCCESS)
return (err);
}
/* create an empty table property */
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS)
return (err);
}
static void
{
return;
}
}
/* event completion handler for PICL_FRU_ADDED/PICL_FRU_REMOVED events */
/*ARGSUSED*/
static void
{
/*
* now frudata has been notified that the node is to be
* removed, we can actually remove it
*/
(void) nvlist_lookup_uint64(earg,
(void) remove_subtree(fruh);
/*
* Now repopulate the frutree with current data.
*/
(void) nvlist_lookup_uint64(earg,
}
}
}
}
/*
* Post the PICL_FRU_ADDED/PICL_FRU_REMOVED event
*/
static void
{
char *ev_name;
return;
return;
}
if (parenth != 0L &&
return;
}
if (fruh != 0L &&
return;
}
frudr_completion_handler) != 0) {
}
}
/*
* updates the picl node 'loc' with the new fru handle (PICL_PROP_SC_HANDLE)
* (helper function for frudr_evhandler, when a stale fru handle is
* detected)
*/
static void
{
int err;
if (err == PICL_SUCCESS) {
(void) ptree_destroy_prop(schproph);
}
}
}
/*
* Get the fru handle of loc by iterating through the parent's children.
* Sets fruhdl and returns PICL_SUCCESS unless an error is encountered.
*/
static int
{
int err;
int num_children;
int i;
if (err != PICL_SUCCESS)
return (err);
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS)
return (err);
return (err);
/* find children of the parent node */
if (fruchildren == NULL)
return (PICL_FAILURE);
/* find the child we're interested in */
return (PICL_SUCCESS);
}
}
return (PICL_FAILURE);
}
/*
* handle EC_DR picl events
*/
/*ARGSUSED*/
static void
{
char *dtype;
char *ap_id;
char *hint;
return;
return;
return;
}
return;
}
return;
}
return;
}
return;
}
/*
* OK - so this is an EC_DR event - let's handle it.
*/
/*
* special case - SSC arrival means that SSC has been reset - we
* need to flush the cached sgfru handles
*/
int got_peer;
int err;
int num_children;
/* find existing chassis node */
PICL_SUCCESS) {
return;
}
/* find new chassis sgfru node */
return;
}
/* update chassis SC_HANDLE property */
&schproph);
if (err != PICL_SUCCESS) {
return;
}
if (err != PICL_SUCCESS) {
return;
}
(void) ptree_destroy_prop(schproph);
if (err != PICL_SUCCESS) {
return;
}
/*
* remove all subtrees except DISK, TAPE, DVD and PCI subtrees
*/
sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
for (;;) {
sizeof (picl_nodehdl_t)) != PICL_SUCCESS)
got_peer = 0;
else
got_peer = 1;
if (err == PICL_SUCCESS) {
strlen("DISK")) != 0 &&
strlen("TAPE")) != 0 &&
strlen("PCI")) != 0 &&
strlen("DVD")) != 0) {
(void) remove_subtree(chdh);
}
}
if (got_peer == 0)
break;
}
}
/* add new subtrees */
return;
}
return;
}
return;
}
/*
* now either add or delete the fru node as appropriate. If no
* hint, treat as insert - add_subtree will update the tree if
* necessary.
*/
/*
* fru was there - but has gone away
*/
}
} else {
/*
* fru has been inserted (or may need to update)
*
* sgfruhdl may be stale due to hotplugging. We check this
* by getting the fru_hdl_t from the parent's children
* and compare it to the cached value in sgfruhdl. If we
* have a stale handle, we update the cached value and
* use it in the call to add_subtree.
*/
PICL_SUCCESS) {
if (sgfruhdl != sgfruhdl_from_parent) {
}
}
}
}
/*
* handle memcfg picl events - need to update reference properties
*/
/*ARGSUSED*/
static void
void *cookie)
{
int err;
int id;
char *ptr;
int value;
return;
/*
* find corresponding frutree dimm nodes
*/
return;
return;
}
sizeof (addr));
if (err != PICL_SUCCESS)
return;
return;
*ptr = '\0';
if (err != PICL_SUCCESS)
return;
&banklochdl, sizeof (banklochdl));
if (err != PICL_SUCCESS)
return;
/*
* walk through the DIMM locations
*/
for (;;) {
&bankfruhdl, sizeof (bankfruhdl));
if (err != PICL_SUCCESS)
goto next_bank;
if (err != PICL_SUCCESS)
goto next_bank;
for (;;) {
if (err != PICL_SUCCESS)
goto next_dimm;
/*
* this is a frutree dimm node corresponding to the
*/
/*
* find bank name
*/
sizeof (tblhdl));
if (err != PICL_SUCCESS)
goto next_dimm;
if (err != PICL_SUCCESS)
goto next_dimm;
if (err != PICL_SUCCESS)
goto next_dimm;
/*
* find memory group node
*/
sizeof (memgrphdl));
if (err != PICL_SUCCESS)
goto next_dimm;
/*
* check if this is the right bank - if not
* move on to sibling
*/
if (err != PICL_SUCCESS)
goto next_dimm;
err =
sizeof (memgrphdl));
if (err != PICL_SUCCESS)
goto next_dimm;
err =
if (err != PICL_SUCCESS)
goto next_dimm;
goto next_dimm;
}
/*
* got the right bank - now create appropriate
* link
*/
sizeof (memhdl));
if (err != PICL_SUCCESS)
goto next_dimm;
for (;;) {
if (err != PICL_SUCCESS)
goto next_dimm;
if (err != PICL_SUCCESS)
return;
if (err != PICL_SUCCESS)
return;
}
if (err == PICL_PROPNOTFOUND)
break;
if (err != PICL_SUCCESS)
return;
}
/*
* XXX - no mechanism for deleting row - so
* delete whole tabel and start again
*/
if (err == PICL_SUCCESS) {
if (err != PICL_SUCCESS)
return;
(void) ptree_destroy_prop(tblproph);
}
if (err != PICL_SUCCESS)
return;
}
if (err == PICL_PROPNOTFOUND)
break;
if (err != PICL_SUCCESS)
return;
}
if (err == PICL_PROPNOTFOUND)
break;
if (err != PICL_SUCCESS)
return;
}
/*
* We don't get an event to say that cpu nodes have been added/
* deleted (in fact as things stand they are never deleted). However
* we know that all cpus must be configured before the MC_ADDED event
* we are handling here. So if the cpu links haven't been set up yet
* then we do it now.
*/
if (err != PICL_SUCCESS)
return;
if (err != PICL_SUCCESS)
return;
if (err != PICL_SUCCESS)
return;
}
}
/*
* subroutine for add_env_nodes(), and add_led_node(). Adds a sensor
* node under the sc node in the platform tree, of name "nodename" and
* class "class". Also add UnitAddress property (always 0 as the nodenames
* in frutree and a Devices table entry pointing to this node from the
*/
static int
{
int err;
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
} else {
}
return (err);
}
/*
* subroutine for add_sensor_node()/add_env_nodes(). Used for adding dynamic
* properties
*/
static int
{
int err;
} else {
}
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
return (PICL_SUCCESS);
}
/*
* Get requested kstat
*/
static int
{
*kcp = kstat_open();
return (PICL_FAILURE);
}
kstat_close(*kcp);
return (PICL_FAILURE);
}
kstat_close(*kcp);
return (PICL_FAILURE);
}
return (PICL_SUCCESS);
}
/*
* dimm status - uses bank-status property on memory-controller node
*/
static int
{
int err;
int i;
/*
* find the name of this node
*/
sizeof (nodename));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* find the name of grandparent (dimm bank) node
*/
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (bankname));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* lookup memory-module node in Devices table
*/
sizeof (tblhdl));
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
/*
* if Devices table empty then dimm is unconfigured
*/
return (PICL_SUCCESS);
}
if (err != PICL_SUCCESS) {
return (err);
}
/*
* walk down second column (ref ptr)
*/
while (err == PICL_SUCCESS) {
if (err != PICL_SUCCESS) {
return (PICL_PROPVALUNAVAILABLE);
}
PICL_CLASS_MEMORY_MODULE) == 0)
break;
err);
return (err);
}
if (err != PICL_SUCCESS) {
/*
* if no memory-module in Devices table
* then dimm is unconfigured
*/
return (PICL_SUCCESS);
}
}
/*
* we've finally found the associated memory-module
* node. Now need to find the bank-status property on
* its parent memory-controller.
*/
&mmgprop, sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (picl_nodehdl_t));
if (err != PICL_SUCCESS) {
return (err);
}
sizeof (tblhdl));
if (err != PICL_SUCCESS) {
return (PICL_SUCCESS);
}
/*
* bank-status is a table. Need to find the entry corresponding
* to this node
*/
if (err != PICL_SUCCESS) {
return (PICL_SUCCESS);
}
for (i = 0; i < 4; i++) {
if (err != PICL_SUCCESS) {
return (err);
}
} else {
}
break;
}
if (err != PICL_SUCCESS) {
break;
}
}
return (PICL_SUCCESS);
}
/*
* cpu status - uses State property on cpu node
*/
static int
{
int err;
/*
* lookup cpu node in Devices table
*/
sizeof (tblhdl));
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
/*
* if Devices table empty then cpu is unconfigured
*/
return (PICL_SUCCESS);
}
if (err != PICL_SUCCESS) {
return (err);
}
/*
* walk down second column (ref ptr)
*/
while (err == PICL_SUCCESS) {
if (err != PICL_SUCCESS) {
return (err);
}
break;
err);
return (err);
}
if (err != PICL_SUCCESS) {
/*
* if no cpu in Devices table
* then cpu is unconfigured
*/
return (PICL_SUCCESS);
}
}
/*
* we've finally found the associated cpu node. Now need to find its
* status property if present (if not assume OK)
*/
if (err == PICL_SUCCESS) {
else
return (PICL_SUCCESS);
}
return (PICL_SUCCESS);
}
/*
*/
static int
{
int i;
sizeof (name));
if (err != PICL_SUCCESS) {
return (err);
}
&kc);
if (err != PICL_SUCCESS) {
return (err);
}
/*
* check this kstat matches the name of the node
*/
} else {
}
continue;
/*
* ok - got the right kstat - get it's value
* note that values 0-4 are defined in sbdp_mbox.h
*/
return (PICL_SUCCESS);
}
return (PICL_PROPVALUNAVAILABLE);
}
static int
{
sizeof (name));
if (err != PICL_SUCCESS) {
return (err);
}
/*
* handle dimms, cpus and system boards specially
*/
if (IS_PROC_NODE(name)) {
} else if (IS_DIMM_NODE(name)) {
}
/*
* otherwise OperationalStatus is derived from the fault led state
*/
/*
* scapp knows FANs 0 and 1 on IB as FAN8 and FAN9
*/
sizeof (loch));
if (err != PICL_SUCCESS)
return (PICL_PROPVALUNAVAILABLE);
sizeof (parentfruh));
if (err != PICL_SUCCESS)
return (PICL_PROPVALUNAVAILABLE);
sizeof (parent_name));
if (err != PICL_SUCCESS)
return (PICL_PROPVALUNAVAILABLE);
return (PICL_PROPVALUNAVAILABLE);
}
"IB6") == 0) {
return (PICL_PROPVALUNAVAILABLE);
}
} else {
return (PICL_PROPVALUNAVAILABLE);
}
}
else
return (PICL_SUCCESS);
}
static int
{
int err;
/*
* check if OperationalStatus property already created for this fru
*/
&prophdl);
if (err == PICL_SUCCESS)
return (PICL_SUCCESS);
/*
* put operational status on dimms, cpus, SBs, IBs, PSUs, FTs, Fans, RPs
*/
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
}
return (PICL_SUCCESS);
}
/*
* environmental information handling - uses sgenv driver kstats
*/
static int
{
int i;
float scale;
if (err != PICL_SUCCESS) {
return (err);
}
/*
* check values from kstat entry are within valid range
*/
continue;
continue;
continue;
continue;
continue;
/*
* does this kstat entry belong to this fru?
* Note sc reports RPS as 10 and 12 via env messages
* but by 0 and 2 via fru messages, so correct here
*/
(SG_HPU_TYPE_REPEATER_BOARD >> 8)) {
} else {
}
continue;
/*
* set up FRUType. Note we only want to do this once per fru
*/
&frutype);
if (err != PICL_SUCCESS) {
if (err != PICL_SUCCESS)
goto done;
}
/*
* create the sensor node with a sensible name
*/
} else {
}
break;
case SG_SENSOR_TYPE_CURRENT:
break;
case SG_SENSOR_TYPE_COOLING:
break;
default: /* voltage */
} else {
}
break;
}
/*
* check if sensor node has already been created
*/
continue;
/*
* create individual fan_unit nodes
*/
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
(SG_HPU_TYPE_CPU_BOARD >> 8) &&
/*
* put sensors under individual processor nodes
*/
else
if (err != PICL_SUCCESS)
goto done;
} else {
}
if (err != PICL_SUCCESS)
goto done;
/*
* add additional properties
*/
case SG_SENSOR_TYPE_COOLING:
if (err != PICL_SUCCESS)
goto done;
/*
* add threshold at 75% of full speed
*/
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
continue;
(SG_HPU_TYPE_CPU_BOARD >> 8)) &&
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
} else {
if (err != PICL_SUCCESS)
goto done;
}
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
continue;
case SG_SENSOR_TYPE_1_5_VDC:
break;
case SG_SENSOR_TYPE_1_8_VDC:
break;
case SG_SENSOR_TYPE_2_5_VDC:
break;
case SG_SENSOR_TYPE_3_3_VDC:
break;
case SG_SENSOR_TYPE_5_VDC:
break;
case SG_SENSOR_TYPE_12_VDC:
break;
case SG_SENSOR_TYPE_48_VDC:
/*
* The 48VDC sensor is just an indicator - doesn't
* give reading or thresholds
*/
if (err != PICL_SUCCESS)
goto done;
continue;
case SG_SENSOR_TYPE_CURRENT:
break;
}
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
if (err != PICL_SUCCESS)
goto done;
}
done:
return (err);
}
static int
{
int i;
sizeof (name));
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS)
return (err);
if (err != PICL_SUCCESS) {
return (err);
}
/*
* check kstat values are within range
*/
continue;
continue;
continue;
continue;
continue;
continue;
/*
* check this kstat matches the name of the node
* note sc reports RPS as 10 and 12 via env messages
* but by 0 and 2 via fru messages, so correct here
*/
(SG_HPU_TYPE_REPEATER_BOARD >> 8))
else
} else {
}
break;
case SG_SENSOR_TYPE_CURRENT:
break;
case SG_SENSOR_TYPE_COOLING:
break;
default: /* voltage */
} else {
}
break;
}
continue;
/*
* ok - this is the kstat we want - update
* Condition, or sensor reading as requested
*/
case SG_SENSOR_STATUS_OK:
break;
case SG_SENSOR_STATUS_LO_WARN:
case SG_SENSOR_STATUS_HI_WARN:
break;
break;
default:
return (PICL_PROPVALUNAVAILABLE);
}
return (PICL_SUCCESS);
}
break;
case SG_SENSOR_TYPE_1_5_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_1_8_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_2_5_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_3_3_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_5_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_12_VDC:
*(float *)result =
break;
case SG_SENSOR_TYPE_CURRENT:
*(float *)result =
break;
case SG_SENSOR_TYPE_COOLING:
PICL_PROP_FAN_SPEED_UNIT) == 0) {
} else {
}
} else {
*(int *)result = 100;
break;
case SG_SENSOR_STATUS_FAN_OFF:
*(int *)result = 0;
break;
default:
case SG_SENSOR_STATUS_FAN_LOW:
return (PICL_PROPVALUNAVAILABLE);
}
}
break;
default:
return (PICL_PROPVALUNAVAILABLE);
}
return (PICL_SUCCESS);
}
return (PICL_PROPVALUNAVAILABLE);
}
/*
* led information handling - uses lw8 driver
*/
static int
{
int err;
int ledfd;
/*
* Open the lw8 pseudo dev to get the led information
*/
return (PICL_SUCCESS);
}
sizeof (lom_get_led.location));
return (PICL_FAILURE);
}
sizeof (lom_get_led.id));
return (PICL_FAILURE);
}
continue;
if (position == LOM_LED_POSITION_LOCATION) {
} else {
}
if (err != PICL_SUCCESS) {
return (err);
}
"locator") == 0) {
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
}
if (err != PICL_SUCCESS) {
return (err);
}
if (err != PICL_SUCCESS) {
return (err);
}
}
return (PICL_SUCCESS);
}
static int
{
int ledfd;
/*
* Open the lw8 pseudo dev to get the led information
*/
return (PICL_FAILURE);
}
sizeof (lom_get_led.location));
return (PICL_PROPVALUNAVAILABLE);
}
else
return (PICL_SUCCESS);
}
static int
{
char *ptr;
sizeof (name));
if (rc != PICL_SUCCESS)
return (rc);
}
static int
{
int ledfd;
/*
* Open the lw8 pseudo dev to set the led information
*/
return (PICL_FAILURE);
}
sizeof (lom_set_led.location));
} else {
}
return (PICL_PROPVALUNAVAILABLE);
}
return (PICL_SUCCESS);
}
static int
{
char *ptr;
sizeof (name));
if (rc != PICL_SUCCESS)
return (rc);
}
static void
disk_leds_init(void)
{
int err = 0, i;
if (!g_mutex_init) {
} else {
return;
}
}
if (ledsthr_created) {
/*
* this is a restart, wake up sleeping threads
*/
if (err != 0) {
return;
}
(void) pthread_cond_broadcast(&g_cv);
(void) pthread_mutex_unlock(&g_mutex);
} else {
if ((pthread_attr_init(&ledsthr_attr) != 0) ||
PTHREAD_SCOPE_SYSTEM) != 0))
return;
disk_leds_thread, NULL)) != 0) {
return;
}
}
for (i = 0; i < N_DISKS; i++) {
}
}
static void
disk_leds_fini(void)
{
int err;
/*
* tell led thread to pause
*/
if (!ledsthr_created)
return;
if (err != 0) {
return;
}
g_wait_now = B_TRUE;
(void) pthread_cond_broadcast(&g_cv);
/*
* and wait for the led thread to acknowledge
*/
while (!disk_leds_thread_ack) {
}
(void) pthread_mutex_unlock(&g_mutex);
}
static void
{
int err;
return;
}
if (err == PICL_SUCCESS) {
return;
if (err != PICL_SUCCESS) {
return;
}
if (err != PICL_SUCCESS)
return;
if (err != PICL_SUCCESS)
return;
if (err != PICL_SUCCESS)
return;
} else {
return;
if (err != PICL_SUCCESS)
return;
(void) ptree_destroy_node(diskndh);
}
}
/*
* Implement a state machine in order to:
*
*
* The machine changes state based on the current, in-memory
* state of the disk (eg, the d_state field of 'struct lw8_disk')
* and libdevice's current view of whether the disk is
* Configured or Unconfigured.
*
* If the new state is the same as the previous state, then
* no side effects occur. Otherwise, the LEDs for the
* disk are set and the disk's associated node in the
* FRU Tree is added or deleted.
*/
static void
{
return;
}
if ((cur_state & DEVICE_OFFLINE) != 0) {
default:
/*
* State machine should never get here.
* When NDEBUG is defined, control will
* fall through and force d_state to
* match the semantics of "DEVICE_OFFLINE".
* During development, NDEBUG can be undefined,
* and this will fire an assertion.
*/
assert(0);
/*FALLTHROUGH*/
case DISK_STATE_NOT_INIT:
case DISK_STATE_READY:
break;
case DISK_STATE_NOT_READY:
break;
}
} else if ((cur_state & DEVICE_ONLINE) != 0) {
default:
/*
* State machine should never get here.
* When NDEBUG is defined, control will
* fall through and force d_state to
* match the semantics of "DEVICE_ONLINE".
* During development, NDEBUG can be undefined,
* and this will fire an assertion.
*/
assert(0);
/*FALLTHROUGH*/
case DISK_STATE_NOT_INIT:
case DISK_STATE_NOT_READY:
break;
case DISK_STATE_READY:
break;
}
}
}
/*
* NOTE: this implementation of disk_leds_thread is based on the version in
* plugins/sun4u/mpxu/frudr/piclfrudr.c (with V440 raid support removed). Some
* day the source code layout and build environment should support common code
* used by platform specific plugins, in which case LW8 support could be added
* to the mpxu version (which would be moved to a common directory).
*/
/*ARGSUSED*/
static void *
{
int i;
int err = 0;
static char *lw8_pci_devs[] = {
};
static char *lw8_pcix_devs[] = {
};
static char **lw8_devs;
if (pcix_io) {
} else {
}
/*
* create aliases for disk names
*/
for (i = 0; i < n_disks; i++) {
lw8_devs[i]);
lw8_devs[i]);
}
for (;;) {
for (i = 0; i < n_disks; i++) {
set_disk_leds(&lw8_disks[i]);
}
/*
* wait a bit until we check again
*/
if (err == -1) {
break;
}
if (err != 0) {
break;
}
if (g_wait_now != B_FALSE) {
/* notify _fini routine that we've paused */
(void) pthread_cond_signal(&g_cv_ack);
/* and go to sleep in case we get restarted */
while (g_wait_now != B_FALSE)
}
(void) pthread_mutex_unlock(&g_mutex);
}
return ((void *)err);
}