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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * PICL plug-in that creates the FRU Hierarchy for the
29 * SUNW,Sun-Fire-480R (Cherrystone) platform
30 */
31
32#include <stdio.h>
33#include <string.h>
34#include <libintl.h>
35#include <libnvpair.h>
36#include <syslog.h>
37#include <picl.h>
38#include <picltree.h>
39#include <picldefs.h>
40
41/*
42 * Plugin registration entry points
43 */
44static void	picl_frutree_register(void);
45static void	picl_frutree_init(void);
46static void	picl_frutree_fini(void);
47static void	picl_frutree_evhandler(const char *ename, const void *earg,
48		    size_t size, void *cookie);
49
50#pragma	init(picl_frutree_register)
51
52/*
53 * Log message texts
54 */
55#define	CREATE_FRUTREE_FAIL	gettext("Failed to create frutree node\n")
56#define	CREATE_CHASSIS_FAIL	gettext("Failed to create chassis node\n")
57#define	IOBRD_INIT_FAIL		gettext("do_ioboard_init() failed\n")
58#define	RSCBRD_INIT_FAIL	gettext("do_rscboard_init() failed\n")
59#define	FCAL_INIT_FAIL		gettext("do_fcal_init() failed\n")
60#define	PS_INIT_FAIL		gettext("do_power_supplies_init() failed\n")
61#define	SYSBOARD_INIT_FAIL	gettext("do_centerplane_init() failed\n")
62
63/*
64 * Viewpoints property field used by SunMC
65 */
66#define	CHASSIS_VIEWPOINTS	gettext("front top rear")
67
68/*
69 * Ref prop values
70 */
71#define	SEEPROM_SOURCE		"_seeprom_source"
72#define	FRU_PARENT		"_fru_parent"
73
74/*
75 * List of all the FRU locations in the platform_frupath[] array, and
76 * location_label[] array
77 */
78#define	PS0		0
79#define	PS1		1
80#define	RSC		2
81#define	DISKBACKPLANE	3
82#define	PDB		4
83#define	CENTERPLANE	5
84#define	IOBRD		6
85#define	CPUMOD0		7
86#define	CPUMOD1		8
87#define	CPU0_DIMM0	9
88#define	DIMMS_PER_MOD	8
89#define	DIMMS_PER_SLOT	16
90
91/*
92 * Local variables
93 */
94static picld_plugin_reg_t  my_reg_info = {
95	PICLD_PLUGIN_VERSION_1,
96	PICLD_PLUGIN_NON_CRITICAL,
97	"SUNW_Cherrystone_frutree",
98	picl_frutree_init,
99	picl_frutree_fini
100};
101
102/*
103 * List of all the FRUs in the /platform tree with SEEPROMs
104 */
105static char *platform_frupath[] = {
106	"/platform/pci@9,700000/ebus@1/i2c@1,30/fru@0,a2", /* PS 0 */
107	"/platform/pci@9,700000/ebus@1/i2c@1,30/fru@0,a0", /* PS 1 */
108	"/platform/pci@9,700000/ebus@1/i2c@1,30/fru@0,a6", /* RSC */
109	"/platform/pci@9,700000/ebus@1/i2c@1,30/fru@0,a8", /* Disk Backplane */
110	"/platform/pci@9,700000/ebus@1/i2c@1,30/fru@0,ae", /* PDB */
111	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@4,a8", /* Centerplane */
112	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@4,aa", /* IO */
113	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@4,a0", /* CPU MOD 0 */
114	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@4,a2", /* CPU MOD 1 */
115	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a0", /* CPU0 DIMM0 */
116	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a2", /* CPU0 DIMM1 */
117	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a4", /* CPU0 DIMM2 */
118	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a6", /* CPU0 DIMM3 */
119	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,a8", /* CPU0 DIMM4 */
120	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,aa", /* CPU0 DIMM5 */
121	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,ac", /* CPU0 DIMM6 */
122	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@0,ae", /* CPU0 DIMM7 */
123	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a0", /* CPU2 DIMM0 */
124	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a2", /* CPU2 DIMM1 */
125	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a4", /* CPU2 DIMM2 */
126	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a6", /* CPU2 DIMM3 */
127	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,a8", /* CPU2 DIMM4 */
128	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,aa", /* CPU2 DIMM5 */
129	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,ac", /* CPU2 DIMM6 */
130	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@2,ae", /* CPU2 DIMM7 */
131	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,a0", /* CPU1 DIMM0 */
132	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,a2", /* CPU1 DIMM1 */
133	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,a4", /* CPU1 DIMM2 */
134	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,a6", /* CPU1 DIMM3 */
135	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,a8", /* CPU1 DIMM4 */
136	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,aa", /* CPU1 DIMM5 */
137	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,ac", /* CPU1 DIMM6 */
138	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@1,ae", /* CPU1 DIMM7 */
139	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,a0", /* CPU3 DIMM0 */
140	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,a2", /* CPU3 DIMM1 */
141	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,a4", /* CPU3 DIMM2 */
142	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,a6", /* CPU3 DIMM3 */
143	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,a8", /* CPU3 DIMM4 */
144	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,aa", /* CPU3 DIMM5 */
145	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,ac", /* CPU3 DIMM6 */
146	"/platform/pci@9,700000/ebus@1/i2c@1,2e/fru@3,ae", /* CPU3 DIMM7 */
147	NULL};
148
149/*
150 * List of Labels for FRU locations (uses the #define's from above)
151 */
152static char *location_label[] = {
153	"0",			/* PS0 */
154	"1",			/* PS1 */
155	NULL,			/* RSC */
156	NULL,			/* DISKBACKPLANE */
157	NULL,			/* PDB */
158	NULL,			/* CENTERPLANE */
159	NULL,			/* IOBRD */
160	"A",			/* CPUMOD0 */
161	"B",			/* CPUMOD1 */
162	"J2900",		/* CPU0 DIMM0 */
163	"J3100",		/* CPU0 DIMM1 */
164	"J2901",		/* CPU0 DIMM2 */
165	"J3101",		/* CPU0 DIMM3 */
166	"J3000",		/* CPU0 DIMM4 */
167	"J3200",		/* CPU0 DIMM5 */
168	"J3001",		/* CPU0 DIMM6 */
169	"J3201",		/* CPU0 DIMM7 */
170	"J7900",		/* CPU1 DIMM0 */
171	"J8100",		/* CPU1 DIMM1 */
172	"J7901",		/* CPU1 DIMM2 */
173	"J8101",		/* CPU1 DIMM3 */
174	"J8000",		/* CPU1 DIMM4 */
175	"J8200",		/* CPU1 DIMM5 */
176	"J8001",		/* CPU1 DIMM6 */
177	"J8201",		/* CPU1 DIMM7 */
178	"0",			/* CPU0 label */
179	"1",			/* CPU1 label */
180	NULL};
181
182/*
183 * List of all the FRU slots for power supplies (hotpluggable)
184 */
185static char *frutree_power_supply[] = {
186	"/frutree/chassis/power-dist-board/power-supply-slot?Slot=0",
187	"/frutree/chassis/power-dist-board/power-supply-slot?Slot=1",
188	NULL};
189
190/* PICL handle for the root node of the "frutree" */
191static picl_nodehdl_t	frutreeh;
192
193static int	do_ioboard_init(picl_nodehdl_t);
194static int	do_rscboard_init(picl_nodehdl_t);
195static int	do_fcal_init(picl_nodehdl_t);
196static int	do_power_supplies_init(picl_nodehdl_t);
197static int	do_centerplane_init(picl_nodehdl_t);
198static int	do_cpu_module_init(picl_nodehdl_t, int);
199static int	do_dimms_init(picl_nodehdl_t, int, int);
200
201static int	add_ref_prop(picl_nodehdl_t, picl_nodehdl_t, char *);
202static int	add_slot_prop(picl_nodehdl_t, int);
203static int	add_label_prop(picl_nodehdl_t, char *);
204static int	add_void_fda_prop(picl_nodehdl_t);
205static int	add_viewpoints_prop(picl_nodehdl_t, char *);
206static int	add_all_nodes();
207static int	remove_all_nodes(picl_nodehdl_t);
208
209static int	add_hotplug_fru_device(void);
210static int	rem_hotplug_fru_device(void);
211static int	is_added_device(char *, char *);
212static int	is_removed_device(char *, char *);
213static int	add_power_supply(int);
214static int	remove_power_supply(int);
215
216/*
217 * This function is executed as part of .init when the plugin is
218 * dlopen()ed
219 */
220static void
221picl_frutree_register()
222{
223	(void) picld_plugin_register(&my_reg_info);
224}
225
226/*
227 * This function is the init entry point of the plugin.
228 * It initializes the /frutree tree
229 */
230static void
231picl_frutree_init()
232{
233	int		err;
234
235	err = add_all_nodes();
236	if (err != PICL_SUCCESS) {
237		(void) remove_all_nodes(frutreeh);
238		return;
239	}
240
241	/* Register the event handler routine */
242	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
243	    picl_frutree_evhandler, NULL);
244	(void) ptree_register_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
245	    picl_frutree_evhandler, NULL);
246}
247
248/*
249 * This function is the fini entry point of the plugin
250 */
251static void
252picl_frutree_fini(void)
253{
254	/* Unregister the event handler routine */
255	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_ADDED,
256	    picl_frutree_evhandler, NULL);
257	(void) ptree_unregister_handler(PICLEVENT_SYSEVENT_DEVICE_REMOVED,
258	    picl_frutree_evhandler, NULL);
259
260	(void) remove_all_nodes(frutreeh);
261}
262
263/*
264 * This function is the event handler of this plug-in.
265 *
266 * It processes the following events:
267 *
268 *	PICLEVENT_SYSEVENT_DEVICE_ADDED
269 *	PICLEVENT_SYSEVENT_DEVICE_REMOVED
270 */
271/* ARGSUSED */
272static void
273picl_frutree_evhandler(const char *ename, const void *earg, size_t size,
274    void *cookie)
275{
276	if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_ADDED) == 0) {
277		/* Check for and add any hotplugged device(s) */
278		(void) add_hotplug_fru_device();
279
280	} else if (strcmp(ename, PICLEVENT_SYSEVENT_DEVICE_REMOVED) == 0) {
281		/* Check for and remove any hotplugged device(s) */
282		(void) rem_hotplug_fru_device();
283	}
284}
285
286/* Initializes the FRU nodes for the IO board */
287static int
288do_ioboard_init(picl_nodehdl_t rooth)
289{
290	picl_nodehdl_t		iobrdh;
291	picl_nodehdl_t		tmph;
292	int			err;
293
294	/* Create the node for the IO board (if it exists) */
295	if (ptree_get_node_by_path(platform_frupath[IOBRD], &tmph) ==
296	    PICL_SUCCESS) {
297		err = ptree_create_node("io-board", "fru", &iobrdh);
298		if (err != PICL_SUCCESS)
299			return (err);
300
301		err = add_ref_prop(iobrdh, tmph, SEEPROM_SOURCE);
302		if (err != PICL_SUCCESS)
303			return (err);
304
305		err = add_void_fda_prop(iobrdh);
306		if (err != PICL_SUCCESS)
307			return (err);
308
309		err = ptree_add_node(rooth, iobrdh);
310		if (err != PICL_SUCCESS)
311			return (err);
312
313		err = add_ref_prop(tmph, iobrdh, FRU_PARENT);
314		if (err != PICL_SUCCESS)
315			return (err);
316	}
317	return (PICL_SUCCESS);
318}
319
320/* Initializes the FRU node for the RSC card */
321static int
322do_rscboard_init(picl_nodehdl_t rooth)
323{
324	picl_nodehdl_t		rscbrdh;
325	picl_nodehdl_t		tmph;
326	int			err;
327
328	/* Create the node for the RSC board (if it exists) */
329	if (ptree_get_node_by_path(platform_frupath[RSC], &tmph) ==
330	    PICL_SUCCESS) {
331		err = ptree_create_node("rsc-board", "fru", &rscbrdh);
332		if (err != PICL_SUCCESS)
333			return (err);
334
335		err = add_ref_prop(rscbrdh, tmph, SEEPROM_SOURCE);
336		if (err != PICL_SUCCESS)
337			return (err);
338
339		err = add_void_fda_prop(rscbrdh);
340		if (err != PICL_SUCCESS)
341			return (err);
342
343		err = ptree_add_node(rooth, rscbrdh);
344		if (err != PICL_SUCCESS)
345			return (err);
346
347		err = add_ref_prop(tmph, rscbrdh, FRU_PARENT);
348		if (err != PICL_SUCCESS)
349			return (err);
350	}
351	return (PICL_SUCCESS);
352}
353
354/* Initializes the FRU nodes for the FCAL backplaned */
355static int
356do_fcal_init(picl_nodehdl_t rooth)
357{
358	picl_nodehdl_t		fcalsloth;
359	picl_nodehdl_t		fcalmodh;
360	picl_nodehdl_t		tmph;
361	int			err;
362
363	/* Create the node for the FCAL backplane slot */
364	err = ptree_create_node("fcal-backplane-slot",
365	    "location", &fcalsloth);
366	if (err != PICL_SUCCESS)
367		return (err);
368
369	err = add_slot_prop(fcalsloth, 0);
370	if (err != PICL_SUCCESS)
371		return (err);
372
373	err = ptree_add_node(rooth, fcalsloth);
374	if (err != PICL_SUCCESS)
375		return (err);
376
377	/* If the FCAL backplane exists, create a node for it */
378	if (ptree_get_node_by_path(platform_frupath[DISKBACKPLANE], &tmph) ==
379	    PICL_SUCCESS) {
380		err = ptree_create_node("fcal-backplane", "fru",
381		    &fcalmodh);
382		if (err != PICL_SUCCESS)
383			return (err);
384
385		err = add_ref_prop(fcalmodh, tmph, SEEPROM_SOURCE);
386		if (err != PICL_SUCCESS)
387			return (err);
388
389		err = add_void_fda_prop(fcalmodh);
390		if (err != PICL_SUCCESS)
391			return (err);
392
393		err = ptree_add_node(fcalsloth, fcalmodh);
394		if (err != PICL_SUCCESS)
395			return (err);
396
397		err = add_ref_prop(tmph, fcalmodh, FRU_PARENT);
398		if (err != PICL_SUCCESS)
399			return (err);
400	}
401	return (PICL_SUCCESS);
402}
403
404/* Initializes the FRU nodes for the PDB and the power supplies */
405static int
406do_power_supplies_init(picl_nodehdl_t rooth)
407{
408	picl_nodehdl_t		powerbrdh;
409	picl_nodehdl_t		powersloth;
410	picl_nodehdl_t		powermodh;
411	picl_nodehdl_t		tmph;
412	int			i, err, slotnum;
413
414	/* Create the node for the PDB (if it exists) */
415	if (ptree_get_node_by_path(platform_frupath[PDB], &tmph) ==
416	    PICL_SUCCESS) {
417		err = ptree_create_node("power-dist-board", "fru", &powerbrdh);
418		if (err != PICL_SUCCESS)
419			return (err);
420
421		err = add_ref_prop(powerbrdh, tmph, SEEPROM_SOURCE);
422		if (err != PICL_SUCCESS)
423			return (err);
424
425		err = add_void_fda_prop(powerbrdh);
426		if (err != PICL_SUCCESS)
427			return (err);
428
429		err = ptree_add_node(rooth, powerbrdh);
430		if (err != PICL_SUCCESS)
431			return (err);
432
433		err = add_ref_prop(tmph, powerbrdh, FRU_PARENT);
434		if (err != PICL_SUCCESS)
435			return (err);
436
437		for (i = PS0; i <= PS1; i++) {
438			/* Create the node for the power supply slot */
439			err = ptree_create_node("power-supply-slot",
440			    "location", &powersloth);
441			if (err != PICL_SUCCESS)
442				return (err);
443
444			slotnum = i - PS0;
445			err = add_slot_prop(powersloth, slotnum);
446			if (err != PICL_SUCCESS)
447				return (err);
448
449			err = add_label_prop(powersloth, location_label[i]);
450			if (err != PICL_SUCCESS)
451				return (err);
452
453			err = ptree_add_node(powerbrdh, powersloth);
454			if (err != PICL_SUCCESS)
455				return (err);
456
457			/* If the PS exists, create a node for it */
458			if (ptree_get_node_by_path(platform_frupath[i],
459			    &tmph) == PICL_SUCCESS) {
460				err = ptree_create_node("power-supply",
461				    "fru", &powermodh);
462				if (err != PICL_SUCCESS)
463					return (err);
464
465				err = add_ref_prop(powermodh, tmph,
466				    SEEPROM_SOURCE);
467				if (err != PICL_SUCCESS)
468					return (err);
469
470				err = add_void_fda_prop(powermodh);
471				if (err != PICL_SUCCESS)
472					return (err);
473
474				err = ptree_add_node(powersloth, powermodh);
475				if (err != PICL_SUCCESS)
476					return (err);
477
478				err = add_ref_prop(tmph, powermodh, FRU_PARENT);
479				if (err != PICL_SUCCESS)
480					return (err);
481			}
482		}
483	}
484	return (PICL_SUCCESS);
485}
486
487/* Initializes the FRU nodes for the centerplane and CPU Memory modules */
488static int
489do_centerplane_init(picl_nodehdl_t rooth)
490{
491	picl_nodehdl_t		sysboardh;
492	picl_nodehdl_t		cpumemsloth;
493	picl_nodehdl_t		cpumemmodh;
494	picl_nodehdl_t		tmph;
495	int			i, err, slotnum;
496
497	/* Create the node for the system board (if it exists) */
498	if (ptree_get_node_by_path(platform_frupath[CENTERPLANE], &tmph) ==
499	    PICL_SUCCESS) {
500		err = ptree_create_node("centerplane", "fru",
501		    &sysboardh);
502		if (err != PICL_SUCCESS)
503			return (err);
504
505		err = add_ref_prop(sysboardh, tmph, SEEPROM_SOURCE);
506		if (err != PICL_SUCCESS)
507			return (err);
508
509		err = add_void_fda_prop(sysboardh);
510		if (err != PICL_SUCCESS)
511			return (err);
512
513		err = ptree_add_node(rooth, sysboardh);
514		if (err != PICL_SUCCESS)
515			return (err);
516
517		err = add_ref_prop(tmph, sysboardh, FRU_PARENT);
518		if (err != PICL_SUCCESS)
519			return (err);
520
521		for (i = CPUMOD0; i <= CPUMOD1; i++) {
522			/* Create the node for the CPU Memory slot */
523			err = ptree_create_node("cpu-mem-slot", "location",
524			    &cpumemsloth);
525			if (err != PICL_SUCCESS)
526				return (err);
527
528			slotnum = i - CPUMOD0;
529			err = add_slot_prop(cpumemsloth, slotnum);
530			if (err != PICL_SUCCESS)
531				return (err);
532
533			err = add_label_prop(cpumemsloth, location_label[i]);
534			if (err != PICL_SUCCESS)
535				return (err);
536
537			err = ptree_add_node(sysboardh, cpumemsloth);
538			if (err != PICL_SUCCESS)
539				return (err);
540
541			/* If CPU Mem module exists, create a node for it */
542			if (ptree_get_node_by_path(platform_frupath[i],
543			    &tmph) == PICL_SUCCESS) {
544				err = ptree_create_node("cpu-mem-module",
545				    "fru", &cpumemmodh);
546				if (err != PICL_SUCCESS)
547					return (err);
548
549				err = add_ref_prop(cpumemmodh, tmph,
550				    SEEPROM_SOURCE);
551				if (err != PICL_SUCCESS)
552					return (err);
553
554				err = add_void_fda_prop(cpumemmodh);
555				if (err != PICL_SUCCESS)
556					return (err);
557
558				err = ptree_add_node(cpumemsloth, cpumemmodh);
559				if (err != PICL_SUCCESS)
560					return (err);
561
562				err = add_ref_prop(tmph, cpumemmodh,
563				    FRU_PARENT);
564				if (err != PICL_SUCCESS)
565					return (err);
566
567				err = do_cpu_module_init(cpumemmodh, slotnum);
568				if (err != PICL_SUCCESS)
569					return (err);
570			}
571		}
572	}
573	return (PICL_SUCCESS);
574}
575
576/* Creates the FRU nodes for the CPU Module and associated DIMMs */
577static int
578do_cpu_module_init(picl_nodehdl_t rooth, int slot)
579{
580	picl_nodehdl_t		cpumodh;
581	int			i, c, err;
582
583	for (i = 0; i <= 1; i++) {
584		err = ptree_create_node("cpu-module", "location",
585		    &cpumodh);
586		if (err != PICL_SUCCESS)
587			return (err);
588
589		err = add_slot_prop(cpumodh, i);
590		if (err != PICL_SUCCESS)
591			return (err);
592
593		c = CPU0_DIMM0 + DIMMS_PER_SLOT + i;
594
595		err = add_label_prop(cpumodh, location_label[c]);
596		if (err != PICL_SUCCESS)
597			return (err);
598
599		err = ptree_add_node(rooth, cpumodh);
600		if (err != PICL_SUCCESS)
601			return (err);
602
603		/* Create the nodes for the memory (if they exist) */
604		err = do_dimms_init(cpumodh, slot, i);
605		if (err != PICL_SUCCESS)
606			return (err);
607	}
608	return (PICL_SUCCESS);
609}
610
611/* Creates the FRU nodes for the DIMMs on a particular CPU Module */
612static int
613do_dimms_init(picl_nodehdl_t rooth, int slot, int module)
614{
615	picl_nodehdl_t		dimmsloth;
616	picl_nodehdl_t		dimmmodh;
617	picl_nodehdl_t		tmph;
618	int			i, c, l, err;
619
620	for (i = 0; i < DIMMS_PER_MOD; i++) {
621		/* Create the node for the memory slot */
622		err = ptree_create_node("dimm-slot", "location",
623		    &dimmsloth);
624		if (err != PICL_SUCCESS)
625			return (err);
626
627		err = add_slot_prop(dimmsloth, i);
628		if (err != PICL_SUCCESS)
629			return (err);
630
631		c = ((slot * DIMMS_PER_SLOT) +
632		    (module * DIMMS_PER_MOD) + i) + CPU0_DIMM0;
633
634		l = c - (DIMMS_PER_SLOT * slot);
635
636		err = add_label_prop(dimmsloth, location_label[l]);
637		if (err != PICL_SUCCESS)
638			return (err);
639
640		err = ptree_add_node(rooth, dimmsloth);
641		if (err != PICL_SUCCESS)
642			return (err);
643
644		/* If the memory module exists, create a node for it */
645		if (ptree_get_node_by_path(platform_frupath[c], &tmph) ==
646		    PICL_SUCCESS) {
647			err = ptree_create_node("dimm-module", "fru",
648			    &dimmmodh);
649			if (err != PICL_SUCCESS)
650				return (err);
651
652			err = add_ref_prop(dimmmodh, tmph, SEEPROM_SOURCE);
653			if (err != PICL_SUCCESS)
654				return (err);
655
656			err = add_void_fda_prop(dimmmodh);
657			if (err != PICL_SUCCESS)
658				return (err);
659
660			err = ptree_add_node(dimmsloth, dimmmodh);
661			if (err != PICL_SUCCESS)
662				return (err);
663
664			err = add_ref_prop(tmph, dimmmodh, FRU_PARENT);
665			if (err != PICL_SUCCESS)
666				return (err);
667		}
668	}
669	return (PICL_SUCCESS);
670}
671
672/* Creates a "reference" property between two PICL nodes */
673static int
674add_ref_prop(picl_nodehdl_t nodeh, picl_nodehdl_t tmph, char *str)
675{
676	picl_prophdl_t		proph;
677	ptree_propinfo_t	propinfo;
678	int			err;
679
680	if (str == NULL)
681		return (PICL_FAILURE);
682
683	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
684	    PICL_PTYPE_REFERENCE, PICL_READ, sizeof (picl_nodehdl_t),
685	    str, NULL, NULL);
686	if (err != PICL_SUCCESS)
687		return (err);
688
689	err = ptree_create_and_add_prop(nodeh, &propinfo, &tmph, &proph);
690	if (err != PICL_SUCCESS)
691		return (err);
692
693	return (PICL_SUCCESS);
694}
695
696/* Creates a "slot" property for a given PICL node */
697static int
698add_slot_prop(picl_nodehdl_t nodeh, int slotnum)
699{
700	picl_prophdl_t		proph;
701	ptree_propinfo_t	propinfo;
702	int			err;
703
704	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
705	    PICL_PTYPE_INT, PICL_READ, 4, "Slot", NULL, NULL);
706	if (err != PICL_SUCCESS)
707		return (err);
708
709	err = ptree_create_and_add_prop(nodeh, &propinfo, &slotnum, &proph);
710	if (err != PICL_SUCCESS)
711		return (err);
712
713	return (PICL_SUCCESS);
714}
715
716/* Creates a "Label" property for a given PICL node */
717static int
718add_label_prop(picl_nodehdl_t nodeh, char *label)
719{
720	picl_prophdl_t		proph;
721	ptree_propinfo_t	propinfo;
722	int			err;
723
724	if (label == NULL)
725		return (PICL_FAILURE);
726
727	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
728	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(label)+1, "Label",
729	    NULL, NULL);
730	if (err != PICL_SUCCESS)
731		return (err);
732
733	err = ptree_create_and_add_prop(nodeh, &propinfo, label, &proph);
734	if (err != PICL_SUCCESS)
735		return (err);
736
737	return (PICL_SUCCESS);
738}
739
740/* Creates a "FRUDataAvailable" void property for the given PICL node */
741static int
742add_void_fda_prop(picl_nodehdl_t nodeh)
743{
744	picl_prophdl_t		proph;
745	ptree_propinfo_t	propinfo;
746	int			err;
747
748	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
749	    PICL_PTYPE_VOID, PICL_READ, 0, "FRUDataAvailable", NULL, NULL);
750	if (err != PICL_SUCCESS)
751		return (err);
752
753	err = ptree_create_and_add_prop(nodeh, &propinfo, NULL, &proph);
754	if (err != PICL_SUCCESS)
755		return (err);
756
757	return (PICL_SUCCESS);
758}
759
760/* Creates a "ViewPoints" property -- used for chassis */
761static int
762add_viewpoints_prop(picl_nodehdl_t nodeh, char *string)
763{
764	picl_prophdl_t		proph;
765	ptree_propinfo_t	propinfo;
766	int			err;
767
768	if (string == NULL)
769		return (PICL_FAILURE);
770
771	err = ptree_init_propinfo(&propinfo, PTREE_PROPINFO_VERSION,
772	    PICL_PTYPE_CHARSTRING, PICL_READ, strlen(string)+1, "ViewPoints",
773	    NULL, NULL);
774	if (err != PICL_SUCCESS)
775		return (err);
776
777	err = ptree_create_and_add_prop(nodeh, &propinfo, string, &proph);
778	if (err != PICL_SUCCESS)
779		return (err);
780
781	return (PICL_SUCCESS);
782}
783
784/* Creates and adds all of the frutree nodes */
785static int
786add_all_nodes()
787{
788	picl_nodehdl_t	rooth;
789	picl_nodehdl_t	chassish;
790	int		err;
791
792	/* Get the root node of the PICL tree */
793	err = ptree_get_root(&rooth);
794	if (err != PICL_SUCCESS) {
795		return (err);
796	}
797
798	/* Create and add the root node of the FRU subtree */
799	err = ptree_create_and_add_node(rooth, "frutree", "picl", &frutreeh);
800	if (err != PICL_SUCCESS) {
801		syslog(LOG_ERR, CREATE_FRUTREE_FAIL);
802		return (err);
803	}
804
805	/* Create and add the chassis node */
806	err = ptree_create_and_add_node(frutreeh, "chassis", "fru", &chassish);
807	if (err != PICL_SUCCESS) {
808		syslog(LOG_ERR, CREATE_CHASSIS_FAIL);
809		return (err);
810	}
811
812	/* Add ViewPoints prop to chassis node */
813	err = add_viewpoints_prop(chassish, CHASSIS_VIEWPOINTS);
814	if (err != PICL_SUCCESS)
815		return (err);
816
817	/* Initialize the FRU nodes for the IO board */
818	err = do_ioboard_init(chassish);
819	if (err != PICL_SUCCESS) {
820		syslog(LOG_ERR, IOBRD_INIT_FAIL);
821		return (err);
822	}
823
824	/* Initialize the FRU node for the RSC card */
825	err = do_rscboard_init(chassish);
826	if (err != PICL_SUCCESS) {
827		syslog(LOG_ERR, RSCBRD_INIT_FAIL);
828		return (err);
829	}
830
831	/* Initialize the FRU nodes for the DISK backplane */
832	err = do_fcal_init(chassish);
833	if (err != PICL_SUCCESS) {
834		syslog(LOG_ERR, FCAL_INIT_FAIL);
835		return (err);
836	}
837
838	/* Initialize the FRU nodes for the PDB and the power supplies */
839	err = do_power_supplies_init(chassish);
840	if (err != PICL_SUCCESS) {
841		syslog(LOG_ERR, PS_INIT_FAIL);
842		return (err);
843	}
844
845	/* Initialize the FRU nodes for the CPU Memory modules */
846	err = do_centerplane_init(chassish);
847	if (err != PICL_SUCCESS) {
848		syslog(LOG_ERR, SYSBOARD_INIT_FAIL);
849		return (err);
850	}
851
852	return (PICL_SUCCESS);
853}
854
855/* Deletes and destroys all PICL nodes for which rooth is a ancestor */
856static int
857remove_all_nodes(picl_nodehdl_t rooth)
858{
859	picl_nodehdl_t		chdh;
860	int			err, done = 0;
861
862	while (!done) {
863		err = ptree_get_propval_by_name(rooth, PICL_PROP_CHILD, &chdh,
864		    sizeof (picl_nodehdl_t));
865		if (err != PICL_PROPNOTFOUND) {
866			(void) remove_all_nodes(chdh);
867		} else {
868			err = ptree_delete_node(rooth);
869			if (err != PICL_SUCCESS) {
870				return (err);
871			} else {
872				(void) ptree_destroy_node(rooth);
873			}
874			done = 1;
875		}
876	}
877	return (PICL_SUCCESS);
878}
879
880/* Searches the list of hotpluggable FRUs, adds the appropriate node(s) */
881static int
882add_hotplug_fru_device()
883{
884	int		i, err, slotnum;
885
886	/* Check for hotplugged power supplies */
887	for (i = PS0; i <= PS1; i++) {
888		/* Compare the /platform tree to the frutree */
889		slotnum = i - PS0;
890		err = is_added_device(platform_frupath[i],
891		    frutree_power_supply[slotnum]);
892		if (err != PICL_SUCCESS)
893			continue;
894
895		/* If they are different, then add a power supply */
896		err = add_power_supply(slotnum);
897		if (err != PICL_SUCCESS)
898			continue;
899	}
900	return (PICL_SUCCESS);
901}
902
903/* Searches the list of hotpluggable FRUs, removes the appropriate node(s) */
904static int
905rem_hotplug_fru_device()
906{
907	int		i, err, slotnum;
908
909	/* Check for hotplugged power supplies */
910	for (i = PS0; i <= PS1; i++) {
911		/* Compare the /platform tree to the frutree */
912		slotnum = i - PS0;
913		err = is_removed_device(platform_frupath[i],
914		    frutree_power_supply[slotnum]);
915		if (err != PICL_SUCCESS)
916			continue;
917
918		/* If they are different, then remove a power supply */
919		err = remove_power_supply(slotnum);
920		if (err != PICL_SUCCESS)
921			continue;
922	}
923	return (PICL_SUCCESS);
924}
925
926/*
927 * Compare the /platform tree to the /frutree to determine if a
928 * new device has been added
929 */
930static int
931is_added_device(char *plat, char *fru)
932{
933	int		err;
934	picl_nodehdl_t	plath, frusloth, frumodh;
935
936	/* Check for node in the /platform tree */
937	err = ptree_get_node_by_path(plat, &plath);
938	if (err != PICL_SUCCESS)
939		return (err);
940
941	/*
942	 * The node is in /platform, so find the corresponding slot in
943	 * the frutree
944	 */
945	err = ptree_get_node_by_path(fru, &frusloth);
946	if (err != PICL_SUCCESS)
947		return (err);
948
949	/*
950	 * If the slot in the frutree has a child, then return
951	 * PICL_FAILURE.  This means that the /platform tree and
952	 * the frutree are consistent and no action is necessary.
953	 * Otherwise return PICL_SUCCESS to indicate that a node needs
954	 * to be added to the frutree
955	 */
956	err = ptree_get_propval_by_name(frusloth, PICL_PROP_CHILD,
957	    &frumodh, sizeof (picl_nodehdl_t));
958	if (err == PICL_SUCCESS)
959		return (PICL_FAILURE);
960
961	return (PICL_SUCCESS);
962}
963
964/*
965 * Compare the /platform tree to the /frutree to determine if a
966 * device has been removed
967 */
968static int
969is_removed_device(char *plat, char *fru)
970{
971	int		err;
972	picl_nodehdl_t	plath, frusloth, frumodh;
973
974
975	/* Check for node in /platform tree */
976	err = ptree_get_node_by_path(plat, &plath);
977	if (err == PICL_SUCCESS)
978		return (PICL_FAILURE);
979
980	/*
981	 * The node is not in /platform, so find the corresponding slot in
982	 * the frutree
983	 */
984	err = ptree_get_node_by_path(fru, &frusloth);
985	if (err != PICL_SUCCESS)
986		return (err);
987
988	/*
989	 * If the slot in the frutree does not have a child, then return
990	 * PICL_FAILURE.  This means that the /platform tree and
991	 * the frutree are consistent and no action is necessary.
992	 * Otherwise return PICL_SUCCESS to indicate that the needs
993	 * to be removed from the frutree
994	 */
995	err = ptree_get_propval_by_name(frusloth, PICL_PROP_CHILD,
996	    &frumodh, sizeof (picl_nodehdl_t));
997	if (err != PICL_SUCCESS)
998		return (err);
999
1000	return (PICL_SUCCESS);
1001}
1002
1003static int
1004remove_picl_node(picl_nodehdl_t nodeh)
1005{
1006	int err;
1007	err = ptree_delete_node(nodeh);
1008	if (err != PICL_SUCCESS)
1009		return (err);
1010	(void) ptree_destroy_node(nodeh);
1011	return (PICL_SUCCESS);
1012}
1013
1014/* event completion handler for PICL_FRU_ADDED/PICL_FRU_REMOVED events */
1015static void
1016frudr_completion_handler(char *ename, void *earg, size_t size)
1017{
1018	picl_nodehdl_t	fruh;
1019
1020	if (strcmp(ename, PICL_FRU_REMOVED) == 0) {
1021		/*
1022		 * now frudata has been notified that the node is to be
1023		 * removed, we can actually remove it
1024		 */
1025		fruh = 0;
1026		(void) nvlist_lookup_uint64(earg,
1027		    PICLEVENTARG_FRUHANDLE, &fruh);
1028		if (fruh != 0) {
1029			(void) remove_picl_node(fruh);
1030		}
1031	}
1032	nvlist_free(earg);
1033	free(earg);
1034	free(ename);
1035}
1036
1037/*
1038 * Post the PICL_FRU_ADDED/PICL_FRU_REMOVED event
1039 */
1040static void
1041post_frudr_event(char *ename, picl_nodehdl_t parenth, picl_nodehdl_t fruh)
1042{
1043	nvlist_t	*nvl;
1044	char		*ev_name;
1045
1046	ev_name = strdup(ename);
1047	if (ev_name == NULL)
1048		return;
1049	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) {
1050		free(ev_name);
1051		return;
1052	}
1053	if (parenth != 0L &&
1054	    nvlist_add_uint64(nvl, PICLEVENTARG_PARENTHANDLE, parenth)) {
1055		free(ev_name);
1056		nvlist_free(nvl);
1057		return;
1058	}
1059	if (fruh != 0L &&
1060	    nvlist_add_uint64(nvl, PICLEVENTARG_FRUHANDLE, fruh)) {
1061		free(ev_name);
1062		nvlist_free(nvl);
1063		return;
1064	}
1065	if (ptree_post_event(ev_name, nvl, sizeof (nvl),
1066	    frudr_completion_handler) != 0) {
1067		free(ev_name);
1068		nvlist_free(nvl);
1069	}
1070}
1071
1072/* Hotplug routine used to add a new power supply */
1073static int
1074add_power_supply(int slotnum)
1075{
1076	picl_nodehdl_t		powersloth;
1077	picl_nodehdl_t		powermodh;
1078	picl_nodehdl_t		tmph;
1079	int			i, err;
1080
1081	/* Find the node for the given power supply slot */
1082	if (ptree_get_node_by_path(frutree_power_supply[slotnum],
1083	    &powersloth) == PICL_SUCCESS) {
1084
1085		i = slotnum + PS0;
1086
1087		/* Make sure it's in /platform and create the frutree node */
1088		if (ptree_get_node_by_path(platform_frupath[i], &tmph) ==
1089		    PICL_SUCCESS) {
1090			err = ptree_create_node("power-supply", "fru",
1091			    &powermodh);
1092			if (err != PICL_SUCCESS)
1093				return (err);
1094
1095			err = add_ref_prop(powermodh, tmph, SEEPROM_SOURCE);
1096			if (err != PICL_SUCCESS)
1097				return (err);
1098
1099			err = add_void_fda_prop(powermodh);
1100			if (err != PICL_SUCCESS)
1101				return (err);
1102
1103			err = ptree_add_node(powersloth, powermodh);
1104			if (err != PICL_SUCCESS)
1105				return (err);
1106
1107			err = add_ref_prop(tmph, powermodh, FRU_PARENT);
1108			if (err != PICL_SUCCESS)
1109				return (err);
1110
1111			/* Post picl-fru-added event */
1112			post_frudr_event(PICL_FRU_ADDED, 0, powermodh);
1113		}
1114	}
1115	return (PICL_SUCCESS);
1116}
1117
1118/* Hotplug routine used to remove an existing power supply */
1119static int
1120remove_power_supply(int slotnum)
1121{
1122	picl_nodehdl_t		powersloth;
1123	picl_nodehdl_t		powermodh;
1124	int			err;
1125
1126	/* Find the node for the given power supply slot */
1127	if (ptree_get_node_by_path(frutree_power_supply[slotnum],
1128	    &powersloth) == PICL_SUCCESS) {
1129		/* Make sure it's got a child, then delete it */
1130		err = ptree_get_propval_by_name(powersloth, PICL_PROP_CHILD,
1131		    &powermodh, sizeof (picl_nodehdl_t));
1132		if (err != PICL_SUCCESS) {
1133			return (err);
1134		}
1135
1136		err = ptree_delete_node(powermodh);
1137		if (err != PICL_SUCCESS) {
1138			return (err);
1139		}
1140		(void) ptree_destroy_node(powermodh);
1141		/* Post picl-fru-removed event */
1142		post_frudr_event(PICL_FRU_REMOVED, 0, powermodh);
1143	}
1144	return (PICL_SUCCESS);
1145}
1146