xref: /illumos-gate/usr/src/cmd/picl/plugins/sun4u/lw8/frutree/piclfrutree.c (revision 1a220b56b93ff1dc80855691548503117af4cc10)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * This plugin-in creates the FRU Hierarchy for the
31  * SUNW,Netra-T12 platform and manages the environmental sensors
32  * on the platform.
33  */
34 
35 #include <stdio.h>
36 #include <errno.h>
37 #include <syslog.h>
38 #include <strings.h>
39 #include <libintl.h>
40 #include <stdlib.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <picl.h>
44 #include <picltree.h>
45 #include <sys/stat.h>
46 #include <libnvpair.h>
47 #include <sys/param.h>
48 #include <kstat.h>
49 #include <config_admin.h>
50 #include <sys/sbd_ioctl.h>
51 #include <sys/sgfrutree.h>
52 #include <sys/sgenv.h>
53 #include <sys/ioccom.h>
54 #include <sys/lw8.h>
55 #include <sys/sysevent/dr.h>
56 #include <pthread.h>
57 #include <sys/obpdefs.h>
58 #include "libdevice.h"
59 #include "picldefs.h"
60 #define	NDEBUG
61 #include <assert.h>
62 
63 /*
64  * Plugin registration entry points
65  */
66 static void	piclfrutree_register(void);
67 static void	piclfrutree_init(void);
68 static void	piclfrutree_fini(void);
69 #pragma	init(piclfrutree_register)
70 
71 static picld_plugin_reg_t  my_reg_info = {
72 	PICLD_PLUGIN_VERSION_1,
73 	PICLD_PLUGIN_CRITICAL,
74 	"SUNW_Netra-T12_frutree",
75 	piclfrutree_init,
76 	piclfrutree_fini,
77 };
78 
79 /*
80  * Log message texts
81  */
82 #define	DEV_OPEN_FAIL gettext("piclfrutree_init: open of %s failed: %s")
83 #define	ADD_NODES_FAIL gettext("piclfrutree_init: add_all_nodes failed: %d")
84 #define	GET_ROOT_FAIL gettext("piclfrutree_init: ptree_get_root failed")
85 #define	ADD_FRUTREE_FAIL gettext("piclfrutree_init: add frutree failed")
86 #define	INVALID_PICL_CLASS gettext("add_subtree: invalid picl class 0x%x")
87 #define	ADD_NODE_FAIL gettext("ptree_create_and_add_node %s failed: %d")
88 #define	GET_NEXT_BY_ROW_FAIL gettext("ptree_get_next_by_row %s failed: %d")
89 #define	PROPINFO_FAIL gettext("ptree_init_propinfo %s failed: %d")
90 #define	GET_PROPVAL_FAIL gettext("ptree_get_propval failed: %d")
91 #define	DELETE_PROP_FAIL gettext("ptree_delete_prop failed: %d")
92 #define	DELETE_NODE_FAIL gettext("ptree_delete_node failed: %d")
93 #define	ADD_PROP_FAIL gettext("ptree_create_and_add_prop %s failed: %d")
94 #define	SGFRU_IOCTL_FAIL gettext("sgfru ioctl 0x%x handle 0x%llx failed: %s")
95 #define	LED_IOCTL_FAIL gettext("led ioctl failed: %s")
96 #define	MALLOC_FAIL gettext("piclfrutree: malloc failed")
97 #define	NO_SC_FAIL gettext("piclfrutree: cannot find sc node")
98 #define	NO_NODE_FAIL gettext("piclfrutree: cannot find node %s: %d")
99 #define	KSTAT_FAIL gettext("piclfrutree: failure accessing kstats")
100 #define	ADD_TBL_ENTRY_FAIL gettext("piclfrutree: cannot add entry to table")
101 #define	PROP_LOOKUP_FAIL gettext("piclfrutree: cannot find %s property: %d")
102 #define	EM_DI_INIT_FAIL	gettext("frutree: di_init failed: %s")
103 #define	EM_THREAD_CREATE_FAILED gettext("frutree: pthread_create failed: %s")
104 #define	EM_MUTEX_FAIL gettext("frutree: pthread_mutex_lock returned: %s")
105 #define	EM_POLL_FAIL gettext("frutree: poll() failed: %s")
106 #define	DEVCTL_DEVICE_ACQUIRE_FAILED \
107     gettext("frutree: devctl_device_acquire() failed: %s")
108 
109 /*
110  * PICL property values
111  */
112 #define	PICL_PROPVAL_TRUE		"true"
113 #define	PICL_PROPVAL_SYSTEM		"system"
114 #define	PICL_PROPVAL_ON			"ON"
115 #define	PICL_PROPVAL_OFF		"OFF"
116 #define	PICL_PROPVAL_BLINKING		"BLINKING"
117 #define	PICL_PROPVAL_FLASHING		"FLASHING"
118 #define	PICL_PROPVAL_CHASSIS		"chassis"
119 #define	PICL_PROPVAL_AMBIENT		"Ambient"
120 #define	PICL_PROPVAL_DIE		"Die"
121 #define	PICL_PROPVAL_GREEN		"green"
122 #define	PICL_PROPVAL_AMBER		"amber"
123 #define	PICL_PROPVAL_OKAY		"okay"
124 #define	PICL_PROPVAL_FAILED		"failed"
125 #define	PICL_PROPVAL_WARNING		"warning"
126 #define	PICL_PROPVAL_DISABLED		"disabled"
127 #define	PICL_PROPVAL_UNKNOWN		"unknown"
128 #define	PICL_PROPVAL_SELF_REGULATING	"self-regulating"
129 #define	PICL_PROPVAL_PER_CENT 		"%"
130 #define	PICL_PROP_BANK_STATUS		"bank-status"
131 
132 /*
133  * PICL property names
134  */
135 #define	PICL_PROP_LOW_WARNING_THRESHOLD	"LowWarningThreshold"
136 
137 /*
138  * Local defines
139  */
140 #define	MAX_LINE_SIZE		1024
141 #define	MAX_TRIES		4
142 #define	MAX_SPEED_UNIT_LEN	20
143 #define	MAX_OPERATIONAL_STATUS_LEN	10
144 #define	MAX_CONDITION_LEN	10
145 #define	MAX_LABEL_LEN		256
146 #define	MAX_STATE_LEN		10
147 #define	MAX_STATE_SIZE		32
148 #define	LED_PSEUDO_DEV "/devices/pseudo/lw8@0:lw8"
149 #define	SC_DEV "/platform/ssm@0,0/pci@18,700000/bootbus-controller@4"
150 #define	SC_DEV_PCIX "/platform/ssm@0,0/pci@18,700000/pci@4/bootbus-controller@3"
151 #define	CPU_DEV "/platform/ssm@0,0/SUNW,UltraSPARC-III@%x,0"
152 #define	CPU_DEV2 "/platform/ssm@0,0/SUNW,UltraSPARC-III+@%x,0"
153 #define	CPU_DEV3C0 "/platform/ssm@0,0/cmp@%x,0/cpu@0"
154 #define	CPU_DEV3C1 "/platform/ssm@0,0/cmp@%x,0/cpu@1"
155 #define	MEMORY_DEV "/platform/ssm@0,0/memory-controller@%x,400000"
156 #define	IO_DEV "/platform/ssm@0,0/pci@%s"
157 #define	DISK0_BASE_PATH "/ssm@0,0/pci@18,600000/scsi@2/sd@0,0"
158 #define	DISK0_DEV "/platform" DISK0_BASE_PATH
159 #define	DISK1_BASE_PATH "/ssm@0,0/pci@18,600000/scsi@2/sd@1,0"
160 #define	DISK1_DEV "/platform" DISK1_BASE_PATH
161 #define	DISK0_BASE_PATH_PCIX "/ssm@0,0/pci@18,700000/scsi@2/sd@0,0"
162 #define	DISK0_DEV_PCIX "/platform" DISK0_BASE_PATH_PCIX
163 #define	DISK1_BASE_PATH_PCIX "/ssm@0,0/pci@18,700000/scsi@2/sd@1,0"
164 #define	DISK1_DEV_PCIX "/platform" DISK1_BASE_PATH_PCIX
165 #define	TAPE_DEV "/platform/ssm@0,0/pci@18,600000/scsi@2/st@5,0"
166 #define	TAPE_DEV_PCIX "/platform/ssm@0,0/pci@18,700000/scsi@2/st@5,0"
167 #define	DVD_DEV "/platform/ssm@0,0/pci@18,700000/ide@3/sd@0,0"
168 #define	DVD_DEV_PCIX "/platform/ssm@0,0/pci@18,700000/pci@4/ide@2/sd@0,0"
169 #define	CHASSIS_PATH "/frutree/chassis"
170 #define	CHASSIS_LOC_PATH "/frutree/chassis/%s"
171 #define	PROC_LOC_PATH "/frutree/chassis/SB%d/SB%d/P%d"
172 #define	PROC_FRU_PATH "/frutree/chassis/SB%d/SB%d/P%d/P%d"
173 /*
174  * Calculate safari address to put in CPU_DEV/MEMORY_DEV string based on
175  * SBx/Py fru path name
176  */
177 #define	SB_P_TO_SAFARI_ADDR(sbname, pname) \
178 	((pname[1] - '0') + (4 * (sbname[2] - '0')))
179 #define	SAFARI_ADDR_TO_SB(value) (value >> 2)
180 #define	SAFARI_ADDR_TO_P(value) (value & 3)
181 #define	AP_ID_PREAMBLE "ssm0:N0."
182 #define	AP_ID_PREAMBLE_LEN 8
183 #define	LABEL_PREAMBLE "N0/"
184 #define	LABEL_PREAMBLE_LEN 3
185 /*
186  * work out type of fru based on name
187  */
188 #define	IS_ECACHE_NODE(name)	(name[0] == 'E')
189 #define	IS_DIMM_NODE(name)	(name[0] == 'D' && name[1] != 'V')
190 #define	IS_PROC_NODE(name)	(name[0] == 'P' && name[1] != 'S')
191 #define	IS_PSU_NODE(name)	(name[0] == 'P' && name[1] == 'S')
192 #define	IS_SB_NODE(name)	(name[0] == 'S' && name[1] == 'B')
193 #define	IS_IB_NODE(name)	(name[0] == 'I')
194 #define	IS_FT_NODE(name)	(name[0] == 'F' && name[1] == 'T')
195 #define	IS_FAN_NODE(name)	(name[0] == 'F' && name[1] != 'T')
196 #define	IS_RP_NODE(name)	(name[0] == 'R')
197 /*
198  * rename sgfru driver's node_t to sgfrunode_t to avoid confusion
199  */
200 #define	sgfrunode_t node_t
201 
202 /*
203  * disk_led data
204  */
205 #define	REMOK_LED "ok_to_remove"
206 #define	FAULT_LED "fault"
207 #define	POWER_LED "power"
208 
209 /*
210  * 'struct lw8_disk' contains the per-disk metadata needed to
211  * manage the current state of one of the internal disks.
212  *
213  * 'lw8_disks[]' is an array that contains the metadata
214  * for N_DISKS disks.
215  *
216  * The d_fruname field of 'struct lw8_disk' is static.
217  * d_plat_path and d_devices_path are aliases for device-paths
218  * to the disk.  They are logically static, as they are computed
219  * when the disk_leds_thread() thread does its initialization.
220  *
221  * d_state is the most interesting field, as it changes
222  * dynamically, based on whether the associated disk
223  * is currently Configured or Unconfigured (by DR).  d_state
224  * is an optimization that minimizes per-disk actions such
225  * as setting of LEDs and updating the FRU Tree.
226  *
227  * A disk starts in a d_state of DISK_STATE_NOT_INIT
228  * and moves to DISK_STATE_READY when the disk is
229  * Configured (by DR) and it moves to DISK_STATE_NOT_READY
230  * when it is Unconfigured (by DR).
231  */
232 typedef enum {
233 	DISK_STATE_NOT_INIT,
234 	DISK_STATE_READY,
235 	DISK_STATE_NOT_READY
236 } disk_state_t;
237 
238 struct lw8_disk {
239 	char		*d_fruname;		/* FRU name */
240 	char		*d_plat_path;		/* /platform */
241 	char		*d_devices_path;	/* /devices */
242 	disk_state_t	d_state;
243 };
244 
245 #define	N_DISKS 2
246 static	struct lw8_disk	lw8_disks[N_DISKS] = {
247 	{"DISK0", NULL, NULL, DISK_STATE_NOT_INIT},
248 	{"DISK1", NULL, NULL, DISK_STATE_NOT_INIT} };
249 
250 /* Duration of inactivity within disk_leds_thread() */
251 #define	THR_POLL_PERIOD 5000    /* milliseconds */
252 
253 static volatile boolean_t	disk_leds_thread_ack = B_FALSE;
254 static pthread_t		ledsthr_tid;
255 static pthread_attr_t		ledsthr_attr;
256 static boolean_t		ledsthr_created = B_FALSE;
257 static uint_t			ledsthr_poll_period =
258 				    THR_POLL_PERIOD;
259 static boolean_t		g_mutex_init = B_FALSE;
260 static pthread_cond_t		g_cv;
261 static pthread_cond_t		g_cv_ack;
262 static pthread_mutex_t		g_mutex;
263 static volatile boolean_t	g_wait_now = B_FALSE;
264 
265 static void disk_leds_init(void);
266 static void disk_leds_fini(void);
267 static void *disk_leds_thread(void *args);
268 
269 /*
270  * Tables to convert sgenv information
271  */
272 static char *hpu_type_table[] = { "", "SSC", "SB", "RP", "FT",
273 	"IB", "PS", "ID"};
274 static char *hpu_fru_type_table[] = { "", "SSC", "CPU", "RP", "FT",
275 	"PCIB", "PS", "ID"};
276 static char *hpu_part_table[] = { "", "sbbc", "sdc",
277 	"ar", "cbh", "dx", "cheetah", "1.5vdc", "3.3vdc",
278 	"5vdc", "12vdc", "output", "current", "board", "sc-app",
279 	"schizo", "fan", "input"};
280 static char *hpu_sensor_table[] = { "", "", "current",
281 	"temp", "cooling", "1.5vdc", "1.8vdc", "3.3vdc", "5vdc",
282 	"12vdc", "48vdc", NULL, "2.4vdc"};
283 static char *hpu_sensor_class_table[] = { "", "", PICL_CLASS_CURRENT_SENSOR,
284 	PICL_CLASS_TEMPERATURE_SENSOR, PICL_CLASS_FAN,
285 	PICL_CLASS_VOLTAGE_SENSOR, PICL_CLASS_VOLTAGE_SENSOR,
286 	PICL_CLASS_VOLTAGE_SENSOR, PICL_CLASS_VOLTAGE_SENSOR,
287 	PICL_CLASS_VOLTAGE_SENSOR, PICL_CLASS_VOLTAGE_INDICATOR,
288 	NULL, PICL_CLASS_VOLTAGE_SENSOR};
289 static char *hpu_sensor_prop_table[] = { "", "", PICL_PROP_CURRENT,
290 	PICL_PROP_TEMPERATURE, PICL_PROP_FAN_SPEED, PICL_PROP_VOLTAGE,
291 	PICL_PROP_VOLTAGE, PICL_PROP_VOLTAGE, PICL_PROP_VOLTAGE,
292 	PICL_PROP_VOLTAGE, PICL_PROP_CONDITION, NULL, PICL_PROP_VOLTAGE};
293 static char *hpu_condition_table[] = {"unknown", "okay", "failing",
294 	"failed", "unusable"};
295 
296 /*
297  * variables set up in init
298  */
299 static picl_nodehdl_t	frutreeh;
300 static picl_nodehdl_t	sch = NULL;
301 static int init_complete;
302 static int pcix_io = 0;
303 
304 /*
305  * forward reference
306  */
307 static int add_all_nodes(void);
308 static int remove_subtree(picl_nodehdl_t parh);
309 static int add_subtree(picl_nodehdl_t parh, fru_hdl_t fruparent);
310 static int add_picl_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
311     picl_nodehdl_t *childp);
312 static int add_chassis_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
313     picl_nodehdl_t *childp);
314 static int add_fru_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
315     picl_nodehdl_t *childp);
316 static int add_location_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
317     picl_nodehdl_t *childp);
318 static int add_led_nodes(picl_nodehdl_t nodeh, char *name, int position,
319     picl_prophdl_t tblhdl);
320 static int add_env_nodes(picl_nodehdl_t nodeh, char *nodename,
321     picl_prophdl_t tblhdl);
322 static int add_intermediate_nodes(picl_nodehdl_t *nodep, char *labelp,
323     picl_prophdl_t *tblhdlp, char *slot_name, char *fru_name);
324 static int add_intermediate_location(picl_nodehdl_t *nodep, char *labelp,
325     char *slot_name);
326 static int add_pci_location(picl_nodehdl_t childh, char *parent_addr,
327     char bus_addr, char *slot_name);
328 static picl_nodehdl_t find_child_by_name(picl_nodehdl_t parh, char *name);
329 static int create_dimm_references(picl_nodehdl_t parh, int dimm_id,
330     picl_nodehdl_t nodeh, picl_prophdl_t tblhdl);
331 static int create_cpu_references(char *pname, picl_nodehdl_t nodeh,
332     picl_prophdl_t tblhdl);
333 static void post_frudr_event(char *ename, picl_nodehdl_t parenth,
334     picl_nodehdl_t fruh);
335 static int remove_references(picl_prophdl_t refprop, char *class);
336 static int remove_picl_node(picl_nodehdl_t nodeh);
337 static sgfrunode_t *get_node_children(fru_hdl_t fruparent, int *num_childrenp);
338 static int add_prop_ull(picl_nodehdl_t nodeh, uint64_t handle, char *name);
339 static int add_prop_void(picl_nodehdl_t nodeh, char *name);
340 static int add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name);
341 static int add_prop_int(picl_nodehdl_t nodeh, int value, char *name);
342 static int add_prop_float(picl_nodehdl_t nodeh, float value, char *name);
343 static int add_prop_charstring(picl_nodehdl_t nodeh, char *value, char *name);
344 static void frudr_evhandler(const char *ename, const void *earg,
345     size_t size, void *cookie);
346 static void frumemcfg_evhandler(const char *ename, const void *earg,
347     size_t size, void *cookie);
348 static int add_sensor_prop(picl_nodehdl_t nodeh, char *class);
349 static int add_sensor_node(picl_nodehdl_t fruhdl, picl_nodehdl_t lochdl,
350     char *nodename, char *class, char *prop_class,
351     picl_prophdl_t tblhdl, picl_nodehdl_t *sensorhdlp);
352 static int create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp,
353     char *tbl_name);
354 static int create_table_entry(picl_prophdl_t tblhdl,
355     picl_nodehdl_t refhdl, char *class);
356 static int get_sensor_data(ptree_rarg_t *arg, void *result);
357 static int get_led(char *name, char *ptr, char *result);
358 static int get_led_data(ptree_rarg_t *arg, void *result);
359 static int set_led_data(ptree_warg_t *arg, const void *value);
360 static int get_cpu_status(ptree_rarg_t *arg, void *result);
361 static int add_board_status(picl_nodehdl_t nodeh, char *nodename);
362 static int get_board_status(ptree_rarg_t *arg, void *result);
363 static int get_op_status(ptree_rarg_t *arg, void *result);
364 
365 #define	sprintf_buf2(buf, a1, a2) (void) snprintf(buf, sizeof (buf), a1, a2)
366 #define	sprintf_buf3(buf, a1, a2, a3) \
367 	(void) snprintf(buf, sizeof (buf), a1, a2, a3)
368 #define	sprintf_buf4(buf, a1, a2, a3, a4) \
369 	(void) snprintf(buf, sizeof (buf), a1, a2, a3, a4)
370 #define	sprintf_buf5(buf, a1, a2, a3, a4, a5) \
371 	(void) snprintf(buf, sizeof (buf), a1, a2, a3, a4, a5)
372 /*
373  * This function is executed as part of .init when the plugin is
374  * dlopen()ed
375  */
376 static void
377 piclfrutree_register(void)
378 {
379 	(void) picld_plugin_register(&my_reg_info);
380 }
381 
382 /*
383  * This function is the init entry point of the plugin.
384  * It initializes the /frutree tree
385  */
386 static void
387 piclfrutree_init(void)
388 {
389 	int err;
390 
391 	(void) ptree_register_handler(PICLEVENT_DR_AP_STATE_CHANGE,
392 	    frudr_evhandler, NULL);
393 	(void) ptree_register_handler(PICLEVENT_MC_ADDED,
394 	    frumemcfg_evhandler, NULL);
395 	(void) ptree_register_handler(PICLEVENT_MC_REMOVED,
396 	    frumemcfg_evhandler, NULL);
397 	init_complete = 0;
398 
399 	err = add_all_nodes();
400 	disk_leds_init();
401 	init_complete = 1;
402 	if (err != PICL_SUCCESS) {
403 		syslog(LOG_ERR, ADD_NODES_FAIL, err);
404 		piclfrutree_fini();
405 	}
406 }
407 
408 /*
409  * This function is the fini entry point of the plugin.
410  */
411 static void
412 piclfrutree_fini(void)
413 {
414 	(void) ptree_unregister_handler(PICLEVENT_DR_AP_STATE_CHANGE,
415 	    frudr_evhandler, NULL);
416 	(void) ptree_unregister_handler(PICLEVENT_MC_ADDED,
417 	    frumemcfg_evhandler, NULL);
418 	(void) ptree_unregister_handler(PICLEVENT_MC_REMOVED,
419 	    frumemcfg_evhandler, NULL);
420 	(void) remove_subtree(frutreeh);
421 	disk_leds_fini();
422 }
423 
424 /*
425  * called from piclfrutree_init() to initialise picl frutree
426  */
427 static int
428 add_all_nodes(void)
429 {
430 	int err;
431 	picl_nodehdl_t rooth;
432 
433 	/* Get the root node of the PICL tree */
434 	err = ptree_get_root(&rooth);
435 	if (err != PICL_SUCCESS) {
436 		syslog(LOG_ERR, GET_ROOT_FAIL);
437 		return (err);
438 	}
439 
440 	/* find sc node so we can create sensor nodes under it */
441 
442 	err = ptree_get_node_by_path(SC_DEV, &sch);
443 	if (err != PICL_SUCCESS) {
444 
445 		/*
446 		 * There is a XMITS/PCI-X IO Board assembly implements
447 		 * a different path for the the bootbus controller.
448 		 */
449 		err = ptree_get_node_by_path(SC_DEV_PCIX, &sch);
450 		if (err == PICL_SUCCESS)
451 			pcix_io = 1;
452 	}
453 
454 	if (err != PICL_SUCCESS) {
455 		syslog(LOG_ERR, NO_SC_FAIL);
456 		return (err);
457 	}
458 
459 	/* Create and add the root node of the FRU subtree */
460 	err = ptree_create_and_add_node(rooth, PICL_NODE_FRUTREE,
461 	    PICL_CLASS_PICL, &frutreeh);
462 	if (err != PICL_SUCCESS) {
463 		syslog(LOG_ERR, ADD_FRUTREE_FAIL);
464 		return (err);
465 	}
466 
467 	/* Recursively query the SC and add frutree nodes */
468 	return (add_subtree(frutreeh, ROOTPARENT));
469 }
470 
471 /*
472  * Recursive routine to add picl nodes to the frutree. Called from
473  * add_all_nodes() for the whole frutree at initialisation, and from
474  * frudr_evhandler() for portions of the frutree on DR insert events
475  */
476 static int
477 add_subtree(picl_nodehdl_t parh, fru_hdl_t handle)
478 {
479 	int	err, i;
480 	int	num_children;
481 	sgfrunode_t	*cp, *fruchildren = NULL;
482 	picl_nodehdl_t childh;
483 
484 	/* find children of the parent node */
485 	fruchildren = get_node_children(handle, &num_children);
486 	if (fruchildren == NULL)
487 		return (PICL_FAILURE);
488 
489 	/* for each child, add a new picl node */
490 	for (i = 0, cp = fruchildren; i < num_children; i++, cp++) {
491 		/*
492 		 * Add the appropriate PICL class
493 		 */
494 		childh = 0;
495 		err = add_picl_node(parh, cp, &childh);
496 		if (err == PICL_NOTNODE)
497 			continue;
498 		if (err != PICL_SUCCESS) {
499 			free(fruchildren);
500 			return (err);
501 		}
502 
503 		/*
504 		 * Recursively call this function based on has_children hint
505 		 */
506 		if (childh && cp->has_children) {
507 			err = add_subtree(childh, cp->handle);
508 			if (err != PICL_SUCCESS) {
509 				free(fruchildren);
510 				return (err);
511 			}
512 		}
513 	}
514 	free(fruchildren);
515 	return (PICL_SUCCESS);
516 }
517 
518 /*
519  * Recursive routine to remove picl nodes to the frutree. Called from
520  * piclfrutree_fini() for the whole frutree at termination, and from
521  * frudr_completion_handler() for portions of the frutree on DR remove events
522  */
523 static int
524 remove_subtree(picl_nodehdl_t parh)
525 {
526 	picl_nodehdl_t chdh;
527 
528 	for (;;) {
529 		if (ptree_get_propval_by_name(parh, PICL_PROP_CHILD, &chdh,
530 		    sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
531 			if (remove_subtree(chdh) != PICL_SUCCESS)
532 				return (PICL_FAILURE);
533 		} else {
534 			return (remove_picl_node(parh));
535 		}
536 	}
537 	/* NOTREACHED */
538 }
539 
540 /*
541  * Add fru and location nodes with SC_handle property
542  * (aka, container handle, for frus).
543  * Return picl_nodehdl of created node in *childp.
544  */
545 static int
546 add_picl_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
547     picl_nodehdl_t *childp)
548 {
549 	switch (sgfrunode->class) {
550 	case PSEUDO_FRU_CLASS:
551 		return (add_chassis_node(parh, sgfrunode, childp));
552 
553 	case FRU_CLASS:
554 		return (add_fru_node(parh, sgfrunode, childp));
555 
556 	case LOCATION_CLASS:
557 		return (add_location_node(parh, sgfrunode, childp));
558 
559 	default:
560 		syslog(LOG_ERR, INVALID_PICL_CLASS, sgfrunode->class);
561 		return (PICL_NOTNODE);
562 	}
563 }
564 
565 /*
566  * create chassis node
567  */
568 static int
569 add_chassis_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
570     picl_nodehdl_t *childp)
571 {
572 	int err;
573 	uint64_t handle = (uint64_t)sgfrunode->handle;
574 	picl_prophdl_t	tblhdl;
575 	picl_nodehdl_t nodeh;
576 	picl_nodehdl_t devhdl;
577 	picl_nodehdl_t childh;
578 
579 	err = ptree_create_and_add_node(parh, PICL_PROPVAL_CHASSIS,
580 	    PICL_CLASS_FRU, &childh);
581 	if (err != PICL_SUCCESS) {
582 		syslog(LOG_ERR, ADD_NODE_FAIL, PICL_PROPVAL_CHASSIS, err);
583 		return (err);
584 	}
585 	err = add_prop_ull(childh, handle, PICL_PROP_SC_HANDLE);
586 	if (err != PICL_SUCCESS)
587 		return (err);
588 
589 	/*
590 	 * add devices table to chassis node (may need references
591 	 * to led devices)
592 	 */
593 	err = create_table(childh, &tblhdl, PICL_PROP_DEVICES);
594 	if (err != PICL_SUCCESS)
595 		return (err);
596 
597 	err = add_led_nodes(childh, "chassis", LOM_LED_POSITION_FRU, tblhdl);
598 	if (err != PICL_SUCCESS)
599 		return (err);
600 
601 	if (pcix_io)
602 		err = ptree_get_node_by_path(DISK0_DEV_PCIX, &devhdl);
603 	else
604 		err = ptree_get_node_by_path(DISK0_DEV, &devhdl);
605 
606 	nodeh = childh;
607 	if (err != PICL_SUCCESS) {
608 		err = add_intermediate_location(&nodeh, "DISK0", "disk-slot");
609 	} else {
610 		err = add_intermediate_nodes(&nodeh, "DISK0", &tblhdl,
611 		    "disk-slot", NULL);
612 		if (err != PICL_SUCCESS)
613 			return (err);
614 		err = add_prop_ref(devhdl, nodeh, PICL_REFPROP_FRU_PARENT);
615 		if (err != PICL_SUCCESS)
616 			return (err);
617 		err = create_table_entry(tblhdl, devhdl, PICL_CLASS_BLOCK);
618 	}
619 	if (err != PICL_SUCCESS)
620 		return (err);
621 
622 	if (pcix_io)
623 		err = ptree_get_node_by_path(DISK1_DEV_PCIX, &devhdl);
624 	else
625 		err = ptree_get_node_by_path(DISK1_DEV, &devhdl);
626 
627 	nodeh = childh;
628 	if (err != PICL_SUCCESS) {
629 		err = add_intermediate_location(&nodeh, "DISK1", "disk-slot");
630 	} else {
631 		err = add_intermediate_nodes(&nodeh, "DISK1", &tblhdl,
632 		    "disk-slot", NULL);
633 		if (err != PICL_SUCCESS)
634 			return (err);
635 		err = add_prop_ref(devhdl, nodeh, PICL_REFPROP_FRU_PARENT);
636 		if (err != PICL_SUCCESS)
637 			return (err);
638 		err = create_table_entry(tblhdl, devhdl, PICL_CLASS_BLOCK);
639 	}
640 	if (err != PICL_SUCCESS)
641 		return (err);
642 
643 	if (pcix_io)
644 		err = ptree_get_node_by_path(TAPE_DEV_PCIX, &devhdl);
645 	else
646 		err = ptree_get_node_by_path(TAPE_DEV, &devhdl);
647 
648 	nodeh = childh;
649 	if (err != PICL_SUCCESS) {
650 		err = add_intermediate_location(&nodeh, "TAPE", "tape-slot");
651 	} else {
652 		err = add_intermediate_nodes(&nodeh, "TAPE", &tblhdl,
653 		    "tape-slot", NULL);
654 		if (err != PICL_SUCCESS)
655 			return (err);
656 		err = add_prop_ref(devhdl, nodeh, PICL_REFPROP_FRU_PARENT);
657 		if (err != PICL_SUCCESS)
658 			return (err);
659 		err = create_table_entry(tblhdl, devhdl, PICL_CLASS_TAPE);
660 	}
661 	if (err != PICL_SUCCESS)
662 		return (err);
663 
664 	if (pcix_io)
665 		err = ptree_get_node_by_path(DVD_DEV_PCIX, &devhdl);
666 	else
667 		err = ptree_get_node_by_path(DVD_DEV, &devhdl);
668 
669 	nodeh = childh;
670 	if (err != PICL_SUCCESS) {
671 		err = add_intermediate_location(&nodeh, "DVD", "dvd-slot");
672 	} else {
673 		err = add_intermediate_nodes(&nodeh, "DVD", &tblhdl,
674 		    "dvd-slot", NULL);
675 		if (err != PICL_SUCCESS)
676 			return (err);
677 		err = add_prop_ref(devhdl, nodeh, PICL_REFPROP_FRU_PARENT);
678 		if (err != PICL_SUCCESS)
679 			return (err);
680 		err = create_table_entry(tblhdl, devhdl, PICL_CLASS_CDROM);
681 	}
682 	if (err != PICL_SUCCESS)
683 		return (err);
684 
685 	if (pcix_io) {
686 		/*
687 		 * The XMITS/PCI-X IO Assembly is layed out a bit differently.
688 		 */
689 		err = add_pci_location(childh, "19,600000", '1', "PCI0");
690 		if (err != PICL_SUCCESS)
691 			return (err);
692 		err = add_pci_location(childh, "19,600000", '2', "PCI1");
693 		if (err != PICL_SUCCESS)
694 			return (err);
695 		err = add_pci_location(childh, "19,700000", '1', "PCI2");
696 		if (err != PICL_SUCCESS)
697 			return (err);
698 		err = add_pci_location(childh, "19,700000", '2', "PCI3");
699 		if (err != PICL_SUCCESS)
700 			return (err);
701 		err = add_pci_location(childh, "18,600000", '1', "PCI4");
702 		if (err != PICL_SUCCESS)
703 			return (err);
704 		err = add_pci_location(childh, "18,600000", '2', "PCI5");
705 		if (err != PICL_SUCCESS)
706 			return (err);
707 	} else {
708 		err = add_pci_location(childh, "18,700000", '1', "PCI0");
709 		if (err != PICL_SUCCESS)
710 			return (err);
711 		err = add_pci_location(childh, "18,700000", '2', "PCI1");
712 		if (err != PICL_SUCCESS)
713 			return (err);
714 		err = add_pci_location(childh, "19,700000", '1', "PCI2");
715 		if (err != PICL_SUCCESS)
716 			return (err);
717 		err = add_pci_location(childh, "19,700000", '2', "PCI3");
718 		if (err != PICL_SUCCESS)
719 			return (err);
720 		err = add_pci_location(childh, "19,700000", '3', "PCI4");
721 		if (err != PICL_SUCCESS)
722 			return (err);
723 		err = add_pci_location(childh, "18,600000", '1', "PCI5");
724 		if (err != PICL_SUCCESS)
725 			return (err);
726 	}
727 	*childp = childh;
728 	return (PICL_SUCCESS);
729 }
730 
731 /*
732  * create fru node, based on sgfru node "sgfrunode" under parent parh. Return
733  * picl_nodehdl of created node in *childp.
734  */
735 static int
736 add_fru_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
737     picl_nodehdl_t *childp)
738 {
739 	int err;
740 	picl_prophdl_t	tblhdl;
741 	picl_nodehdl_t childh;
742 	uint64_t handle = (uint64_t)sgfrunode->handle;
743 	char *nodename = sgfrunode->nodename;
744 
745 	/*
746 	 * if sgfrunode already there, then just carry on own the tree
747 	 */
748 	childh = find_child_by_name(parh, nodename);
749 	if (childh != NULL) {
750 		/*
751 		 * for frus other than dimms and ecaches, update environmental
752 		 * sensors and board status if necessary
753 		 */
754 		if (IS_ECACHE_NODE(nodename)) {
755 			*childp = childh;
756 			return (PICL_SUCCESS);
757 		}
758 		if (IS_DIMM_NODE(nodename)) {
759 			/*
760 			 * for dimms we just want status
761 			 */
762 			err = add_board_status(childh, nodename);
763 			if (err != PICL_SUCCESS)
764 				return (err);
765 			*childp = childh;
766 			return (PICL_SUCCESS);
767 		}
768 		err = add_board_status(childh, nodename);
769 		if (err != PICL_SUCCESS)
770 			return (err);
771 		err = ptree_get_propval_by_name(childh, PICL_PROP_DEVICES,
772 		    &tblhdl, sizeof (tblhdl));
773 		if (err != PICL_SUCCESS)
774 			return (err);
775 		err = add_env_nodes(childh, nodename, tblhdl);
776 		if (err != PICL_SUCCESS)
777 			return (err);
778 		*childp = childh;
779 		return (PICL_SUCCESS);
780 	}
781 
782 	/*
783 	 * create requested fru node
784 	 */
785 	err = ptree_create_and_add_node(parh, nodename, PICL_CLASS_FRU,
786 	    &childh);
787 	if (err != PICL_SUCCESS) {
788 		syslog(LOG_ERR, ADD_NODE_FAIL, nodename, err);
789 		return (err);
790 	}
791 
792 	/*
793 	 * if sgfru has sent us a valid handle, then there is fruid information.
794 	 * create the SC_handle, and FRUDateAvailable properties for FRUID.
795 	 */
796 	if (handle != -1ULL) {
797 		err = add_prop_ull(childh, handle, PICL_PROP_SC_HANDLE);
798 		if (err != PICL_SUCCESS)
799 			return (err);
800 		err = add_prop_void(childh, PICL_PROP_FRUDATA_AVAIL);
801 		if (err != PICL_SUCCESS)
802 			return (err);
803 	}
804 
805 	/*
806 	 * post fru added event to fru data plugin if this was due to
807 	 * a dr event - ie post-initialisation
808 	 */
809 	if (init_complete)
810 		post_frudr_event(PICL_FRU_ADDED, parh, NULL);
811 
812 	/*
813 	 * Create empty Devices table - we'll add lines to it as we go along
814 	 */
815 	err = create_table(childh, &tblhdl, PICL_PROP_DEVICES);
816 	if (err != PICL_SUCCESS)
817 		return (err);
818 
819 	/*
820 	 * Ecache nodes don't have sensors - just set up FRUType
821 	 */
822 	if (IS_ECACHE_NODE(nodename)) {
823 		err = add_prop_charstring(childh, "EEPROM", PICL_PROP_FRU_TYPE);
824 		if (err != PICL_SUCCESS)
825 			return (err);
826 		*childp = childh;
827 		return (PICL_SUCCESS);
828 	}
829 
830 	/*
831 	 * Dimm nodes don't have sensors - just set up FRUType and
832 	 * also reference properties to memory module nodes and OpStatus
833 	 */
834 	if (IS_DIMM_NODE(nodename)) {
835 		err = add_prop_charstring(childh, "DIMM", PICL_PROP_FRU_TYPE);
836 		if (err != PICL_SUCCESS)
837 			return (err);
838 		err = create_dimm_references(parh, nodename[1] - '0',
839 		    childh, tblhdl);
840 		if (err != PICL_SUCCESS)
841 			return (err);
842 		err = add_board_status(childh, nodename);
843 		if (err != PICL_SUCCESS)
844 			return (err);
845 		*childp = childh;
846 		return (PICL_SUCCESS);
847 	}
848 
849 	/*
850 	 * not a Dimm or Ecache node - set up environmental info,
851 	 * board status and led info
852 	 */
853 	err = add_env_nodes(childh, nodename, tblhdl);
854 	if (err != PICL_SUCCESS)
855 		return (err);
856 
857 	err = add_board_status(childh, nodename);
858 	if (err != PICL_SUCCESS)
859 		return (err);
860 
861 	err = add_led_nodes(childh, nodename, LOM_LED_POSITION_FRU, tblhdl);
862 	if (err != PICL_SUCCESS)
863 		return (err);
864 
865 	*childp = childh;
866 	return (PICL_SUCCESS);
867 }
868 
869 /*
870  * create location node, based on sgfru node "sgfrunode" under parent parh.
871  * Return picl_nodehdl of created node in *childp.
872  */
873 static int
874 add_location_node(picl_nodehdl_t parh, sgfrunode_t *sgfrunode,
875     picl_nodehdl_t *childp)
876 {
877 	int err;
878 	uint64_t handle = (uint64_t)sgfrunode->handle;
879 	char *labelp;
880 	char	label[MAX_LABEL_LEN];
881 	char *ptr;
882 	picl_prophdl_t tblhdl;
883 	picl_nodehdl_t childh;
884 
885 	/*
886 	 * strip "N0/" off the label if present (hang-over from wildcat)
887 	 */
888 	if (strncmp(sgfrunode->location_label, LABEL_PREAMBLE,
889 	    LABEL_PREAMBLE_LEN) == 0)
890 		(void) strlcpy(label, &sgfrunode->location_label[
891 		    LABEL_PREAMBLE_LEN], sizeof (label));
892 	else
893 		(void) strlcpy(label, &sgfrunode->location_label[0],
894 		    sizeof (label));
895 
896 	/*
897 	 * some of the locations returned by sgfru are actually of the form
898 	 * XX/YY/ZZ - we need to create multiple levels in the picl tree for
899 	 * these.
900 	 */
901 	labelp = label;
902 	while ((ptr = strchr(labelp, '/')) != NULL) {
903 		/*
904 		 * null end of this section of label
905 		 */
906 		*ptr = '\0';
907 
908 		/*
909 		 * add intermediate nodes - parh will point to the created node
910 		 */
911 		if (IS_PROC_NODE(labelp)) {
912 			err = add_intermediate_nodes(&parh, labelp, &tblhdl,
913 			    "cpu", "PROC");
914 		} else {
915 			err = add_intermediate_nodes(&parh, labelp, &tblhdl,
916 			    NULL, NULL);
917 		}
918 		if (err != PICL_SUCCESS)
919 			return (err);
920 		/*
921 		 * if processor node, then create links to associated cpu node
922 		 * and OpStatus property
923 		 */
924 		if (IS_PROC_NODE(labelp)) {
925 			err = create_cpu_references(labelp, parh, tblhdl);
926 			if (err != PICL_SUCCESS)
927 				return (err);
928 			err = add_board_status(parh, labelp);
929 			if (err != PICL_SUCCESS)
930 				return (err);
931 		}
932 		labelp = ptr + 1;
933 
934 		/*
935 		 * set back to "/"
936 		 */
937 		*ptr = '/';
938 	}
939 
940 	/*
941 	 * if node already there, then just carry on down the tree
942 	 */
943 	childh = find_child_by_name(parh, labelp);
944 	if (childh != NULL) {
945 		*childp = childh;
946 		return (PICL_SUCCESS);
947 	}
948 
949 	/*
950 	 * now just have the final level of the node left. First create it.
951 	 */
952 	err = ptree_create_and_add_node(parh, labelp, PICL_CLASS_LOCATION,
953 	    &childh);
954 	if (err != PICL_SUCCESS) {
955 		syslog(LOG_ERR, ADD_NODE_FAIL, labelp, err);
956 		return (err);
957 	}
958 
959 	/*
960 	 * if sgfru has sent us a valid handle, then there is fruid information.
961 	 * create the SC_handle property for FRUID.
962 	 */
963 	if (handle != -1ULL) {
964 		err = add_prop_ull(childh, handle, PICL_PROP_SC_HANDLE);
965 		if (err != PICL_SUCCESS)
966 			return (err);
967 	}
968 
969 	/* create label property for location class */
970 	err = add_prop_charstring(childh, labelp, PICL_PROP_LABEL);
971 	if (err != PICL_SUCCESS)
972 		return (err);
973 
974 	/* create SlotType property where appropriate */
975 	if (IS_ECACHE_NODE(sgfrunode->nodename)) {
976 		err = add_prop_charstring(childh,
977 		    "ecache", PICL_PROP_SLOT_TYPE);
978 		/*
979 		 * For Ecache, don't need to add environmental info
980 		 * so return here
981 		 */
982 		*childp = childh;
983 		return (err);
984 	} else if (IS_DIMM_NODE(sgfrunode->nodename)) {
985 		err = add_prop_charstring(childh, "memory-module",
986 		    PICL_PROP_SLOT_TYPE);
987 		/*
988 		 * For Dimm, don't need to add environmental info
989 		 * so return here
990 		 */
991 		*childp = childh;
992 		return (err);
993 	} else if (IS_SB_NODE(sgfrunode->nodename)) {
994 		err = add_prop_charstring(childh, "system-board",
995 		    PICL_PROP_SLOT_TYPE);
996 	} else if (IS_PSU_NODE(sgfrunode->nodename)) {
997 		err = add_prop_charstring(childh, "power-supply",
998 		    PICL_PROP_SLOT_TYPE);
999 	} else if (IS_FT_NODE(sgfrunode->nodename)) {
1000 		err = add_prop_charstring(childh, "fan-tray",
1001 		    PICL_PROP_SLOT_TYPE);
1002 	}
1003 	if (err != PICL_SUCCESS)
1004 		return (err);
1005 
1006 	/*
1007 	 * add devices table to location node (may need
1008 	 * references to led devices)
1009 	 */
1010 	err = create_table(childh, &tblhdl, PICL_PROP_DEVICES);
1011 	if (err != PICL_SUCCESS)
1012 		return (err);
1013 
1014 	err = add_led_nodes(childh, labelp, LOM_LED_POSITION_LOCATION, tblhdl);
1015 	if (err != PICL_SUCCESS)
1016 		return (err);
1017 	*childp = childh;
1018 	return (PICL_SUCCESS);
1019 }
1020 
1021 /*
1022  * remove an individual picl node - called from remove_subtree()
1023  * also removes any sensor nodes pointed at by Devices table
1024  */
1025 static int
1026 remove_picl_node(picl_nodehdl_t nodeh)
1027 {
1028 	int err;
1029 	picl_prophdl_t  tblhdl;
1030 	picl_prophdl_t  nextprop;
1031 	picl_prophdl_t  refprop;
1032 	char	class[PICL_CLASSNAMELEN_MAX];
1033 
1034 	/*
1035 	 * first scan Devices table so we can find any sensor nodes
1036 	 * we need to delete as well
1037 	 */
1038 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_DEVICES,
1039 	    &tblhdl, sizeof (tblhdl));
1040 
1041 	/*
1042 	 * If Devices table present, then read first column.
1043 	 * Devices table may be empty so don't treat this as an error
1044 	 */
1045 	if (err == PICL_SUCCESS &&
1046 	    ptree_get_next_by_row(tblhdl, &nextprop) == PICL_SUCCESS) {
1047 		/* find second column */
1048 		err = ptree_get_next_by_row(nextprop, &nextprop);
1049 		if (err != PICL_SUCCESS) {
1050 			syslog(LOG_ERR, GET_NEXT_BY_ROW_FAIL,
1051 			    PICL_PROP_DEVICES, err);
1052 			return (err);
1053 		}
1054 
1055 		/*
1056 		 * walk down second column (ref ptr)
1057 		 * deleting the referenced nodes
1058 		 */
1059 		while (err == PICL_SUCCESS) {
1060 			err = ptree_get_propval(nextprop, &refprop,
1061 			    sizeof (refprop));
1062 			if (err != PICL_SUCCESS) {
1063 				syslog(LOG_ERR, GET_PROPVAL_FAIL, err);
1064 				return (err);
1065 			}
1066 
1067 			/*
1068 			 * don't delete memory-module nodes
1069 			 * or cpu nodes (they weren't created
1070 			 * by this plugin)
1071 			 */
1072 			err = ptree_get_propval_by_name(refprop,
1073 			    PICL_PROP_CLASSNAME, class, sizeof (class));
1074 			if (err == PICL_STALEHANDLE) {
1075 				/*
1076 				 * if another plugin has already deleted the
1077 				 * node for us then that is ok
1078 				 */
1079 				err = ptree_get_next_by_col(nextprop,
1080 				    &nextprop);
1081 				continue;
1082 			}
1083 			if (err != PICL_SUCCESS) {
1084 				syslog(LOG_ERR, PROP_LOOKUP_FAIL,
1085 				    PICL_PROP_CLASSNAME, err);
1086 				return (err);
1087 			}
1088 			if (strcmp(class, PICL_CLASS_MEMORY_MODULE) == 0 ||
1089 			    strcmp(class, PICL_CLASS_CPU) == 0) {
1090 				/*
1091 				 * but - do need to remove _fru_parent
1092 				 * property and Environment table (for cpu)
1093 				 */
1094 				err = remove_references(refprop, class);
1095 				if (err != PICL_SUCCESS)
1096 					return (err);
1097 			} else {
1098 				/*
1099 				 * sensor node - need to delete it
1100 				 */
1101 				err = ptree_delete_node(refprop);
1102 				if (err != PICL_SUCCESS) {
1103 					syslog(LOG_ERR, DELETE_PROP_FAIL, err);
1104 					return (err);
1105 				}
1106 				(void) ptree_destroy_node(refprop);
1107 			}
1108 			err = ptree_get_next_by_col(nextprop, &nextprop);
1109 		}
1110 	}
1111 
1112 	/*
1113 	 * now we can remove the frutree node
1114 	 */
1115 	err = ptree_delete_node(nodeh);
1116 	if (err != PICL_SUCCESS) {
1117 		syslog(LOG_ERR, DELETE_PROP_FAIL, err);
1118 		return (err);
1119 	}
1120 	(void) ptree_destroy_node(nodeh);
1121 	return (PICL_SUCCESS);
1122 }
1123 
1124 static int
1125 add_child_pci_references(picl_nodehdl_t nodeh, picl_prophdl_t tblhdl,
1126     picl_nodehdl_t devnodeh)
1127 {
1128 	int err = PICL_SUCCESS;
1129 	picl_nodehdl_t childnodeh;
1130 	char	class[PICL_CLASSNAMELEN_MAX];
1131 
1132 	if (ptree_get_propval_by_name(devnodeh, PICL_PROP_CHILD, &childnodeh,
1133 	    sizeof (childnodeh)) != PICL_SUCCESS) {
1134 		return (PICL_SUCCESS);
1135 	}
1136 	for (;;) {
1137 		err = ptree_get_propval_by_name(childnodeh,
1138 		    PICL_PROP_CLASSNAME, class, sizeof (class));
1139 		if (err != PICL_SUCCESS)
1140 			break;
1141 		err = add_prop_ref(childnodeh, nodeh, PICL_REFPROP_FRU_PARENT);
1142 		if (err != PICL_SUCCESS)
1143 			break;
1144 		err = create_table_entry(tblhdl, childnodeh, class);
1145 		if (err != PICL_SUCCESS)
1146 			break;
1147 		err = add_child_pci_references(nodeh, tblhdl, childnodeh);
1148 		if (err != PICL_SUCCESS)
1149 			break;
1150 		err = ptree_get_propval_by_name(childnodeh,
1151 		    PICL_PROP_PEER, &childnodeh, sizeof (picl_nodehdl_t));
1152 		if (err != PICL_SUCCESS) {
1153 			err = PICL_SUCCESS;
1154 			break;
1155 		}
1156 	}
1157 	return (err);
1158 }
1159 
1160 static int
1161 add_pci_location(picl_nodehdl_t childh, char *parent_addr, char bus_addr,
1162     char *slot_name)
1163 {
1164 	int err;
1165 	int got_one = 0;
1166 	picl_nodehdl_t nodeh;
1167 	picl_nodehdl_t devnodeh;
1168 	picl_nodehdl_t devhdl;
1169 	char	addr[MAXPATHLEN];
1170 	char parent_path[MAXPATHLEN];
1171 	picl_prophdl_t tblhdl;
1172 	char	class[PICL_CLASSNAMELEN_MAX];
1173 
1174 	/*
1175 	 * search for any device nodes whose BUS_ADDR or UNIT_ADDRESS
1176 	 * are appropriate for this pci slot
1177 	 */
1178 	sprintf_buf2(parent_path, IO_DEV, parent_addr);
1179 	if (ptree_get_node_by_path(parent_path, &devhdl) == PICL_SUCCESS &&
1180 	    ptree_get_propval_by_name(devhdl, PICL_PROP_CHILD, &devnodeh,
1181 	    sizeof (devnodeh)) == PICL_SUCCESS) {
1182 		while (!got_one) {
1183 			err = ptree_get_propval_by_name(devnodeh,
1184 			    PICL_PROP_BUS_ADDR, addr, sizeof (addr));
1185 			if (err == PICL_SUCCESS && addr[0] == bus_addr &&
1186 			    (addr[1] == ',' || addr[1] == '\0')) {
1187 				got_one = 1;
1188 				break;
1189 			}
1190 			err = ptree_get_propval_by_name(devnodeh,
1191 			    PICL_PROP_UNIT_ADDRESS, addr, sizeof (addr));
1192 			if (err == PICL_SUCCESS && addr[0] == bus_addr &&
1193 			    (addr[1] == ',' || addr[1] == '\0')) {
1194 				got_one = 1;
1195 				break;
1196 			}
1197 			err = ptree_get_propval_by_name(devnodeh,
1198 			    PICL_PROP_PEER, &devnodeh, sizeof (picl_nodehdl_t));
1199 			if (err != PICL_SUCCESS)
1200 				break;
1201 		}
1202 	}
1203 	nodeh = childh;
1204 	if (got_one == 0) {
1205 		/*
1206 		 * no devnodes for this slot. Create location node but
1207 		 * no fru node (empty slot)
1208 		 */
1209 		return (add_intermediate_location(&nodeh, slot_name, "pci"));
1210 	}
1211 
1212 	/*
1213 	 * we've got the first devnode for this slot. Create the fru node
1214 	 * then walk along other nodes looking for further devnodes
1215 	 */
1216 	err = add_intermediate_nodes(&nodeh, slot_name, &tblhdl, "pci", NULL);
1217 	if (err != PICL_SUCCESS)
1218 		return (err);
1219 
1220 	for (;;) {
1221 		if (((err = ptree_get_propval_by_name(devnodeh,
1222 		    PICL_PROP_BUS_ADDR, addr, sizeof (addr))) ==
1223 		    PICL_SUCCESS && addr[0] == bus_addr &&
1224 		    (addr[1] == ',' || addr[1] == '\0')) ||
1225 		    ((err = ptree_get_propval_by_name(devnodeh,
1226 		    PICL_PROP_UNIT_ADDRESS, addr, sizeof (addr))) ==
1227 		    PICL_SUCCESS && addr[0] == bus_addr &&
1228 		    (addr[1] == ',' || addr[1] == '\0'))) {
1229 			err = ptree_get_propval_by_name(devnodeh,
1230 			    PICL_PROP_CLASSNAME, class, sizeof (class));
1231 			if (err != PICL_SUCCESS)
1232 				break;
1233 			err = add_prop_ref(devnodeh, nodeh,
1234 			    PICL_REFPROP_FRU_PARENT);
1235 			if (err != PICL_SUCCESS)
1236 				break;
1237 			err = create_table_entry(tblhdl, devnodeh, class);
1238 			if (err != PICL_SUCCESS)
1239 				break;
1240 			err = add_child_pci_references(nodeh, tblhdl, devnodeh);
1241 			if (err != PICL_SUCCESS)
1242 				break;
1243 		}
1244 		err = ptree_get_propval_by_name(devnodeh,
1245 		    PICL_PROP_PEER, &devnodeh, sizeof (picl_nodehdl_t));
1246 		if (err != PICL_SUCCESS) {
1247 			err = PICL_SUCCESS;
1248 			break;
1249 		}
1250 	}
1251 	return (err);
1252 }
1253 
1254 /*
1255  * add intermediate location into frutree (ie a location that we know
1256  * exists but sgfru doesn't)
1257  */
1258 static int
1259 add_intermediate_location(picl_nodehdl_t *nodep, char *labelp, char *slot_name)
1260 {
1261 	int err;
1262 	picl_nodehdl_t intermediate;
1263 	picl_prophdl_t tblhdl;
1264 	char	parent_name[PICL_PROPNAMELEN_MAX];
1265 
1266 	err = ptree_create_and_add_node(*nodep, labelp, PICL_CLASS_LOCATION,
1267 	    &intermediate);
1268 	if (err != PICL_SUCCESS) {
1269 		syslog(LOG_ERR, ADD_NODE_FAIL, labelp, err);
1270 		return (err);
1271 	}
1272 
1273 	/*
1274 	 * create label property for location class
1275 	 */
1276 	err = add_prop_charstring(intermediate, labelp, PICL_PROP_LABEL);
1277 	if (err != PICL_SUCCESS)
1278 		return (err);
1279 
1280 	/*
1281 	 * add devices table to location node (may need references to led
1282 	 * devices)
1283 	 */
1284 	err = create_table(intermediate, &tblhdl, PICL_PROP_DEVICES);
1285 	if (err != PICL_SUCCESS)
1286 		return (err);
1287 
1288 	/*
1289 	 * scapp knows FANs 0 and 1 on IB as FAN8 and FAN9
1290 	 */
1291 	err = ptree_get_propval_by_name(*nodep, PICL_PROP_NAME, parent_name,
1292 	    sizeof (parent_name));
1293 	if (err != PICL_SUCCESS)
1294 		return (err);
1295 	if (strcmp(labelp, "FAN0") == 0 && strcmp(parent_name, "IB6") == 0)
1296 		err = add_led_nodes(intermediate, "FAN8",
1297 		    LOM_LED_POSITION_LOCATION, tblhdl);
1298 	else if (strcmp(labelp, "FAN1") == 0 && strcmp(parent_name, "IB6") == 0)
1299 		err = add_led_nodes(intermediate, "FAN9",
1300 		    LOM_LED_POSITION_LOCATION, tblhdl);
1301 	else
1302 		err = add_led_nodes(intermediate, labelp,
1303 		    LOM_LED_POSITION_LOCATION, tblhdl);
1304 	if (err != PICL_SUCCESS)
1305 		return (err);
1306 
1307 	if (slot_name) {
1308 		err = add_prop_charstring(intermediate, slot_name,
1309 		    PICL_PROP_SLOT_TYPE);
1310 		if (err != PICL_SUCCESS)
1311 			return (err);
1312 	}
1313 	*nodep = intermediate;
1314 	return (PICL_SUCCESS);
1315 }
1316 
1317 /*
1318  * adds an intermediate location/fru pair into frutree
1319  */
1320 static int
1321 add_intermediate_nodes(picl_nodehdl_t *nodep, char *labelp,
1322     picl_prophdl_t *tblhdlp, char *slot_name, char *fru_name)
1323 {
1324 	int err;
1325 	picl_nodehdl_t intermediate;
1326 	picl_nodehdl_t intermediate2;
1327 
1328 	/*
1329 	 * create intermediate location node (unless it has already been
1330 	 * created)
1331 	 */
1332 	intermediate = find_child_by_name(*nodep, labelp);
1333 	if (intermediate == NULL) {
1334 		intermediate = *nodep;
1335 		err = add_intermediate_location(&intermediate, labelp,
1336 		    slot_name);
1337 		if (err != PICL_SUCCESS) {
1338 			return (err);
1339 		}
1340 	}
1341 
1342 	/*
1343 	 * create intermediate fru node (unless it has already been
1344 	 * created)
1345 	 */
1346 	intermediate2 = find_child_by_name(intermediate, labelp);
1347 	if (intermediate2 == NULL) {
1348 		/*
1349 		 * need to create intermediate fru node node
1350 		 */
1351 		err = ptree_create_and_add_node(intermediate, labelp,
1352 		    PICL_CLASS_FRU, &intermediate2);
1353 		if (err != PICL_SUCCESS) {
1354 			syslog(LOG_ERR, ADD_NODE_FAIL, labelp, err);
1355 			return (err);
1356 		}
1357 
1358 		/*
1359 		 * Create empty Devices table
1360 		 */
1361 		err = create_table(intermediate2, tblhdlp, PICL_PROP_DEVICES);
1362 		if (err != PICL_SUCCESS)
1363 			return (err);
1364 
1365 		if (fru_name) {
1366 			err = add_prop_charstring(intermediate2, fru_name,
1367 			    PICL_PROP_FRU_TYPE);
1368 			if (err != PICL_SUCCESS)
1369 				return (err);
1370 		}
1371 	} else  {
1372 		err = ptree_get_propval_by_name(intermediate2,
1373 		    PICL_PROP_DEVICES, tblhdlp, sizeof (*tblhdlp));
1374 		if (err != PICL_SUCCESS)
1375 			return (err);
1376 	}
1377 	*nodep = intermediate2;
1378 	return (PICL_SUCCESS);
1379 }
1380 
1381 /*
1382  * need to remove _fru_parent property and Environment table (for cpu)
1383  */
1384 static int
1385 remove_references(picl_prophdl_t refprop, char *class)
1386 {
1387 	picl_prophdl_t  platprop;
1388 	int err;
1389 
1390 	err = ptree_get_prop_by_name(refprop, PICL_REFPROP_FRU_PARENT,
1391 	    &platprop);
1392 	if (err != PICL_SUCCESS) {
1393 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1394 		return (err);
1395 	}
1396 	err = ptree_delete_prop(platprop);
1397 	if (err != PICL_SUCCESS) {
1398 		syslog(LOG_ERR, DELETE_PROP_FAIL, err);
1399 		return (err);
1400 	}
1401 	(void) ptree_destroy_prop(platprop);
1402 	if (strcmp(class, PICL_CLASS_CPU) == 0) {
1403 		err = ptree_get_prop_by_name(refprop, PICL_PROP_ENV, &platprop);
1404 		if (err != PICL_SUCCESS) {
1405 			/*
1406 			 * multi-core cpu is setup with only one cpu having
1407 			 * env table so ignore PICL_PROPNOTFOUND error.
1408 			 */
1409 			if (err == PICL_PROPNOTFOUND) {
1410 				return (PICL_SUCCESS);
1411 			}
1412 			syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_ENV, err);
1413 			return (err);
1414 		}
1415 		err = ptree_delete_prop(platprop);
1416 		if (err != PICL_SUCCESS) {
1417 			syslog(LOG_ERR, DELETE_PROP_FAIL, err);
1418 			return (err);
1419 		}
1420 		(void) ptree_destroy_prop(platprop);
1421 	}
1422 	return (PICL_SUCCESS);
1423 }
1424 
1425 /*
1426  * subroutine for various functions. Finds immediate child of parh with
1427  * requested name if present. Otherwise returns NULL.
1428  */
1429 static picl_nodehdl_t
1430 find_child_by_name(picl_nodehdl_t parh, char *name)
1431 {
1432 	picl_nodehdl_t nodeh;
1433 	int err;
1434 	char	nodename[PICL_PROPNAMELEN_MAX];
1435 
1436 	err = ptree_get_propval_by_name(parh, PICL_PROP_CHILD,
1437 	    &nodeh, sizeof (picl_nodehdl_t));
1438 	if (err != PICL_SUCCESS)
1439 		return (NULL);
1440 	for (;;) {
1441 		err = ptree_get_propval_by_name(nodeh, PICL_PROP_NAME, nodename,
1442 		    sizeof (nodename));
1443 		if (err != PICL_SUCCESS)
1444 			return (NULL);
1445 		if (strcmp(name, nodename) == 0) {
1446 			return (nodeh);
1447 		}
1448 		err = ptree_get_propval_by_name(nodeh, PICL_PROP_PEER,
1449 		    &nodeh, sizeof (picl_nodehdl_t));
1450 		if (err != PICL_SUCCESS)
1451 			return (NULL);
1452 	}
1453 }
1454 
1455 static int
1456 create_dimm_references(picl_nodehdl_t parh, int dimm_id,
1457     picl_nodehdl_t nodeh, picl_prophdl_t tblhdl)
1458 {
1459 	int err;
1460 	picl_nodehdl_t memctlhdl = NULL;
1461 	picl_nodehdl_t memgrphdl;
1462 	picl_nodehdl_t memhdl;
1463 	char name[MAXPATHLEN];
1464 	char	sbname[PICL_PROPNAMELEN_MAX];
1465 	char	pname[PICL_PROPNAMELEN_MAX];
1466 	char	bname[PICL_PROPNAMELEN_MAX];
1467 	picl_nodehdl_t parentfruh;
1468 	picl_nodehdl_t parentloch;
1469 	int id;
1470 
1471 	/*
1472 	 * create reference properties for memory nodes
1473 	 * - first find names of ancestor frus - ie "SBx/Py/Bz"
1474 	 */
1475 	err = ptree_get_propval_by_name(parh, PICL_PROP_PARENT, &parentfruh,
1476 	    sizeof (picl_nodehdl_t));
1477 	if (err != PICL_SUCCESS) {
1478 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1479 		return (err);
1480 	}
1481 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_NAME,
1482 	    bname, sizeof (bname));
1483 	if (err != PICL_SUCCESS) {
1484 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
1485 		return (err);
1486 	}
1487 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_PARENT,
1488 	    &parentloch, sizeof (picl_nodehdl_t));
1489 	if (err != PICL_SUCCESS) {
1490 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1491 		return (err);
1492 	}
1493 	err = ptree_get_propval_by_name(parentloch, PICL_PROP_PARENT,
1494 	    &parentfruh, sizeof (picl_nodehdl_t));
1495 	if (err != PICL_SUCCESS) {
1496 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1497 		return (err);
1498 	}
1499 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_NAME,
1500 	    pname, sizeof (pname));
1501 	if (err != PICL_SUCCESS) {
1502 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
1503 		return (err);
1504 	}
1505 
1506 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_PARENT,
1507 	    &parentloch, sizeof (picl_nodehdl_t));
1508 	if (err != PICL_SUCCESS) {
1509 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1510 		return (err);
1511 	}
1512 	err = ptree_get_propval_by_name(parentloch, PICL_PROP_PARENT,
1513 	    &parentfruh, sizeof (picl_nodehdl_t));
1514 	if (err != PICL_SUCCESS) {
1515 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1516 		return (err);
1517 	}
1518 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_NAME, sbname,
1519 	    sizeof (sbname));
1520 	if (err != PICL_SUCCESS) {
1521 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
1522 		return (err);
1523 	}
1524 
1525 	/*
1526 	 * ok - we've now got name of system board node in sbname and
1527 	 * name of processor node in pname.
1528 	 * Now find corresponding memory-controller node if present
1529 	 */
1530 	sprintf_buf2(name, MEMORY_DEV, SB_P_TO_SAFARI_ADDR(sbname, pname));
1531 	err = ptree_get_node_by_path(name, &memctlhdl);
1532 	if (err != PICL_SUCCESS)
1533 		return (PICL_SUCCESS);
1534 
1535 	/*
1536 	 * now find corresponding memory-module-group node if present
1537 	 */
1538 	err = ptree_get_propval_by_name(memctlhdl, PICL_PROP_CHILD, &memgrphdl,
1539 	    sizeof (picl_nodehdl_t));
1540 	if (err != PICL_SUCCESS)
1541 		return (PICL_SUCCESS);
1542 
1543 	/*
1544 	 * check if this is the right bank - if not move on to sibling
1545 	 */
1546 	err = ptree_get_propval_by_name(memgrphdl, PICL_PROP_ID,
1547 	    &id, sizeof (int));
1548 	if (err != PICL_SUCCESS)
1549 		return (PICL_SUCCESS);
1550 	if (bname[1] != id + '0') {
1551 		err = ptree_get_propval_by_name(memgrphdl, PICL_PROP_PEER,
1552 		    &memgrphdl, sizeof (picl_nodehdl_t));
1553 		if (err != PICL_SUCCESS)
1554 			return (PICL_SUCCESS);
1555 		err = ptree_get_propval_by_name(memgrphdl, PICL_PROP_ID,
1556 		    &id, sizeof (int));
1557 		if (err != PICL_SUCCESS)
1558 			return (PICL_SUCCESS);
1559 		if (bname[1] != id + '0')
1560 			return (PICL_SUCCESS);
1561 	}
1562 
1563 	/*
1564 	 * now find corresponding memory-module node if present
1565 	 */
1566 	err = ptree_get_propval_by_name(memgrphdl, PICL_PROP_CHILD, &memhdl,
1567 	    sizeof (picl_nodehdl_t));
1568 	if (err != PICL_SUCCESS)
1569 		return (PICL_SUCCESS);
1570 
1571 	/*
1572 	 * for each DIMM set up links with matching memory-module node
1573 	 */
1574 	for (;;) {
1575 		err = ptree_get_propval_by_name(memhdl, PICL_PROP_ID,
1576 		    &id, sizeof (int));
1577 		if (err == PICL_SUCCESS && dimm_id == id) {
1578 			err = add_prop_ref(memhdl, nodeh,
1579 			    PICL_REFPROP_FRU_PARENT);
1580 			if (err != PICL_SUCCESS)
1581 				return (err);
1582 			err = create_table_entry(tblhdl, memhdl,
1583 			    PICL_CLASS_MEMORY_MODULE);
1584 			if (err != PICL_SUCCESS)
1585 				return (err);
1586 		}
1587 		err = ptree_get_propval_by_name(memhdl, PICL_PROP_PEER,
1588 		    &memhdl, sizeof (picl_nodehdl_t));
1589 		if (err != PICL_SUCCESS)
1590 			break;
1591 	}
1592 	return (PICL_SUCCESS);
1593 }
1594 
1595 static int
1596 create_cpu_references(char *pname, picl_nodehdl_t nodeh, picl_prophdl_t tblhdl)
1597 {
1598 	int err;
1599 	picl_nodehdl_t sensorhdl;
1600 	picl_nodehdl_t parentloch;
1601 	picl_nodehdl_t parentfruh;
1602 	picl_nodehdl_t cpuhdl;
1603 	picl_nodehdl_t cpuhdl1;
1604 	picl_prophdl_t envtblhdl;
1605 	picl_prophdl_t prophdl;
1606 	char name[MAXPATHLEN];
1607 	char	sbname[PICL_PROPNAMELEN_MAX];
1608 
1609 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_PARENT,
1610 	    &parentloch, sizeof (picl_nodehdl_t));
1611 	if (err != PICL_SUCCESS) {
1612 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1613 		return (err);
1614 	}
1615 	err = ptree_get_propval_by_name(parentloch, PICL_PROP_PARENT,
1616 	    &parentfruh, sizeof (picl_nodehdl_t));
1617 	if (err != PICL_SUCCESS) {
1618 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
1619 		return (err);
1620 	}
1621 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_NAME, sbname,
1622 	    sizeof (sbname));
1623 	if (err != PICL_SUCCESS) {
1624 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
1625 		return (err);
1626 	}
1627 
1628 	/*
1629 	 * Find corresponding cpu node if present. Note, this code will
1630 	 * attempt to find a corresponding cpu node, by searching for devices
1631 	 * of the types  /platform/ssm@0,0/SUNW,UltraSPARC-III+@%x,0,
1632 	 * /platform/ssm@0,0/SUNW,UltraSPARC-III@%x,0 or
1633 	 * /platform/ssm@0,0/cmp@%x,0/cpu@0 or 1. If we can not find
1634 	 * any such device, we return PICL_SUCCESS such that we
1635 	 * continue the construction of the remaining part of the
1636 	 * tree. We first check for UltraSPARC-III. If we do not
1637 	 * find such a device we check for UltraSPARC-III+. If
1638 	 * we are unsuccesful again we try one of the jaguar cores
1639 	 * /platform/ssm@0,0/cmp@%x,0/cpu@. If we do not find the
1640 	 * first one, there's no point in continuing and we just
1641 	 * return PICL_SUCCESS. Similarly if we find one core
1642 	 * but not the other, something must be wrong, so we
1643 	 * again just return PICL_SUCCESS without creating any
1644 	 * references.
1645 	 */
1646 	sprintf_buf2(name, CPU_DEV, SB_P_TO_SAFARI_ADDR(sbname, pname));
1647 
1648 	err = ptree_get_node_by_path(name, &cpuhdl);
1649 
1650 	if (err != PICL_SUCCESS) {
1651 		sprintf_buf2(name, CPU_DEV2,
1652 		    SB_P_TO_SAFARI_ADDR(sbname, pname));
1653 		err = ptree_get_node_by_path(name, &cpuhdl);
1654 		if (err != PICL_SUCCESS) {
1655 			/* check for jaguar cores */
1656 			sprintf_buf2(name, CPU_DEV3C1,
1657 			    SB_P_TO_SAFARI_ADDR(sbname, pname));
1658 			err = ptree_get_node_by_path(name, &cpuhdl1);
1659 			if (err != PICL_SUCCESS)
1660 				return (PICL_SUCCESS);
1661 			/* add fru parent reference for the second core */
1662 			err = ptree_get_prop_by_name(cpuhdl1,
1663 			    PICL_REFPROP_FRU_PARENT, &prophdl);
1664 			if (err != PICL_SUCCESS) {
1665 			    err = add_prop_ref(cpuhdl1, nodeh,
1666 				PICL_REFPROP_FRU_PARENT);
1667 			if (err != PICL_SUCCESS)
1668 				return (err);
1669 			err = create_table_entry(tblhdl, cpuhdl1,
1670 			    PICL_CLASS_CPU);
1671 			if (err != PICL_SUCCESS)
1672 				return (err);
1673 			}
1674 			sprintf_buf2(name, CPU_DEV3C0,
1675 			    SB_P_TO_SAFARI_ADDR(sbname, pname));
1676 			err = ptree_get_node_by_path(name, &cpuhdl);
1677 			if (err != PICL_SUCCESS)
1678 				return (PICL_SUCCESS);
1679 
1680 		}
1681 	}
1682 
1683 	/*
1684 	 * now create reference properties
1685 	 */
1686 	err = ptree_get_prop_by_name(cpuhdl, PICL_REFPROP_FRU_PARENT, &prophdl);
1687 	if (err != PICL_SUCCESS) {
1688 		err = add_prop_ref(cpuhdl, nodeh, PICL_REFPROP_FRU_PARENT);
1689 		if (err != PICL_SUCCESS)
1690 			return (err);
1691 		err = create_table_entry(tblhdl, cpuhdl, PICL_CLASS_CPU);
1692 		if (err != PICL_SUCCESS)
1693 			return (err);
1694 	}
1695 
1696 	/*
1697 	 * create Environment table on cpu node - with Die and Ambient
1698 	 * temperature sensors if present. If already there, delete and start
1699 	 * again
1700 	 */
1701 	err = ptree_get_prop_by_name(cpuhdl, PICL_PROP_ENV, &prophdl);
1702 	if (err == PICL_SUCCESS) {
1703 		err = ptree_delete_prop(prophdl);
1704 		if (err != PICL_SUCCESS)
1705 			return (err);
1706 		(void) ptree_destroy_prop(prophdl);
1707 	}
1708 	err = create_table(cpuhdl, &envtblhdl, PICL_PROP_ENV);
1709 	if (err != PICL_SUCCESS)
1710 		return (err);
1711 
1712 	if (pcix_io)
1713 		sprintf_buf4(name, "%s/%s_t_cheetah%d@0", SC_DEV_PCIX, sbname,
1714 		    (pname[1] - '0'));
1715 	else
1716 		sprintf_buf4(name, "%s/%s_t_cheetah%d@0", SC_DEV, sbname,
1717 		    (pname[1] - '0'));
1718 
1719 	err = ptree_get_node_by_path(name, &sensorhdl);
1720 	if (err == PICL_SUCCESS) {
1721 		err = create_table_entry(envtblhdl, sensorhdl,
1722 		    PICL_CLASS_TEMPERATURE_SENSOR);
1723 		if (err != PICL_SUCCESS)
1724 			return (err);
1725 	}
1726 
1727 	if (pcix_io)
1728 		sprintf_buf4(name, "%s/%s_t_ambient%d@0", SC_DEV_PCIX, sbname,
1729 		    (pname[1] - '0'));
1730 	else
1731 		sprintf_buf4(name, "%s/%s_t_ambient%d@0", SC_DEV, sbname,
1732 		    (pname[1] - '0'));
1733 
1734 	err = ptree_get_node_by_path(name, &sensorhdl);
1735 	if (err == PICL_SUCCESS) {
1736 		return (create_table_entry(envtblhdl, sensorhdl,
1737 		    PICL_CLASS_TEMPERATURE_SENSOR));
1738 	}
1739 	return (PICL_SUCCESS);
1740 }
1741 
1742 /*
1743  * subroutine of add_subtree - get a list of children of a parent node
1744  */
1745 static sgfrunode_t *
1746 get_node_children(fru_hdl_t fruparent, int *num_childrenp)
1747 {
1748 	int	max_children, i;
1749 	sgfrunode_t	*fruchildren = NULL;
1750 	child_info_t child_info;
1751 	int  frufd;
1752 
1753 	/*
1754 	 * Open the sgfru pseudo dev
1755 	 */
1756 	if ((frufd = open(FRU_PSEUDO_DEV, O_RDWR, 0)) == -1) {
1757 		syslog(LOG_ERR, DEV_OPEN_FAIL, FRU_PSEUDO_DEV, strerror(errno));
1758 		return (NULL);
1759 	}
1760 	for (i = 1; i <= MAX_TRIES; i++) {
1761 		max_children = i * MAX_NODE_CHILDREN;
1762 		if ((fruchildren = calloc(max_children,
1763 		    sizeof (sgfrunode_t))) == NULL) {
1764 			(void) close(frufd);
1765 			syslog(LOG_ERR, MALLOC_FAIL);
1766 			return (NULL);
1767 		}
1768 		child_info.fru_hdl = fruparent;
1769 		child_info.fru_cnt = max_children;
1770 		child_info.frus = (void *)fruchildren;
1771 		if (ioctl(frufd, SGFRU_GETCHILDLIST, &child_info) == 0) {
1772 			/*
1773 			 * got them - return success
1774 			 */
1775 			(void) close(frufd);
1776 			*num_childrenp = child_info.fru_cnt;
1777 			return (fruchildren);
1778 		}
1779 		free(fruchildren);
1780 
1781 		/*
1782 		 * if ENOMEM, need to calloc more space - so go round loop again
1783 		 * otherwise fail
1784 		 */
1785 		if (errno != ENOMEM) {
1786 			(void) close(frufd);
1787 			syslog(LOG_ERR, SGFRU_IOCTL_FAIL, SGFRU_GETCHILDLIST,
1788 			    fruparent, strerror(errno));
1789 			return (NULL);
1790 		}
1791 	}
1792 	(void) close(frufd);
1793 	syslog(LOG_ERR, MALLOC_FAIL);
1794 	return (NULL);
1795 }
1796 
1797 /* Creates an unsigned longlong property for a given PICL node */
1798 static int
1799 add_prop_ull(picl_nodehdl_t nodeh, uint64_t handle, char *name)
1800 {
1801 	picl_prophdl_t proph;
1802 	ptree_propinfo_t propinfo;
1803 	int err;
1804 
1805 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1806 	    PICL_PTYPE_UNSIGNED_INT, PICL_READ, sizeof (unsigned long long),
1807 	    PICL_PROP_SC_HANDLE, NULL, NULL);
1808 	if (err != PICL_SUCCESS) {
1809 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1810 		return (err);
1811 	}
1812 	err = ptree_create_and_add_prop(nodeh, &propinfo, &handle, &proph);
1813 	if (err != PICL_SUCCESS) {
1814 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1815 		return (err);
1816 	}
1817 	return (PICL_SUCCESS);
1818 }
1819 
1820 /* Creates a void property for a given PICL node */
1821 static int
1822 add_prop_void(picl_nodehdl_t nodeh, char *name)
1823 {
1824 	picl_prophdl_t proph;
1825 	ptree_propinfo_t propinfo;
1826 	int err;
1827 
1828 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1829 	    PICL_PTYPE_VOID, PICL_READ, 0, PICL_PROP_FRUDATA_AVAIL, NULL, NULL);
1830 	if (err != PICL_SUCCESS) {
1831 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1832 		return (err);
1833 	}
1834 	err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, &proph);
1835 	if (err != PICL_SUCCESS) {
1836 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1837 		return (err);
1838 	}
1839 	return (PICL_SUCCESS);
1840 }
1841 
1842 /* Creates a reference property for a given PICL node */
1843 static int
1844 add_prop_ref(picl_nodehdl_t nodeh, picl_nodehdl_t value, char *name)
1845 {
1846 	picl_prophdl_t proph;
1847 	ptree_propinfo_t propinfo;
1848 	int err;
1849 
1850 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1851 	    PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t), name,
1852 	    NULL, NULL);
1853 	if (err != PICL_SUCCESS) {
1854 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1855 		return (err);
1856 	}
1857 	err = ptree_create_and_add_prop(nodeh, &propinfo, &value, &proph);
1858 	if (err != PICL_SUCCESS) {
1859 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1860 		return (err);
1861 	}
1862 	return (PICL_SUCCESS);
1863 }
1864 
1865 /* Creates an integer property for a given PICL node */
1866 static int
1867 add_prop_int(picl_nodehdl_t nodeh, int value, char *name)
1868 {
1869 	picl_prophdl_t proph;
1870 	ptree_propinfo_t propinfo;
1871 	int err;
1872 
1873 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1874 	    PICL_PTYPE_INT, PICL_READ, sizeof (int), name, NULL, NULL);
1875 	if (err != PICL_SUCCESS) {
1876 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1877 		return (err);
1878 	}
1879 	err = ptree_create_and_add_prop(nodeh, &propinfo, &value, &proph);
1880 	if (err != PICL_SUCCESS) {
1881 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1882 		return (err);
1883 	}
1884 	return (PICL_SUCCESS);
1885 }
1886 
1887 /* Creates an integer property for a given PICL node */
1888 static int
1889 add_prop_float(picl_nodehdl_t nodeh, float value, char *name)
1890 {
1891 	picl_prophdl_t proph;
1892 	ptree_propinfo_t propinfo;
1893 	int err;
1894 
1895 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1896 	    PICL_PTYPE_FLOAT, PICL_READ, sizeof (float), name, NULL, NULL);
1897 	if (err != PICL_SUCCESS) {
1898 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1899 		return (err);
1900 	}
1901 	err = ptree_create_and_add_prop(nodeh, &propinfo, &value, &proph);
1902 	if (err != PICL_SUCCESS) {
1903 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1904 		return (err);
1905 	}
1906 	return (PICL_SUCCESS);
1907 }
1908 
1909 /* Creates a charstring property for a given PICL node */
1910 static int
1911 add_prop_charstring(picl_nodehdl_t nodeh, char *value, char *name)
1912 {
1913 	picl_prophdl_t proph;
1914 	ptree_propinfo_t propinfo;
1915 	int err;
1916 
1917 	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
1918 	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(value) + 1,
1919 	    name, NULL, NULL);
1920 	if (err != PICL_SUCCESS) {
1921 		syslog(LOG_ERR, PROPINFO_FAIL, name, err);
1922 		return (err);
1923 	}
1924 	err = ptree_create_and_add_prop(nodeh, &propinfo, value, &proph);
1925 	if (err != PICL_SUCCESS) {
1926 		syslog(LOG_ERR, ADD_PROP_FAIL, name, err);
1927 		return (err);
1928 	}
1929 	return (PICL_SUCCESS);
1930 }
1931 
1932 /* create an entry in the specified table */
1933 static int
1934 create_table_entry(picl_prophdl_t tblhdl, picl_nodehdl_t refhdl, char *class)
1935 {
1936 	int			err;
1937 	ptree_propinfo_t	prop;
1938 	picl_prophdl_t		prophdl[2];
1939 
1940 	/* first column is class */
1941 	prop.version = PTREE_PROPINFO_VERSION;
1942 	prop.piclinfo.type =  PICL_PTYPE_CHARSTRING;
1943 	prop.piclinfo.accessmode = PICL_READ;
1944 	prop.piclinfo.size = PICL_CLASSNAMELEN_MAX;
1945 	prop.read = NULL;
1946 	prop.write = NULL;
1947 	(void) strlcpy(prop.piclinfo.name, PICL_PROP_CLASS,
1948 	    sizeof (prop.piclinfo.name));
1949 	err = ptree_create_prop(&prop, class, &prophdl[0]);
1950 	if (err != PICL_SUCCESS) {
1951 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
1952 		return (err);
1953 	}
1954 
1955 	/* second column is refernce property */
1956 	prop.version = PTREE_PROPINFO_VERSION;
1957 	prop.piclinfo.type =  PICL_PTYPE_REFERENCE;
1958 	prop.piclinfo.accessmode = PICL_READ;
1959 	prop.piclinfo.size = sizeof (picl_nodehdl_t);
1960 	prop.read = NULL;
1961 	prop.write = NULL;
1962 	sprintf_buf2(prop.piclinfo.name, "_%s_", class);
1963 	err = ptree_create_prop(&prop, &refhdl, &prophdl[1]);
1964 	if (err != PICL_SUCCESS) {
1965 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
1966 		return (err);
1967 	}
1968 
1969 	/* add row to table */
1970 	err = ptree_add_row_to_table(tblhdl, 2, prophdl);
1971 	if (err != PICL_SUCCESS)
1972 		syslog(LOG_ERR, ADD_TBL_ENTRY_FAIL, err);
1973 	return (err);
1974 }
1975 
1976 /* create an empty table property */
1977 static int
1978 create_table(picl_nodehdl_t fruhdl, picl_prophdl_t *tblhdlp, char *tbl_name)
1979 {
1980 	int			err;
1981 	ptree_propinfo_t	prop;
1982 	picl_prophdl_t		tblprophdl;
1983 
1984 	err = ptree_create_table(tblhdlp);
1985 	if (err != PICL_SUCCESS) {
1986 		syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
1987 		return (err);
1988 	}
1989 	prop.version = PTREE_PROPINFO_VERSION;
1990 	prop.piclinfo.type =  PICL_PTYPE_TABLE;
1991 	prop.piclinfo.accessmode = PICL_READ;
1992 	prop.piclinfo.size = sizeof (picl_prophdl_t);
1993 	prop.read = NULL;
1994 	prop.write = NULL;
1995 	(void) strlcpy(prop.piclinfo.name, tbl_name,
1996 	    sizeof (prop.piclinfo.name));
1997 	err = ptree_create_and_add_prop(fruhdl, &prop, tblhdlp, &tblprophdl);
1998 	if (err != PICL_SUCCESS)
1999 		syslog(LOG_ERR, ADD_PROP_FAIL, tbl_name, err);
2000 	return (err);
2001 }
2002 
2003 static void
2004 frudr_add_subtree(picl_nodehdl_t parh)
2005 {
2006 	fru_hdl_t	sgfruhdl;
2007 	if (ptree_get_propval_by_name(parh, PICL_PROP_SC_HANDLE,
2008 	    &sgfruhdl, sizeof (sgfruhdl)) != PICL_SUCCESS) {
2009 		return;
2010 	}
2011 	(void) add_subtree(parh, sgfruhdl);
2012 }
2013 
2014 /* event completion handler for PICL_FRU_ADDED/PICL_FRU_REMOVED events */
2015 /*ARGSUSED*/
2016 static void
2017 frudr_completion_handler(char *ename, void *earg, size_t size)
2018 {
2019 	picl_nodehdl_t	fruh;
2020 	picl_nodehdl_t	parh;
2021 
2022 	if (strcmp(ename, PICL_FRU_REMOVED) == 0) {
2023 		/*
2024 		 * now frudata has been notified that the node is to be
2025 		 * removed, we can actually remove it
2026 		 */
2027 		fruh = NULL;
2028 		(void) nvlist_lookup_uint64(earg,
2029 		    PICLEVENTARG_FRUHANDLE, &fruh);
2030 		if (fruh != NULL) {
2031 			(void) remove_subtree(fruh);
2032 
2033 			/*
2034 			 * Now repopulate the frutree with current data.
2035 			 */
2036 			parh = NULL;
2037 			(void) nvlist_lookup_uint64(earg,
2038 			    PICLEVENTARG_PARENTHANDLE, &parh);
2039 			if (parh != NULL) {
2040 				frudr_add_subtree(parh);
2041 			}
2042 		}
2043 	}
2044 	nvlist_free(earg);
2045 	free(earg);
2046 	free(ename);
2047 }
2048 
2049 /*
2050  * Post the PICL_FRU_ADDED/PICL_FRU_REMOVED event
2051  */
2052 static void
2053 post_frudr_event(char *ename, picl_nodehdl_t parenth, picl_nodehdl_t fruh)
2054 {
2055 	nvlist_t	*nvl;
2056 	char		*ev_name;
2057 
2058 	ev_name = strdup(ename);
2059 	if (ev_name == NULL)
2060 		return;
2061 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, NULL)) {
2062 		free(ev_name);
2063 		return;
2064 	}
2065 	if (parenth != 0L &&
2066 	    nvlist_add_uint64(nvl, PICLEVENTARG_PARENTHANDLE, parenth)) {
2067 		free(ev_name);
2068 		nvlist_free(nvl);
2069 		return;
2070 	}
2071 	if (fruh != 0L &&
2072 	    nvlist_add_uint64(nvl, PICLEVENTARG_FRUHANDLE, fruh)) {
2073 		free(ev_name);
2074 		nvlist_free(nvl);
2075 		return;
2076 	}
2077 	if (ptree_post_event(ev_name, nvl, sizeof (nvl),
2078 	    frudr_completion_handler) != 0) {
2079 		free(ev_name);
2080 		nvlist_free(nvl);
2081 	}
2082 }
2083 
2084 /*
2085  * updates the picl node 'loc' with the new fru handle (PICL_PROP_SC_HANDLE)
2086  * (helper function for frudr_evhandler, when a stale fru handle is
2087  * detected)
2088  */
2089 static void
2090 update_fru_hdl(picl_nodehdl_t loc, fru_hdl_t newsgfruhdl)
2091 {
2092 	picl_prophdl_t	schproph;
2093 	int		err;
2094 
2095 	err = ptree_get_prop_by_name(loc, PICL_PROP_SC_HANDLE, &schproph);
2096 	if (err == PICL_SUCCESS) {
2097 		if (ptree_delete_prop(schproph) == PICL_SUCCESS) {
2098 			(void) ptree_destroy_prop(schproph);
2099 		}
2100 	}
2101 	(void) add_prop_ull(loc, (uint64_t)newsgfruhdl, PICL_PROP_SC_HANDLE);
2102 }
2103 
2104 /*
2105  * Get the fru handle of loc by iterating through the parent's children.
2106  * Sets fruhdl and returns PICL_SUCCESS unless an error is encountered.
2107  */
2108 static int
2109 get_fruhdl_from_parent(picl_nodehdl_t loc, fru_hdl_t *fruhdl)
2110 {
2111 	picl_nodehdl_t	parlocnodeh;
2112 	fru_hdl_t	parsgfruhdl;
2113 	sgfrunode_t	*cp;
2114 	sgfrunode_t	*fruchildren;
2115 	char		nodename[PICL_PROPNAMELEN_MAX];
2116 	int		err;
2117 	int		num_children;
2118 	int		i;
2119 
2120 	err = ptree_get_propval_by_name(loc, PICL_PROP_NAME, (void *)nodename,
2121 	    PICL_PROPNAMELEN_MAX);
2122 	if (err != PICL_SUCCESS)
2123 		return (err);
2124 	err = ptree_get_propval_by_name(loc, PICL_PROP_PARENT, &parlocnodeh,
2125 	    sizeof (picl_nodehdl_t));
2126 	if (err != PICL_SUCCESS)
2127 		return (err);
2128 	if ((err = ptree_get_propval_by_name(parlocnodeh, PICL_PROP_SC_HANDLE,
2129 	    &parsgfruhdl, sizeof (parsgfruhdl))) != PICL_SUCCESS)
2130 		return (err);
2131 	/* find children of the parent node */
2132 	fruchildren = get_node_children(parsgfruhdl, &num_children);
2133 	if (fruchildren == NULL)
2134 		return (PICL_FAILURE);
2135 	for (i = 0, cp = fruchildren; i < num_children; i++, cp++) {
2136 		/* find the child we're interested in */
2137 		if (strcmp(cp->nodename, nodename) == 0) {
2138 			*fruhdl = cp->handle;
2139 			free(fruchildren);
2140 			return (PICL_SUCCESS);
2141 		}
2142 	}
2143 	free(fruchildren);
2144 	return (PICL_FAILURE);
2145 }
2146 
2147 /*
2148  * handle EC_DR picl events
2149  */
2150 /*ARGSUSED*/
2151 static void
2152 frudr_evhandler(const char *ename, const void *earg, size_t size, void *cookie)
2153 {
2154 	nvlist_t		*nvlp;
2155 	char			*dtype;
2156 	char			*ap_id;
2157 	char			*hint;
2158 	char			path[MAXPATHLEN];
2159 	picl_nodehdl_t		fruh;
2160 	picl_nodehdl_t		locnodeh;
2161 	fru_hdl_t		sgfruhdl;
2162 	fru_hdl_t		sgfruhdl_from_parent;
2163 
2164 	if (strcmp(ename, PICLEVENT_DR_AP_STATE_CHANGE) != 0)
2165 		return;
2166 
2167 	if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
2168 		return;
2169 
2170 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_DATA_TYPE, &dtype)) {
2171 		nvlist_free(nvlp);
2172 		return;
2173 	}
2174 
2175 	if (strcmp(dtype, PICLEVENTARG_PICLEVENT_DATA) != 0) {
2176 		nvlist_free(nvlp);
2177 		return;
2178 	}
2179 
2180 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_AP_ID, &ap_id)) {
2181 		nvlist_free(nvlp);
2182 		return;
2183 	}
2184 
2185 	if (nvlist_lookup_string(nvlp, PICLEVENTARG_HINT, &hint)) {
2186 		nvlist_free(nvlp);
2187 		return;
2188 	}
2189 
2190 	if (strncmp(ap_id, AP_ID_PREAMBLE, AP_ID_PREAMBLE_LEN) != 0) {
2191 		nvlist_free(nvlp);
2192 		return;
2193 	}
2194 
2195 	/*
2196 	 * OK - so this is an EC_DR event - let's handle it.
2197 	 */
2198 	sprintf_buf2(path, CHASSIS_LOC_PATH, &ap_id[AP_ID_PREAMBLE_LEN]);
2199 
2200 	/*
2201 	 * special case - SSC arrival means that SSC has been reset - we
2202 	 * need to flush the cached sgfru handles
2203 	 */
2204 	if (strcmp(&ap_id[AP_ID_PREAMBLE_LEN], "SSC1") == 0) {
2205 		picl_nodehdl_t chdh;
2206 		picl_nodehdl_t peerh;
2207 		picl_nodehdl_t parh;
2208 		int got_peer;
2209 		char	label[MAX_LABEL_LEN];
2210 		int err;
2211 		sgfrunode_t	*sgfruchassisp = NULL;
2212 		int num_children;
2213 		picl_prophdl_t	schproph;
2214 
2215 		/* find existing chassis node */
2216 		if (ptree_get_node_by_path(CHASSIS_PATH, &parh) !=
2217 		    PICL_SUCCESS) {
2218 			nvlist_free(nvlp);
2219 			return;
2220 		}
2221 
2222 		/* find new chassis sgfru node */
2223 		sgfruchassisp = get_node_children(ROOTPARENT, &num_children);
2224 		if (sgfruchassisp == NULL || num_children != 1) {
2225 			nvlist_free(nvlp);
2226 			return;
2227 		}
2228 
2229 		/* update chassis SC_HANDLE property */
2230 		err = ptree_get_prop_by_name(parh, PICL_PROP_SC_HANDLE,
2231 		    &schproph);
2232 		if (err != PICL_SUCCESS) {
2233 			nvlist_free(nvlp);
2234 			return;
2235 		}
2236 		err = ptree_delete_prop(schproph);
2237 		if (err != PICL_SUCCESS) {
2238 			nvlist_free(nvlp);
2239 			return;
2240 		}
2241 		(void) ptree_destroy_prop(schproph);
2242 		err = add_prop_ull(parh, sgfruchassisp->handle,
2243 		    PICL_PROP_SC_HANDLE);
2244 		if (err != PICL_SUCCESS) {
2245 			nvlist_free(nvlp);
2246 			return;
2247 		}
2248 
2249 		/*
2250 		 * remove all subtrees except DISK, TAPE, DVD and PCI subtrees
2251 		 */
2252 		if (ptree_get_propval_by_name(parh, PICL_PROP_CHILD, &chdh,
2253 		    sizeof (picl_nodehdl_t)) == PICL_SUCCESS) {
2254 			for (;;) {
2255 				if (ptree_get_propval_by_name(chdh,
2256 				    PICL_PROP_PEER, &peerh,
2257 				    sizeof (picl_nodehdl_t)) != PICL_SUCCESS)
2258 					got_peer = 0;
2259 				else
2260 					got_peer = 1;
2261 				err = ptree_get_propval_by_name(chdh,
2262 				    PICL_PROP_LABEL, label, sizeof (label));
2263 				if (err == PICL_SUCCESS) {
2264 					if (strncmp(label, "DISK",
2265 					    strlen("DISK")) != 0 &&
2266 					    strncmp(label, "TAPE",
2267 					    strlen("TAPE")) != 0 &&
2268 					    strncmp(label, "PCI",
2269 					    strlen("PCI")) != 0 &&
2270 					    strncmp(label, "DVD",
2271 					    strlen("DVD")) != 0) {
2272 						(void) remove_subtree(chdh);
2273 					}
2274 				}
2275 				if (got_peer == 0)
2276 					break;
2277 				chdh = peerh;
2278 			}
2279 		}
2280 
2281 		/* add new subtrees */
2282 		(void) add_subtree(parh, sgfruchassisp->handle);
2283 		free(sgfruchassisp);
2284 
2285 		nvlist_free(nvlp);
2286 		return;
2287 	}
2288 
2289 	if (ptree_get_node_by_path(path, &locnodeh) != PICL_SUCCESS) {
2290 		nvlist_free(nvlp);
2291 		return;
2292 	}
2293 	if (ptree_get_propval_by_name(locnodeh, PICL_PROP_SC_HANDLE,
2294 	    &sgfruhdl, sizeof (sgfruhdl)) != PICL_SUCCESS) {
2295 		nvlist_free(nvlp);
2296 		return;
2297 	}
2298 
2299 	/*
2300 	 * now either add or delete the fru node as appropriate. If no
2301 	 * hint, treat as insert - add_subtree will update the tree if
2302 	 * necessary.
2303 	 */
2304 	if (strcmp(hint, DR_HINT_REMOVE) == 0) {
2305 		if (ptree_get_propval_by_name(locnodeh, PICL_PROP_CHILD,
2306 		    &fruh, sizeof (picl_nodehdl_t)) != PICL_PROPNOTFOUND) {
2307 			/*
2308 			 * fru was there - but has gone away
2309 			 */
2310 			post_frudr_event(PICL_FRU_REMOVED, locnodeh, fruh);
2311 		}
2312 	} else {
2313 		/*
2314 		 * fru has been inserted (or may need to update)
2315 		 *
2316 		 * sgfruhdl may be stale due to hotplugging. We check this
2317 		 * by getting the fru_hdl_t from the parent's children
2318 		 * and compare it to the cached value in sgfruhdl.  If we
2319 		 * have a stale handle, we update the cached value and
2320 		 * use it in the call to add_subtree.
2321 		 */
2322 		if (get_fruhdl_from_parent(locnodeh, &sgfruhdl_from_parent) ==
2323 		    PICL_SUCCESS) {
2324 			if (sgfruhdl != sgfruhdl_from_parent) {
2325 				update_fru_hdl(locnodeh, sgfruhdl_from_parent);
2326 				sgfruhdl = sgfruhdl_from_parent;
2327 			}
2328 		}
2329 
2330 		(void) add_subtree(locnodeh, sgfruhdl);
2331 	}
2332 	nvlist_free(nvlp);
2333 }
2334 
2335 /*
2336  * handle memcfg picl events - need to update reference properties
2337  */
2338 /*ARGSUSED*/
2339 static void
2340 frumemcfg_evhandler(const char *ename, const void *earg, size_t size,
2341     void *cookie)
2342 {
2343 	picl_nodehdl_t	nodeh;
2344 	picl_nodehdl_t	lochdl;
2345 	picl_nodehdl_t	fruhdl;
2346 	picl_nodehdl_t	memgrphdl;
2347 	picl_nodehdl_t	memhdl;
2348 	picl_prophdl_t	tblhdl;
2349 	picl_prophdl_t	tblproph;
2350 	nvlist_t	*nvlp;
2351 	char	addr[MAXPATHLEN];
2352 	char	bname[PICL_PROPNAMELEN_MAX];
2353 	picl_nodehdl_t	banklochdl;
2354 	picl_nodehdl_t	bankfruhdl;
2355 	char	label[MAX_LABEL_LEN];
2356 	int err;
2357 	int id;
2358 	char *ptr;
2359 	int value;
2360 	char buf[MAX_LINE_SIZE];
2361 
2362 	if (strcmp(ename, PICLEVENT_MC_ADDED) != 0 &&
2363 	    strcmp(ename, PICLEVENT_MC_REMOVED) != 0)
2364 		return;
2365 
2366 	/*
2367 	 * find corresponding frutree dimm nodes
2368 	 */
2369 	if (nvlist_unpack((char *)earg, size, &nvlp, NULL))
2370 		return;
2371 	if (nvlist_lookup_uint64(nvlp, PICLEVENTARG_NODEHANDLE, &nodeh)) {
2372 		nvlist_free(nvlp);
2373 		return;
2374 	}
2375 	nvlist_free(nvlp);
2376 	err = ptree_get_propval_by_name(nodeh, PICL_PROP_UNIT_ADDRESS, addr,
2377 	    sizeof (addr));
2378 	if (err != PICL_SUCCESS)
2379 		return;
2380 	ptr = strchr(addr, ',');
2381 	if (ptr == NULL)
2382 		return;
2383 	*ptr = '\0';
2384 	value = strtol(addr, NULL, 16);
2385 	sprintf_buf5(buf, PROC_FRU_PATH, SAFARI_ADDR_TO_SB(value),
2386 	    SAFARI_ADDR_TO_SB(value), SAFARI_ADDR_TO_P(value),
2387 	    SAFARI_ADDR_TO_P(value));
2388 	err = ptree_get_node_by_path(buf, &fruhdl);
2389 	if (err != PICL_SUCCESS)
2390 		return;
2391 	err = ptree_get_propval_by_name(fruhdl, PICL_PROP_CHILD,
2392 	    &banklochdl, sizeof (banklochdl));
2393 	if (err != PICL_SUCCESS)
2394 		return;
2395 
2396 	/*
2397 	 * walk through the DIMM locations
2398 	 */
2399 	for (;;) {
2400 		err = ptree_get_propval_by_name(banklochdl, PICL_PROP_CHILD,
2401 		    &bankfruhdl, sizeof (bankfruhdl));
2402 		if (err != PICL_SUCCESS)
2403 			goto next_bank;
2404 		err = ptree_get_propval_by_name(bankfruhdl, PICL_PROP_CHILD,
2405 		    &lochdl, sizeof (lochdl));
2406 		if (err != PICL_SUCCESS)
2407 			goto next_bank;
2408 		for (;;) {
2409 			err = ptree_get_propval_by_name(lochdl, PICL_PROP_CHILD,
2410 			    &fruhdl, sizeof (fruhdl));
2411 			if (err != PICL_SUCCESS)
2412 				goto next_dimm;
2413 
2414 			/*
2415 			 * this is a frutree dimm node corresponding to the
2416 			 * memory controller that has been added/deleted
2417 			 * - so create/delete reference properties
2418 			 */
2419 			if (strcmp(ename, PICLEVENT_MC_ADDED) == 0) {
2420 				/*
2421 				 * find bank name
2422 				 */
2423 				err = ptree_get_propval_by_name(fruhdl,
2424 				    PICL_PROP_DEVICES, &tblhdl,
2425 				    sizeof (tblhdl));
2426 				if (err != PICL_SUCCESS)
2427 					goto next_dimm;
2428 				err = ptree_get_propval_by_name(lochdl,
2429 				    PICL_PROP_LABEL, label, sizeof (label));
2430 				if (err != PICL_SUCCESS)
2431 					goto next_dimm;
2432 
2433 				err = ptree_get_propval_by_name(bankfruhdl,
2434 				    PICL_PROP_NAME, bname, sizeof (bname));
2435 				if (err != PICL_SUCCESS)
2436 					goto next_dimm;
2437 
2438 				/*
2439 				 * find memory group node
2440 				 */
2441 				err = ptree_get_propval_by_name(nodeh,
2442 				    PICL_PROP_CHILD, &memgrphdl,
2443 				    sizeof (memgrphdl));
2444 				if (err != PICL_SUCCESS)
2445 					goto next_dimm;
2446 
2447 				/*
2448 				 * check if this is the right bank - if not
2449 				 * move on to sibling
2450 				 */
2451 				err = ptree_get_propval_by_name(memgrphdl,
2452 				    PICL_PROP_ID, &id, sizeof (id));
2453 				if (err != PICL_SUCCESS)
2454 					goto next_dimm;
2455 				if (bname[1] != id + '0') {
2456 					err =
2457 					    ptree_get_propval_by_name(memgrphdl,
2458 					    PICL_PROP_PEER, &memgrphdl,
2459 					    sizeof (memgrphdl));
2460 					if (err != PICL_SUCCESS)
2461 						goto next_dimm;
2462 					err =
2463 					    ptree_get_propval_by_name(memgrphdl,
2464 					    PICL_PROP_ID, &id, sizeof (id));
2465 					if (err != PICL_SUCCESS)
2466 						goto next_dimm;
2467 					if (bname[1] != id + '0')
2468 						goto next_dimm;
2469 				}
2470 
2471 				/*
2472 				 * got the right bank - now create appropriate
2473 				 * link
2474 				 */
2475 				err = ptree_get_propval_by_name(memgrphdl,
2476 				    PICL_PROP_CHILD, &memhdl,
2477 				    sizeof (memhdl));
2478 				if (err != PICL_SUCCESS)
2479 					goto next_dimm;
2480 				for (;;) {
2481 					err = ptree_get_propval_by_name(memhdl,
2482 					    PICL_PROP_ID, &id, sizeof (id));
2483 					if (err != PICL_SUCCESS)
2484 						goto next_dimm;
2485 					if (label[1] == ('0' + id)) {
2486 						err = add_prop_ref(memhdl,
2487 						    fruhdl,
2488 						    PICL_REFPROP_FRU_PARENT);
2489 						if (err != PICL_SUCCESS)
2490 							return;
2491 						err = create_table_entry(tblhdl,
2492 						    memhdl,
2493 						    PICL_CLASS_MEMORY_MODULE);
2494 						if (err != PICL_SUCCESS)
2495 							return;
2496 					}
2497 					err = ptree_get_propval_by_name(memhdl,
2498 					    PICL_PROP_PEER,
2499 					    &memhdl, sizeof (memhdl));
2500 					if (err == PICL_PROPNOTFOUND)
2501 						break;
2502 					if (err != PICL_SUCCESS)
2503 						return;
2504 				}
2505 			} else if (strcmp(ename, PICLEVENT_MC_REMOVED) == 0) {
2506 				/*
2507 				 * XXX - no mechanism for deleting row - so
2508 				 * delete whole tabel and start again
2509 				 */
2510 				err = ptree_get_prop_by_name(fruhdl,
2511 				    PICL_PROP_DEVICES, &tblproph);
2512 				if (err == PICL_SUCCESS) {
2513 					err = ptree_delete_prop(tblproph);
2514 					if (err != PICL_SUCCESS)
2515 						return;
2516 					(void) ptree_destroy_prop(tblproph);
2517 				}
2518 				err = create_table(fruhdl, &tblhdl,
2519 				    PICL_PROP_DEVICES);
2520 				if (err != PICL_SUCCESS)
2521 					return;
2522 			}
2523 next_dimm:
2524 			err = ptree_get_propval_by_name(lochdl,
2525 			    PICL_PROP_PEER, &lochdl, sizeof (lochdl));
2526 			if (err == PICL_PROPNOTFOUND)
2527 				break;
2528 			if (err != PICL_SUCCESS)
2529 				return;
2530 		}
2531 next_bank:
2532 		err = ptree_get_propval_by_name(banklochdl,
2533 		    PICL_PROP_PEER, &banklochdl, sizeof (banklochdl));
2534 		if (err == PICL_PROPNOTFOUND)
2535 			break;
2536 		if (err != PICL_SUCCESS)
2537 			return;
2538 	}
2539 	/*
2540 	 * We don't get an event to say that cpu nodes have been added/
2541 	 * deleted (in fact as things stand they are never deleted). However
2542 	 * we know that all cpus must be configured before the MC_ADDED event
2543 	 * we are handling here. So if the cpu links haven't been set up yet
2544 	 * then we do it now.
2545 	 */
2546 	if (strcmp(ename, PICLEVENT_MC_ADDED) == 0) {
2547 		sprintf_buf4(buf, PROC_LOC_PATH, SAFARI_ADDR_TO_SB(value),
2548 		    SAFARI_ADDR_TO_SB(value), SAFARI_ADDR_TO_P(value));
2549 		err = ptree_get_node_by_path(buf, &lochdl);
2550 		if (err != PICL_SUCCESS)
2551 			return;
2552 		sprintf_buf5(buf, PROC_FRU_PATH, SAFARI_ADDR_TO_SB(value),
2553 		    SAFARI_ADDR_TO_SB(value), SAFARI_ADDR_TO_P(value),
2554 		    SAFARI_ADDR_TO_P(value));
2555 		err = ptree_get_node_by_path(buf, &fruhdl);
2556 		if (err != PICL_SUCCESS)
2557 			return;
2558 		sprintf_buf2(buf, "P%d", SAFARI_ADDR_TO_P(value));
2559 		err = ptree_get_propval_by_name(fruhdl,
2560 		    PICL_PROP_DEVICES, &tblhdl, sizeof (tblhdl));
2561 		if (err != PICL_SUCCESS)
2562 			return;
2563 		(void) create_cpu_references(buf, fruhdl, tblhdl);
2564 	}
2565 }
2566 
2567 /*
2568  * subroutine for add_env_nodes(), and add_led_node(). Adds a sensor
2569  * node under the sc node in the platform tree, of name "nodename" and
2570  * class "class". Also add UnitAddress property (always 0 as the nodenames
2571  * are unique anyway). Add reference property back to parent fru/location node
2572  * in frutree and a Devices table entry pointing to this node from the
2573  * parent fru/location node in frutree.
2574  */
2575 static int
2576 add_sensor_node(picl_nodehdl_t fruhdl, picl_nodehdl_t lochdl, char *nodename,
2577 	char *class, char *prop_class, picl_prophdl_t tblhdl,
2578 	picl_nodehdl_t *sensorhdlp)
2579 {
2580 	int err;
2581 
2582 	err = ptree_create_and_add_node(sch, nodename, class, sensorhdlp);
2583 	if (err != PICL_SUCCESS) {
2584 		syslog(LOG_ERR, ADD_NODE_FAIL, nodename, err);
2585 		return (err);
2586 	}
2587 
2588 	err = create_table_entry(tblhdl, *sensorhdlp, class);
2589 	if (err != PICL_SUCCESS)
2590 		return (err);
2591 
2592 	err = add_sensor_prop(*sensorhdlp, prop_class);
2593 	if (err != PICL_SUCCESS)
2594 		return (err);
2595 
2596 	err = add_prop_charstring(*sensorhdlp, "0", PICL_PROP_UNIT_ADDRESS);
2597 	if (err != PICL_SUCCESS)
2598 		return (err);
2599 
2600 	if (fruhdl != NULL) {
2601 		err = add_prop_ref(*sensorhdlp, fruhdl,
2602 		    PICL_REFPROP_FRU_PARENT);
2603 	} else {
2604 		err = add_prop_ref(*sensorhdlp, lochdl,
2605 		    PICL_REFPROP_LOC_PARENT);
2606 	}
2607 	return (err);
2608 }
2609 
2610 /*
2611  * subroutine for add_sensor_node()/add_env_nodes(). Used for adding dynamic
2612  * properties
2613  */
2614 static int
2615 add_sensor_prop(picl_nodehdl_t nodeh, char *class)
2616 {
2617 	ptree_propinfo_t propinfo;
2618 	int err;
2619 
2620 	if (strcmp(class, PICL_PROP_TEMPERATURE) == 0) {
2621 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2622 		    PICL_PTYPE_INT, PICL_READ + PICL_VOLATILE,
2623 		    sizeof (int), class, get_sensor_data, NULL);
2624 	} else if (strcmp(class, PICL_PROP_FAN_SPEED) == 0) {
2625 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2626 		    PICL_PTYPE_INT, PICL_READ + PICL_VOLATILE,
2627 		    sizeof (int), class, get_sensor_data, NULL);
2628 	} else if (strcmp(class, PICL_PROP_FAN_SPEED_UNIT) == 0) {
2629 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2630 		    PICL_PTYPE_CHARSTRING, PICL_READ + PICL_VOLATILE,
2631 		    MAX_SPEED_UNIT_LEN, class, get_sensor_data, NULL);
2632 	} else if (strcmp(class, PICL_PROP_CONDITION) == 0) {
2633 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2634 		    PICL_PTYPE_CHARSTRING, PICL_READ + PICL_VOLATILE,
2635 		    MAX_CONDITION_LEN, class, get_sensor_data, NULL);
2636 	} else if (strcmp(class, PICL_PROP_STATE) == 0) {
2637 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2638 		    PICL_PTYPE_CHARSTRING, PICL_READ + PICL_WRITE +
2639 		    PICL_VOLATILE, MAX_STATE_LEN, class, get_led_data,
2640 		    set_led_data);
2641 	} else {
2642 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
2643 		    PICL_PTYPE_FLOAT, PICL_READ + PICL_VOLATILE,
2644 		    sizeof (float), class, get_sensor_data, NULL);
2645 	}
2646 	if (err != PICL_SUCCESS) {
2647 		syslog(LOG_ERR, PROPINFO_FAIL, class, err);
2648 		return (err);
2649 	}
2650 
2651 	err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, NULL);
2652 	if (err != PICL_SUCCESS) {
2653 		syslog(LOG_ERR, ADD_PROP_FAIL, class, err);
2654 		return (err);
2655 	}
2656 	return (PICL_SUCCESS);
2657 }
2658 
2659 /*
2660  * Get requested kstat
2661  */
2662 static int
2663 open_kstat(char *name, void **ptr, kstat_ctl_t **kcp)
2664 {
2665 	kstat_t *info_ksp;
2666 
2667 	*kcp = kstat_open();
2668 	if (*kcp == NULL) {
2669 		syslog(LOG_ERR, KSTAT_FAIL);
2670 		return (PICL_FAILURE);
2671 	}
2672 	info_ksp = kstat_lookup(*kcp, NULL, -1, name);
2673 	if (info_ksp == NULL) {
2674 		kstat_close(*kcp);
2675 		syslog(LOG_ERR, KSTAT_FAIL);
2676 		return (PICL_FAILURE);
2677 	}
2678 	if (kstat_read(*kcp, info_ksp, NULL) == -1) {
2679 		kstat_close(*kcp);
2680 		syslog(LOG_ERR, KSTAT_FAIL);
2681 		return (PICL_FAILURE);
2682 	}
2683 	*ptr = info_ksp;
2684 	return (PICL_SUCCESS);
2685 }
2686 
2687 /*
2688  * dimm status - uses bank-status property on memory-controller node
2689  */
2690 
2691 static int
2692 get_dimm_status(ptree_rarg_t *arg, void *result)
2693 {
2694 	int err;
2695 	int i;
2696 	picl_prophdl_t	tblhdl;
2697 	picl_prophdl_t  nextprop;
2698 	picl_prophdl_t  refprop;
2699 	picl_prophdl_t  mmgprop;
2700 	picl_prophdl_t  mcprop;
2701 	picl_prophdl_t  bankprop;
2702 	char	nodename[PICL_PROPNAMELEN_MAX];
2703 	char    class[PICL_CLASSNAMELEN_MAX];
2704 	char	bankname[PICL_PROPNAMELEN_MAX];
2705 	char    state[MAX_STATE_SIZE];
2706 
2707 	/*
2708 	 * find the name of this node
2709 	 */
2710 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, nodename,
2711 	    sizeof (nodename));
2712 	if (err != PICL_SUCCESS) {
2713 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
2714 		return (err);
2715 	}
2716 
2717 	/*
2718 	 * find the name of grandparent (dimm bank) node
2719 	 */
2720 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_PARENT, &bankprop,
2721 	    sizeof (picl_nodehdl_t));
2722 	if (err != PICL_SUCCESS) {
2723 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
2724 		return (err);
2725 	}
2726 	err = ptree_get_propval_by_name(bankprop, PICL_PROP_PARENT, &bankprop,
2727 	    sizeof (picl_nodehdl_t));
2728 	if (err != PICL_SUCCESS) {
2729 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
2730 		return (err);
2731 	}
2732 	err = ptree_get_propval_by_name(bankprop, PICL_PROP_NAME, bankname,
2733 	    sizeof (bankname));
2734 	if (err != PICL_SUCCESS) {
2735 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_NAME, err);
2736 		return (err);
2737 	}
2738 
2739 	/*
2740 	 * lookup memory-module node in Devices table
2741 	 */
2742 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_DEVICES, &tblhdl,
2743 	    sizeof (tblhdl));
2744 	if (err != PICL_SUCCESS) {
2745 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_DEVICES, err);
2746 		return (err);
2747 	}
2748 	err = ptree_get_next_by_row(tblhdl, &nextprop);
2749 	if (err != PICL_SUCCESS) {
2750 		/*
2751 		 * if Devices table empty then dimm is unconfigured
2752 		 */
2753 		(void) strlcpy(result, PICL_PROPVAL_DISABLED,
2754 		    MAX_OPERATIONAL_STATUS_LEN);
2755 		return (PICL_SUCCESS);
2756 	}
2757 	err = ptree_get_next_by_row(nextprop, &nextprop);
2758 	if (err != PICL_SUCCESS) {
2759 		syslog(LOG_ERR, GET_NEXT_BY_ROW_FAIL, PICL_PROP_DEVICES, err);
2760 		return (err);
2761 	}
2762 
2763 	/*
2764 	 * walk down second column (ref ptr)
2765 	 */
2766 	while (err == PICL_SUCCESS) {
2767 		err = ptree_get_propval(nextprop, &refprop, sizeof (refprop));
2768 		if (err != PICL_SUCCESS) {
2769 			syslog(LOG_ERR, GET_PROPVAL_FAIL, err);
2770 			return (PICL_PROPVALUNAVAILABLE);
2771 		}
2772 		err = ptree_get_propval_by_name(refprop, PICL_PROP_CLASSNAME,
2773 		    class, sizeof (class));
2774 		if (err == PICL_SUCCESS && strcmp(class,
2775 		    PICL_CLASS_MEMORY_MODULE) == 0)
2776 			break;
2777 		if (err != PICL_SUCCESS && err != PICL_STALEHANDLE) {
2778 			syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_CLASSNAME,
2779 			    err);
2780 			return (err);
2781 		}
2782 		err = ptree_get_next_by_col(nextprop, &nextprop);
2783 		if (err != PICL_SUCCESS) {
2784 			/*
2785 			 * if no memory-module in Devices table
2786 			 *  then dimm is unconfigured
2787 			 */
2788 			(void) strlcpy(result, PICL_PROPVAL_DISABLED,
2789 			    MAX_OPERATIONAL_STATUS_LEN);
2790 			return (PICL_SUCCESS);
2791 		}
2792 	}
2793 
2794 	/*
2795 	 * we've finally found the associated memory-module
2796 	 * node. Now need to find the bank-status property on
2797 	 * its parent memory-controller.
2798 	 */
2799 	err = ptree_get_propval_by_name(refprop, PICL_PROP_PARENT,
2800 	    &mmgprop, sizeof (picl_nodehdl_t));
2801 	if (err != PICL_SUCCESS) {
2802 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
2803 		return (err);
2804 	}
2805 	err = ptree_get_propval_by_name(mmgprop, PICL_PROP_PARENT, &mcprop,
2806 	    sizeof (picl_nodehdl_t));
2807 	if (err != PICL_SUCCESS) {
2808 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_PARENT, err);
2809 		return (err);
2810 	}
2811 	err = ptree_get_propval_by_name(mcprop, PICL_PROP_BANK_STATUS, &tblhdl,
2812 	    sizeof (tblhdl));
2813 	if (err != PICL_SUCCESS) {
2814 		(void) strlcpy(result, PICL_PROPVAL_UNKNOWN,
2815 		    MAX_OPERATIONAL_STATUS_LEN);
2816 		return (PICL_SUCCESS);
2817 	}
2818 
2819 	/*
2820 	 * bank-status is a table. Need to find the entry corresponding
2821 	 * to this node
2822 	 */
2823 	err = ptree_get_next_by_row(tblhdl, &nextprop);
2824 	if (err != PICL_SUCCESS) {
2825 		(void) strlcpy(result, PICL_PROPVAL_UNKNOWN,
2826 		    MAX_OPERATIONAL_STATUS_LEN);
2827 		return (PICL_SUCCESS);
2828 	}
2829 	for (i = 0; i < 4; i++) {
2830 		err = ptree_get_propval(nextprop, &state, sizeof (state));
2831 		if (err != PICL_SUCCESS) {
2832 			(void) strlcpy(result, PICL_PROPVAL_UNKNOWN,
2833 			    MAX_OPERATIONAL_STATUS_LEN);
2834 			return (err);
2835 		}
2836 		if ((i & 1) == (bankname[1] - '0')) {
2837 			if (strcmp(state, "pass") == 0) {
2838 				(void) strlcpy(result, PICL_PROPVAL_OKAY,
2839 				    MAX_OPERATIONAL_STATUS_LEN);
2840 			} else if (strcmp(state, "fail") == 0) {
2841 				(void) strlcpy(result, PICL_PROPVAL_FAILED,
2842 				    MAX_OPERATIONAL_STATUS_LEN);
2843 			} else {
2844 				(void) strlcpy(result, state,
2845 				    MAX_OPERATIONAL_STATUS_LEN);
2846 			}
2847 			break;
2848 		}
2849 		err = ptree_get_next_by_col(nextprop, &nextprop);
2850 		if (err != PICL_SUCCESS) {
2851 			(void) strlcpy(result, PICL_PROPVAL_OKAY,
2852 			    MAX_OPERATIONAL_STATUS_LEN);
2853 			break;
2854 		}
2855 	}
2856 	return (PICL_SUCCESS);
2857 }
2858 
2859 /*
2860  * cpu status - uses State property on cpu node
2861  */
2862 
2863 static int
2864 get_cpu_status(ptree_rarg_t *arg, void *result)
2865 {
2866 	int err;
2867 	picl_prophdl_t	tblhdl;
2868 	picl_prophdl_t  nextprop;
2869 	picl_prophdl_t  refprop;
2870 	char    class[PICL_CLASSNAMELEN_MAX];
2871 	char    state[MAX_STATE_SIZE];
2872 
2873 	/*
2874 	 * lookup cpu node in Devices table
2875 	 */
2876 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_DEVICES, &tblhdl,
2877 	    sizeof (tblhdl));
2878 	if (err != PICL_SUCCESS) {
2879 		syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_DEVICES, err);
2880 		return (err);
2881 	}
2882 	err = ptree_get_next_by_row(tblhdl, &nextprop);
2883 	if (err != PICL_SUCCESS) {
2884 		/*
2885 		 * if Devices table empty then cpu is unconfigured
2886 		 */
2887 		(void) strlcpy(result, PICL_PROPVAL_DISABLED,
2888 		    MAX_OPERATIONAL_STATUS_LEN);
2889 		return (PICL_SUCCESS);
2890 	}
2891 	err = ptree_get_next_by_row(nextprop, &nextprop);
2892 	if (err != PICL_SUCCESS) {
2893 		syslog(LOG_ERR, GET_NEXT_BY_ROW_FAIL, PICL_PROP_DEVICES, err);
2894 		return (err);
2895 	}
2896 
2897 	/*
2898 	 * walk down second column (ref ptr)
2899 	 */
2900 	while (err == PICL_SUCCESS) {
2901 		err = ptree_get_propval(nextprop, &refprop, sizeof (refprop));
2902 		if (err != PICL_SUCCESS) {
2903 			syslog(LOG_ERR, GET_PROPVAL_FAIL, err);
2904 			return (err);
2905 		}
2906 		err = ptree_get_propval_by_name(refprop, PICL_PROP_CLASSNAME,
2907 		    class, sizeof (class));
2908 		if (err == PICL_SUCCESS && strcmp(class, PICL_CLASS_CPU) == 0)
2909 			break;
2910 		if (err != PICL_SUCCESS && err != PICL_STALEHANDLE) {
2911 			syslog(LOG_ERR, PROP_LOOKUP_FAIL, PICL_PROP_CLASSNAME,
2912 			    err);
2913 			return (err);
2914 		}
2915 		err = ptree_get_next_by_col(nextprop, &nextprop);
2916 		if (err != PICL_SUCCESS) {
2917 			/*
2918 			 * if no cpu in Devices table
2919 			 *  then cpu is unconfigured
2920 			 */
2921 			(void) strlcpy(result, PICL_PROPVAL_DISABLED,
2922 			    MAX_OPERATIONAL_STATUS_LEN);
2923 			return (PICL_SUCCESS);
2924 		}
2925 	}
2926 
2927 	/*
2928 	 * we've finally found the associated cpu node. Now need to find its
2929 	 * status property if present (if not assume OK)
2930 	 */
2931 	err = ptree_get_propval_by_name(refprop, OBP_STATUS,
2932 	    state, sizeof (state));
2933 	if (err == PICL_SUCCESS) {
2934 		if (strcmp(state, "fail") == 0)
2935 			(void) strlcpy(result, PICL_PROPVAL_FAILED,
2936 			    MAX_OPERATIONAL_STATUS_LEN);
2937 		else
2938 			(void) strlcpy(result, state,
2939 			    MAX_OPERATIONAL_STATUS_LEN);
2940 		return (PICL_SUCCESS);
2941 	}
2942 
2943 	(void) strlcpy(result, PICL_PROPVAL_OKAY, MAX_OPERATIONAL_STATUS_LEN);
2944 	return (PICL_SUCCESS);
2945 }
2946 
2947 /*
2948  * system/io board condition - uses sgenv driver kstats
2949  */
2950 
2951 static int
2952 get_board_status(ptree_rarg_t *arg, void *result)
2953 {
2954 	int err = PICL_SUCCESS;
2955 	int i;
2956 	sg_board_info_t	*brd;
2957 	char name[PICL_PROPNAMELEN_MAX];
2958 	char buf[PICL_PROPNAMELEN_MAX];
2959 	kstat_ctl_t *kc;
2960 	kstat_t *board_info_ksp;
2961 
2962 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, name,
2963 	    sizeof (name));
2964 	if (err != PICL_SUCCESS) {
2965 		return (err);
2966 	}
2967 
2968 	err = open_kstat(SG_BOARD_STATUS_KSTAT_NAME, (void **)&board_info_ksp,
2969 	    &kc);
2970 	if (err != PICL_SUCCESS) {
2971 		return (err);
2972 	}
2973 
2974 	brd = board_info_ksp->ks_data;
2975 	for (i = 0; i < SGENV_NUM_BOARD_READINGS(board_info_ksp); i++, brd++) {
2976 		/*
2977 		 * check this kstat matches the name of the node
2978 		 */
2979 		if (SG_BOARD_IS_CPU_TYPE(brd->board_num)) {
2980 			sprintf_buf3(buf, "%s%d",
2981 			    SG_HPU_TYPE_CPU_BOARD_ID, brd->board_num);
2982 		} else {
2983 			sprintf_buf3(buf, "%s%d",
2984 			    SG_HPU_TYPE_PCI_IO_BOARD_ID, brd->board_num);
2985 		}
2986 		if (strncmp(buf, name, strlen(buf)) != 0)
2987 			continue;
2988 
2989 		/*
2990 		 * ok - got the right kstat - get it's value
2991 		 * note that values 0-4 are defined in sbdp_mbox.h
2992 		 */
2993 		if (brd->condition >= 0 && brd->condition < 5)
2994 			(void) strlcpy(result,
2995 			    hpu_condition_table[brd->condition],
2996 			    MAX_OPERATIONAL_STATUS_LEN);
2997 		kstat_close(kc);
2998 		return (PICL_SUCCESS);
2999 	}
3000 	kstat_close(kc);
3001 	return (PICL_PROPVALUNAVAILABLE);
3002 }
3003 
3004 static int
3005 get_op_status(ptree_rarg_t *arg, void *result)
3006 {
3007 	int err = PICL_SUCCESS;
3008 	char name[PICL_PROPNAMELEN_MAX];
3009 	char value[MAX_STATE_LEN];
3010 	char	parent_name[PICL_PROPNAMELEN_MAX];
3011 	picl_nodehdl_t loch;
3012 	picl_nodehdl_t parentfruh;
3013 
3014 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, name,
3015 	    sizeof (name));
3016 	if (err != PICL_SUCCESS) {
3017 		return (err);
3018 	}
3019 
3020 	/*
3021 	 * handle dimms, cpus and system boards specially
3022 	 */
3023 	if (IS_PROC_NODE(name)) {
3024 		return (get_cpu_status(arg, result));
3025 	} else if (IS_DIMM_NODE(name)) {
3026 		return (get_dimm_status(arg, result));
3027 	} else if (IS_SB_NODE(name) || IS_IB_NODE(name)) {
3028 		return (get_board_status(arg, result));
3029 	}
3030 
3031 	/*
3032 	 * otherwise OperationalStatus is derived from the fault led state
3033 	 */
3034 
3035 	/*
3036 	 * scapp knows FANs 0 and 1 on IB as FAN8 and FAN9
3037 	 */
3038 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_PARENT, &loch,
3039 	    sizeof (loch));
3040 	if (err != PICL_SUCCESS)
3041 		return (PICL_PROPVALUNAVAILABLE);
3042 	err = ptree_get_propval_by_name(loch, PICL_PROP_PARENT, &parentfruh,
3043 	    sizeof (parentfruh));
3044 	if (err != PICL_SUCCESS)
3045 		return (PICL_PROPVALUNAVAILABLE);
3046 	err = ptree_get_propval_by_name(parentfruh, PICL_PROP_NAME, parent_name,
3047 	    sizeof (parent_name));
3048 	if (err != PICL_SUCCESS)
3049 		return (PICL_PROPVALUNAVAILABLE);
3050 	if (strcmp(name, "FAN0") == 0 && strcmp(parent_name, "IB6") == 0) {
3051 		if (get_led("FAN8", FAULT_LED, value) != PICL_SUCCESS) {
3052 			return (PICL_PROPVALUNAVAILABLE);
3053 		}
3054 	} else if (strcmp(name, "FAN1") == 0 && strcmp(parent_name,
3055 	    "IB6") == 0) {
3056 		if (get_led("FAN9", FAULT_LED, value) != PICL_SUCCESS) {
3057 			return (PICL_PROPVALUNAVAILABLE);
3058 		}
3059 	} else {
3060 		if (get_led(name, FAULT_LED, value) != PICL_SUCCESS) {
3061 			return (PICL_PROPVALUNAVAILABLE);
3062 		}
3063 	}
3064 	if (strcmp(value, PICL_PROPVAL_ON) == 0)
3065 		(void) strlcpy(result, PICL_PROPVAL_FAILED,
3066 		    MAX_OPERATIONAL_STATUS_LEN);
3067 	else
3068 		(void) strlcpy(result, PICL_PROPVAL_OKAY,
3069 		    MAX_OPERATIONAL_STATUS_LEN);
3070 	return (PICL_SUCCESS);
3071 }
3072 
3073 static int
3074 add_board_status(picl_nodehdl_t nodeh, char *nodename)
3075 {
3076 	ptree_propinfo_t propinfo;
3077 	int err;
3078 	picl_prophdl_t prophdl;
3079 
3080 	/*
3081 	 * check if OperationalStatus property already created for this fru
3082 	 */
3083 	err = ptree_get_prop_by_name(nodeh, PICL_PROP_OPERATIONAL_STATUS,
3084 	    &prophdl);
3085 	if (err == PICL_SUCCESS)
3086 		return (PICL_SUCCESS);
3087 
3088 	/*
3089 	 * put operational status on dimms, cpus, SBs, IBs, PSUs, FTs, Fans, RPs
3090 	 */
3091 	if (IS_DIMM_NODE(nodename) || IS_PROC_NODE(nodename) ||
3092 	    IS_SB_NODE(nodename) || IS_IB_NODE(nodename) ||
3093 	    IS_PSU_NODE(nodename) || IS_FT_NODE(nodename) ||
3094 	    IS_FAN_NODE(nodename) || IS_RP_NODE(nodename)) {
3095 		err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
3096 		    PICL_PTYPE_CHARSTRING, PICL_READ + PICL_VOLATILE,
3097 		    MAX_OPERATIONAL_STATUS_LEN, PICL_PROP_OPERATIONAL_STATUS,
3098 		    get_op_status, NULL);
3099 		if (err != PICL_SUCCESS) {
3100 			syslog(LOG_ERR, PROPINFO_FAIL,
3101 			    PICL_PROP_OPERATIONAL_STATUS, err);
3102 			return (err);
3103 		}
3104 		err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, NULL);
3105 		if (err != PICL_SUCCESS) {
3106 			syslog(LOG_ERR, ADD_PROP_FAIL,
3107 			    PICL_PROP_OPERATIONAL_STATUS, err);
3108 			return (err);
3109 		}
3110 	}
3111 	return (PICL_SUCCESS);
3112 }
3113 
3114 /*
3115  * environmental information handling - uses sgenv driver kstats
3116  */
3117 
3118 static int
3119 add_env_nodes(picl_nodehdl_t nodeh, char *nodename, picl_prophdl_t tblhdl)
3120 {
3121 	int err = PICL_SUCCESS;
3122 	env_sensor_t	*env;
3123 	int	i;
3124 	picl_prophdl_t	tblhdl2;
3125 	picl_prophdl_t	frutype;
3126 	char fruname[PICL_PROPNAMELEN_MAX];
3127 	char buf[PICL_PROPNAMELEN_MAX];
3128 	char id[PICL_PROPNAMELEN_MAX];
3129 	float scale;
3130 	picl_nodehdl_t childh;
3131 	picl_nodehdl_t sensorhdl;
3132 	kstat_ctl_t *kc;
3133 	kstat_t *env_info_ksp;
3134 
3135 	err = open_kstat(SG_ENV_INFO_KSTAT_NAME, (void **)&env_info_ksp, &kc);
3136 	if (err != PICL_SUCCESS) {
3137 		return (err);
3138 	}
3139 
3140 	env = env_info_ksp->ks_data;
3141 	for (i = 0; i < SGENV_NUM_ENV_READINGS(env_info_ksp); i++, env++) {
3142 		/*
3143 		 * check values from kstat entry are within valid range
3144 		 */
3145 		if (env->sd_id.id.sensor_type < SG_SENSOR_TYPE_CURRENT)
3146 			continue;
3147 		if (env->sd_id.id.sensor_type == SG_SENSOR_TYPE_ENVDB)
3148 			continue;
3149 		if (env->sd_id.id.sensor_type > SG_SENSOR_TYPE_2_5_VDC)
3150 			continue;
3151 		if ((env->sd_id.id.hpu_type >> 8) >=
3152 		    (SG_HPU_TYPE_SUN_FIRE_3800_CENTERPLANE >> 8))
3153 			continue;
3154 		if (env->sd_id.id.sensor_part > SG_SENSOR_PART_INPUT)
3155 			continue;
3156 
3157 		/*
3158 		 * does this kstat entry belong to this fru?
3159 		 * Note sc reports RPS as 10 and 12 via env messages
3160 		 * but by 0 and 2 via fru messages, so correct here
3161 		 */
3162 		if ((env->sd_id.id.hpu_type >> 8) ==
3163 		    (SG_HPU_TYPE_REPEATER_BOARD >> 8)) {
3164 			sprintf_buf3(fruname, "%s%d",
3165 			    hpu_type_table[env->sd_id.id.hpu_type >> 8],
3166 			    env->sd_id.id.hpu_slot - 10);
3167 		} else {
3168 			sprintf_buf3(fruname, "%s%d",
3169 			    hpu_type_table[env->sd_id.id.hpu_type >> 8],
3170 			    env->sd_id.id.hpu_slot);
3171 		}
3172 		if (strcmp(nodename, fruname) != 0)
3173 			continue;
3174 
3175 		/*
3176 		 * set up FRUType. Note we only want to do this once per fru
3177 		 */
3178 		err = ptree_get_prop_by_name(nodeh, PICL_PROP_FRU_TYPE,
3179 		    &frutype);
3180 		if (err != PICL_SUCCESS) {
3181 			err = add_prop_charstring(nodeh,
3182 			    hpu_fru_type_table[env->sd_id.id.hpu_type >> 8],
3183 			    PICL_PROP_FRU_TYPE);
3184 			if (err != PICL_SUCCESS)
3185 				goto done;
3186 		}
3187 
3188 		/*
3189 		 * create the sensor node with a sensible name
3190 		 */
3191 		switch (env->sd_id.id.sensor_type) {
3192 		case SG_SENSOR_TYPE_TEMPERATURE:
3193 			if (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD) {
3194 				sprintf_buf2(id, "t_ambient%d",
3195 				    env->sd_id.id.sensor_typenum);
3196 			} else {
3197 				sprintf_buf3(id, "t_%s%d",
3198 				    hpu_part_table[env->sd_id.id.sensor_part],
3199 				    env->sd_id.id.sensor_partnum);
3200 			}
3201 			break;
3202 		case SG_SENSOR_TYPE_CURRENT:
3203 			sprintf_buf3(id, "i_%s%d",
3204 			    hpu_part_table[env->sd_id.id.sensor_part],
3205 			    env->sd_id.id.sensor_partnum);
3206 			break;
3207 		case SG_SENSOR_TYPE_COOLING:
3208 			sprintf_buf3(id, "ft_%s%d",
3209 			    hpu_part_table[env->sd_id.id.sensor_part],
3210 			    env->sd_id.id.sensor_partnum);
3211 			break;
3212 		default: /* voltage */
3213 			if (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD) {
3214 				sprintf_buf3(id, "v_%s%d",
3215 				    hpu_sensor_table[env->sd_id.id.sensor_type],
3216 				    env->sd_id.id.sensor_typenum);
3217 			} else {
3218 				sprintf_buf3(id, "v_%s%d",
3219 				    hpu_part_table[env->sd_id.id.sensor_part],
3220 				    env->sd_id.id.sensor_partnum);
3221 			}
3222 			break;
3223 		}
3224 
3225 		/*
3226 		 * check if sensor node has already been created
3227 		 */
3228 		sprintf_buf3(buf, "%s_%s", nodename, id);
3229 		if (find_child_by_name(sch, buf) != NULL)
3230 			continue;
3231 
3232 		if (env->sd_id.id.sensor_type == SG_SENSOR_TYPE_COOLING) {
3233 			/*
3234 			 * create individual fan_unit nodes
3235 			 */
3236 			childh = nodeh;
3237 			sprintf_buf2(fruname, "FAN%d",
3238 			    env->sd_id.id.sensor_partnum);
3239 			err = add_intermediate_nodes(&childh, fruname,
3240 			    &tblhdl2, "fan-unit", "FAN");
3241 			if (err != PICL_SUCCESS)
3242 				goto done;
3243 			err = add_board_status(childh, fruname);
3244 			if (err != PICL_SUCCESS)
3245 				goto done;
3246 		} else if (env->sd_id.id.sensor_part ==
3247 		    SG_SENSOR_PART_CHEETAH ||
3248 		    ((env->sd_id.id.hpu_type >> 8) ==
3249 		    (SG_HPU_TYPE_CPU_BOARD >> 8) &&
3250 		    (env->sd_id.id.sensor_type == SG_SENSOR_TYPE_TEMPERATURE) &&
3251 		    (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD))) {
3252 			/*
3253 			 * put sensors under individual processor nodes
3254 			 */
3255 			childh = nodeh;
3256 			if (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD)
3257 				sprintf_buf2(fruname, "P%d",
3258 				    env->sd_id.id.sensor_typenum);
3259 			else
3260 				sprintf_buf2(fruname, "P%d",
3261 				    env->sd_id.id.sensor_partnum);
3262 			err = add_intermediate_nodes(&childh, fruname,
3263 			    &tblhdl2, "cpu", "PROC");
3264 			if (err != PICL_SUCCESS)
3265 				goto done;
3266 		} else {
3267 			childh = nodeh;
3268 			tblhdl2 = tblhdl;
3269 		}
3270 		err = add_sensor_node(childh, NULL, buf,
3271 		    hpu_sensor_class_table[env->sd_id.id.sensor_type],
3272 		    hpu_sensor_prop_table[env->sd_id.id.sensor_type],
3273 		    tblhdl2, &sensorhdl);
3274 		if (err != PICL_SUCCESS)
3275 			goto done;
3276 
3277 		/*
3278 		 * add additional properties
3279 		 */
3280 		switch (env->sd_id.id.sensor_type) {
3281 		case SG_SENSOR_TYPE_COOLING:
3282 			err = add_prop_charstring(sensorhdl, id,
3283 			    PICL_PROP_LABEL);
3284 			if (err != PICL_SUCCESS)
3285 				goto done;
3286 			/*
3287 			 * add threshold at 75% of full speed
3288 			 */
3289 			err = add_prop_int(sensorhdl, 75,
3290 			    PICL_PROP_LOW_WARNING_THRESHOLD);
3291 			if (err != PICL_SUCCESS)
3292 				goto done;
3293 			err = add_sensor_prop(sensorhdl,
3294 			    PICL_PROP_FAN_SPEED_UNIT);
3295 			if (err != PICL_SUCCESS)
3296 				goto done;
3297 			continue;
3298 		case SG_SENSOR_TYPE_TEMPERATURE:
3299 			if ((env->sd_id.id.hpu_type >> 8 ==
3300 			    (SG_HPU_TYPE_CPU_BOARD >> 8)) &&
3301 			    (env->sd_id.id.sensor_part ==
3302 			    SG_SENSOR_PART_BOARD)) {
3303 				err = add_prop_charstring(sensorhdl,
3304 				    PICL_PROPVAL_AMBIENT, PICL_PROP_LABEL);
3305 				if (err != PICL_SUCCESS)
3306 					goto done;
3307 			} else if (env->sd_id.id.sensor_part ==
3308 			    SG_SENSOR_PART_CHEETAH) {
3309 				err = add_prop_charstring(sensorhdl,
3310 				    PICL_PROPVAL_DIE, PICL_PROP_LABEL);
3311 				if (err != PICL_SUCCESS)
3312 					goto done;
3313 			} else {
3314 				err = add_prop_charstring(sensorhdl, id,
3315 				    PICL_PROP_LABEL);
3316 				if (err != PICL_SUCCESS)
3317 					goto done;
3318 			}
3319 			err = add_prop_int(sensorhdl, env->sd_lo_warn /
3320 			    SG_TEMPERATURE_SCALE, PICL_PROP_LOW_WARNING);
3321 			if (err != PICL_SUCCESS)
3322 				goto done;
3323 			err = add_prop_int(sensorhdl, env->sd_lo /
3324 			    SG_TEMPERATURE_SCALE, PICL_PROP_LOW_SHUTDOWN);
3325 			if (err != PICL_SUCCESS)
3326 				goto done;
3327 			err = add_prop_int(sensorhdl, env->sd_hi_warn /
3328 			    SG_TEMPERATURE_SCALE, PICL_PROP_HIGH_WARNING);
3329 			if (err != PICL_SUCCESS)
3330 				goto done;
3331 			err = add_prop_int(sensorhdl, env->sd_hi /
3332 			    SG_TEMPERATURE_SCALE, PICL_PROP_HIGH_SHUTDOWN);
3333 			if (err != PICL_SUCCESS)
3334 				goto done;
3335 			continue;
3336 		case SG_SENSOR_TYPE_1_5_VDC:
3337 			scale = SG_1_5_VDC_SCALE;
3338 			break;
3339 		case SG_SENSOR_TYPE_1_8_VDC:
3340 			scale = SG_1_8_VDC_SCALE;
3341 			break;
3342 		case SG_SENSOR_TYPE_2_5_VDC:
3343 			scale = SG_2_5_VDC_SCALE;
3344 			break;
3345 		case SG_SENSOR_TYPE_3_3_VDC:
3346 			scale = SG_3_3_VDC_SCALE;
3347 			break;
3348 		case SG_SENSOR_TYPE_5_VDC:
3349 			scale = SG_5_VDC_SCALE;
3350 			break;
3351 		case SG_SENSOR_TYPE_12_VDC:
3352 			scale = SG_12_VDC_SCALE;
3353 			break;
3354 		case SG_SENSOR_TYPE_48_VDC:
3355 			/*
3356 			 * The 48VDC sensor is just an indicator - doesn't
3357 			 * give reading or thresholds
3358 			 */
3359 			err = add_prop_charstring(sensorhdl, id,
3360 			    PICL_PROP_LABEL);
3361 			if (err != PICL_SUCCESS)
3362 				goto done;
3363 			continue;
3364 		case SG_SENSOR_TYPE_CURRENT:
3365 			scale = SG_CURRENT_SCALE;
3366 			break;
3367 		}
3368 		err = add_prop_charstring(sensorhdl, id, PICL_PROP_LABEL);
3369 		if (err != PICL_SUCCESS)
3370 			goto done;
3371 		err = add_prop_float(sensorhdl, (float)env->sd_lo_warn / scale,
3372 		    PICL_PROP_LOW_WARNING);
3373 		if (err != PICL_SUCCESS)
3374 			goto done;
3375 		err = add_prop_float(sensorhdl, (float)env->sd_lo / scale,
3376 		    PICL_PROP_LOW_SHUTDOWN);
3377 		if (err != PICL_SUCCESS)
3378 			goto done;
3379 		err = add_prop_float(sensorhdl, (float)env->sd_hi_warn / scale,
3380 		    PICL_PROP_HIGH_WARNING);
3381 		if (err != PICL_SUCCESS)
3382 			goto done;
3383 		err = add_prop_float(sensorhdl, (float)env->sd_hi / scale,
3384 		    PICL_PROP_HIGH_SHUTDOWN);
3385 		if (err != PICL_SUCCESS)
3386 			goto done;
3387 	}
3388 done:
3389 	kstat_close(kc);
3390 	return (err);
3391 }
3392 
3393 static int
3394 get_sensor_data(ptree_rarg_t *arg, void *result)
3395 {
3396 	int err;				/* return code */
3397 	kstat_ctl_t		*kc;
3398 	char	name[PICL_PROPNAMELEN_MAX];
3399 	ptree_propinfo_t propinfo;
3400 	int	i;
3401 	env_sensor_t	*env;
3402 	char buf[PICL_PROPNAMELEN_MAX];
3403 	char buf1[PICL_PROPNAMELEN_MAX];
3404 	kstat_t *env_info_ksp;
3405 
3406 	err = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, name,
3407 	    sizeof (name));
3408 	if (err != PICL_SUCCESS)
3409 		return (err);
3410 	err = ptree_get_propinfo(arg->proph, &propinfo);
3411 	if (err != PICL_SUCCESS)
3412 		return (err);
3413 
3414 	err = open_kstat(SG_ENV_INFO_KSTAT_NAME, (void **)&env_info_ksp, &kc);
3415 	if (err != PICL_SUCCESS) {
3416 		return (err);
3417 	}
3418 
3419 	env = env_info_ksp->ks_data;
3420 	for (i = 0; i < SGENV_NUM_ENV_READINGS(env_info_ksp); i++, env++) {
3421 		/*
3422 		 * check kstat values are within range
3423 		 */
3424 		if (SG_INFO_VALUESTATUS(env->sd_infostamp) != SG_INFO_VALUE_OK)
3425 			continue;
3426 		if (env->sd_id.id.sensor_type < SG_SENSOR_TYPE_CURRENT)
3427 			continue;
3428 		if (env->sd_id.id.sensor_type == SG_SENSOR_TYPE_ENVDB)
3429 			continue;
3430 		if (env->sd_id.id.sensor_type > SG_SENSOR_TYPE_2_5_VDC)
3431 			continue;
3432 		if ((env->sd_id.id.hpu_type >> 8) >=
3433 		    (SG_HPU_TYPE_SUN_FIRE_3800_CENTERPLANE >> 8))
3434 			continue;
3435 		if (env->sd_id.id.sensor_part > SG_SENSOR_PART_INPUT)
3436 			continue;
3437 
3438 		/*
3439 		 * check this kstat matches the name of the node
3440 		 * note sc reports RPS as 10 and 12 via env messages
3441 		 * but by 0 and 2 via fru messages, so correct here
3442 		 */
3443 		if ((env->sd_id.id.hpu_type >> 8) ==
3444 		    (SG_HPU_TYPE_REPEATER_BOARD >> 8))
3445 			sprintf_buf3(buf, "%s%d",
3446 			    hpu_type_table[env->sd_id.id.hpu_type >> 8],
3447 			    env->sd_id.id.hpu_slot - 10);
3448 		else
3449 			sprintf_buf3(buf, "%s%d",
3450 			    hpu_type_table[env->sd_id.id.hpu_type >> 8],
3451 			    env->sd_id.id.hpu_slot);
3452 		switch (env->sd_id.id.sensor_type) {
3453 		case SG_SENSOR_TYPE_TEMPERATURE:
3454 			if (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD) {
3455 				sprintf_buf3(buf1, "%s_t_ambient%d",
3456 				    buf, env->sd_id.id.sensor_typenum);
3457 			} else {
3458 				sprintf_buf4(buf1, "%s_t_%s%d", buf,
3459 				    hpu_part_table[env->sd_id.id.sensor_part],
3460 				    env->sd_id.id.sensor_partnum);
3461 			}
3462 			break;
3463 		case SG_SENSOR_TYPE_CURRENT:
3464 			sprintf_buf4(buf1, "%s_i_%s%d", buf,
3465 			    hpu_part_table[env->sd_id.id.sensor_part],
3466 			    env->sd_id.id.sensor_partnum);
3467 			break;
3468 		case SG_SENSOR_TYPE_COOLING:
3469 			sprintf_buf4(buf1, "%s_ft_%s%d", buf,
3470 			    hpu_part_table[env->sd_id.id.sensor_part],
3471 			    env->sd_id.id.sensor_partnum);
3472 			break;
3473 		default: /* voltage */
3474 			if (env->sd_id.id.sensor_part == SG_SENSOR_PART_BOARD) {
3475 				sprintf_buf4(buf1, "%s_v_%s%d", buf,
3476 				    hpu_sensor_table[env->sd_id.id.sensor_type],
3477 				    env->sd_id.id.sensor_typenum);
3478 			} else {
3479 				sprintf_buf4(buf1, "%s_v_%s%d", buf,
3480 				    hpu_part_table[env->sd_id.id.sensor_part],
3481 				    env->sd_id.id.sensor_partnum);
3482 			}
3483 			break;
3484 		}
3485 		if (strcmp(buf1, name) != 0)
3486 			continue;
3487 
3488 		/*
3489 		 * ok - this is the kstat we want - update
3490 		 * Condition, or sensor reading as requested
3491 		 */
3492 		if (strcmp(propinfo.piclinfo.name, PICL_PROP_CONDITION) == 0) {
3493 			switch (SG_GET_SENSOR_STATUS(env->sd_status)) {
3494 			case SG_SENSOR_STATUS_OK:
3495 				(void) strlcpy(result, PICL_PROPVAL_OKAY,
3496 				    MAX_CONDITION_LEN);
3497 				break;
3498 			case SG_SENSOR_STATUS_LO_WARN:
3499 			case SG_SENSOR_STATUS_HI_WARN:
3500 				(void) strlcpy(result, PICL_PROPVAL_WARNING,
3501 				    MAX_CONDITION_LEN);
3502 				break;
3503 			case SG_SENSOR_STATUS_LO_DANGER:
3504 			case SG_SENSOR_STATUS_HI_DANGER:
3505 				(void) strlcpy(result, PICL_PROPVAL_FAILED,
3506 				    MAX_CONDITION_LEN);
3507 				break;
3508 			default:
3509 				kstat_close(kc);
3510 				return (PICL_PROPVALUNAVAILABLE);
3511 			}
3512 			kstat_close(kc);
3513 			return (PICL_SUCCESS);
3514 		}
3515 		switch (env->sd_id.id.sensor_type) {
3516 		case SG_SENSOR_TYPE_TEMPERATURE:
3517 			*(int *)result = env->sd_value / SG_TEMPERATURE_SCALE;
3518 			break;
3519 		case SG_SENSOR_TYPE_1_5_VDC:
3520 			*(float *)result =
3521 			    (float)env->sd_value / (float)SG_1_5_VDC_SCALE;
3522 			break;
3523 		case SG_SENSOR_TYPE_1_8_VDC:
3524 			*(float *)result =
3525 			    (float)env->sd_value / (float)SG_1_8_VDC_SCALE;
3526 			break;
3527 		case SG_SENSOR_TYPE_2_5_VDC:
3528 			*(float *)result =
3529 			    (float)env->sd_value / (float)SG_2_5_VDC_SCALE;
3530 			break;
3531 		case SG_SENSOR_TYPE_3_3_VDC:
3532 			*(float *)result =
3533 			    (float)env->sd_value / (float)SG_3_3_VDC_SCALE;
3534 			break;
3535 		case SG_SENSOR_TYPE_5_VDC:
3536 			*(float *)result =
3537 			    (float)env->sd_value / (float)SG_5_VDC_SCALE;
3538 			break;
3539 		case SG_SENSOR_TYPE_12_VDC:
3540 			*(float *)result =
3541 			    (float)env->sd_value / (float)SG_12_VDC_SCALE;
3542 			break;
3543 		case SG_SENSOR_TYPE_CURRENT:
3544 			*(float *)result =
3545 			    (float)env->sd_value / (float)SG_CURRENT_SCALE;
3546 			break;
3547 		case SG_SENSOR_TYPE_COOLING:
3548 			if (strcmp(propinfo.piclinfo.name,
3549 			    PICL_PROP_FAN_SPEED_UNIT) == 0) {
3550 				if (SG_GET_SENSOR_STATUS(env->sd_status) ==
3551 				    SG_SENSOR_STATUS_FAN_LOW) {
3552 					(void) strlcpy(result,
3553 					    PICL_PROPVAL_SELF_REGULATING,
3554 					    MAX_SPEED_UNIT_LEN);
3555 				} else {
3556 					(void) strlcpy(result,
3557 					    PICL_PROPVAL_PER_CENT,
3558 					    MAX_SPEED_UNIT_LEN);
3559 				}
3560 			} else {
3561 				switch (SG_GET_SENSOR_STATUS(env->sd_status)) {
3562 				case SG_SENSOR_STATUS_FAN_HIGH:
3563 					*(int *)result = 100;
3564 					break;
3565 				case SG_SENSOR_STATUS_FAN_FAIL:
3566 				case SG_SENSOR_STATUS_FAN_OFF:
3567 					*(int *)result = 0;
3568 					break;
3569 				default:
3570 				case SG_SENSOR_STATUS_FAN_LOW:
3571 					kstat_close(kc);
3572 					return (PICL_PROPVALUNAVAILABLE);
3573 				}
3574 			}
3575 			break;
3576 		default:
3577 			kstat_close(kc);
3578 			return (PICL_PROPVALUNAVAILABLE);
3579 		}
3580 		kstat_close(kc);
3581 		return (PICL_SUCCESS);
3582 	}
3583 	kstat_close(kc);
3584 	return (PICL_PROPVALUNAVAILABLE);
3585 }
3586 
3587 /*
3588  * led information handling - uses lw8 driver
3589  */
3590 
3591 static int
3592 add_led_nodes(picl_nodehdl_t nodeh, char *name, int position,
3593     picl_prophdl_t tblhdl)
3594 {
3595 	int err;
3596 	int  ledfd;
3597 	lom_get_led_t lom_get_led;
3598 	picl_nodehdl_t sensorhdl;
3599 	char buf[PICL_PROPNAMELEN_MAX];
3600 
3601 	/*
3602 	 * Open the lw8 pseudo dev to get the led information
3603 	 */
3604 	if ((ledfd = open(LED_PSEUDO_DEV, O_RDWR, 0)) == -1) {
3605 		syslog(LOG_ERR, DEV_OPEN_FAIL, LED_PSEUDO_DEV, strerror(errno));
3606 		return (PICL_SUCCESS);
3607 	}
3608 	bzero(&lom_get_led, sizeof (lom_get_led));
3609 	(void) strlcpy(lom_get_led.location, name,
3610 	    sizeof (lom_get_led.location));
3611 	if (ioctl(ledfd, LOMIOCGETLED, &lom_get_led) == -1) {
3612 		(void) close(ledfd);
3613 		syslog(LOG_ERR, LED_IOCTL_FAIL, strerror(errno));
3614 		return (PICL_FAILURE);
3615 	}
3616 	while (lom_get_led.next_id[0] != '\0') {
3617 		(void) strlcpy(lom_get_led.id, lom_get_led.next_id,
3618 		    sizeof (lom_get_led.id));
3619 		lom_get_led.next_id[0] = '\0';
3620 		lom_get_led.position = LOM_LED_POSITION_FRU;
3621 		if (ioctl(ledfd, LOMIOCGETLED, &lom_get_led) == -1) {
3622 			(void) close(ledfd);
3623 			syslog(LOG_ERR, LED_IOCTL_FAIL, strerror(errno));
3624 			return (PICL_FAILURE);
3625 		}
3626 		sprintf_buf3(buf, "%s_%s", name, lom_get_led.id);
3627 		if (position != lom_get_led.position)
3628 			continue;
3629 		if (position == LOM_LED_POSITION_LOCATION) {
3630 			err = add_sensor_node(NULL, nodeh, buf, PICL_CLASS_LED,
3631 			    PICL_PROP_STATE, tblhdl, &sensorhdl);
3632 		} else {
3633 			err = add_sensor_node(nodeh, NULL, buf, PICL_CLASS_LED,
3634 			    PICL_PROP_STATE, tblhdl, &sensorhdl);
3635 		}
3636 		if (err != PICL_SUCCESS) {
3637 			(void) close(ledfd);
3638 			return (err);
3639 		}
3640 		if (strcmp(name, "chassis") == 0 && strcmp(lom_get_led.id,
3641 		    "locator") == 0) {
3642 			err = add_prop_charstring(sensorhdl, PICL_PROPVAL_TRUE,
3643 			    PICL_PROP_IS_LOCATOR);
3644 			if (err != PICL_SUCCESS) {
3645 				(void) close(ledfd);
3646 				return (err);
3647 			}
3648 			err = add_prop_charstring(sensorhdl,
3649 			    PICL_PROPVAL_SYSTEM, PICL_PROP_LOCATOR_NAME);
3650 			if (err != PICL_SUCCESS) {
3651 				(void) close(ledfd);
3652 				return (err);
3653 			}
3654 		}
3655 		err = add_prop_charstring(sensorhdl, lom_get_led.id,
3656 		    PICL_PROP_LABEL);
3657 		if (err != PICL_SUCCESS) {
3658 			(void) close(ledfd);
3659 			return (err);
3660 		}
3661 		err = add_prop_charstring(sensorhdl, lom_get_led.color,
3662 		    PICL_PROP_COLOR);
3663 		if (err != PICL_SUCCESS) {
3664 			(void) close(ledfd);
3665 			return (err);
3666 		}
3667 	}
3668 	(void) close(ledfd);
3669 	return (PICL_SUCCESS);
3670 }
3671 
3672 static int
3673 get_led(char *name, char *ptr, char *result)
3674 {
3675 	int ledfd;
3676 	lom_get_led_t lom_get_led;
3677 
3678 	/*
3679 	 * Open the lw8 pseudo dev to get the led information
3680 	 */
3681 	if ((ledfd = open(LED_PSEUDO_DEV, O_RDWR, 0)) == -1) {
3682 		syslog(LOG_ERR, DEV_OPEN_FAIL, LED_PSEUDO_DEV, strerror(errno));
3683 		return (PICL_FAILURE);
3684 	}
3685 	bzero(&lom_get_led, sizeof (lom_get_led));
3686 	(void) strlcpy(lom_get_led.location, name,
3687 	    sizeof (lom_get_led.location));
3688 	(void) strlcpy(lom_get_led.id, ptr, sizeof (lom_get_led.id));
3689 	if (ioctl(ledfd, LOMIOCGETLED, &lom_get_led) == -1) {
3690 		(void) close(ledfd);
3691 		syslog(LOG_ERR, LED_IOCTL_FAIL, strerror(errno));
3692 		return (PICL_PROPVALUNAVAILABLE);
3693 	}
3694 	if (lom_get_led.status == LOM_LED_STATUS_ON)
3695 		(void) strlcpy(result, PICL_PROPVAL_ON, MAX_STATE_LEN);
3696 	else if (lom_get_led.status == LOM_LED_STATUS_FLASHING)
3697 		(void) strlcpy(result, PICL_PROPVAL_FLASHING, MAX_STATE_LEN);
3698 	else if (lom_get_led.status == LOM_LED_STATUS_BLINKING)
3699 		(void) strlcpy(result, PICL_PROPVAL_BLINKING, MAX_STATE_LEN);
3700 	else
3701 		(void) strlcpy(result, PICL_PROPVAL_OFF, MAX_STATE_LEN);
3702 	(void) close(ledfd);
3703 	return (PICL_SUCCESS);
3704 }
3705 
3706 static int
3707 get_led_data(ptree_rarg_t *arg, void *result)
3708 {
3709 	int rc;				/* return code */
3710 	char	name[PICL_PROPNAMELEN_MAX];
3711 	char *ptr;
3712 
3713 	rc = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, name,
3714 	    sizeof (name));
3715 	if (rc != PICL_SUCCESS)
3716 		return (rc);
3717 
3718 	ptr = strchr(name, '_');
3719 	*ptr++ = '\0'; /* now name is fru name, ptr is led name */
3720 	return (get_led(name, ptr, (char *)result));
3721 }
3722 
3723 static int
3724 set_led(char *name, char *ptr, char *value)
3725 {
3726 	int ledfd;
3727 	lom_set_led_t lom_set_led;
3728 
3729 	/*
3730 	 * Open the lw8 pseudo dev to set the led information
3731 	 */
3732 	if ((ledfd = open(LED_PSEUDO_DEV, O_RDWR, 0)) == -1) {
3733 		syslog(LOG_ERR, DEV_OPEN_FAIL, LED_PSEUDO_DEV, strerror(errno));
3734 		return (PICL_FAILURE);
3735 	}
3736 	bzero(&lom_set_led, sizeof (lom_set_led));
3737 	(void) strlcpy(lom_set_led.location, name,
3738 	    sizeof (lom_set_led.location));
3739 	(void) strlcpy(lom_set_led.id, ptr, sizeof (lom_set_led.id));
3740 	if (strcmp(value, PICL_PROPVAL_ON) == 0) {
3741 		lom_set_led.status = LOM_LED_STATUS_ON;
3742 	} else if (strcmp(value, PICL_PROPVAL_FLASHING) == 0) {
3743 		lom_set_led.status = LOM_LED_STATUS_FLASHING;
3744 	} else if (strcmp(value, PICL_PROPVAL_BLINKING) == 0) {
3745 		lom_set_led.status = LOM_LED_STATUS_BLINKING;
3746 	} else {
3747 		lom_set_led.status = LOM_LED_STATUS_OFF;
3748 	}
3749 	if (ioctl(ledfd, LOMIOCSETLED, &lom_set_led) == -1) {
3750 		(void) close(ledfd);
3751 		syslog(LOG_ERR, LED_IOCTL_FAIL, strerror(errno));
3752 		return (PICL_PROPVALUNAVAILABLE);
3753 	}
3754 	(void) close(ledfd);
3755 	return (PICL_SUCCESS);
3756 }
3757 
3758 static int
3759 set_led_data(ptree_warg_t *arg, const void *value)
3760 {
3761 	int rc;				/* return code */
3762 	char	name[PICL_PROPNAMELEN_MAX];
3763 	char *ptr;
3764 
3765 	rc = ptree_get_propval_by_name(arg->nodeh, PICL_PROP_NAME, name,
3766 	    sizeof (name));
3767 	if (rc != PICL_SUCCESS)
3768 		return (rc);
3769 
3770 	ptr = strchr(name, '_');
3771 	*ptr++ = '\0'; /* now name is fru name, ptr is led name */
3772 	return (set_led(name, ptr, (char *)value));
3773 }
3774 
3775 static void
3776 disk_leds_init(void)
3777 {
3778 	int err = 0, i;
3779 
3780 	if (!g_mutex_init) {
3781 		if ((pthread_cond_init(&g_cv, NULL) == 0) &&
3782 		    (pthread_cond_init(&g_cv_ack, NULL) == 0) &&
3783 		    (pthread_mutex_init(&g_mutex, NULL) == 0)) {
3784 			g_mutex_init = B_TRUE;
3785 		} else {
3786 			return;
3787 		}
3788 	}
3789 
3790 	if (ledsthr_created) {
3791 		/*
3792 		 * this is a restart, wake up sleeping threads
3793 		 */
3794 		err = pthread_mutex_lock(&g_mutex);
3795 		if (err != 0) {
3796 			syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(err));
3797 			return;
3798 		}
3799 		g_wait_now = B_FALSE;
3800 		(void) pthread_cond_broadcast(&g_cv);
3801 		(void) pthread_mutex_unlock(&g_mutex);
3802 	} else {
3803 		if ((pthread_attr_init(&ledsthr_attr) != 0) ||
3804 		    (pthread_attr_setscope(&ledsthr_attr,
3805 		    PTHREAD_SCOPE_SYSTEM) != 0))
3806 			return;
3807 		if ((err = pthread_create(&ledsthr_tid, &ledsthr_attr,
3808 		    disk_leds_thread, NULL)) != 0) {
3809 			syslog(LOG_ERR, EM_THREAD_CREATE_FAILED, strerror(err));
3810 			return;
3811 		}
3812 		ledsthr_created = B_TRUE;
3813 	}
3814 	for (i = 0; i < N_DISKS; i++) {
3815 		(void) set_led(lw8_disks[i].d_fruname, FAULT_LED,
3816 		    PICL_PROPVAL_OFF);
3817 	}
3818 }
3819 
3820 static void
3821 disk_leds_fini(void)
3822 {
3823 	int	err;
3824 
3825 	/*
3826 	 * tell led thread to pause
3827 	 */
3828 	if (!ledsthr_created)
3829 		return;
3830 	err = pthread_mutex_lock(&g_mutex);
3831 	if (err != 0) {
3832 		syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(err));
3833 		return;
3834 	}
3835 	g_wait_now = B_TRUE;
3836 	disk_leds_thread_ack = B_FALSE;
3837 	(void) pthread_cond_broadcast(&g_cv);
3838 
3839 	/*
3840 	 * and wait for the led thread to acknowledge
3841 	 */
3842 	while (!disk_leds_thread_ack) {
3843 		(void) pthread_cond_wait(&g_cv_ack, &g_mutex);
3844 	}
3845 	(void) pthread_mutex_unlock(&g_mutex);
3846 }
3847 
3848 static void
3849 update_disk_node(struct lw8_disk *diskp)
3850 {
3851 	picl_nodehdl_t slotndh;
3852 	picl_nodehdl_t diskndh;
3853 	picl_nodehdl_t devhdl;
3854 	picl_prophdl_t	tblhdl;
3855 	int err;
3856 	char path[MAXPATHLEN];
3857 	char *fruname = diskp->d_fruname;
3858 
3859 	sprintf_buf2(path, CHASSIS_LOC_PATH, fruname);
3860 	if (ptree_get_node_by_path(path, &slotndh) != PICL_SUCCESS) {
3861 		return;
3862 	}
3863 	diskndh = find_child_by_name(slotndh, fruname);
3864 	err = ptree_get_node_by_path(diskp->d_plat_path, &devhdl);
3865 	if (err == PICL_SUCCESS) {
3866 		if (diskndh != NULL)
3867 			return;
3868 		err = ptree_create_and_add_node(slotndh, fruname,
3869 		    PICL_CLASS_FRU, &diskndh);
3870 		if (err != PICL_SUCCESS) {
3871 			syslog(LOG_ERR, ADD_NODE_FAIL, fruname, err);
3872 			return;
3873 		}
3874 		err = create_table(diskndh, &tblhdl, PICL_PROP_DEVICES);
3875 		if (err != PICL_SUCCESS)
3876 			return;
3877 		err = create_table_entry(tblhdl, devhdl, PICL_CLASS_BLOCK);
3878 		if (err != PICL_SUCCESS)
3879 			return;
3880 		err = add_prop_ref(devhdl, diskndh, PICL_REFPROP_FRU_PARENT);
3881 		if (err != PICL_SUCCESS)
3882 			return;
3883 	} else {
3884 		if (diskndh == NULL)
3885 			return;
3886 		err = ptree_delete_node(diskndh);
3887 		if (err != PICL_SUCCESS)
3888 			return;
3889 		(void) ptree_destroy_node(diskndh);
3890 	}
3891 }
3892 
3893 /*
3894  * Implement a state machine in order to:
3895  *
3896  *  o enable/disable disk LEDs
3897  *  o add/delete the disk's node in the FRU tree
3898  *
3899  * The machine changes state based on the current, in-memory
3900  * state of the disk (eg, the d_state field of 'struct lw8_disk')
3901  * and libdevice's current view of whether the disk is
3902  * Configured or Unconfigured.
3903  *
3904  * If the new state is the same as the previous state, then
3905  * no side effects occur.  Otherwise, the LEDs for the
3906  * disk are set and the disk's associated node in the
3907  * FRU Tree is added or deleted.
3908  */
3909 static void
3910 set_disk_leds(struct lw8_disk *disk)
3911 {
3912 	devctl_hdl_t	dhdl;
3913 	uint_t		cur_state = 0;
3914 
3915 	dhdl = devctl_device_acquire(disk->d_devices_path, 0);
3916 	if (dhdl == NULL) {
3917 		int err = errno;
3918 		syslog(LOG_ERR, DEVCTL_DEVICE_ACQUIRE_FAILED,
3919 		    strerror(err));
3920 		return;
3921 	}
3922 	devctl_device_getstate(dhdl, &cur_state);
3923 	devctl_release(dhdl);
3924 
3925 	if ((cur_state & DEVICE_OFFLINE) != 0) {
3926 		switch (disk->d_state) {
3927 		default:
3928 			/*
3929 			 * State machine should never get here.
3930 			 * When NDEBUG is defined, control will
3931 			 * fall through and force d_state to
3932 			 * match the semantics of "DEVICE_OFFLINE".
3933 			 * During development, NDEBUG can be undefined,
3934 			 * and this will fire an assertion.
3935 			 */
3936 			assert(0);
3937 			/*FALLTHROUGH*/
3938 
3939 		case DISK_STATE_NOT_INIT:
3940 		case DISK_STATE_READY:
3941 			disk->d_state = DISK_STATE_NOT_READY;
3942 
3943 			(void) set_led(disk->d_fruname, POWER_LED,
3944 			    PICL_PROPVAL_OFF);
3945 			(void) set_led(disk->d_fruname, REMOK_LED,
3946 			    PICL_PROPVAL_ON);
3947 
3948 			update_disk_node(disk);
3949 			break;
3950 
3951 		case DISK_STATE_NOT_READY:
3952 			break;
3953 		}
3954 	} else if ((cur_state & DEVICE_ONLINE) != 0) {
3955 		switch (disk->d_state) {
3956 		default:
3957 			/*
3958 			 * State machine should never get here.
3959 			 * When NDEBUG is defined, control will
3960 			 * fall through and force d_state to
3961 			 * match the semantics of "DEVICE_ONLINE".
3962 			 * During development, NDEBUG can be undefined,
3963 			 * and this will fire an assertion.
3964 			 */
3965 			assert(0);
3966 			/*FALLTHROUGH*/
3967 
3968 		case DISK_STATE_NOT_INIT:
3969 		case DISK_STATE_NOT_READY:
3970 			disk->d_state = DISK_STATE_READY;
3971 
3972 			(void) set_led(disk->d_fruname, REMOK_LED,
3973 			    PICL_PROPVAL_OFF);
3974 			(void) set_led(disk->d_fruname, POWER_LED,
3975 			    PICL_PROPVAL_ON);
3976 
3977 			update_disk_node(disk);
3978 			break;
3979 
3980 		case DISK_STATE_READY:
3981 			break;
3982 		}
3983 	}
3984 }
3985 
3986 /*
3987  * NOTE: this implementation of disk_leds_thread is based on the version in
3988  * plugins/sun4u/mpxu/frudr/piclfrudr.c (with V440 raid support removed). Some
3989  * day the source code layout and build environment should support common code
3990  * used by platform specific plugins, in which case LW8 support could be added
3991  * to the mpxu version (which would be moved to a common directory).
3992  */
3993 /*ARGSUSED*/
3994 static void *
3995 disk_leds_thread(void *args)
3996 {
3997 	int	i;
3998 	int	err = 0;
3999 	int	n_disks = N_DISKS;
4000 
4001 	static char *lw8_pci_devs[] = {
4002 		DISK0_BASE_PATH,
4003 		DISK1_BASE_PATH
4004 	};
4005 
4006 	static char *lw8_pcix_devs[] = {
4007 		DISK0_BASE_PATH_PCIX,
4008 		DISK1_BASE_PATH_PCIX
4009 	};
4010 
4011 	static char **lw8_devs;
4012 
4013 	if (pcix_io) {
4014 		lw8_devs = lw8_pcix_devs;
4015 	} else {
4016 		lw8_devs = lw8_pci_devs;
4017 	}
4018 
4019 	/*
4020 	 * create aliases for disk names
4021 	 */
4022 	for (i = 0; i < n_disks; i++) {
4023 		char buffer[MAXPATHLEN];
4024 
4025 		(void) snprintf(buffer, sizeof (buffer), "/devices%s",
4026 		    lw8_devs[i]);
4027 		lw8_disks[i].d_devices_path = strdup(buffer);
4028 
4029 		(void) snprintf(buffer, sizeof (buffer), "/platform%s",
4030 		    lw8_devs[i]);
4031 		lw8_disks[i].d_plat_path = strdup(buffer);
4032 	}
4033 
4034 	for (;;) {
4035 		for (i = 0; i < n_disks; i++) {
4036 			set_disk_leds(&lw8_disks[i]);
4037 		}
4038 
4039 		/*
4040 		 * wait a bit until we check again
4041 		 */
4042 		err = poll(NULL, 0, ledsthr_poll_period);
4043 		if (err == -1) {
4044 			err = errno;
4045 			syslog(LOG_ERR, EM_POLL_FAIL, strerror(err));
4046 			break;
4047 		}
4048 		err = pthread_mutex_lock(&g_mutex);
4049 		if (err != 0) {
4050 			syslog(LOG_ERR, EM_MUTEX_FAIL, strerror(err));
4051 			break;
4052 		}
4053 		if (g_wait_now != B_FALSE) {
4054 			/* notify _fini routine that we've paused */
4055 			disk_leds_thread_ack = B_TRUE;
4056 			(void) pthread_cond_signal(&g_cv_ack);
4057 			/* and go to sleep in case we get restarted */
4058 			while (g_wait_now != B_FALSE)
4059 				(void) pthread_cond_wait(&g_cv, &g_mutex);
4060 		}
4061 		(void) pthread_mutex_unlock(&g_mutex);
4062 	}
4063 	return ((void *)err);
4064 }
4065