1940d71d2Seschrock /*
2940d71d2Seschrock  * CDDL HEADER START
3940d71d2Seschrock  *
4940d71d2Seschrock  * The contents of this file are subject to the terms of the
5940d71d2Seschrock  * Common Development and Distribution License (the "License").
6940d71d2Seschrock  * You may not use this file except in compliance with the License.
7940d71d2Seschrock  *
8940d71d2Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9940d71d2Seschrock  * or http://www.opensolaris.org/os/licensing.
10940d71d2Seschrock  * See the License for the specific language governing permissions
11940d71d2Seschrock  * and limitations under the License.
12940d71d2Seschrock  *
13940d71d2Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14940d71d2Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15940d71d2Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16940d71d2Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17940d71d2Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18940d71d2Seschrock  *
19940d71d2Seschrock  * CDDL HEADER END
20940d71d2Seschrock  */
21940d71d2Seschrock 
22940d71d2Seschrock /*
23*44ed9dbbSStephen Hanson  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24940d71d2Seschrock  */
25940d71d2Seschrock 
266efb64caSEric Schrock #include <alloca.h>
27940d71d2Seschrock #include <dirent.h>
28940d71d2Seschrock #include <devid.h>
29940d71d2Seschrock #include <fm/libdiskstatus.h>
30940d71d2Seschrock #include <inttypes.h>
31940d71d2Seschrock #include <pthread.h>
32940d71d2Seschrock #include <strings.h>
33940d71d2Seschrock #include <unistd.h>
34940d71d2Seschrock #include <sys/dkio.h>
35940d71d2Seschrock #include <sys/fm/protocol.h>
36*44ed9dbbSStephen Hanson #include <sys/libdevid.h>
37940d71d2Seschrock #include <sys/scsi/scsi_types.h>
38d91236feSeschrock 
39940d71d2Seschrock #include "disk.h"
40d91236feSeschrock #include "ses.h"
41940d71d2Seschrock 
42940d71d2Seschrock #define	SES_VERSION	1
43940d71d2Seschrock 
44525b85dbSEric Schrock static int ses_snap_freq = 250;		/* in milliseconds */
45940d71d2Seschrock 
46d91236feSeschrock #define	SES_STATUS_UNAVAIL(s)	\
470b32bb8bSEric Schrock 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
48940d71d2Seschrock 
49940d71d2Seschrock /*
50940d71d2Seschrock  * Because multiple SES targets can be part of a single chassis, we construct
51940d71d2Seschrock  * our own hierarchy that takes this into account.  These SES targets may refer
52940d71d2Seschrock  * to the same devices (multiple paths) or to different devices (managing
53940d71d2Seschrock  * different portions of the space).  We arrange things into a
54940d71d2Seschrock  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
55940d71d2Seschrock  * nodes found so far.
56940d71d2Seschrock  */
570b32bb8bSEric Schrock typedef struct ses_alt_node {
580b32bb8bSEric Schrock 	topo_list_t		san_link;
590b32bb8bSEric Schrock 	ses_node_t		*san_node;
600b32bb8bSEric Schrock } ses_alt_node_t;
61940d71d2Seschrock 
62940d71d2Seschrock typedef struct ses_enum_node {
63940d71d2Seschrock 	topo_list_t		sen_link;
64940d71d2Seschrock 	ses_node_t		*sen_node;
650b32bb8bSEric Schrock 	topo_list_t		sen_alt_nodes;
66940d71d2Seschrock 	uint64_t		sen_type;
67940d71d2Seschrock 	uint64_t		sen_instance;
68940d71d2Seschrock 	ses_enum_target_t	*sen_target;
69940d71d2Seschrock } ses_enum_node_t;
70940d71d2Seschrock 
71940d71d2Seschrock typedef struct ses_enum_chassis {
72940d71d2Seschrock 	topo_list_t		sec_link;
7353dbcc59SSundeep Panicker 	topo_list_t		sec_subchassis;
74940d71d2Seschrock 	topo_list_t		sec_nodes;
75940d71d2Seschrock 	topo_list_t		sec_targets;
76940d71d2Seschrock 	const char		*sec_csn;
7753dbcc59SSundeep Panicker 	const char		*sec_lid;
78940d71d2Seschrock 	ses_node_t		*sec_enclosure;
79940d71d2Seschrock 	ses_enum_target_t	*sec_target;
80940d71d2Seschrock 	topo_instance_t		sec_instance;
8153dbcc59SSundeep Panicker 	topo_instance_t		sec_scinstance;
82940d71d2Seschrock 	boolean_t		sec_hasdev;
83d91236feSeschrock 	boolean_t		sec_internal;
84940d71d2Seschrock } ses_enum_chassis_t;
85940d71d2Seschrock 
86940d71d2Seschrock typedef struct ses_enum_data {
87940d71d2Seschrock 	topo_list_t		sed_disks;
88940d71d2Seschrock 	topo_list_t		sed_chassis;
89940d71d2Seschrock 	ses_enum_chassis_t	*sed_current;
90940d71d2Seschrock 	ses_enum_target_t	*sed_target;
91940d71d2Seschrock 	int			sed_errno;
92940d71d2Seschrock 	char			*sed_name;
93940d71d2Seschrock 	topo_mod_t		*sed_mod;
94940d71d2Seschrock 	topo_instance_t		sed_instance;
95940d71d2Seschrock } ses_enum_data_t;
96940d71d2Seschrock 
9753dbcc59SSundeep Panicker typedef enum {
9853dbcc59SSundeep Panicker 	SES_NEW_CHASSIS		= 0x1,
9953dbcc59SSundeep Panicker 	SES_NEW_SUBCHASSIS	= 0x2,
10053dbcc59SSundeep Panicker 	SES_DUP_CHASSIS		= 0x4,
10153dbcc59SSundeep Panicker 	SES_DUP_SUBCHASSIS	= 0x8
10253dbcc59SSundeep Panicker } ses_chassis_type_e;
10353dbcc59SSundeep Panicker 
104940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
105940d71d2Seschrock     nvlist_t **);
106940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
107940d71d2Seschrock     nvlist_t **);
108940d71d2Seschrock 
109940d71d2Seschrock static const topo_method_t ses_component_methods[] = {
110940d71d2Seschrock 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
111940d71d2Seschrock 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
112d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
113d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
11488045cffSRobert Johnston 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
11588045cffSRobert Johnston 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
11688045cffSRobert Johnston 	    topo_method_sensor_failure },
117d91236feSeschrock 	{ NULL }
118d91236feSeschrock };
119d91236feSeschrock 
120d91236feSeschrock static const topo_method_t ses_bay_methods[] = {
121d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
122d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
123940d71d2Seschrock 	{ NULL }
124940d71d2Seschrock };
125940d71d2Seschrock 
126940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = {
127940d71d2Seschrock 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
128940d71d2Seschrock 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
129d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
130d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
131940d71d2Seschrock 	{ NULL }
132940d71d2Seschrock };
133940d71d2Seschrock 
134940d71d2Seschrock static void
135940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
136940d71d2Seschrock {
137940d71d2Seschrock 	if (--stp->set_refcount == 0) {
138940d71d2Seschrock 		ses_snap_rele(stp->set_snap);
139940d71d2Seschrock 		ses_close(stp->set_target);
140940d71d2Seschrock 		topo_mod_strfree(mod, stp->set_devpath);
141940d71d2Seschrock 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
142940d71d2Seschrock 	}
143940d71d2Seschrock }
144940d71d2Seschrock 
145940d71d2Seschrock static void
14653dbcc59SSundeep Panicker ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
147940d71d2Seschrock {
148940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
149940d71d2Seschrock 	ses_enum_chassis_t *cp;
150940d71d2Seschrock 	ses_enum_node_t *np;
151940d71d2Seschrock 	ses_enum_target_t *tp;
1520b32bb8bSEric Schrock 	ses_alt_node_t *ap;
15353dbcc59SSundeep Panicker 	topo_list_t *cpl;
15453dbcc59SSundeep Panicker 
15553dbcc59SSundeep Panicker 
15653dbcc59SSundeep Panicker 	if (pcp != NULL)
15753dbcc59SSundeep Panicker 		cpl = &pcp->sec_subchassis;
15853dbcc59SSundeep Panicker 	else
15953dbcc59SSundeep Panicker 		cpl = &sdp->sed_chassis;
160940d71d2Seschrock 
16153dbcc59SSundeep Panicker 	while ((cp = topo_list_next(cpl)) != NULL) {
16253dbcc59SSundeep Panicker 		topo_list_delete(cpl, cp);
163940d71d2Seschrock 
164940d71d2Seschrock 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
1650b32bb8bSEric Schrock 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
1660b32bb8bSEric Schrock 			    NULL) {
1670b32bb8bSEric Schrock 				topo_list_delete(&np->sen_alt_nodes, ap);
1680b32bb8bSEric Schrock 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
1690b32bb8bSEric Schrock 			}
170940d71d2Seschrock 			topo_list_delete(&cp->sec_nodes, np);
171940d71d2Seschrock 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
172940d71d2Seschrock 		}
173940d71d2Seschrock 
174940d71d2Seschrock 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
175940d71d2Seschrock 			topo_list_delete(&cp->sec_targets, tp);
176940d71d2Seschrock 			ses_target_free(mod, tp);
177940d71d2Seschrock 		}
178940d71d2Seschrock 
179940d71d2Seschrock 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
180940d71d2Seschrock 	}
181940d71d2Seschrock 
18253dbcc59SSundeep Panicker 	if (pcp == NULL) {
18353dbcc59SSundeep Panicker 		disk_list_free(mod, &sdp->sed_disks);
18453dbcc59SSundeep Panicker 		topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
18553dbcc59SSundeep Panicker 	}
186940d71d2Seschrock }
187940d71d2Seschrock 
188940d71d2Seschrock /*
189940d71d2Seschrock  * For enclosure nodes, we have a special contains method.  By default, the hc
190940d71d2Seschrock  * walker will compare the node name and instance number to determine if an
191940d71d2Seschrock  * FMRI matches.  For enclosures where the enumeration order is impossible to
192940d71d2Seschrock  * predict, we instead use the chassis-id as a unique identifier, and ignore
193940d71d2Seschrock  * the instance number.
194940d71d2Seschrock  */
195940d71d2Seschrock static int
196940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
197940d71d2Seschrock {
198940d71d2Seschrock 	uint8_t v1, v2;
199940d71d2Seschrock 	nvlist_t **hcp1, **hcp2;
200940d71d2Seschrock 	int err, i;
201940d71d2Seschrock 	uint_t nhcp1, nhcp2;
202940d71d2Seschrock 	nvlist_t *a1, *a2;
203940d71d2Seschrock 	char *c1, *c2;
204940d71d2Seschrock 	int mindepth;
205940d71d2Seschrock 
206940d71d2Seschrock 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
207940d71d2Seschrock 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
208940d71d2Seschrock 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
209940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
210940d71d2Seschrock 
211940d71d2Seschrock 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
212940d71d2Seschrock 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
213940d71d2Seschrock 	if (err != 0)
214940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
215940d71d2Seschrock 
216940d71d2Seschrock 	/*
217940d71d2Seschrock 	 * If the chassis-id doesn't match, then these FMRIs are not
218940d71d2Seschrock 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
219940d71d2Seschrock 	 * have no choice but to fall back to the instance ID.
220940d71d2Seschrock 	 */
221940d71d2Seschrock 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
222940d71d2Seschrock 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
223940d71d2Seschrock 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
224940d71d2Seschrock 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
225940d71d2Seschrock 		if (strcmp(c1, c2) != 0)
226940d71d2Seschrock 			return (0);
227940d71d2Seschrock 
228940d71d2Seschrock 		mindepth = 1;
229940d71d2Seschrock 	} else {
230940d71d2Seschrock 		mindepth = 0;
231940d71d2Seschrock 	}
232940d71d2Seschrock 
233940d71d2Seschrock 	if (nhcp2 < nhcp1)
234940d71d2Seschrock 		return (0);
235940d71d2Seschrock 
236940d71d2Seschrock 	for (i = 0; i < nhcp1; i++) {
237940d71d2Seschrock 		char *nm1 = NULL;
238940d71d2Seschrock 		char *nm2 = NULL;
239940d71d2Seschrock 		char *id1 = NULL;
240940d71d2Seschrock 		char *id2 = NULL;
241940d71d2Seschrock 
242940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
243940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
244940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
245940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
246940d71d2Seschrock 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
247940d71d2Seschrock 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
248940d71d2Seschrock 
249940d71d2Seschrock 		if (strcmp(nm1, nm2) == 0 &&
250940d71d2Seschrock 		    (i < mindepth || strcmp(id1, id2) == 0))
251940d71d2Seschrock 			continue;
252940d71d2Seschrock 
253940d71d2Seschrock 		return (0);
254940d71d2Seschrock 	}
255940d71d2Seschrock 
256940d71d2Seschrock 	return (1);
257940d71d2Seschrock }
258940d71d2Seschrock 
259940d71d2Seschrock /*ARGSUSED*/
260940d71d2Seschrock static int
261940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
262940d71d2Seschrock     nvlist_t *in, nvlist_t **out)
263940d71d2Seschrock {
264940d71d2Seschrock 	int ret;
265940d71d2Seschrock 	nvlist_t *nv1, *nv2;
266940d71d2Seschrock 
267940d71d2Seschrock 	if (version > TOPO_METH_CONTAINS_VERSION)
268940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
269940d71d2Seschrock 
270940d71d2Seschrock 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
271940d71d2Seschrock 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
272940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
273940d71d2Seschrock 
274940d71d2Seschrock 	ret = fmri_contains(mod, nv1, nv2);
275940d71d2Seschrock 	if (ret < 0)
276940d71d2Seschrock 		return (-1);
277940d71d2Seschrock 
278940d71d2Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
27973e32a37SRobert Johnston 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
280940d71d2Seschrock 		    ret) == 0)
281940d71d2Seschrock 			return (0);
282940d71d2Seschrock 		else
283940d71d2Seschrock 			nvlist_free(*out);
284940d71d2Seschrock 	}
285940d71d2Seschrock 
286940d71d2Seschrock 	return (-1);
287940d71d2Seschrock 
288940d71d2Seschrock }
289940d71d2Seschrock 
290940d71d2Seschrock /*
291d91236feSeschrock  * Return a current instance of the node.  This is somewhat complicated because
292d91236feSeschrock  * we need to take a new snapshot in order to get the new data, but we don't
293940d71d2Seschrock  * want to be constantly taking SES snapshots if the consumer is going to do a
294940d71d2Seschrock  * series of queries.  So we adopt the strategy of assuming that the SES state
295d91236feSeschrock  * is not going to be rapidly changing, and limit our snapshot frequency to
296d91236feSeschrock  * some defined bounds.
297940d71d2Seschrock  */
298d91236feSeschrock ses_node_t *
2990b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn)
300940d71d2Seschrock {
301940d71d2Seschrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
302525b85dbSEric Schrock 	hrtime_t now;
303940d71d2Seschrock 	ses_snap_t *snap;
304940d71d2Seschrock 	int err;
305d91236feSeschrock 	uint64_t nodeid;
306940d71d2Seschrock 	ses_node_t *np;
307940d71d2Seschrock 
308d91236feSeschrock 	if (tp == NULL) {
309d91236feSeschrock 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
310d91236feSeschrock 		return (NULL);
311d91236feSeschrock 	}
312940d71d2Seschrock 
3130b32bb8bSEric Schrock 	(void) pthread_mutex_lock(&tp->set_lock);
3140b32bb8bSEric Schrock 
315940d71d2Seschrock 	/*
316940d71d2Seschrock 	 * Determine if we need to take a new snapshot.
317940d71d2Seschrock 	 */
318525b85dbSEric Schrock 	now = gethrtime();
319940d71d2Seschrock 
320525b85dbSEric Schrock 	if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
321940d71d2Seschrock 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
322940d71d2Seschrock 		if (ses_snap_generation(snap) !=
323940d71d2Seschrock 		    ses_snap_generation(tp->set_snap)) {
324940d71d2Seschrock 			/*
325940d71d2Seschrock 			 * If we find ourselves in this situation, we're in
326940d71d2Seschrock 			 * trouble.  The generation count has changed, which
327940d71d2Seschrock 			 * indicates that our current topology is out of date.
328940d71d2Seschrock 			 * But we need to consult the new topology in order to
329940d71d2Seschrock 			 * determine presence at this moment in time.  We can't
330940d71d2Seschrock 			 * go back and change the topo snapshot in situ, so
331d91236feSeschrock 			 * we'll just have to fail the call in this unlikely
332d91236feSeschrock 			 * scenario.
333940d71d2Seschrock 			 */
334940d71d2Seschrock 			ses_snap_rele(snap);
335d91236feSeschrock 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
3360b32bb8bSEric Schrock 			(void) pthread_mutex_unlock(&tp->set_lock);
337d91236feSeschrock 			return (NULL);
338940d71d2Seschrock 		} else {
339940d71d2Seschrock 			ses_snap_rele(tp->set_snap);
340940d71d2Seschrock 			tp->set_snap = snap;
341940d71d2Seschrock 		}
342525b85dbSEric Schrock 		tp->set_snaptime = gethrtime();
343940d71d2Seschrock 	}
344940d71d2Seschrock 
345940d71d2Seschrock 	snap = tp->set_snap;
346940d71d2Seschrock 
347940d71d2Seschrock 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
348940d71d2Seschrock 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
349940d71d2Seschrock 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
350d91236feSeschrock 
351d91236feSeschrock 	return (np);
352d91236feSeschrock }
353d91236feSeschrock 
3540b32bb8bSEric Schrock /*ARGSUSED*/
3550b32bb8bSEric Schrock void
3560b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
3570b32bb8bSEric Schrock {
3580b32bb8bSEric Schrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
3590b32bb8bSEric Schrock 
3600b32bb8bSEric Schrock 	verify(tp != NULL);
3610b32bb8bSEric Schrock 
3620b32bb8bSEric Schrock 	(void) pthread_mutex_unlock(&tp->set_lock);
3630b32bb8bSEric Schrock }
3640b32bb8bSEric Schrock 
365d91236feSeschrock /*
366d91236feSeschrock  * Determine if the element is present.
367d91236feSeschrock  */
368d91236feSeschrock /*ARGSUSED*/
369d91236feSeschrock static int
370d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
371d91236feSeschrock     nvlist_t *in, nvlist_t **out)
372d91236feSeschrock {
373d91236feSeschrock 	boolean_t present;
374d91236feSeschrock 	ses_node_t *np;
375d91236feSeschrock 	nvlist_t *props, *nvl;
376d91236feSeschrock 	uint64_t status;
377d91236feSeschrock 
3780b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL)
379d91236feSeschrock 		return (-1);
380d91236feSeschrock 
381940d71d2Seschrock 	verify((props = ses_node_props(np)) != NULL);
382940d71d2Seschrock 	verify(nvlist_lookup_uint64(props,
383940d71d2Seschrock 	    SES_PROP_STATUS_CODE, &status) == 0);
384940d71d2Seschrock 
3850b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
3860b32bb8bSEric Schrock 
387940d71d2Seschrock 	present = (status != SES_ESC_NOT_INSTALLED);
388940d71d2Seschrock 
389940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
390940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
391940d71d2Seschrock 
392940d71d2Seschrock 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
393940d71d2Seschrock 	    present) != 0) {
394940d71d2Seschrock 		nvlist_free(nvl);
395940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
396940d71d2Seschrock 	}
397940d71d2Seschrock 
398940d71d2Seschrock 	*out = nvl;
399940d71d2Seschrock 
400940d71d2Seschrock 	return (0);
401940d71d2Seschrock }
402940d71d2Seschrock 
403940d71d2Seschrock /*
404940d71d2Seschrock  * Sets standard properties for a ses node (enclosure or bay).  This includes
405940d71d2Seschrock  * setting the FRU to be the same as the resource, as well as setting the
406940d71d2Seschrock  * authority information.
407940d71d2Seschrock  */
408940d71d2Seschrock static int
409940d71d2Seschrock ses_set_standard_props(topo_mod_t *mod, tnode_t *tn, nvlist_t *auth,
410940d71d2Seschrock     uint64_t nodeid, const char *path)
411940d71d2Seschrock {
412940d71d2Seschrock 	int err;
413940d71d2Seschrock 	char *product, *chassis;
414940d71d2Seschrock 	nvlist_t *fmri;
415940d71d2Seschrock 	topo_pgroup_info_t pgi;
416940d71d2Seschrock 
417940d71d2Seschrock 	/*
418940d71d2Seschrock 	 * Set the authority explicitly if specified.
419940d71d2Seschrock 	 */
420940d71d2Seschrock 	if (auth) {
421940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
422940d71d2Seschrock 		    &product) == 0);
423940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
424940d71d2Seschrock 		    &chassis) == 0);
425940d71d2Seschrock 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
426940d71d2Seschrock 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
427940d71d2Seschrock 		    &err) != 0 ||
428940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
429940d71d2Seschrock 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
430940d71d2Seschrock 		    &err) != 0 ||
431940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
432940d71d2Seschrock 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
433940d71d2Seschrock 		    &err) != 0) {
434940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to add authority "
435d91236feSeschrock 			    "properties: %s\n", topo_strerror(err));
436940d71d2Seschrock 			return (topo_mod_seterrno(mod, err));
437940d71d2Seschrock 		}
438940d71d2Seschrock 	}
439940d71d2Seschrock 
440940d71d2Seschrock 	/*
441940d71d2Seschrock 	 * Copy the resource and set that as the FRU.
442940d71d2Seschrock 	 */
443940d71d2Seschrock 	if (topo_node_resource(tn, &fmri, &err) != 0) {
444940d71d2Seschrock 		topo_mod_dprintf(mod,
445940d71d2Seschrock 		    "topo_node_resource() failed : %s\n",
446940d71d2Seschrock 		    topo_strerror(err));
447940d71d2Seschrock 		return (topo_mod_seterrno(mod, err));
448940d71d2Seschrock 	}
449940d71d2Seschrock 
450940d71d2Seschrock 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
451940d71d2Seschrock 		topo_mod_dprintf(mod,
452940d71d2Seschrock 		    "topo_node_fru_set() failed : %s\n",
453940d71d2Seschrock 		    topo_strerror(err));
454940d71d2Seschrock 		nvlist_free(fmri);
455940d71d2Seschrock 		return (topo_mod_seterrno(mod, err));
456940d71d2Seschrock 	}
457940d71d2Seschrock 
458940d71d2Seschrock 	nvlist_free(fmri);
459940d71d2Seschrock 
460940d71d2Seschrock 	/*
461940d71d2Seschrock 	 * Set the SES-specific properties so that consumers can query
462940d71d2Seschrock 	 * additional information about the particular SES element.
463940d71d2Seschrock 	 */
464940d71d2Seschrock 	pgi.tpi_name = TOPO_PGROUP_SES;
465940d71d2Seschrock 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
466940d71d2Seschrock 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
467940d71d2Seschrock 	pgi.tpi_version = TOPO_VERSION;
468940d71d2Seschrock 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
469940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to create propgroup "
470940d71d2Seschrock 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
471940d71d2Seschrock 		return (-1);
472940d71d2Seschrock 	}
473940d71d2Seschrock 
474940d71d2Seschrock 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
475940d71d2Seschrock 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
476940d71d2Seschrock 	    nodeid, &err) != 0) {
477940d71d2Seschrock 		topo_mod_dprintf(mod,
478940d71d2Seschrock 		    "failed to create property %s: %s\n",
479940d71d2Seschrock 		    TOPO_PROP_NODE_ID, topo_strerror(err));
480940d71d2Seschrock 		return (-1);
481940d71d2Seschrock 	}
482940d71d2Seschrock 
483940d71d2Seschrock 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
484940d71d2Seschrock 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
485940d71d2Seschrock 	    path, &err) != 0) {
486940d71d2Seschrock 		topo_mod_dprintf(mod,
487940d71d2Seschrock 		    "failed to create property %s: %s\n",
488940d71d2Seschrock 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
489940d71d2Seschrock 		return (-1);
490940d71d2Seschrock 	}
491940d71d2Seschrock 
492940d71d2Seschrock 	return (0);
493940d71d2Seschrock }
494940d71d2Seschrock 
495940d71d2Seschrock /*
496940d71d2Seschrock  * Callback to add a disk to a given bay.  We first check the status-code to
497940d71d2Seschrock  * determine if a disk is present, ignoring those that aren't in an appropriate
4980b32bb8bSEric Schrock  * state.  We then scan the parent bay node's SAS address array to determine
4990b32bb8bSEric Schrock  * possible attached SAS addresses.  We create a disk node if the disk is not
5000b32bb8bSEric Schrock  * SAS or the SES target does not support the necessary pages for this; if we
5010b32bb8bSEric Schrock  * find the SAS address, we create a disk node and also correlate it with
5020b32bb8bSEric Schrock  * the corresponding Solaris device node to fill in the rest of the data.
503940d71d2Seschrock  */
504940d71d2Seschrock static int
505940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
506940d71d2Seschrock {
507940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
508940d71d2Seschrock 	uint64_t status;
509940d71d2Seschrock 	nvlist_t **sas;
510940d71d2Seschrock 	uint_t s, nsas;
5110b32bb8bSEric Schrock 	char **paths;
512*44ed9dbbSStephen Hanson 	int err, ret;
513*44ed9dbbSStephen Hanson 	tnode_t *child = NULL;
514940d71d2Seschrock 
515940d71d2Seschrock 	/*
516940d71d2Seschrock 	 * Skip devices that are not in a present (and possibly damaged) state.
517940d71d2Seschrock 	 */
518940d71d2Seschrock 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
519940d71d2Seschrock 		return (0);
520940d71d2Seschrock 
521940d71d2Seschrock 	if (status != SES_ESC_OK &&
522940d71d2Seschrock 	    status != SES_ESC_CRITICAL &&
523940d71d2Seschrock 	    status != SES_ESC_NONCRITICAL &&
524940d71d2Seschrock 	    status != SES_ESC_UNRECOVERABLE &&
525940d71d2Seschrock 	    status != SES_ESC_NO_ACCESS)
526940d71d2Seschrock 		return (0);
527940d71d2Seschrock 
528940d71d2Seschrock 	topo_mod_dprintf(mod, "found attached disk");
529940d71d2Seschrock 
530940d71d2Seschrock 	/*
531940d71d2Seschrock 	 * Create the disk range.
532940d71d2Seschrock 	 */
5330b32bb8bSEric Schrock 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
534940d71d2Seschrock 		topo_mod_dprintf(mod,
535940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
536940d71d2Seschrock 		    topo_mod_errmsg(mod));
537940d71d2Seschrock 		return (-1);
538940d71d2Seschrock 	}
539940d71d2Seschrock 
540940d71d2Seschrock 	/*
541940d71d2Seschrock 	 * Look through all SAS addresses and attempt to correlate them to a
542940d71d2Seschrock 	 * known Solaris device.  If we don't find a matching node, then we
543940d71d2Seschrock 	 * don't enumerate the disk node.
544940d71d2Seschrock 	 */
545940d71d2Seschrock 	if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
546940d71d2Seschrock 	    &sas, &nsas) != 0)
547940d71d2Seschrock 		return (0);
548940d71d2Seschrock 
5490b32bb8bSEric Schrock 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
5500b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
5510b32bb8bSEric Schrock 		return (0);
5520b32bb8bSEric Schrock 
5530b32bb8bSEric Schrock 	err = 0;
5540b32bb8bSEric Schrock 
555940d71d2Seschrock 	for (s = 0; s < nsas; s++) {
556*44ed9dbbSStephen Hanson 		ret = disk_declare_addr(mod, pnode, &sdp->sed_disks, paths[s],
557*44ed9dbbSStephen Hanson 		    &child);
558*44ed9dbbSStephen Hanson 		if (ret == 0) {
559*44ed9dbbSStephen Hanson 			break;
560*44ed9dbbSStephen Hanson 		} else if (ret < 0) {
5610b32bb8bSEric Schrock 			err = -1;
5620b32bb8bSEric Schrock 			break;
5630b32bb8bSEric Schrock 		}
5640b32bb8bSEric Schrock 	}
5650b32bb8bSEric Schrock 
566*44ed9dbbSStephen Hanson 	if (s == nsas)
567*44ed9dbbSStephen Hanson 		disk_declare_non_enumerated(mod, pnode, &child);
568*44ed9dbbSStephen Hanson 
569*44ed9dbbSStephen Hanson 	/* copy sas_addresses (target-ports) from parent (with 'w'added) */
570*44ed9dbbSStephen Hanson 	if (child != NULL) {
571*44ed9dbbSStephen Hanson 		int i;
572*44ed9dbbSStephen Hanson 		char **tports;
573*44ed9dbbSStephen Hanson 		uint64_t wwn;
574*44ed9dbbSStephen Hanson 
575*44ed9dbbSStephen Hanson 		tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
576*44ed9dbbSStephen Hanson 		if (tports != NULL) {
577*44ed9dbbSStephen Hanson 			for (i = 0; i < nsas; i++) {
578*44ed9dbbSStephen Hanson 				if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
579*44ed9dbbSStephen Hanson 				    DDI_SUCCESS)
580*44ed9dbbSStephen Hanson 					break;
581*44ed9dbbSStephen Hanson 				tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
582*44ed9dbbSStephen Hanson 				if (tports[i] == NULL)
583*44ed9dbbSStephen Hanson 					break;
584*44ed9dbbSStephen Hanson 			}
585*44ed9dbbSStephen Hanson 			/* if they all worked then create the property */
586*44ed9dbbSStephen Hanson 			if (i == nsas)
587*44ed9dbbSStephen Hanson 				(void) topo_prop_set_string_array(child,
588*44ed9dbbSStephen Hanson 				    TOPO_PGROUP_STORAGE,
589*44ed9dbbSStephen Hanson 				    TOPO_STORAGE_TARGET_PORT_L0IDS,
590*44ed9dbbSStephen Hanson 				    TOPO_PROP_IMMUTABLE, (const char **)tports,
591*44ed9dbbSStephen Hanson 				    nsas, &err);
592*44ed9dbbSStephen Hanson 
593*44ed9dbbSStephen Hanson 			for (i = 0; i < nsas; i++)
594*44ed9dbbSStephen Hanson 				if (tports[i] != NULL)
595*44ed9dbbSStephen Hanson 					scsi_free_wwnstr(tports[i]);
596*44ed9dbbSStephen Hanson 			topo_mod_free(mod, tports, sizeof (char *) * nsas);
597*44ed9dbbSStephen Hanson 		}
598*44ed9dbbSStephen Hanson 	}
599*44ed9dbbSStephen Hanson 
6000b32bb8bSEric Schrock 	for (s = 0; s < nsas; s++)
6010b32bb8bSEric Schrock 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
6020b32bb8bSEric Schrock 	topo_mod_free(mod, paths, nsas * sizeof (char *));
6030b32bb8bSEric Schrock 
6040b32bb8bSEric Schrock 	return (err);
6050b32bb8bSEric Schrock }
6060b32bb8bSEric Schrock 
6070b32bb8bSEric Schrock static int
6080b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
6090b32bb8bSEric Schrock {
6100b32bb8bSEric Schrock 	ses_alt_node_t *ap;
6110b32bb8bSEric Schrock 	ses_node_t *np;
6120b32bb8bSEric Schrock 	nvlist_t *props;
6130b32bb8bSEric Schrock 
6140b32bb8bSEric Schrock 	nvlist_t **phys;
6150b32bb8bSEric Schrock 	uint_t i, j, n_phys, all_phys = 0;
6160b32bb8bSEric Schrock 	char **paths;
6170b32bb8bSEric Schrock 	uint64_t addr;
6180b32bb8bSEric Schrock 	size_t len;
6190b32bb8bSEric Schrock 	int terr, err = -1;
6200b32bb8bSEric Schrock 
6210b32bb8bSEric Schrock 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
6220b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
6230b32bb8bSEric Schrock 		np = ap->san_node;
6240b32bb8bSEric Schrock 		props = ses_node_props(np);
6250b32bb8bSEric Schrock 
6260b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
6270b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
6280b32bb8bSEric Schrock 			continue;
6290b32bb8bSEric Schrock 
6300b32bb8bSEric Schrock 		all_phys += n_phys;
6310b32bb8bSEric Schrock 	}
6320b32bb8bSEric Schrock 
6330b32bb8bSEric Schrock 	if (all_phys == 0)
6340b32bb8bSEric Schrock 		return (0);
6350b32bb8bSEric Schrock 
6360b32bb8bSEric Schrock 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
6370b32bb8bSEric Schrock 		return (-1);
6380b32bb8bSEric Schrock 
6390b32bb8bSEric Schrock 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
6400b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
6410b32bb8bSEric Schrock 		np = ap->san_node;
6420b32bb8bSEric Schrock 		props = ses_node_props(np);
6430b32bb8bSEric Schrock 
6440b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
6450b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
646940d71d2Seschrock 			continue;
647940d71d2Seschrock 
6480b32bb8bSEric Schrock 		for (j = 0; j < n_phys; j++) {
6490b32bb8bSEric Schrock 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
6500b32bb8bSEric Schrock 			    &addr) != 0)
6510b32bb8bSEric Schrock 				continue;
652940d71d2Seschrock 
6530b32bb8bSEric Schrock 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
6540b32bb8bSEric Schrock 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
6550b32bb8bSEric Schrock 				goto error;
6560b32bb8bSEric Schrock 
6570b32bb8bSEric Schrock 			(void) snprintf(paths[i], len, "%016llx", addr);
6580b32bb8bSEric Schrock 
6590b32bb8bSEric Schrock 			++i;
6600b32bb8bSEric Schrock 		}
661940d71d2Seschrock 	}
662940d71d2Seschrock 
6630b32bb8bSEric Schrock 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
6640b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
6650b32bb8bSEric Schrock 	    (const char **)paths, i, &terr);
6660b32bb8bSEric Schrock 	if (err != 0)
6670b32bb8bSEric Schrock 		err = topo_mod_seterrno(mod, terr);
6680b32bb8bSEric Schrock 
6690b32bb8bSEric Schrock error:
6700b32bb8bSEric Schrock 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
6710b32bb8bSEric Schrock 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
6720b32bb8bSEric Schrock 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
6730b32bb8bSEric Schrock 
6740b32bb8bSEric Schrock 	return (err);
675940d71d2Seschrock }
676940d71d2Seschrock 
677940d71d2Seschrock /*
678940d71d2Seschrock  * Callback to create a basic node (bay, psu, fan, or controller).
679940d71d2Seschrock  */
680940d71d2Seschrock static int
681940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp,
682940d71d2Seschrock     tnode_t *pnode, const char *nodename, const char *labelname)
683940d71d2Seschrock {
684940d71d2Seschrock 	ses_node_t *np = snp->sen_node;
685d91236feSeschrock 	ses_node_t *parent;
686940d71d2Seschrock 	uint64_t instance = snp->sen_instance;
687940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
688940d71d2Seschrock 	nvlist_t *props, *aprops;
689940d71d2Seschrock 	nvlist_t *auth = NULL, *fmri = NULL;
690940d71d2Seschrock 	tnode_t *tn;
691940d71d2Seschrock 	char label[128];
692940d71d2Seschrock 	int err;
693d91236feSeschrock 	char *part = NULL, *serial = NULL, *revision = NULL;
694d91236feSeschrock 	char *desc;
695d91236feSeschrock 	boolean_t report;
696940d71d2Seschrock 
697940d71d2Seschrock 	props = ses_node_props(np);
698940d71d2Seschrock 
699940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
700940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
701940d71d2Seschrock 
702940d71d2Seschrock 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
703940d71d2Seschrock 
704940d71d2Seschrock 	/*
705940d71d2Seschrock 	 * Create the node.  The interesting information is all copied from the
706940d71d2Seschrock 	 * parent enclosure node, so there is not much to do.
707940d71d2Seschrock 	 */
708940d71d2Seschrock 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
709940d71d2Seschrock 		goto error;
710940d71d2Seschrock 
711d91236feSeschrock 	/*
712d91236feSeschrock 	 * We want to report revision information for the controller nodes, but
713d91236feSeschrock 	 * we do not get per-element revision information.  However, we do have
714d91236feSeschrock 	 * revision information for the entire enclosure, and we can use the
715d91236feSeschrock 	 * 'reported-via' property to know that this controller corresponds to
716d91236feSeschrock 	 * the given revision information.  This means we cannot get revision
717d91236feSeschrock 	 * information for targets we are not explicitly connected to, but
718d91236feSeschrock 	 * there is little we can do about the situation.
719d91236feSeschrock 	 */
720d91236feSeschrock 	if (strcmp(nodename, CONTROLLER) == 0 &&
721d91236feSeschrock 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
722d91236feSeschrock 	    report) {
723d91236feSeschrock 		for (parent = ses_node_parent(np); parent != NULL;
724d91236feSeschrock 		    parent = ses_node_parent(parent)) {
725d91236feSeschrock 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
726d91236feSeschrock 				(void) nvlist_lookup_string(
727d91236feSeschrock 				    ses_node_props(parent),
728d91236feSeschrock 				    SES_EN_PROP_REV, &revision);
729d91236feSeschrock 				break;
730d91236feSeschrock 			}
731d91236feSeschrock 		}
732d91236feSeschrock 	}
733d91236feSeschrock 
734940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
735d91236feSeschrock 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
736940d71d2Seschrock 	    serial)) == NULL) {
737940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
738940d71d2Seschrock 		    topo_mod_errmsg(mod));
739940d71d2Seschrock 		goto error;
740940d71d2Seschrock 	}
741940d71d2Seschrock 
742940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, nodename,
743940d71d2Seschrock 	    instance, fmri)) == NULL) {
744940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
745940d71d2Seschrock 		    topo_mod_errmsg(mod));
746940d71d2Seschrock 		goto error;
747940d71d2Seschrock 	}
748940d71d2Seschrock 
749940d71d2Seschrock 	/*
750d91236feSeschrock 	 * For the node label, we look for the following in order:
751d91236feSeschrock 	 *
752d91236feSeschrock 	 * 	<ses-description>
753d91236feSeschrock 	 * 	<ses-class-description> <instance>
754d91236feSeschrock 	 * 	<default-type-label> <instance>
755940d71d2Seschrock 	 */
756d91236feSeschrock 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
757d91236feSeschrock 	    desc[0] == '\0') {
758d91236feSeschrock 		parent = ses_node_parent(np);
759d91236feSeschrock 		aprops = ses_node_props(parent);
760d91236feSeschrock 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
761d91236feSeschrock 		    &desc) == 0 && desc[0] != '\0')
762d91236feSeschrock 			labelname = desc;
763d91236feSeschrock 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
764d91236feSeschrock 		    instance);
765d91236feSeschrock 		desc = label;
766d91236feSeschrock 	}
767d91236feSeschrock 
768d91236feSeschrock 	if (topo_node_label_set(tn, desc, &err) != 0)
769940d71d2Seschrock 		goto error;
770940d71d2Seschrock 
771940d71d2Seschrock 	if (ses_set_standard_props(mod, tn, NULL, ses_node_id(np),
772940d71d2Seschrock 	    snp->sen_target->set_devpath) != 0)
773940d71d2Seschrock 		goto error;
774940d71d2Seschrock 
775940d71d2Seschrock 	if (strcmp(nodename, "bay") == 0) {
7760b32bb8bSEric Schrock 		if (ses_add_bay_props(mod, tn, snp) != 0)
7770b32bb8bSEric Schrock 			goto error;
7780b32bb8bSEric Schrock 
779940d71d2Seschrock 		if (ses_create_disk(sdp, tn, props) != 0)
780940d71d2Seschrock 			goto error;
781d91236feSeschrock 
782d91236feSeschrock 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
783d91236feSeschrock 			topo_mod_dprintf(mod,
784d91236feSeschrock 			    "topo_method_register() failed: %s",
785d91236feSeschrock 			    topo_mod_errmsg(mod));
786d91236feSeschrock 			goto error;
787d91236feSeschrock 		}
788940d71d2Seschrock 	} else {
789940d71d2Seschrock 		/*
790d91236feSeschrock 		 * Only fan, psu, and controller nodes have a 'present' method.
791d91236feSeschrock 		 * Bay nodes are always present, and disk nodes are present by
792d91236feSeschrock 		 * virtue of being enumerated.
793940d71d2Seschrock 		 */
794940d71d2Seschrock 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
795940d71d2Seschrock 			topo_mod_dprintf(mod,
796940d71d2Seschrock 			    "topo_method_register() failed: %s",
797940d71d2Seschrock 			    topo_mod_errmsg(mod));
798940d71d2Seschrock 			goto error;
799940d71d2Seschrock 		}
800940d71d2Seschrock 
801940d71d2Seschrock 	}
802940d71d2Seschrock 
803d91236feSeschrock 	snp->sen_target->set_refcount++;
804d91236feSeschrock 	topo_node_setspecific(tn, snp->sen_target);
805d91236feSeschrock 
806940d71d2Seschrock 	nvlist_free(auth);
807940d71d2Seschrock 	nvlist_free(fmri);
808940d71d2Seschrock 	return (0);
809940d71d2Seschrock 
810940d71d2Seschrock error:
811940d71d2Seschrock 	nvlist_free(auth);
812940d71d2Seschrock 	nvlist_free(fmri);
813940d71d2Seschrock 	return (-1);
814940d71d2Seschrock }
815940d71d2Seschrock 
816940d71d2Seschrock /*
817940d71d2Seschrock  * Instantiate any children of a given type.
818940d71d2Seschrock  */
819940d71d2Seschrock static int
820940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
821d91236feSeschrock     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
822d91236feSeschrock     boolean_t dorange)
823940d71d2Seschrock {
824940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
825940d71d2Seschrock 	boolean_t found;
826940d71d2Seschrock 	uint64_t max;
827940d71d2Seschrock 	ses_enum_node_t *snp;
828940d71d2Seschrock 
829940d71d2Seschrock 	/*
830940d71d2Seschrock 	 * First go through and count how many matching nodes we have.
831940d71d2Seschrock 	 */
832940d71d2Seschrock 	max = 0;
833940d71d2Seschrock 	found = B_FALSE;
834940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
835940d71d2Seschrock 	    snp = topo_list_next(snp)) {
836940d71d2Seschrock 		if (snp->sen_type == type) {
837940d71d2Seschrock 			found = B_TRUE;
838940d71d2Seschrock 			if (snp->sen_instance > max)
839940d71d2Seschrock 				max = snp->sen_instance;
840940d71d2Seschrock 		}
841940d71d2Seschrock 	}
842940d71d2Seschrock 
843940d71d2Seschrock 	/*
844940d71d2Seschrock 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
845940d71d2Seschrock 	 * Since we map both of these to 'disk', if an enclosure does this, we
846940d71d2Seschrock 	 * just ignore the array elements.
847940d71d2Seschrock 	 */
848940d71d2Seschrock 	if (!found ||
849940d71d2Seschrock 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
850940d71d2Seschrock 		return (0);
851940d71d2Seschrock 
852940d71d2Seschrock 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
85353dbcc59SSundeep Panicker 	    cp->sec_csn, max + 1, nodename);
854940d71d2Seschrock 
855d91236feSeschrock 	if (dorange && topo_node_range_create(mod, pnode,
856940d71d2Seschrock 	    nodename, 0, max) != 0) {
857940d71d2Seschrock 		topo_mod_dprintf(mod,
858940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
859940d71d2Seschrock 		    topo_mod_errmsg(mod));
860940d71d2Seschrock 		return (-1);
861940d71d2Seschrock 	}
862940d71d2Seschrock 
863940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
864940d71d2Seschrock 	    snp = topo_list_next(snp)) {
865940d71d2Seschrock 		if (snp->sen_type == type) {
866940d71d2Seschrock 			if (ses_create_generic(sdp, snp, pnode,
867940d71d2Seschrock 			    nodename, defaultlabel) != 0)
868940d71d2Seschrock 				return (-1);
869940d71d2Seschrock 		}
870940d71d2Seschrock 	}
871940d71d2Seschrock 
872940d71d2Seschrock 	return (0);
873940d71d2Seschrock }
874940d71d2Seschrock 
87553dbcc59SSundeep Panicker /*
87653dbcc59SSundeep Panicker  * Instantiate a new subchassis instance in the topology.
87753dbcc59SSundeep Panicker  */
87853dbcc59SSundeep Panicker static int
87953dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
88053dbcc59SSundeep Panicker     ses_enum_chassis_t *scp)
88153dbcc59SSundeep Panicker {
88253dbcc59SSundeep Panicker 	topo_mod_t *mod = sdp->sed_mod;
88353dbcc59SSundeep Panicker 	tnode_t *tn;
88453dbcc59SSundeep Panicker 	nvlist_t *props;
88553dbcc59SSundeep Panicker 	nvlist_t *auth = NULL, *fmri = NULL;
88653dbcc59SSundeep Panicker 	char *part = NULL, *revision = NULL;
88753dbcc59SSundeep Panicker 	uint64_t instance = scp->sec_instance;
88853dbcc59SSundeep Panicker 	char *desc;
88953dbcc59SSundeep Panicker 	char label[128];
89053dbcc59SSundeep Panicker 	char **paths;
89153dbcc59SSundeep Panicker 	int i, err;
89253dbcc59SSundeep Panicker 	ses_enum_target_t *stp;
89353dbcc59SSundeep Panicker 	int ret = -1;
89453dbcc59SSundeep Panicker 
89553dbcc59SSundeep Panicker 	/*
89653dbcc59SSundeep Panicker 	 * Copy authority information from parent enclosure node
89753dbcc59SSundeep Panicker 	 */
89853dbcc59SSundeep Panicker 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
89953dbcc59SSundeep Panicker 		goto error;
90053dbcc59SSundeep Panicker 
90153dbcc59SSundeep Panicker 	/*
90253dbcc59SSundeep Panicker 	 * Record the subchassis serial number in the FMRI.
90353dbcc59SSundeep Panicker 	 * For now, we assume that logical id is the subchassis serial number.
90453dbcc59SSundeep Panicker 	 * If this assumption changes in future, then the following
90553dbcc59SSundeep Panicker 	 * piece of code will need to be updated via an RFE.
90653dbcc59SSundeep Panicker 	 */
90753dbcc59SSundeep Panicker 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
90853dbcc59SSundeep Panicker 	    SUBCHASSIS, (topo_instance_t)instance, NULL, auth, part, revision,
90953dbcc59SSundeep Panicker 	    scp->sec_lid)) == NULL) {
91053dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
91153dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
91253dbcc59SSundeep Panicker 		goto error;
91353dbcc59SSundeep Panicker 	}
91453dbcc59SSundeep Panicker 
91553dbcc59SSundeep Panicker 	if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
91653dbcc59SSundeep Panicker 	    instance, fmri)) == NULL) {
91753dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
91853dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
91953dbcc59SSundeep Panicker 		goto error;
92053dbcc59SSundeep Panicker 	}
92153dbcc59SSundeep Panicker 
92253dbcc59SSundeep Panicker 	props = ses_node_props(scp->sec_enclosure);
92353dbcc59SSundeep Panicker 
92453dbcc59SSundeep Panicker 	/*
92553dbcc59SSundeep Panicker 	 * Look for the subchassis label in the following order:
92653dbcc59SSundeep Panicker 	 *	<ses-description>
92753dbcc59SSundeep Panicker 	 *	<ses-class-description> <instance>
92853dbcc59SSundeep Panicker 	 *	<default-type-label> <instance>
92953dbcc59SSundeep Panicker 	 *
93053dbcc59SSundeep Panicker 	 * For subchassis, the default label is "SUBCHASSIS"
93153dbcc59SSundeep Panicker 	 */
93253dbcc59SSundeep Panicker 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
93353dbcc59SSundeep Panicker 	    desc[0] == '\0') {
93453dbcc59SSundeep Panicker 		if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
93553dbcc59SSundeep Panicker 		    &desc) == 0 && desc[0] != '\0')
93653dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label), "%s %llu", desc,
93753dbcc59SSundeep Panicker 			    instance);
93853dbcc59SSundeep Panicker 		else
93953dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label),
94053dbcc59SSundeep Panicker 			    "SUBCHASSIS %llu", instance);
94153dbcc59SSundeep Panicker 		desc = label;
94253dbcc59SSundeep Panicker 	}
94353dbcc59SSundeep Panicker 
94453dbcc59SSundeep Panicker 	if (topo_node_label_set(tn, desc, &err) != 0)
94553dbcc59SSundeep Panicker 		goto error;
94653dbcc59SSundeep Panicker 
94753dbcc59SSundeep Panicker 	if (ses_set_standard_props(mod, tn, NULL,
94853dbcc59SSundeep Panicker 	    ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
94953dbcc59SSundeep Panicker 		goto error;
95053dbcc59SSundeep Panicker 
95153dbcc59SSundeep Panicker 	/*
95253dbcc59SSundeep Panicker 	 * For enclosures, we want to include all possible targets (for upgrade
95353dbcc59SSundeep Panicker 	 * purposes).
95453dbcc59SSundeep Panicker 	 */
95553dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
95653dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
95753dbcc59SSundeep Panicker 		;
95853dbcc59SSundeep Panicker 
95953dbcc59SSundeep Panicker 	verify(i != 0);
96053dbcc59SSundeep Panicker 	paths = alloca(i * sizeof (char *));
96153dbcc59SSundeep Panicker 
96253dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
96353dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
96453dbcc59SSundeep Panicker 		paths[i] = stp->set_devpath;
96553dbcc59SSundeep Panicker 
96653dbcc59SSundeep Panicker 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
96753dbcc59SSundeep Panicker 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
96853dbcc59SSundeep Panicker 	    i, &err) != 0) {
96953dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
97053dbcc59SSundeep Panicker 		    TOPO_PROP_PATHS, topo_strerror(err));
97153dbcc59SSundeep Panicker 		goto error;
97253dbcc59SSundeep Panicker 	}
97353dbcc59SSundeep Panicker 
97453dbcc59SSundeep Panicker 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
97553dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
97653dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
97753dbcc59SSundeep Panicker 		goto error;
97853dbcc59SSundeep Panicker 	}
97953dbcc59SSundeep Panicker 
98053dbcc59SSundeep Panicker 	/*
98153dbcc59SSundeep Panicker 	 * Create the nodes for controllers and bays.
98253dbcc59SSundeep Panicker 	 */
98353dbcc59SSundeep Panicker 	if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
98453dbcc59SSundeep Panicker 	    CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
98553dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
98653dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0 ||
98753dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
98853dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0)
98953dbcc59SSundeep Panicker 		goto error;
99053dbcc59SSundeep Panicker 
99153dbcc59SSundeep Panicker 	ret = 0;
99253dbcc59SSundeep Panicker 
99353dbcc59SSundeep Panicker error:
99453dbcc59SSundeep Panicker 	nvlist_free(auth);
99553dbcc59SSundeep Panicker 	nvlist_free(fmri);
99653dbcc59SSundeep Panicker 	return (ret);
99753dbcc59SSundeep Panicker }
99853dbcc59SSundeep Panicker 
999940d71d2Seschrock /*
1000940d71d2Seschrock  * Instantiate a new chassis instance in the topology.
1001940d71d2Seschrock  */
1002940d71d2Seschrock static int
1003940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
1004940d71d2Seschrock {
1005940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1006940d71d2Seschrock 	nvlist_t *props;
1007940d71d2Seschrock 	char *raw_manufacturer, *raw_model, *raw_revision;
1008940d71d2Seschrock 	char *manufacturer = NULL, *model = NULL, *product = NULL;
1009940d71d2Seschrock 	char *revision = NULL;
1010940d71d2Seschrock 	char *serial;
10116efb64caSEric Schrock 	char **paths;
1012940d71d2Seschrock 	size_t prodlen;
1013940d71d2Seschrock 	tnode_t *tn;
1014940d71d2Seschrock 	nvlist_t *fmri = NULL, *auth = NULL;
1015940d71d2Seschrock 	int ret = -1;
1016940d71d2Seschrock 	ses_enum_node_t *snp;
10176efb64caSEric Schrock 	ses_enum_target_t *stp;
101853dbcc59SSundeep Panicker 	ses_enum_chassis_t *scp;
10196efb64caSEric Schrock 	int i, err;
102053dbcc59SSundeep Panicker 	uint64_t sc_count = 0;
1021940d71d2Seschrock 
1022d91236feSeschrock 	/*
1023d91236feSeschrock 	 * Ignore any internal enclosures.
1024d91236feSeschrock 	 */
1025d91236feSeschrock 	if (cp->sec_internal)
1026d91236feSeschrock 		return (0);
1027d91236feSeschrock 
1028940d71d2Seschrock 	/*
1029940d71d2Seschrock 	 * Check to see if there are any devices presennt in the chassis.  If
1030940d71d2Seschrock 	 * not, ignore the chassis alltogether.  This is most useful for
1031940d71d2Seschrock 	 * ignoring internal HBAs that present a SES target but don't actually
1032940d71d2Seschrock 	 * manage any of the devices.
1033940d71d2Seschrock 	 */
1034940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1035940d71d2Seschrock 	    snp = topo_list_next(snp)) {
1036940d71d2Seschrock 		if (snp->sen_type == SES_ET_DEVICE ||
1037940d71d2Seschrock 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
1038940d71d2Seschrock 			break;
1039940d71d2Seschrock 	}
1040940d71d2Seschrock 
1041940d71d2Seschrock 	if (snp == NULL)
1042940d71d2Seschrock 		return (0);
1043940d71d2Seschrock 
1044940d71d2Seschrock 	props = ses_node_props(cp->sec_enclosure);
1045940d71d2Seschrock 
1046940d71d2Seschrock 	/*
1047940d71d2Seschrock 	 * We use the following property mappings:
1048940d71d2Seschrock 	 *
1049940d71d2Seschrock 	 * 	manufacturer		vendor-id
1050940d71d2Seschrock 	 * 	model			product-id
1051940d71d2Seschrock 	 * 	serial-number		libses-chassis-serial
1052940d71d2Seschrock 	 */
1053940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
1054940d71d2Seschrock 	    &raw_manufacturer) == 0);
1055940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
1056940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
1057940d71d2Seschrock 	    &raw_revision) == 0);
1058940d71d2Seschrock 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
1059940d71d2Seschrock 
1060940d71d2Seschrock 	/*
1061940d71d2Seschrock 	 * To construct the authority information, we 'clean' each string by
1062940d71d2Seschrock 	 * removing any offensive characters and trimmming whitespace.  For the
1063940d71d2Seschrock 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
1064940d71d2Seschrock 	 * also take the numerical serial number and convert it to a string.
1065940d71d2Seschrock 	 */
1066940d71d2Seschrock 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
1067940d71d2Seschrock 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
1068940d71d2Seschrock 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
1069940d71d2Seschrock 		goto error;
1070940d71d2Seschrock 	}
1071940d71d2Seschrock 
1072940d71d2Seschrock 	prodlen = strlen(manufacturer) + strlen(model) + 2;
1073940d71d2Seschrock 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
1074940d71d2Seschrock 		goto error;
1075940d71d2Seschrock 
1076940d71d2Seschrock 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
1077940d71d2Seschrock 
1078940d71d2Seschrock 	/*
1079940d71d2Seschrock 	 * Construct the topo node and bind it to our parent.
1080940d71d2Seschrock 	 */
1081940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
1082940d71d2Seschrock 		goto error;
1083940d71d2Seschrock 
1084940d71d2Seschrock 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
1085940d71d2Seschrock 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
1086940d71d2Seschrock 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1087940d71d2Seschrock 		goto error;
1088940d71d2Seschrock 	}
1089940d71d2Seschrock 
1090940d71d2Seschrock 	/*
1091940d71d2Seschrock 	 * We pass NULL for the parent FMRI because there is no resource
1092940d71d2Seschrock 	 * associated with it.  For the toplevel enclosure, we leave the
1093940d71d2Seschrock 	 * serial/part/revision portions empty, which are reserved for
1094940d71d2Seschrock 	 * individual components within the chassis.
1095940d71d2Seschrock 	 */
1096940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
1097940d71d2Seschrock 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
1098940d71d2Seschrock 	    model, revision, serial)) == NULL) {
1099940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1100940d71d2Seschrock 		    topo_mod_errmsg(mod));
1101940d71d2Seschrock 		goto error;
1102940d71d2Seschrock 	}
1103940d71d2Seschrock 
1104940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
1105940d71d2Seschrock 	    cp->sec_instance, fmri)) == NULL) {
1106940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1107940d71d2Seschrock 		    topo_mod_errmsg(mod));
1108940d71d2Seschrock 		goto error;
1109940d71d2Seschrock 	}
1110940d71d2Seschrock 
1111940d71d2Seschrock 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
1112940d71d2Seschrock 		topo_mod_dprintf(mod,
1113940d71d2Seschrock 		    "topo_method_register() failed: %s",
1114940d71d2Seschrock 		    topo_mod_errmsg(mod));
1115940d71d2Seschrock 		goto error;
1116940d71d2Seschrock 	}
1117940d71d2Seschrock 
1118940d71d2Seschrock 	if (ses_set_standard_props(mod, tn, auth,
1119940d71d2Seschrock 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
1120940d71d2Seschrock 		goto error;
1121940d71d2Seschrock 
11226efb64caSEric Schrock 	/*
11236efb64caSEric Schrock 	 * For enclosures, we want to include all possible targets (for upgrade
11246efb64caSEric Schrock 	 * purposes).
11256efb64caSEric Schrock 	 */
11266efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
11276efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
11286efb64caSEric Schrock 		;
11296efb64caSEric Schrock 
11306efb64caSEric Schrock 	verify(i != 0);
11316efb64caSEric Schrock 	paths = alloca(i * sizeof (char *));
11326efb64caSEric Schrock 
11336efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
11346efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
11356efb64caSEric Schrock 		paths[i] = stp->set_devpath;
11366efb64caSEric Schrock 
11376efb64caSEric Schrock 
11386efb64caSEric Schrock 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
11396efb64caSEric Schrock 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
11406efb64caSEric Schrock 	    i, &err) != 0) {
11416efb64caSEric Schrock 		topo_mod_dprintf(mod,
11426efb64caSEric Schrock 		    "failed to create property %s: %s\n",
11436efb64caSEric Schrock 		    TOPO_PROP_PATHS, topo_strerror(err));
11446efb64caSEric Schrock 		goto error;
11456efb64caSEric Schrock 	}
11466efb64caSEric Schrock 
1147940d71d2Seschrock 	/*
1148940d71d2Seschrock 	 * Create the nodes for power supplies, fans, and devices.
1149940d71d2Seschrock 	 */
1150940d71d2Seschrock 	if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
1151d91236feSeschrock 	    PSU, "PSU", cp, B_TRUE) != 0 ||
1152940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_COOLING,
1153d91236feSeschrock 	    FAN, "FAN", cp, B_TRUE) != 0 ||
1154940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
1155d91236feSeschrock 	    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
1156940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
1157d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0 ||
1158940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
1159d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0)
1160940d71d2Seschrock 		goto error;
1161940d71d2Seschrock 
116253dbcc59SSundeep Panicker 	if (cp->sec_scinstance > 0 &&
116353dbcc59SSundeep Panicker 	    topo_node_range_create(mod, tn, SUBCHASSIS, 0,
116453dbcc59SSundeep Panicker 	    cp->sec_scinstance - 1) != 0) {
116553dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
116653dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
116753dbcc59SSundeep Panicker 		goto error;
116853dbcc59SSundeep Panicker 	}
116953dbcc59SSundeep Panicker 
117053dbcc59SSundeep Panicker 	for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
117153dbcc59SSundeep Panicker 	    scp = topo_list_next(scp)) {
117253dbcc59SSundeep Panicker 
117353dbcc59SSundeep Panicker 		if (ses_create_subchassis(sdp, tn, scp) != 0)
117453dbcc59SSundeep Panicker 			goto error;
117553dbcc59SSundeep Panicker 
117653dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "created Subchassis node with "
117753dbcc59SSundeep Panicker 		    "LID (%s)\n  and target (%s) under Chassis with CSN (%s)",
117853dbcc59SSundeep Panicker 		    scp->sec_lid, scp->sec_target->set_devpath, cp->sec_csn);
117953dbcc59SSundeep Panicker 
118053dbcc59SSundeep Panicker 		sc_count++;
118153dbcc59SSundeep Panicker 	}
118253dbcc59SSundeep Panicker 
118353dbcc59SSundeep Panicker 	topo_mod_dprintf(mod, "%s: created %llu %s nodes",
118453dbcc59SSundeep Panicker 	    cp->sec_csn, sc_count, SUBCHASSIS);
118553dbcc59SSundeep Panicker 
11866efb64caSEric Schrock 	cp->sec_target->set_refcount++;
11876efb64caSEric Schrock 	topo_node_setspecific(tn, cp->sec_target);
1188d91236feSeschrock 
1189940d71d2Seschrock 	ret = 0;
1190940d71d2Seschrock error:
1191940d71d2Seschrock 	topo_mod_strfree(mod, manufacturer);
1192940d71d2Seschrock 	topo_mod_strfree(mod, model);
1193940d71d2Seschrock 	topo_mod_strfree(mod, revision);
1194940d71d2Seschrock 	topo_mod_strfree(mod, product);
1195940d71d2Seschrock 
1196940d71d2Seschrock 	nvlist_free(fmri);
1197940d71d2Seschrock 	nvlist_free(auth);
1198940d71d2Seschrock 	return (ret);
1199940d71d2Seschrock }
1200940d71d2Seschrock 
1201d91236feSeschrock /*
1202d91236feSeschrock  * Create a bay node explicitly enumerated via XML.
1203d91236feSeschrock  */
1204d91236feSeschrock static int
1205d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
1206d91236feSeschrock {
1207d91236feSeschrock 	topo_mod_t *mod = sdp->sed_mod;
1208d91236feSeschrock 	ses_enum_chassis_t *cp;
1209d91236feSeschrock 
1210d91236feSeschrock 	/*
1211d91236feSeschrock 	 * Iterate over chassis looking for an internal enclosure.  This
1212d91236feSeschrock 	 * property is set via a vendor-specific plugin, and there should only
1213d91236feSeschrock 	 * ever be a single internal chassis in a system.
1214d91236feSeschrock 	 */
1215d91236feSeschrock 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1216d91236feSeschrock 	    cp = topo_list_next(cp)) {
1217d91236feSeschrock 		if (cp->sec_internal)
1218d91236feSeschrock 			break;
1219d91236feSeschrock 	}
1220d91236feSeschrock 
1221d91236feSeschrock 	if (cp == NULL) {
1222d91236feSeschrock 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
1223d91236feSeschrock 		return (-1);
1224d91236feSeschrock 	}
1225d91236feSeschrock 
1226d91236feSeschrock 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
1227d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0 ||
1228d91236feSeschrock 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
1229d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0)
1230d91236feSeschrock 		return (-1);
1231d91236feSeschrock 
1232d91236feSeschrock 	return (0);
1233d91236feSeschrock }
123453dbcc59SSundeep Panicker 
123553dbcc59SSundeep Panicker /*
123653dbcc59SSundeep Panicker  * Initialize chassis or subchassis.
123753dbcc59SSundeep Panicker  */
123853dbcc59SSundeep Panicker static int
123953dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
124053dbcc59SSundeep Panicker     ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
124153dbcc59SSundeep Panicker     char *lid, ses_chassis_type_e flags)
124253dbcc59SSundeep Panicker {
124353dbcc59SSundeep Panicker 	boolean_t internal, ident;
124453dbcc59SSundeep Panicker 
124553dbcc59SSundeep Panicker 	assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
124653dbcc59SSundeep Panicker 	    SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
124753dbcc59SSundeep Panicker 
124853dbcc59SSundeep Panicker 	assert((cp != NULL) && (np != NULL) && (props != NULL) &&
124953dbcc59SSundeep Panicker 	    (lid != NULL));
125053dbcc59SSundeep Panicker 
125153dbcc59SSundeep Panicker 	if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
125253dbcc59SSundeep Panicker 		assert(pcp != NULL);
125353dbcc59SSundeep Panicker 
125453dbcc59SSundeep Panicker 	topo_mod_dprintf(mod, "ses_init_chassis: %s: lid(%s), flags (%d)",
125553dbcc59SSundeep Panicker 	    sdp->sed_name, lid, flags);
125653dbcc59SSundeep Panicker 
125753dbcc59SSundeep Panicker 	if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
125853dbcc59SSundeep Panicker 
125953dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "new chassis/subchassis");
126053dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
126153dbcc59SSundeep Panicker 		    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
126253dbcc59SSundeep Panicker 			cp->sec_internal = internal;
126353dbcc59SSundeep Panicker 
126453dbcc59SSundeep Panicker 		cp->sec_lid = lid;
126553dbcc59SSundeep Panicker 		cp->sec_enclosure = np;
126653dbcc59SSundeep Panicker 		cp->sec_target = sdp->sed_target;
126753dbcc59SSundeep Panicker 
126853dbcc59SSundeep Panicker 		if (flags & SES_NEW_CHASSIS) {
126953dbcc59SSundeep Panicker 			cp->sec_instance = sdp->sed_instance++;
127053dbcc59SSundeep Panicker 			topo_list_append(&sdp->sed_chassis, cp);
127153dbcc59SSundeep Panicker 		} else {
127253dbcc59SSundeep Panicker 			cp->sec_instance = pcp->sec_scinstance++;
127353dbcc59SSundeep Panicker 			topo_list_append(&pcp->sec_subchassis, cp);
127453dbcc59SSundeep Panicker 		}
127553dbcc59SSundeep Panicker 
127653dbcc59SSundeep Panicker 	} else {
127753dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "dup chassis/subchassis");
127853dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
127953dbcc59SSundeep Panicker 		    SES_PROP_IDENT, &ident) == 0) {
128053dbcc59SSundeep Panicker 			topo_mod_dprintf(mod,  "overriding enclosure node");
128153dbcc59SSundeep Panicker 
128253dbcc59SSundeep Panicker 			cp->sec_enclosure = np;
128353dbcc59SSundeep Panicker 			cp->sec_target = sdp->sed_target;
128453dbcc59SSundeep Panicker 		}
128553dbcc59SSundeep Panicker 	}
128653dbcc59SSundeep Panicker 
128753dbcc59SSundeep Panicker 	topo_list_append(&cp->sec_targets, sdp->sed_target);
128853dbcc59SSundeep Panicker 	sdp->sed_current = cp;
128953dbcc59SSundeep Panicker 
129053dbcc59SSundeep Panicker 	return (0);
129153dbcc59SSundeep Panicker }
129253dbcc59SSundeep Panicker 
1293940d71d2Seschrock /*
1294940d71d2Seschrock  * Gather nodes from the current SES target into our chassis list, merging the
1295940d71d2Seschrock  * results if necessary.
1296940d71d2Seschrock  */
1297940d71d2Seschrock static ses_walk_action_t
1298940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data)
1299940d71d2Seschrock {
1300940d71d2Seschrock 	nvlist_t *props = ses_node_props(np);
1301940d71d2Seschrock 	ses_enum_data_t *sdp = data;
1302940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
130353dbcc59SSundeep Panicker 	ses_enum_chassis_t *cp, *scp;
1304940d71d2Seschrock 	ses_enum_node_t *snp;
13050b32bb8bSEric Schrock 	ses_alt_node_t *sap;
1306940d71d2Seschrock 	char *csn;
1307940d71d2Seschrock 	uint64_t instance, type;
1308d91236feSeschrock 	uint64_t prevstatus, status;
130953dbcc59SSundeep Panicker 	boolean_t report;
131053dbcc59SSundeep Panicker 	boolean_t have_subchassis = B_TRUE;
131153dbcc59SSundeep Panicker 	char *lid;
1312940d71d2Seschrock 
1313940d71d2Seschrock 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
1314940d71d2Seschrock 		/*
1315940d71d2Seschrock 		 * If we have already identified the chassis for this target,
1316940d71d2Seschrock 		 * then this is a secondary enclosure and we should ignore it,
1317940d71d2Seschrock 		 * along with the rest of the tree (since this is depth-first).
1318940d71d2Seschrock 		 */
1319940d71d2Seschrock 		if (sdp->sed_current != NULL)
1320940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1321940d71d2Seschrock 
1322940d71d2Seschrock 		/*
1323940d71d2Seschrock 		 * Go through the list of chassis we have seen so far and see
1324940d71d2Seschrock 		 * if this serial number matches one of the known values.
132553dbcc59SSundeep Panicker 		 * If so, check whether this enclosure is a subchassis.
1326940d71d2Seschrock 		 */
1327940d71d2Seschrock 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
1328940d71d2Seschrock 		    &csn) != 0)
1329940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1330940d71d2Seschrock 
133153dbcc59SSundeep Panicker 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
133253dbcc59SSundeep Panicker 		    &lid) != 0) {
133353dbcc59SSundeep Panicker 			have_subchassis = B_FALSE;
1334480c3503SEric Schrock 			lid = "";
133553dbcc59SSundeep Panicker 		}
133653dbcc59SSundeep Panicker 
133753dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
133853dbcc59SSundeep Panicker 		    "CSN (%s), LID (%s)", sdp->sed_name, csn, lid);
133953dbcc59SSundeep Panicker 
134053dbcc59SSundeep Panicker 		/*
134153dbcc59SSundeep Panicker 		 * We need to determine whether this enclosure node
134253dbcc59SSundeep Panicker 		 * represents a chassis or a subchassis. Since we may
134353dbcc59SSundeep Panicker 		 * receive the enclosure nodes in a non-deterministic
134453dbcc59SSundeep Panicker 		 * manner, we need to account for all possible combinations:
134553dbcc59SSundeep Panicker 		 *	1. Chassis for the current CSN has not yet been
134653dbcc59SSundeep Panicker 		 *	   allocated
134753dbcc59SSundeep Panicker 		 *		1.1 This is a new chassis:
134853dbcc59SSundeep Panicker 		 *			allocate and instantiate the chassis
134953dbcc59SSundeep Panicker 		 *		1.2 This is a new subchassis:
135053dbcc59SSundeep Panicker 		 *			allocate a placeholder chassis
135153dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
135253dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
135353dbcc59SSundeep Panicker 		 *	2. Chassis for the current CSN has been allocated
135453dbcc59SSundeep Panicker 		 *		2.1 This is a duplicate chassis enclosure
135553dbcc59SSundeep Panicker 		 *			check whether to override old chassis
135653dbcc59SSundeep Panicker 		 *			append to chassis' target list
135753dbcc59SSundeep Panicker 		 *		2.2 Only placeholder chassis exists
135853dbcc59SSundeep Panicker 		 *			fill in the chassis fields
135953dbcc59SSundeep Panicker 		 *		2.3 This is a new subchassis
136053dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
136153dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
136253dbcc59SSundeep Panicker 		 *		2.4 This is a duplicate subchassis enclosure
136353dbcc59SSundeep Panicker 		 *			 check whether to override old chassis
136453dbcc59SSundeep Panicker 		 *			 append to chassis' target list
136553dbcc59SSundeep Panicker 		 */
136653dbcc59SSundeep Panicker 
1367940d71d2Seschrock 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
136853dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
136953dbcc59SSundeep Panicker 			if (strcmp(cp->sec_csn, csn) == 0)
1370940d71d2Seschrock 				break;
1371940d71d2Seschrock 
1372940d71d2Seschrock 		if (cp == NULL) {
137353dbcc59SSundeep Panicker 			/* 1. Haven't seen a chassis with this CSN before */
1374940d71d2Seschrock 
1375940d71d2Seschrock 			if ((cp = topo_mod_zalloc(mod,
1376940d71d2Seschrock 			    sizeof (ses_enum_chassis_t))) == NULL)
1377940d71d2Seschrock 				goto error;
1378940d71d2Seschrock 
1379940d71d2Seschrock 			cp->sec_csn = csn;
138053dbcc59SSundeep Panicker 
138153dbcc59SSundeep Panicker 			if (!have_subchassis || strcmp(csn, lid) == 0) {
138253dbcc59SSundeep Panicker 				/* 1.1 This is a new chassis */
138353dbcc59SSundeep Panicker 
138453dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
138553dbcc59SSundeep Panicker 				    "chassis with CSN (%s) and LID (%s)",
138653dbcc59SSundeep Panicker 				    sdp->sed_name, csn, lid);
138753dbcc59SSundeep Panicker 
138853dbcc59SSundeep Panicker 				if (ses_init_chassis(mod, sdp, NULL, cp,
138953dbcc59SSundeep Panicker 				    np, props, lid, SES_NEW_CHASSIS) < 0)
139053dbcc59SSundeep Panicker 					goto error;
139153dbcc59SSundeep Panicker 			} else {
139253dbcc59SSundeep Panicker 				/* 1.2 This is a new subchassis */
139353dbcc59SSundeep Panicker 
139453dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
139553dbcc59SSundeep Panicker 				    "subchassis with CSN (%s) and LID (%s)",
139653dbcc59SSundeep Panicker 				    sdp->sed_name, csn, lid);
139753dbcc59SSundeep Panicker 
139853dbcc59SSundeep Panicker 				if ((scp = topo_mod_zalloc(mod,
139953dbcc59SSundeep Panicker 				    sizeof (ses_enum_chassis_t))) == NULL)
140053dbcc59SSundeep Panicker 					goto error;
140153dbcc59SSundeep Panicker 
140253dbcc59SSundeep Panicker 				scp->sec_csn = csn;
140353dbcc59SSundeep Panicker 
140453dbcc59SSundeep Panicker 				if (ses_init_chassis(mod, sdp, cp, scp,
140553dbcc59SSundeep Panicker 				    np, props, lid, SES_NEW_SUBCHASSIS) < 0)
140653dbcc59SSundeep Panicker 					goto error;
140753dbcc59SSundeep Panicker 			}
14086efb64caSEric Schrock 		} else {
140953dbcc59SSundeep Panicker 			/* 2. We have a chassis with this CSN */
141053dbcc59SSundeep Panicker 
141153dbcc59SSundeep Panicker 			if (!have_subchassis || strcmp(csn, lid) == 0) {
141253dbcc59SSundeep Panicker 				/* This is a chassis */
141353dbcc59SSundeep Panicker 
141453dbcc59SSundeep Panicker 				if (!have_subchassis ||
141553dbcc59SSundeep Panicker 				    strlen(cp->sec_lid) > 0) {
141653dbcc59SSundeep Panicker 					/* 2.1 This is a duplicate chassis */
141753dbcc59SSundeep Panicker 
141853dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
141953dbcc59SSundeep Panicker 					    "duplicate chassis with CSN (%s) "
142053dbcc59SSundeep Panicker 					    "and LID (%s)",
142153dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
142253dbcc59SSundeep Panicker 
142353dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, NULL, cp,
142453dbcc59SSundeep Panicker 					    np, props, lid,
142553dbcc59SSundeep Panicker 					    SES_DUP_CHASSIS) < 0)
142653dbcc59SSundeep Panicker 						goto error;
142753dbcc59SSundeep Panicker 				} else {
142853dbcc59SSundeep Panicker 					/* 2.2 Init the placeholder chassis */
142953dbcc59SSundeep Panicker 
143053dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize"
143153dbcc59SSundeep Panicker 					    "placeholder chassis with CSN (%s) "
143253dbcc59SSundeep Panicker 					    "and LID (%s)",
143353dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
143453dbcc59SSundeep Panicker 
143553dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, NULL, cp,
143653dbcc59SSundeep Panicker 					    np, props, lid,
143753dbcc59SSundeep Panicker 					    SES_NEW_CHASSIS) < 0)
143853dbcc59SSundeep Panicker 						goto error;
143953dbcc59SSundeep Panicker 
144053dbcc59SSundeep Panicker 				}
144153dbcc59SSundeep Panicker 			} else {
144253dbcc59SSundeep Panicker 				/* This is a subchassis */
144353dbcc59SSundeep Panicker 
144453dbcc59SSundeep Panicker 				for (scp = topo_list_next(&cp->sec_subchassis);
144553dbcc59SSundeep Panicker 				    scp != NULL; scp = topo_list_next(scp))
144653dbcc59SSundeep Panicker 					if (strcmp(scp->sec_lid, lid) == 0)
144753dbcc59SSundeep Panicker 						break;
144853dbcc59SSundeep Panicker 
144953dbcc59SSundeep Panicker 				if (scp == NULL) {
145053dbcc59SSundeep Panicker 					/* 2.3 This is a new subchassis */
145153dbcc59SSundeep Panicker 
145253dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize "
145353dbcc59SSundeep Panicker 					    "new subchassis with CSN (%s) "
145453dbcc59SSundeep Panicker 					    "and LID (%s)",
145553dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
145653dbcc59SSundeep Panicker 
145753dbcc59SSundeep Panicker 					if ((scp = topo_mod_zalloc(mod,
145853dbcc59SSundeep Panicker 					    sizeof (ses_enum_chassis_t)))
145953dbcc59SSundeep Panicker 					    == NULL)
146053dbcc59SSundeep Panicker 						goto error;
146153dbcc59SSundeep Panicker 
146253dbcc59SSundeep Panicker 					scp->sec_csn = csn;
146353dbcc59SSundeep Panicker 
146453dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
146553dbcc59SSundeep Panicker 					    np, props, lid,
146653dbcc59SSundeep Panicker 					    SES_NEW_SUBCHASSIS) < 0)
146753dbcc59SSundeep Panicker 						goto error;
146853dbcc59SSundeep Panicker 				} else {
146953dbcc59SSundeep Panicker 					/* 2.4 This is a duplicate subchassis */
147053dbcc59SSundeep Panicker 
147153dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
147253dbcc59SSundeep Panicker 					    "duplicate subchassis with "
147353dbcc59SSundeep Panicker 					    "CSN (%s) and LID (%s)",
147453dbcc59SSundeep Panicker 					    sdp->sed_name, csn, lid);
147553dbcc59SSundeep Panicker 
147653dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
147753dbcc59SSundeep Panicker 					    np, props, lid,
147853dbcc59SSundeep Panicker 					    SES_DUP_SUBCHASSIS) < 0)
147953dbcc59SSundeep Panicker 						goto error;
148053dbcc59SSundeep Panicker 				}
14816efb64caSEric Schrock 			}
1482940d71d2Seschrock 		}
1483940d71d2Seschrock 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
1484940d71d2Seschrock 		/*
1485940d71d2Seschrock 		 * If we haven't yet seen an enclosure node and identified the
1486940d71d2Seschrock 		 * current chassis, something is very wrong; bail out.
1487940d71d2Seschrock 		 */
1488940d71d2Seschrock 		if (sdp->sed_current == NULL)
1489940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
1490940d71d2Seschrock 
1491940d71d2Seschrock 		/*
1492940d71d2Seschrock 		 * If this isn't one of the element types we care about, then
1493940d71d2Seschrock 		 * ignore it.
1494940d71d2Seschrock 		 */
1495940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
1496940d71d2Seschrock 		    &type) == 0);
1497940d71d2Seschrock 		if (type != SES_ET_DEVICE &&
1498940d71d2Seschrock 		    type != SES_ET_ARRAY_DEVICE &&
1499940d71d2Seschrock 		    type != SES_ET_COOLING &&
1500940d71d2Seschrock 		    type != SES_ET_POWER_SUPPLY &&
1501940d71d2Seschrock 		    type != SES_ET_ESC_ELECTRONICS)
1502940d71d2Seschrock 			return (SES_WALK_ACTION_CONTINUE);
1503940d71d2Seschrock 
1504940d71d2Seschrock 		/*
1505940d71d2Seschrock 		 * Get the current instance number and see if we already know
1506940d71d2Seschrock 		 * about this element.  If so, it means we have multiple paths
1507940d71d2Seschrock 		 * to the same elements, and we should ignore the current path.
1508940d71d2Seschrock 		 */
1509940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
1510940d71d2Seschrock 		    &instance) == 0);
1511940d71d2Seschrock 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
1512940d71d2Seschrock 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
1513940d71d2Seschrock 			    &instance);
1514940d71d2Seschrock 
1515940d71d2Seschrock 		cp = sdp->sed_current;
1516940d71d2Seschrock 
1517940d71d2Seschrock 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1518940d71d2Seschrock 		    snp = topo_list_next(snp)) {
1519940d71d2Seschrock 			if (snp->sen_type == type &&
1520940d71d2Seschrock 			    snp->sen_instance == instance)
1521d91236feSeschrock 				break;
1522d91236feSeschrock 		}
1523d91236feSeschrock 
1524d91236feSeschrock 		/*
1525d91236feSeschrock 		 * We prefer the new element under the following circumstances:
1526d91236feSeschrock 		 *
1527d91236feSeschrock 		 * - The currently known element's status is unknown or not
1528d91236feSeschrock 		 *   available, but the new element has a known status.  This
1529d91236feSeschrock 		 *   occurs if a given element is only available through a
1530d91236feSeschrock 		 *   particular target.
1531d91236feSeschrock 		 *
1532d91236feSeschrock 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
1533d91236feSeschrock 		 *   property is set.  This allows us to get reliable firmware
1534d91236feSeschrock 		 *   revision information from the enclosure node.
1535d91236feSeschrock 		 */
1536d91236feSeschrock 		if (snp != NULL) {
1537d91236feSeschrock 			if (nvlist_lookup_uint64(
1538d91236feSeschrock 			    ses_node_props(snp->sen_node),
1539d91236feSeschrock 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
1540d91236feSeschrock 				prevstatus = SES_ESC_UNSUPPORTED;
1541d91236feSeschrock 			if (nvlist_lookup_uint64(
1542d91236feSeschrock 			    props, SES_PROP_STATUS_CODE, &status) != 0)
1543d91236feSeschrock 				status = SES_ESC_UNSUPPORTED;
1544d91236feSeschrock 			if (nvlist_lookup_boolean_value(
1545d91236feSeschrock 			    props, SES_PROP_REPORT, &report) != 0)
1546d91236feSeschrock 				report = B_FALSE;
1547d91236feSeschrock 
1548d91236feSeschrock 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
1549d91236feSeschrock 			    !SES_STATUS_UNAVAIL(status)) ||
1550d91236feSeschrock 			    (type == SES_ET_ESC_ELECTRONICS &&
1551d91236feSeschrock 			    report)) {
1552d91236feSeschrock 				snp->sen_node = np;
1553d91236feSeschrock 				snp->sen_target = sdp->sed_target;
1554d91236feSeschrock 			}
1555d91236feSeschrock 
15560b32bb8bSEric Schrock 			if ((sap = topo_mod_zalloc(mod,
15570b32bb8bSEric Schrock 			    sizeof (ses_alt_node_t))) == NULL)
15580b32bb8bSEric Schrock 				goto error;
15590b32bb8bSEric Schrock 
15600b32bb8bSEric Schrock 			sap->san_node = np;
15610b32bb8bSEric Schrock 			topo_list_append(&snp->sen_alt_nodes, sap);
15620b32bb8bSEric Schrock 
1563d91236feSeschrock 			return (SES_WALK_ACTION_CONTINUE);
1564940d71d2Seschrock 		}
1565940d71d2Seschrock 
1566940d71d2Seschrock 		if ((snp = topo_mod_zalloc(mod,
1567940d71d2Seschrock 		    sizeof (ses_enum_node_t))) == NULL)
1568940d71d2Seschrock 			goto error;
1569940d71d2Seschrock 
15700b32bb8bSEric Schrock 		if ((sap = topo_mod_zalloc(mod,
15710b32bb8bSEric Schrock 		    sizeof (ses_alt_node_t))) == NULL) {
15720b32bb8bSEric Schrock 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
15730b32bb8bSEric Schrock 			goto error;
15740b32bb8bSEric Schrock 		}
15750b32bb8bSEric Schrock 
1576940d71d2Seschrock 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
1577940d71d2Seschrock 		    sdp->sed_name, type, instance);
1578940d71d2Seschrock 		snp->sen_node = np;
1579940d71d2Seschrock 		snp->sen_type = type;
1580940d71d2Seschrock 		snp->sen_instance = instance;
1581940d71d2Seschrock 		snp->sen_target = sdp->sed_target;
15820b32bb8bSEric Schrock 		sap->san_node = np;
15830b32bb8bSEric Schrock 		topo_list_append(&snp->sen_alt_nodes, sap);
1584940d71d2Seschrock 		topo_list_append(&cp->sec_nodes, snp);
1585940d71d2Seschrock 
1586940d71d2Seschrock 		if (type == SES_ET_DEVICE)
1587940d71d2Seschrock 			cp->sec_hasdev = B_TRUE;
1588940d71d2Seschrock 	}
1589940d71d2Seschrock 
1590940d71d2Seschrock 	return (SES_WALK_ACTION_CONTINUE);
1591940d71d2Seschrock 
1592940d71d2Seschrock error:
1593940d71d2Seschrock 	sdp->sed_errno = -1;
1594940d71d2Seschrock 	return (SES_WALK_ACTION_TERMINATE);
1595940d71d2Seschrock }
1596940d71d2Seschrock 
1597940d71d2Seschrock static int
1598940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
1599940d71d2Seschrock {
1600940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1601940d71d2Seschrock 	DIR *dir;
1602940d71d2Seschrock 	struct dirent *dp;
1603940d71d2Seschrock 	char path[PATH_MAX];
1604940d71d2Seschrock 	ses_enum_target_t *stp;
1605940d71d2Seschrock 	int err = -1;
1606940d71d2Seschrock 
1607940d71d2Seschrock 	/*
1608940d71d2Seschrock 	 * Open the SES target directory and iterate over any available
1609940d71d2Seschrock 	 * targets.
1610940d71d2Seschrock 	 */
1611940d71d2Seschrock 	if ((dir = opendir(dirpath)) == NULL) {
1612940d71d2Seschrock 		/*
1613940d71d2Seschrock 		 * If the SES target directory does not exist, then return as if
1614940d71d2Seschrock 		 * there are no active targets.
1615940d71d2Seschrock 		 */
1616940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to open ses "
1617940d71d2Seschrock 		    "directory '%s'", dirpath);
1618940d71d2Seschrock 		return (0);
1619940d71d2Seschrock 	}
1620940d71d2Seschrock 
1621940d71d2Seschrock 	while ((dp = readdir(dir)) != NULL) {
1622940d71d2Seschrock 		if (strcmp(dp->d_name, ".") == 0 ||
1623940d71d2Seschrock 		    strcmp(dp->d_name, "..") == 0)
1624940d71d2Seschrock 			continue;
1625940d71d2Seschrock 
1626940d71d2Seschrock 		/*
1627940d71d2Seschrock 		 * Create a new target instance and take a snapshot.
1628940d71d2Seschrock 		 */
1629940d71d2Seschrock 		if ((stp = topo_mod_zalloc(mod,
1630940d71d2Seschrock 		    sizeof (ses_enum_target_t))) == NULL)
1631940d71d2Seschrock 			goto error;
1632940d71d2Seschrock 
16330b32bb8bSEric Schrock 		(void) pthread_mutex_init(&stp->set_lock, NULL);
16340b32bb8bSEric Schrock 
1635940d71d2Seschrock 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
1636940d71d2Seschrock 		    dp->d_name);
1637940d71d2Seschrock 
1638940d71d2Seschrock 		/*
1639940d71d2Seschrock 		 * We keep track of the SES device path and export it on a
1640940d71d2Seschrock 		 * per-node basis to allow higher level software to get to the
1641940d71d2Seschrock 		 * corresponding SES state.
1642940d71d2Seschrock 		 */
1643940d71d2Seschrock 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
1644940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1645940d71d2Seschrock 			goto error;
1646940d71d2Seschrock 		}
1647940d71d2Seschrock 
1648940d71d2Seschrock 		if ((stp->set_target =
1649940d71d2Seschrock 		    ses_open(LIBSES_VERSION, path)) == NULL) {
1650940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to open ses target "
1651940d71d2Seschrock 			    "'%s': %s", dp->d_name, ses_errmsg());
1652940d71d2Seschrock 			topo_mod_strfree(mod, stp->set_devpath);
1653940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
1654940d71d2Seschrock 			continue;
1655940d71d2Seschrock 		}
1656940d71d2Seschrock 
1657940d71d2Seschrock 		stp->set_refcount = 1;
1658940d71d2Seschrock 		sdp->sed_target = stp;
1659940d71d2Seschrock 		stp->set_snap = ses_snap_hold(stp->set_target);
1660525b85dbSEric Schrock 		stp->set_snaptime = gethrtime();
1661940d71d2Seschrock 
1662940d71d2Seschrock 		/*
1663940d71d2Seschrock 		 * Enumerate over all SES elements and merge them into the
1664940d71d2Seschrock 		 * correct ses_enum_chassis_t.
1665940d71d2Seschrock 		 */
1666940d71d2Seschrock 		sdp->sed_current = NULL;
1667940d71d2Seschrock 		sdp->sed_errno = 0;
1668940d71d2Seschrock 		sdp->sed_name = dp->d_name;
1669940d71d2Seschrock 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
1670940d71d2Seschrock 
1671940d71d2Seschrock 		if (sdp->sed_errno != 0)
1672940d71d2Seschrock 			goto error;
1673940d71d2Seschrock 	}
1674940d71d2Seschrock 
1675940d71d2Seschrock 	err = 0;
1676940d71d2Seschrock error:
1677940d71d2Seschrock 	closedir(dir);
1678940d71d2Seschrock 	return (err);
1679940d71d2Seschrock }
1680940d71d2Seschrock 
1681940d71d2Seschrock static void
1682940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn)
1683940d71d2Seschrock {
1684940d71d2Seschrock 	ses_enum_target_t *stp;
1685940d71d2Seschrock 
1686940d71d2Seschrock 	if ((stp = topo_node_getspecific(tn)) != NULL)
1687940d71d2Seschrock 		ses_target_free(mod, stp);
1688940d71d2Seschrock }
1689940d71d2Seschrock 
1690940d71d2Seschrock /*ARGSUSED*/
1691940d71d2Seschrock static int
1692940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
1693940d71d2Seschrock     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
1694940d71d2Seschrock {
1695940d71d2Seschrock 	ses_enum_chassis_t *cp;
1696d91236feSeschrock 	ses_enum_data_t *data;
1697940d71d2Seschrock 
1698940d71d2Seschrock 	/*
1699940d71d2Seschrock 	 * Check to make sure we're being invoked sensibly, and that we're not
1700940d71d2Seschrock 	 * being invoked as part of a post-processing step.
1701940d71d2Seschrock 	 */
1702d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
1703940d71d2Seschrock 		return (0);
1704940d71d2Seschrock 
1705940d71d2Seschrock 	/*
1706d91236feSeschrock 	 * If this is the first time we've called our enumeration method, then
1707d91236feSeschrock 	 * gather information about any available enclosures.
1708940d71d2Seschrock 	 */
1709d91236feSeschrock 	if ((data = topo_mod_getspecific(mod)) == NULL) {
1710d91236feSeschrock 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
1711d91236feSeschrock 		    NULL)
1712d91236feSeschrock 			return (-1);
1713940d71d2Seschrock 
1714d91236feSeschrock 		data->sed_mod = mod;
1715d91236feSeschrock 		topo_mod_setspecific(mod, data);
1716d91236feSeschrock 
1717d91236feSeschrock 		if (disk_list_gather(mod, &data->sed_disks) != 0)
1718d91236feSeschrock 			goto error;
1719d91236feSeschrock 
1720d91236feSeschrock 		/*
1721d91236feSeschrock 		 * We search both the ses(7D) and sgen(7D) locations, so we are
1722d91236feSeschrock 		 * independent of any particular driver class bindings.
1723d91236feSeschrock 		 */
1724d91236feSeschrock 		if (ses_process_dir("/dev/es", data) != 0 ||
1725d91236feSeschrock 		    ses_process_dir("/dev/scsi/ses", data) != 0)
1726d91236feSeschrock 			goto error;
1727d91236feSeschrock 	}
1728d91236feSeschrock 
1729d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
1730d91236feSeschrock 		/*
1731d91236feSeschrock 		 * This is a request to enumerate external enclosures.  Go
1732d91236feSeschrock 		 * through all the targets and create chassis nodes where
1733d91236feSeschrock 		 * necessary.
1734d91236feSeschrock 		 */
1735d91236feSeschrock 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
1736d91236feSeschrock 		    cp = topo_list_next(cp)) {
1737d91236feSeschrock 			if (ses_create_chassis(data, rnode, cp) != 0)
1738d91236feSeschrock 				goto error;
1739d91236feSeschrock 		}
1740d91236feSeschrock 	} else {
1741d91236feSeschrock 		/*
1742d91236feSeschrock 		 * This is a request to enumerate a specific bay underneath the
1743d91236feSeschrock 		 * root chassis (for internal disks).
1744d91236feSeschrock 		 */
1745d91236feSeschrock 		if (ses_create_bays(data, rnode) != 0)
1746940d71d2Seschrock 			goto error;
1747940d71d2Seschrock 	}
1748940d71d2Seschrock 
1749d91236feSeschrock 	/*
1750d91236feSeschrock 	 * This is a bit of a kludge.  In order to allow internal disks to be
1751d91236feSeschrock 	 * enumerated and share snapshot-specific information with the external
1752d91236feSeschrock 	 * enclosure enumeration, we rely on the fact that we will be invoked
1753d91236feSeschrock 	 * for the 'ses-enclosure' node last.
1754d91236feSeschrock 	 */
1755d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
175653dbcc59SSundeep Panicker 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
175753dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
175853dbcc59SSundeep Panicker 			ses_data_free(data, cp);
175953dbcc59SSundeep Panicker 		ses_data_free(data, NULL);
1760d91236feSeschrock 		topo_mod_setspecific(mod, NULL);
1761d91236feSeschrock 	}
1762940d71d2Seschrock 	return (0);
1763940d71d2Seschrock 
1764940d71d2Seschrock error:
176553dbcc59SSundeep Panicker 	for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
176653dbcc59SSundeep Panicker 	    cp = topo_list_next(cp))
176753dbcc59SSundeep Panicker 		ses_data_free(data, cp);
176853dbcc59SSundeep Panicker 	ses_data_free(data, NULL);
1769d91236feSeschrock 	topo_mod_setspecific(mod, NULL);
1770940d71d2Seschrock 	return (-1);
1771940d71d2Seschrock }
1772940d71d2Seschrock 
1773940d71d2Seschrock static const topo_modops_t ses_ops =
1774940d71d2Seschrock 	{ ses_enum, ses_release };
1775940d71d2Seschrock 
1776940d71d2Seschrock static topo_modinfo_t ses_info =
1777940d71d2Seschrock 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
1778940d71d2Seschrock 
1779940d71d2Seschrock /*ARGSUSED*/
1780940d71d2Seschrock int
1781940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version)
1782940d71d2Seschrock {
1783940d71d2Seschrock 	if (getenv("TOPOSESDEBUG") != NULL)
1784940d71d2Seschrock 		topo_mod_setdebug(mod);
1785940d71d2Seschrock 
1786940d71d2Seschrock 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
1787940d71d2Seschrock 	    SES_ENCLOSURE);
1788940d71d2Seschrock 
1789940d71d2Seschrock 	return (topo_mod_register(mod, &ses_info, TOPO_VERSION));
1790940d71d2Seschrock }
1791940d71d2Seschrock 
1792940d71d2Seschrock void
1793940d71d2Seschrock _topo_fini(topo_mod_t *mod)
1794940d71d2Seschrock {
1795940d71d2Seschrock 	topo_mod_unregister(mod);
1796940d71d2Seschrock }
1797