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 /*
2344ed9dbbSStephen 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>
33*ac88567aSHyon Kim #include <string.h>
34940d71d2Seschrock #include <unistd.h>
35940d71d2Seschrock #include <sys/dkio.h>
36940d71d2Seschrock #include <sys/fm/protocol.h>
3744ed9dbbSStephen Hanson #include <sys/libdevid.h>
38940d71d2Seschrock #include <sys/scsi/scsi_types.h>
39*ac88567aSHyon Kim #include <sys/byteorder.h>
40d91236feSeschrock 
41940d71d2Seschrock #include "disk.h"
42d91236feSeschrock #include "ses.h"
43940d71d2Seschrock 
44940d71d2Seschrock #define	SES_VERSION	1
45940d71d2Seschrock 
46*ac88567aSHyon Kim #define	SES_STARTING_SUBCHASSIS 256	/* valid subchassis IDs are uint8_t */
47*ac88567aSHyon Kim #define	NO_SUBCHASSIS	((uint64_t)-1)
48*ac88567aSHyon Kim 
49525b85dbSEric Schrock static int ses_snap_freq = 250;		/* in milliseconds */
50940d71d2Seschrock 
51d91236feSeschrock #define	SES_STATUS_UNAVAIL(s)	\
520b32bb8bSEric Schrock 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
53940d71d2Seschrock 
54940d71d2Seschrock /*
55940d71d2Seschrock  * Because multiple SES targets can be part of a single chassis, we construct
56940d71d2Seschrock  * our own hierarchy that takes this into account.  These SES targets may refer
57940d71d2Seschrock  * to the same devices (multiple paths) or to different devices (managing
58940d71d2Seschrock  * different portions of the space).  We arrange things into a
59940d71d2Seschrock  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
60940d71d2Seschrock  * nodes found so far.
61940d71d2Seschrock  */
620b32bb8bSEric Schrock typedef struct ses_alt_node {
630b32bb8bSEric Schrock 	topo_list_t		san_link;
640b32bb8bSEric Schrock 	ses_node_t		*san_node;
650b32bb8bSEric Schrock } ses_alt_node_t;
66940d71d2Seschrock 
67940d71d2Seschrock typedef struct ses_enum_node {
68940d71d2Seschrock 	topo_list_t		sen_link;
69940d71d2Seschrock 	ses_node_t		*sen_node;
700b32bb8bSEric Schrock 	topo_list_t		sen_alt_nodes;
71940d71d2Seschrock 	uint64_t		sen_type;
72940d71d2Seschrock 	uint64_t		sen_instance;
73940d71d2Seschrock 	ses_enum_target_t	*sen_target;
74940d71d2Seschrock } ses_enum_node_t;
75940d71d2Seschrock 
76940d71d2Seschrock typedef struct ses_enum_chassis {
77940d71d2Seschrock 	topo_list_t		sec_link;
7853dbcc59SSundeep Panicker 	topo_list_t		sec_subchassis;
79940d71d2Seschrock 	topo_list_t		sec_nodes;
80940d71d2Seschrock 	topo_list_t		sec_targets;
81940d71d2Seschrock 	const char		*sec_csn;
82940d71d2Seschrock 	ses_node_t		*sec_enclosure;
83940d71d2Seschrock 	ses_enum_target_t	*sec_target;
84940d71d2Seschrock 	topo_instance_t		sec_instance;
8553dbcc59SSundeep Panicker 	topo_instance_t		sec_scinstance;
86*ac88567aSHyon Kim 	topo_instance_t		sec_maxinstance;
87940d71d2Seschrock 	boolean_t		sec_hasdev;
88d91236feSeschrock 	boolean_t		sec_internal;
89940d71d2Seschrock } ses_enum_chassis_t;
90940d71d2Seschrock 
91940d71d2Seschrock typedef struct ses_enum_data {
92*ac88567aSHyon Kim 	topo_list_t		sed_devs;
93940d71d2Seschrock 	topo_list_t		sed_chassis;
94940d71d2Seschrock 	ses_enum_chassis_t	*sed_current;
95940d71d2Seschrock 	ses_enum_target_t	*sed_target;
96940d71d2Seschrock 	int			sed_errno;
97940d71d2Seschrock 	char			*sed_name;
98940d71d2Seschrock 	topo_mod_t		*sed_mod;
99940d71d2Seschrock 	topo_instance_t		sed_instance;
100940d71d2Seschrock } ses_enum_data_t;
101940d71d2Seschrock 
102*ac88567aSHyon Kim typedef struct sas_connector_phy_data {
103*ac88567aSHyon Kim 	uint64_t    index;
104*ac88567aSHyon Kim 	uint64_t    phy_mask;
105*ac88567aSHyon Kim } sas_connector_phy_data_t;
106*ac88567aSHyon Kim 
107*ac88567aSHyon Kim typedef struct sas_connector_type {
108*ac88567aSHyon Kim 	uint64_t    type;
109*ac88567aSHyon Kim 	char	    *name;
110*ac88567aSHyon Kim } sas_connector_type_t;
111*ac88567aSHyon Kim 
112*ac88567aSHyon Kim static const sas_connector_type_t sas_connector_type_list[] = {
113*ac88567aSHyon Kim 	{   0x0, "Information unknown"  },
114*ac88567aSHyon Kim 	{   0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)"	},
115*ac88567aSHyon Kim 	{   0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
116*ac88567aSHyon Kim 	{   0xF, "Vendor-specific external connector"	},
117*ac88567aSHyon Kim 	{   0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)"	},
118*ac88567aSHyon Kim 	{   0x11,
119*ac88567aSHyon Kim 	"Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)"	},
120*ac88567aSHyon Kim 	{   0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)"  },
121*ac88567aSHyon Kim 	{   0x21, "Internal SATA host plug (see SAS-2 and SATA-2)"  },
122*ac88567aSHyon Kim 	{   0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)"	},
123*ac88567aSHyon Kim 	{   0x23, "Internal SATA device plug (see SAS-2 and SATA-2)"	},
124*ac88567aSHyon Kim 	{   0x2F, "Internal SAS virtual connector"  },
125*ac88567aSHyon Kim 	{   0x3F, "Vendor-specific internal connector"	},
126*ac88567aSHyon Kim 	{   0x70, "Other Vendor-specific connector"	},
127*ac88567aSHyon Kim 	{   0x71, "Other Vendor-specific connector"	},
128*ac88567aSHyon Kim 	{   0x72, "Other Vendor-specific connector"	},
129*ac88567aSHyon Kim 	{   0x73, "Other Vendor-specific connector"	},
130*ac88567aSHyon Kim 	{   0x74, "Other Vendor-specific connector"	},
131*ac88567aSHyon Kim 	{   0x75, "Other Vendor-specific connector"	},
132*ac88567aSHyon Kim 	{   0x76, "Other Vendor-specific connector"	},
133*ac88567aSHyon Kim 	{   0x77, "Other Vendor-specific connector"	},
134*ac88567aSHyon Kim 	{   0x78, "Other Vendor-specific connector"	},
135*ac88567aSHyon Kim 	{   0x79, "Other Vendor-specific connector"	},
136*ac88567aSHyon Kim 	{   0x7A, "Other Vendor-specific connector"	},
137*ac88567aSHyon Kim 	{   0x7B, "Other Vendor-specific connector"	},
138*ac88567aSHyon Kim 	{   0x7C, "Other Vendor-specific connector"	},
139*ac88567aSHyon Kim 	{   0x7D, "Other Vendor-specific connector"	},
140*ac88567aSHyon Kim 	{   0x7E, "Other Vendor-specific connector"	},
141*ac88567aSHyon Kim 	{   0x7F, "Other Vendor-specific connector"	},
142*ac88567aSHyon Kim 	{   0x80, "Not Defined"	}
143*ac88567aSHyon Kim };
144*ac88567aSHyon Kim 
145*ac88567aSHyon Kim #define	SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED  0x80
146*ac88567aSHyon Kim #define	SAS_CONNECTOR_TYPE_NOT_DEFINED \
147*ac88567aSHyon Kim 	"Connector type not definedi by SES-2 standard"
148*ac88567aSHyon Kim #define	SAS_CONNECTOR_TYPE_RESERVED \
149*ac88567aSHyon Kim 	"Connector type reserved by SES-2 standard"
150*ac88567aSHyon Kim 
15153dbcc59SSundeep Panicker typedef enum {
15253dbcc59SSundeep Panicker 	SES_NEW_CHASSIS		= 0x1,
15353dbcc59SSundeep Panicker 	SES_NEW_SUBCHASSIS	= 0x2,
15453dbcc59SSundeep Panicker 	SES_DUP_CHASSIS		= 0x4,
15553dbcc59SSundeep Panicker 	SES_DUP_SUBCHASSIS	= 0x8
15653dbcc59SSundeep Panicker } ses_chassis_type_e;
15753dbcc59SSundeep Panicker 
158*ac88567aSHyon Kim static const topo_pgroup_info_t io_pgroup = {
159*ac88567aSHyon Kim 	TOPO_PGROUP_IO,
160*ac88567aSHyon Kim 	TOPO_STABILITY_PRIVATE,
161*ac88567aSHyon Kim 	TOPO_STABILITY_PRIVATE,
162*ac88567aSHyon Kim 	1
163*ac88567aSHyon Kim };
164*ac88567aSHyon Kim 
165*ac88567aSHyon Kim static const topo_pgroup_info_t storage_pgroup = {
166*ac88567aSHyon Kim 	TOPO_PGROUP_STORAGE,
167*ac88567aSHyon Kim 	TOPO_STABILITY_PRIVATE,
168*ac88567aSHyon Kim 	TOPO_STABILITY_PRIVATE,
169*ac88567aSHyon Kim 	1
170*ac88567aSHyon Kim };
171*ac88567aSHyon Kim 
172940d71d2Seschrock static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
173940d71d2Seschrock     nvlist_t **);
174940d71d2Seschrock static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
175940d71d2Seschrock     nvlist_t **);
176940d71d2Seschrock 
177940d71d2Seschrock static const topo_method_t ses_component_methods[] = {
178940d71d2Seschrock 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
179940d71d2Seschrock 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
180d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
181d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
18288045cffSRobert Johnston 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
18388045cffSRobert Johnston 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
18488045cffSRobert Johnston 	    topo_method_sensor_failure },
185d91236feSeschrock 	{ NULL }
186d91236feSeschrock };
187d91236feSeschrock 
188d91236feSeschrock static const topo_method_t ses_bay_methods[] = {
189d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
190d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
191940d71d2Seschrock 	{ NULL }
192940d71d2Seschrock };
193940d71d2Seschrock 
194940d71d2Seschrock static const topo_method_t ses_enclosure_methods[] = {
195940d71d2Seschrock 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
196940d71d2Seschrock 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
197d91236feSeschrock 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
198d91236feSeschrock 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
199940d71d2Seschrock 	{ NULL }
200940d71d2Seschrock };
201940d71d2Seschrock 
202940d71d2Seschrock static void
203940d71d2Seschrock ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
204940d71d2Seschrock {
205940d71d2Seschrock 	if (--stp->set_refcount == 0) {
206940d71d2Seschrock 		ses_snap_rele(stp->set_snap);
207940d71d2Seschrock 		ses_close(stp->set_target);
208940d71d2Seschrock 		topo_mod_strfree(mod, stp->set_devpath);
209940d71d2Seschrock 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
210940d71d2Seschrock 	}
211940d71d2Seschrock }
212940d71d2Seschrock 
213940d71d2Seschrock static void
21453dbcc59SSundeep Panicker ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
215940d71d2Seschrock {
216940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
217940d71d2Seschrock 	ses_enum_chassis_t *cp;
218940d71d2Seschrock 	ses_enum_node_t *np;
219940d71d2Seschrock 	ses_enum_target_t *tp;
2200b32bb8bSEric Schrock 	ses_alt_node_t *ap;
22153dbcc59SSundeep Panicker 	topo_list_t *cpl;
22253dbcc59SSundeep Panicker 
22353dbcc59SSundeep Panicker 
22453dbcc59SSundeep Panicker 	if (pcp != NULL)
22553dbcc59SSundeep Panicker 		cpl = &pcp->sec_subchassis;
22653dbcc59SSundeep Panicker 	else
22753dbcc59SSundeep Panicker 		cpl = &sdp->sed_chassis;
228940d71d2Seschrock 
22953dbcc59SSundeep Panicker 	while ((cp = topo_list_next(cpl)) != NULL) {
23053dbcc59SSundeep Panicker 		topo_list_delete(cpl, cp);
231940d71d2Seschrock 
232940d71d2Seschrock 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
2330b32bb8bSEric Schrock 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
2340b32bb8bSEric Schrock 			    NULL) {
2350b32bb8bSEric Schrock 				topo_list_delete(&np->sen_alt_nodes, ap);
2360b32bb8bSEric Schrock 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
2370b32bb8bSEric Schrock 			}
238940d71d2Seschrock 			topo_list_delete(&cp->sec_nodes, np);
239940d71d2Seschrock 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
240940d71d2Seschrock 		}
241940d71d2Seschrock 
242940d71d2Seschrock 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
243940d71d2Seschrock 			topo_list_delete(&cp->sec_targets, tp);
244940d71d2Seschrock 			ses_target_free(mod, tp);
245940d71d2Seschrock 		}
246940d71d2Seschrock 
247940d71d2Seschrock 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
248940d71d2Seschrock 	}
249940d71d2Seschrock 
25053dbcc59SSundeep Panicker 	if (pcp == NULL) {
251*ac88567aSHyon Kim 		dev_list_free(mod, &sdp->sed_devs);
25253dbcc59SSundeep Panicker 		topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
25353dbcc59SSundeep Panicker 	}
254940d71d2Seschrock }
255940d71d2Seschrock 
256940d71d2Seschrock /*
257940d71d2Seschrock  * For enclosure nodes, we have a special contains method.  By default, the hc
258940d71d2Seschrock  * walker will compare the node name and instance number to determine if an
259940d71d2Seschrock  * FMRI matches.  For enclosures where the enumeration order is impossible to
260940d71d2Seschrock  * predict, we instead use the chassis-id as a unique identifier, and ignore
261940d71d2Seschrock  * the instance number.
262940d71d2Seschrock  */
263940d71d2Seschrock static int
264940d71d2Seschrock fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
265940d71d2Seschrock {
266940d71d2Seschrock 	uint8_t v1, v2;
267940d71d2Seschrock 	nvlist_t **hcp1, **hcp2;
268940d71d2Seschrock 	int err, i;
269940d71d2Seschrock 	uint_t nhcp1, nhcp2;
270940d71d2Seschrock 	nvlist_t *a1, *a2;
271940d71d2Seschrock 	char *c1, *c2;
272940d71d2Seschrock 	int mindepth;
273940d71d2Seschrock 
274940d71d2Seschrock 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
275940d71d2Seschrock 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
276940d71d2Seschrock 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
277940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
278940d71d2Seschrock 
279940d71d2Seschrock 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
280940d71d2Seschrock 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
281940d71d2Seschrock 	if (err != 0)
282940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
283940d71d2Seschrock 
284940d71d2Seschrock 	/*
285940d71d2Seschrock 	 * If the chassis-id doesn't match, then these FMRIs are not
286940d71d2Seschrock 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
287940d71d2Seschrock 	 * have no choice but to fall back to the instance ID.
288940d71d2Seschrock 	 */
289940d71d2Seschrock 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
290940d71d2Seschrock 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
291940d71d2Seschrock 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
292940d71d2Seschrock 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
293940d71d2Seschrock 		if (strcmp(c1, c2) != 0)
294940d71d2Seschrock 			return (0);
295940d71d2Seschrock 
296940d71d2Seschrock 		mindepth = 1;
297940d71d2Seschrock 	} else {
298940d71d2Seschrock 		mindepth = 0;
299940d71d2Seschrock 	}
300940d71d2Seschrock 
301940d71d2Seschrock 	if (nhcp2 < nhcp1)
302940d71d2Seschrock 		return (0);
303940d71d2Seschrock 
304940d71d2Seschrock 	for (i = 0; i < nhcp1; i++) {
305940d71d2Seschrock 		char *nm1 = NULL;
306940d71d2Seschrock 		char *nm2 = NULL;
307940d71d2Seschrock 		char *id1 = NULL;
308940d71d2Seschrock 		char *id2 = NULL;
309940d71d2Seschrock 
310940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
311940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
312940d71d2Seschrock 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
313940d71d2Seschrock 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
314940d71d2Seschrock 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
315940d71d2Seschrock 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
316940d71d2Seschrock 
317940d71d2Seschrock 		if (strcmp(nm1, nm2) == 0 &&
318940d71d2Seschrock 		    (i < mindepth || strcmp(id1, id2) == 0))
319940d71d2Seschrock 			continue;
320940d71d2Seschrock 
321940d71d2Seschrock 		return (0);
322940d71d2Seschrock 	}
323940d71d2Seschrock 
324940d71d2Seschrock 	return (1);
325940d71d2Seschrock }
326940d71d2Seschrock 
327940d71d2Seschrock /*ARGSUSED*/
328940d71d2Seschrock static int
329940d71d2Seschrock ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
330940d71d2Seschrock     nvlist_t *in, nvlist_t **out)
331940d71d2Seschrock {
332940d71d2Seschrock 	int ret;
333940d71d2Seschrock 	nvlist_t *nv1, *nv2;
334940d71d2Seschrock 
335940d71d2Seschrock 	if (version > TOPO_METH_CONTAINS_VERSION)
336940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
337940d71d2Seschrock 
338940d71d2Seschrock 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
339940d71d2Seschrock 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
340940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
341940d71d2Seschrock 
342940d71d2Seschrock 	ret = fmri_contains(mod, nv1, nv2);
343940d71d2Seschrock 	if (ret < 0)
344940d71d2Seschrock 		return (-1);
345940d71d2Seschrock 
346940d71d2Seschrock 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
34773e32a37SRobert Johnston 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
348940d71d2Seschrock 		    ret) == 0)
349940d71d2Seschrock 			return (0);
350940d71d2Seschrock 		else
351940d71d2Seschrock 			nvlist_free(*out);
352940d71d2Seschrock 	}
353940d71d2Seschrock 
354940d71d2Seschrock 	return (-1);
355940d71d2Seschrock 
356940d71d2Seschrock }
357940d71d2Seschrock 
358940d71d2Seschrock /*
359d91236feSeschrock  * Return a current instance of the node.  This is somewhat complicated because
360d91236feSeschrock  * we need to take a new snapshot in order to get the new data, but we don't
361940d71d2Seschrock  * want to be constantly taking SES snapshots if the consumer is going to do a
362940d71d2Seschrock  * series of queries.  So we adopt the strategy of assuming that the SES state
363d91236feSeschrock  * is not going to be rapidly changing, and limit our snapshot frequency to
364d91236feSeschrock  * some defined bounds.
365940d71d2Seschrock  */
366d91236feSeschrock ses_node_t *
3670b32bb8bSEric Schrock ses_node_lock(topo_mod_t *mod, tnode_t *tn)
368940d71d2Seschrock {
369940d71d2Seschrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
370525b85dbSEric Schrock 	hrtime_t now;
371940d71d2Seschrock 	ses_snap_t *snap;
372940d71d2Seschrock 	int err;
373d91236feSeschrock 	uint64_t nodeid;
374940d71d2Seschrock 	ses_node_t *np;
375940d71d2Seschrock 
376d91236feSeschrock 	if (tp == NULL) {
377d91236feSeschrock 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
378d91236feSeschrock 		return (NULL);
379d91236feSeschrock 	}
380940d71d2Seschrock 
3810b32bb8bSEric Schrock 	(void) pthread_mutex_lock(&tp->set_lock);
3820b32bb8bSEric Schrock 
383940d71d2Seschrock 	/*
384940d71d2Seschrock 	 * Determine if we need to take a new snapshot.
385940d71d2Seschrock 	 */
386525b85dbSEric Schrock 	now = gethrtime();
387940d71d2Seschrock 
388525b85dbSEric Schrock 	if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
389940d71d2Seschrock 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
390940d71d2Seschrock 		if (ses_snap_generation(snap) !=
391940d71d2Seschrock 		    ses_snap_generation(tp->set_snap)) {
392940d71d2Seschrock 			/*
393940d71d2Seschrock 			 * If we find ourselves in this situation, we're in
394940d71d2Seschrock 			 * trouble.  The generation count has changed, which
395940d71d2Seschrock 			 * indicates that our current topology is out of date.
396940d71d2Seschrock 			 * But we need to consult the new topology in order to
397940d71d2Seschrock 			 * determine presence at this moment in time.  We can't
398940d71d2Seschrock 			 * go back and change the topo snapshot in situ, so
399d91236feSeschrock 			 * we'll just have to fail the call in this unlikely
400d91236feSeschrock 			 * scenario.
401940d71d2Seschrock 			 */
402940d71d2Seschrock 			ses_snap_rele(snap);
403d91236feSeschrock 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
4040b32bb8bSEric Schrock 			(void) pthread_mutex_unlock(&tp->set_lock);
405d91236feSeschrock 			return (NULL);
406940d71d2Seschrock 		} else {
407940d71d2Seschrock 			ses_snap_rele(tp->set_snap);
408940d71d2Seschrock 			tp->set_snap = snap;
409940d71d2Seschrock 		}
410525b85dbSEric Schrock 		tp->set_snaptime = gethrtime();
411940d71d2Seschrock 	}
412940d71d2Seschrock 
413940d71d2Seschrock 	snap = tp->set_snap;
414940d71d2Seschrock 
415940d71d2Seschrock 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
416940d71d2Seschrock 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
417940d71d2Seschrock 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
418d91236feSeschrock 
419d91236feSeschrock 	return (np);
420d91236feSeschrock }
421d91236feSeschrock 
4220b32bb8bSEric Schrock /*ARGSUSED*/
4230b32bb8bSEric Schrock void
4240b32bb8bSEric Schrock ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
4250b32bb8bSEric Schrock {
4260b32bb8bSEric Schrock 	ses_enum_target_t *tp = topo_node_getspecific(tn);
4270b32bb8bSEric Schrock 
4280b32bb8bSEric Schrock 	verify(tp != NULL);
4290b32bb8bSEric Schrock 
4300b32bb8bSEric Schrock 	(void) pthread_mutex_unlock(&tp->set_lock);
4310b32bb8bSEric Schrock }
4320b32bb8bSEric Schrock 
433d91236feSeschrock /*
434d91236feSeschrock  * Determine if the element is present.
435d91236feSeschrock  */
436d91236feSeschrock /*ARGSUSED*/
437d91236feSeschrock static int
438d91236feSeschrock ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
439d91236feSeschrock     nvlist_t *in, nvlist_t **out)
440d91236feSeschrock {
441d91236feSeschrock 	boolean_t present;
442d91236feSeschrock 	ses_node_t *np;
443d91236feSeschrock 	nvlist_t *props, *nvl;
444d91236feSeschrock 	uint64_t status;
445d91236feSeschrock 
4460b32bb8bSEric Schrock 	if ((np = ses_node_lock(mod, tn)) == NULL)
447d91236feSeschrock 		return (-1);
448d91236feSeschrock 
449940d71d2Seschrock 	verify((props = ses_node_props(np)) != NULL);
450940d71d2Seschrock 	verify(nvlist_lookup_uint64(props,
451940d71d2Seschrock 	    SES_PROP_STATUS_CODE, &status) == 0);
452940d71d2Seschrock 
4530b32bb8bSEric Schrock 	ses_node_unlock(mod, tn);
4540b32bb8bSEric Schrock 
455940d71d2Seschrock 	present = (status != SES_ESC_NOT_INSTALLED);
456940d71d2Seschrock 
457940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
458940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
459940d71d2Seschrock 
460940d71d2Seschrock 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
461940d71d2Seschrock 	    present) != 0) {
462940d71d2Seschrock 		nvlist_free(nvl);
463940d71d2Seschrock 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
464940d71d2Seschrock 	}
465940d71d2Seschrock 
466940d71d2Seschrock 	*out = nvl;
467940d71d2Seschrock 
468940d71d2Seschrock 	return (0);
469940d71d2Seschrock }
470940d71d2Seschrock 
471940d71d2Seschrock /*
472*ac88567aSHyon Kim  * Sets standard properties for a ses node (enclosure, bay, controller
473*ac88567aSHyon Kim  * or expander).
474*ac88567aSHyon Kim  * This includes setting the FRU, as well as setting the
475*ac88567aSHyon Kim  * authority information.  When  the fru topo node(frutn) is not NULL
476*ac88567aSHyon Kim  * its resouce should be used as FRU.
477940d71d2Seschrock  */
478940d71d2Seschrock static int
479*ac88567aSHyon Kim ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn,
480*ac88567aSHyon Kim     nvlist_t *auth, uint64_t nodeid, const char *path)
481940d71d2Seschrock {
482940d71d2Seschrock 	int err;
483940d71d2Seschrock 	char *product, *chassis;
484940d71d2Seschrock 	nvlist_t *fmri;
485940d71d2Seschrock 	topo_pgroup_info_t pgi;
486940d71d2Seschrock 
487940d71d2Seschrock 	/*
488940d71d2Seschrock 	 * Set the authority explicitly if specified.
489940d71d2Seschrock 	 */
490940d71d2Seschrock 	if (auth) {
491940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
492940d71d2Seschrock 		    &product) == 0);
493940d71d2Seschrock 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
494940d71d2Seschrock 		    &chassis) == 0);
495940d71d2Seschrock 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
496940d71d2Seschrock 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
497940d71d2Seschrock 		    &err) != 0 ||
498940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
499940d71d2Seschrock 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
500940d71d2Seschrock 		    &err) != 0 ||
501940d71d2Seschrock 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
502940d71d2Seschrock 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
503940d71d2Seschrock 		    &err) != 0) {
504940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to add authority "
505d91236feSeschrock 			    "properties: %s\n", topo_strerror(err));
506940d71d2Seschrock 			return (topo_mod_seterrno(mod, err));
507940d71d2Seschrock 		}
508940d71d2Seschrock 	}
509940d71d2Seschrock 
510940d71d2Seschrock 	/*
511940d71d2Seschrock 	 * Copy the resource and set that as the FRU.
512940d71d2Seschrock 	 */
513*ac88567aSHyon Kim 	if (frutn != NULL) {
514*ac88567aSHyon Kim 		if (topo_node_resource(frutn, &fmri, &err) != 0) {
515*ac88567aSHyon Kim 			topo_mod_dprintf(mod,
516*ac88567aSHyon Kim 			    "topo_node_resource() failed : %s\n",
517*ac88567aSHyon Kim 			    topo_strerror(err));
518*ac88567aSHyon Kim 			return (topo_mod_seterrno(mod, err));
519*ac88567aSHyon Kim 		}
520*ac88567aSHyon Kim 	} else {
521*ac88567aSHyon Kim 		if (topo_node_resource(tn, &fmri, &err) != 0) {
522*ac88567aSHyon Kim 			topo_mod_dprintf(mod,
523*ac88567aSHyon Kim 			    "topo_node_resource() failed : %s\n",
524*ac88567aSHyon Kim 			    topo_strerror(err));
525*ac88567aSHyon Kim 			return (topo_mod_seterrno(mod, err));
526*ac88567aSHyon Kim 		}
527940d71d2Seschrock 	}
528940d71d2Seschrock 
529940d71d2Seschrock 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
530940d71d2Seschrock 		topo_mod_dprintf(mod,
531940d71d2Seschrock 		    "topo_node_fru_set() failed : %s\n",
532940d71d2Seschrock 		    topo_strerror(err));
533940d71d2Seschrock 		nvlist_free(fmri);
534940d71d2Seschrock 		return (topo_mod_seterrno(mod, err));
535940d71d2Seschrock 	}
536940d71d2Seschrock 
537940d71d2Seschrock 	nvlist_free(fmri);
538940d71d2Seschrock 
539940d71d2Seschrock 	/*
540940d71d2Seschrock 	 * Set the SES-specific properties so that consumers can query
541940d71d2Seschrock 	 * additional information about the particular SES element.
542940d71d2Seschrock 	 */
543940d71d2Seschrock 	pgi.tpi_name = TOPO_PGROUP_SES;
544940d71d2Seschrock 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
545940d71d2Seschrock 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
546940d71d2Seschrock 	pgi.tpi_version = TOPO_VERSION;
547940d71d2Seschrock 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
548940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to create propgroup "
549940d71d2Seschrock 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
550940d71d2Seschrock 		return (-1);
551940d71d2Seschrock 	}
552940d71d2Seschrock 
553940d71d2Seschrock 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
554940d71d2Seschrock 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
555940d71d2Seschrock 	    nodeid, &err) != 0) {
556940d71d2Seschrock 		topo_mod_dprintf(mod,
557940d71d2Seschrock 		    "failed to create property %s: %s\n",
558940d71d2Seschrock 		    TOPO_PROP_NODE_ID, topo_strerror(err));
559940d71d2Seschrock 		return (-1);
560940d71d2Seschrock 	}
561940d71d2Seschrock 
562940d71d2Seschrock 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
563940d71d2Seschrock 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
564940d71d2Seschrock 	    path, &err) != 0) {
565940d71d2Seschrock 		topo_mod_dprintf(mod,
566940d71d2Seschrock 		    "failed to create property %s: %s\n",
567940d71d2Seschrock 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
568940d71d2Seschrock 		return (-1);
569940d71d2Seschrock 	}
570940d71d2Seschrock 
571940d71d2Seschrock 	return (0);
572940d71d2Seschrock }
573940d71d2Seschrock 
574940d71d2Seschrock /*
575940d71d2Seschrock  * Callback to add a disk to a given bay.  We first check the status-code to
576940d71d2Seschrock  * determine if a disk is present, ignoring those that aren't in an appropriate
5770b32bb8bSEric Schrock  * state.  We then scan the parent bay node's SAS address array to determine
5780b32bb8bSEric Schrock  * possible attached SAS addresses.  We create a disk node if the disk is not
5790b32bb8bSEric Schrock  * SAS or the SES target does not support the necessary pages for this; if we
5800b32bb8bSEric Schrock  * find the SAS address, we create a disk node and also correlate it with
5810b32bb8bSEric Schrock  * the corresponding Solaris device node to fill in the rest of the data.
582940d71d2Seschrock  */
583940d71d2Seschrock static int
584940d71d2Seschrock ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
585940d71d2Seschrock {
586940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
587940d71d2Seschrock 	uint64_t status;
588940d71d2Seschrock 	nvlist_t **sas;
589940d71d2Seschrock 	uint_t s, nsas;
5900b32bb8bSEric Schrock 	char **paths;
59144ed9dbbSStephen Hanson 	int err, ret;
59244ed9dbbSStephen Hanson 	tnode_t *child = NULL;
593940d71d2Seschrock 
594940d71d2Seschrock 	/*
595940d71d2Seschrock 	 * Skip devices that are not in a present (and possibly damaged) state.
596940d71d2Seschrock 	 */
597940d71d2Seschrock 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
598940d71d2Seschrock 		return (0);
599940d71d2Seschrock 
600*ac88567aSHyon Kim 	if (status != SES_ESC_UNSUPPORTED &&
601*ac88567aSHyon Kim 	    status != SES_ESC_OK &&
602940d71d2Seschrock 	    status != SES_ESC_CRITICAL &&
603940d71d2Seschrock 	    status != SES_ESC_NONCRITICAL &&
604940d71d2Seschrock 	    status != SES_ESC_UNRECOVERABLE &&
605940d71d2Seschrock 	    status != SES_ESC_NO_ACCESS)
606940d71d2Seschrock 		return (0);
607940d71d2Seschrock 
608940d71d2Seschrock 	topo_mod_dprintf(mod, "found attached disk");
609940d71d2Seschrock 
610940d71d2Seschrock 	/*
611940d71d2Seschrock 	 * Create the disk range.
612940d71d2Seschrock 	 */
6130b32bb8bSEric Schrock 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
614940d71d2Seschrock 		topo_mod_dprintf(mod,
615940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
616940d71d2Seschrock 		    topo_mod_errmsg(mod));
617940d71d2Seschrock 		return (-1);
618940d71d2Seschrock 	}
619940d71d2Seschrock 
620940d71d2Seschrock 	/*
621940d71d2Seschrock 	 * Look through all SAS addresses and attempt to correlate them to a
622940d71d2Seschrock 	 * known Solaris device.  If we don't find a matching node, then we
623940d71d2Seschrock 	 * don't enumerate the disk node.
624940d71d2Seschrock 	 */
625940d71d2Seschrock 	if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
626940d71d2Seschrock 	    &sas, &nsas) != 0)
627940d71d2Seschrock 		return (0);
628940d71d2Seschrock 
6290b32bb8bSEric Schrock 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
6300b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
6310b32bb8bSEric Schrock 		return (0);
6320b32bb8bSEric Schrock 
6330b32bb8bSEric Schrock 	err = 0;
6340b32bb8bSEric Schrock 
635940d71d2Seschrock 	for (s = 0; s < nsas; s++) {
636*ac88567aSHyon Kim 		ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s],
63744ed9dbbSStephen Hanson 		    &child);
63844ed9dbbSStephen Hanson 		if (ret == 0) {
63944ed9dbbSStephen Hanson 			break;
64044ed9dbbSStephen Hanson 		} else if (ret < 0) {
6410b32bb8bSEric Schrock 			err = -1;
6420b32bb8bSEric Schrock 			break;
6430b32bb8bSEric Schrock 		}
6440b32bb8bSEric Schrock 	}
6450b32bb8bSEric Schrock 
64644ed9dbbSStephen Hanson 	if (s == nsas)
64744ed9dbbSStephen Hanson 		disk_declare_non_enumerated(mod, pnode, &child);
64844ed9dbbSStephen Hanson 
64944ed9dbbSStephen Hanson 	/* copy sas_addresses (target-ports) from parent (with 'w'added) */
65044ed9dbbSStephen Hanson 	if (child != NULL) {
65144ed9dbbSStephen Hanson 		int i;
65244ed9dbbSStephen Hanson 		char **tports;
65344ed9dbbSStephen Hanson 		uint64_t wwn;
65444ed9dbbSStephen Hanson 
65544ed9dbbSStephen Hanson 		tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
65644ed9dbbSStephen Hanson 		if (tports != NULL) {
65744ed9dbbSStephen Hanson 			for (i = 0; i < nsas; i++) {
65844ed9dbbSStephen Hanson 				if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
65944ed9dbbSStephen Hanson 				    DDI_SUCCESS)
66044ed9dbbSStephen Hanson 					break;
66144ed9dbbSStephen Hanson 				tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
66244ed9dbbSStephen Hanson 				if (tports[i] == NULL)
66344ed9dbbSStephen Hanson 					break;
66444ed9dbbSStephen Hanson 			}
66544ed9dbbSStephen Hanson 			/* if they all worked then create the property */
66644ed9dbbSStephen Hanson 			if (i == nsas)
66744ed9dbbSStephen Hanson 				(void) topo_prop_set_string_array(child,
66844ed9dbbSStephen Hanson 				    TOPO_PGROUP_STORAGE,
66944ed9dbbSStephen Hanson 				    TOPO_STORAGE_TARGET_PORT_L0IDS,
67044ed9dbbSStephen Hanson 				    TOPO_PROP_IMMUTABLE, (const char **)tports,
67144ed9dbbSStephen Hanson 				    nsas, &err);
67244ed9dbbSStephen Hanson 
67344ed9dbbSStephen Hanson 			for (i = 0; i < nsas; i++)
67444ed9dbbSStephen Hanson 				if (tports[i] != NULL)
67544ed9dbbSStephen Hanson 					scsi_free_wwnstr(tports[i]);
67644ed9dbbSStephen Hanson 			topo_mod_free(mod, tports, sizeof (char *) * nsas);
67744ed9dbbSStephen Hanson 		}
67844ed9dbbSStephen Hanson 	}
67944ed9dbbSStephen Hanson 
6800b32bb8bSEric Schrock 	for (s = 0; s < nsas; s++)
6810b32bb8bSEric Schrock 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
6820b32bb8bSEric Schrock 	topo_mod_free(mod, paths, nsas * sizeof (char *));
6830b32bb8bSEric Schrock 
6840b32bb8bSEric Schrock 	return (err);
6850b32bb8bSEric Schrock }
6860b32bb8bSEric Schrock 
6870b32bb8bSEric Schrock static int
6880b32bb8bSEric Schrock ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
6890b32bb8bSEric Schrock {
6900b32bb8bSEric Schrock 	ses_alt_node_t *ap;
6910b32bb8bSEric Schrock 	ses_node_t *np;
6920b32bb8bSEric Schrock 	nvlist_t *props;
6930b32bb8bSEric Schrock 
6940b32bb8bSEric Schrock 	nvlist_t **phys;
6950b32bb8bSEric Schrock 	uint_t i, j, n_phys, all_phys = 0;
6960b32bb8bSEric Schrock 	char **paths;
6970b32bb8bSEric Schrock 	uint64_t addr;
6980b32bb8bSEric Schrock 	size_t len;
6990b32bb8bSEric Schrock 	int terr, err = -1;
7000b32bb8bSEric Schrock 
7010b32bb8bSEric Schrock 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
7020b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
7030b32bb8bSEric Schrock 		np = ap->san_node;
7040b32bb8bSEric Schrock 		props = ses_node_props(np);
7050b32bb8bSEric Schrock 
7060b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
7070b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
7080b32bb8bSEric Schrock 			continue;
7090b32bb8bSEric Schrock 
7100b32bb8bSEric Schrock 		all_phys += n_phys;
7110b32bb8bSEric Schrock 	}
7120b32bb8bSEric Schrock 
7130b32bb8bSEric Schrock 	if (all_phys == 0)
7140b32bb8bSEric Schrock 		return (0);
7150b32bb8bSEric Schrock 
7160b32bb8bSEric Schrock 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
7170b32bb8bSEric Schrock 		return (-1);
7180b32bb8bSEric Schrock 
7190b32bb8bSEric Schrock 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
7200b32bb8bSEric Schrock 	    ap = topo_list_next(ap)) {
7210b32bb8bSEric Schrock 		np = ap->san_node;
7220b32bb8bSEric Schrock 		props = ses_node_props(np);
7230b32bb8bSEric Schrock 
7240b32bb8bSEric Schrock 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
7250b32bb8bSEric Schrock 		    &phys, &n_phys) != 0)
726940d71d2Seschrock 			continue;
727940d71d2Seschrock 
7280b32bb8bSEric Schrock 		for (j = 0; j < n_phys; j++) {
7290b32bb8bSEric Schrock 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
7300b32bb8bSEric Schrock 			    &addr) != 0)
7310b32bb8bSEric Schrock 				continue;
732940d71d2Seschrock 
7330b32bb8bSEric Schrock 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
7340b32bb8bSEric Schrock 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
7350b32bb8bSEric Schrock 				goto error;
7360b32bb8bSEric Schrock 
7370b32bb8bSEric Schrock 			(void) snprintf(paths[i], len, "%016llx", addr);
7380b32bb8bSEric Schrock 
7390b32bb8bSEric Schrock 			++i;
7400b32bb8bSEric Schrock 		}
741940d71d2Seschrock 	}
742940d71d2Seschrock 
7430b32bb8bSEric Schrock 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
7440b32bb8bSEric Schrock 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
7450b32bb8bSEric Schrock 	    (const char **)paths, i, &terr);
7460b32bb8bSEric Schrock 	if (err != 0)
7470b32bb8bSEric Schrock 		err = topo_mod_seterrno(mod, terr);
7480b32bb8bSEric Schrock 
7490b32bb8bSEric Schrock error:
7500b32bb8bSEric Schrock 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
7510b32bb8bSEric Schrock 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
7520b32bb8bSEric Schrock 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
7530b32bb8bSEric Schrock 
7540b32bb8bSEric Schrock 	return (err);
755940d71d2Seschrock }
756940d71d2Seschrock 
757940d71d2Seschrock /*
758*ac88567aSHyon Kim  * Callback to create a basic node (bay, psu, fan, or controller and expander).
759940d71d2Seschrock  */
760940d71d2Seschrock static int
761940d71d2Seschrock ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp,
762*ac88567aSHyon Kim     tnode_t *pnode, const char *nodename, const char *labelname, tnode_t **node)
763940d71d2Seschrock {
764940d71d2Seschrock 	ses_node_t *np = snp->sen_node;
765d91236feSeschrock 	ses_node_t *parent;
766940d71d2Seschrock 	uint64_t instance = snp->sen_instance;
767940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
768940d71d2Seschrock 	nvlist_t *props, *aprops;
769940d71d2Seschrock 	nvlist_t *auth = NULL, *fmri = NULL;
770*ac88567aSHyon Kim 	tnode_t *tn = NULL, *frutn = NULL;
771940d71d2Seschrock 	char label[128];
772940d71d2Seschrock 	int err;
773d91236feSeschrock 	char *part = NULL, *serial = NULL, *revision = NULL;
774d91236feSeschrock 	char *desc;
775d91236feSeschrock 	boolean_t report;
776940d71d2Seschrock 
777940d71d2Seschrock 	props = ses_node_props(np);
778940d71d2Seschrock 
779940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
780940d71d2Seschrock 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
781940d71d2Seschrock 
782940d71d2Seschrock 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
783940d71d2Seschrock 
784940d71d2Seschrock 	/*
785940d71d2Seschrock 	 * Create the node.  The interesting information is all copied from the
786940d71d2Seschrock 	 * parent enclosure node, so there is not much to do.
787940d71d2Seschrock 	 */
788940d71d2Seschrock 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
789940d71d2Seschrock 		goto error;
790940d71d2Seschrock 
791d91236feSeschrock 	/*
792d91236feSeschrock 	 * We want to report revision information for the controller nodes, but
793d91236feSeschrock 	 * we do not get per-element revision information.  However, we do have
794d91236feSeschrock 	 * revision information for the entire enclosure, and we can use the
795d91236feSeschrock 	 * 'reported-via' property to know that this controller corresponds to
796d91236feSeschrock 	 * the given revision information.  This means we cannot get revision
797d91236feSeschrock 	 * information for targets we are not explicitly connected to, but
798d91236feSeschrock 	 * there is little we can do about the situation.
799d91236feSeschrock 	 */
800d91236feSeschrock 	if (strcmp(nodename, CONTROLLER) == 0 &&
801d91236feSeschrock 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
802d91236feSeschrock 	    report) {
803d91236feSeschrock 		for (parent = ses_node_parent(np); parent != NULL;
804d91236feSeschrock 		    parent = ses_node_parent(parent)) {
805d91236feSeschrock 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
806d91236feSeschrock 				(void) nvlist_lookup_string(
807d91236feSeschrock 				    ses_node_props(parent),
808d91236feSeschrock 				    SES_EN_PROP_REV, &revision);
809d91236feSeschrock 				break;
810d91236feSeschrock 			}
811d91236feSeschrock 		}
812d91236feSeschrock 	}
813d91236feSeschrock 
814940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
815d91236feSeschrock 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
816940d71d2Seschrock 	    serial)) == NULL) {
817940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
818940d71d2Seschrock 		    topo_mod_errmsg(mod));
819940d71d2Seschrock 		goto error;
820940d71d2Seschrock 	}
821940d71d2Seschrock 
822940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, nodename,
823940d71d2Seschrock 	    instance, fmri)) == NULL) {
824940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
825940d71d2Seschrock 		    topo_mod_errmsg(mod));
826940d71d2Seschrock 		goto error;
827940d71d2Seschrock 	}
828940d71d2Seschrock 
829940d71d2Seschrock 	/*
830d91236feSeschrock 	 * For the node label, we look for the following in order:
831d91236feSeschrock 	 *
832d91236feSeschrock 	 * 	<ses-description>
833d91236feSeschrock 	 * 	<ses-class-description> <instance>
834d91236feSeschrock 	 * 	<default-type-label> <instance>
835940d71d2Seschrock 	 */
836d91236feSeschrock 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
837d91236feSeschrock 	    desc[0] == '\0') {
838d91236feSeschrock 		parent = ses_node_parent(np);
839d91236feSeschrock 		aprops = ses_node_props(parent);
840d91236feSeschrock 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
841*ac88567aSHyon Kim 		    &desc) != 0 || desc[0] == '\0')
842*ac88567aSHyon Kim 			desc = (char *)labelname;
843d91236feSeschrock 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
844d91236feSeschrock 		    instance);
845d91236feSeschrock 		desc = label;
846d91236feSeschrock 	}
847d91236feSeschrock 
848d91236feSeschrock 	if (topo_node_label_set(tn, desc, &err) != 0)
849940d71d2Seschrock 		goto error;
850940d71d2Seschrock 
851*ac88567aSHyon Kim 	/*
852*ac88567aSHyon Kim 	 * For an expander node, set the FRU to its parent(controller).
853*ac88567aSHyon Kim 	 * For a connector node, set the FRU to its grand parent(controller).
854*ac88567aSHyon Kim 	 */
855*ac88567aSHyon Kim 	if (strcmp(nodename, SASEXPANDER) == 0) {
856*ac88567aSHyon Kim 		frutn = pnode;
857*ac88567aSHyon Kim 	} else if (strcmp(nodename, RECEPTACLE) == 0) {
858*ac88567aSHyon Kim 		frutn = topo_node_parent(pnode);
859*ac88567aSHyon Kim 	}
860*ac88567aSHyon Kim 
861*ac88567aSHyon Kim 	if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np),
862940d71d2Seschrock 	    snp->sen_target->set_devpath) != 0)
863940d71d2Seschrock 		goto error;
864940d71d2Seschrock 
865*ac88567aSHyon Kim 	if (strcmp(nodename, BAY) == 0) {
8660b32bb8bSEric Schrock 		if (ses_add_bay_props(mod, tn, snp) != 0)
8670b32bb8bSEric Schrock 			goto error;
8680b32bb8bSEric Schrock 
869940d71d2Seschrock 		if (ses_create_disk(sdp, tn, props) != 0)
870940d71d2Seschrock 			goto error;
871d91236feSeschrock 
872d91236feSeschrock 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
873d91236feSeschrock 			topo_mod_dprintf(mod,
874d91236feSeschrock 			    "topo_method_register() failed: %s",
875d91236feSeschrock 			    topo_mod_errmsg(mod));
876d91236feSeschrock 			goto error;
877d91236feSeschrock 		}
878*ac88567aSHyon Kim 	} else if ((strcmp(nodename, FAN) == 0) ||
879*ac88567aSHyon Kim 	    (strcmp(nodename, PSU) == 0) ||
880*ac88567aSHyon Kim 	    (strcmp(nodename, CONTROLLER) == 0)) {
881940d71d2Seschrock 		/*
882d91236feSeschrock 		 * Only fan, psu, and controller nodes have a 'present' method.
883d91236feSeschrock 		 * Bay nodes are always present, and disk nodes are present by
884*ac88567aSHyon Kim 		 * virtue of being enumerated and SAS expander nodes and
885*ac88567aSHyon Kim 		 * SAS connector nodes are also always present once
886*ac88567aSHyon Kim 		 * the parent controller is found.
887940d71d2Seschrock 		 */
888940d71d2Seschrock 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
889940d71d2Seschrock 			topo_mod_dprintf(mod,
890940d71d2Seschrock 			    "topo_method_register() failed: %s",
891940d71d2Seschrock 			    topo_mod_errmsg(mod));
892940d71d2Seschrock 			goto error;
893940d71d2Seschrock 		}
894940d71d2Seschrock 
895940d71d2Seschrock 	}
896940d71d2Seschrock 
897d91236feSeschrock 	snp->sen_target->set_refcount++;
898d91236feSeschrock 	topo_node_setspecific(tn, snp->sen_target);
899d91236feSeschrock 
900940d71d2Seschrock 	nvlist_free(auth);
901940d71d2Seschrock 	nvlist_free(fmri);
902*ac88567aSHyon Kim 	if (node != NULL) *node = tn;
903*ac88567aSHyon Kim 	return (0);
904*ac88567aSHyon Kim 
905*ac88567aSHyon Kim error:
906*ac88567aSHyon Kim 	nvlist_free(auth);
907*ac88567aSHyon Kim 	nvlist_free(fmri);
908*ac88567aSHyon Kim 	return (-1);
909*ac88567aSHyon Kim }
910*ac88567aSHyon Kim 
911*ac88567aSHyon Kim /*
912*ac88567aSHyon Kim  * Create SAS expander specific props.
913*ac88567aSHyon Kim  */
914*ac88567aSHyon Kim /*ARGSUSED*/
915*ac88567aSHyon Kim static int
916*ac88567aSHyon Kim ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
917*ac88567aSHyon Kim     tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist)
918*ac88567aSHyon Kim {
919*ac88567aSHyon Kim 	ses_node_t *np = snp->sen_node;
920*ac88567aSHyon Kim 	topo_mod_t *mod = sdp->sed_mod;
921*ac88567aSHyon Kim 	nvlist_t *auth = NULL, *fmri = NULL;
922*ac88567aSHyon Kim 	nvlist_t *props, **phylist;
923*ac88567aSHyon Kim 	int err, i;
924*ac88567aSHyon Kim 	uint_t pcount;
925*ac88567aSHyon Kim 	uint64_t sasaddr, connidx;
926*ac88567aSHyon Kim 	char sasaddr_str[17];
927*ac88567aSHyon Kim 	boolean_t found = B_FALSE;
928*ac88567aSHyon Kim 	dev_di_node_t *dnode;
929*ac88567aSHyon Kim 
930*ac88567aSHyon Kim 	props = ses_node_props(np);
931*ac88567aSHyon Kim 
932*ac88567aSHyon Kim 	/*
933*ac88567aSHyon Kim 	 * the uninstalled expander is not enumerated by checking
934*ac88567aSHyon Kim 	 * the element status code.  No present present' method provided.
935*ac88567aSHyon Kim 	 */
936*ac88567aSHyon Kim 	/*
937*ac88567aSHyon Kim 	 * Get the Expander SAS address.  It should exist.
938*ac88567aSHyon Kim 	 */
939*ac88567aSHyon Kim 	if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR,
940*ac88567aSHyon Kim 	    &sasaddr) != 0) {
941*ac88567aSHyon Kim 		topo_mod_dprintf(mod,
942*ac88567aSHyon Kim 		    "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
943*ac88567aSHyon Kim 		goto error;
944*ac88567aSHyon Kim 	}
945*ac88567aSHyon Kim 
946*ac88567aSHyon Kim 	(void) sprintf(sasaddr_str, "%llx", sasaddr);
947*ac88567aSHyon Kim 
948*ac88567aSHyon Kim 	/* search matching dev_di_node. */
949*ac88567aSHyon Kim 	for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL;
950*ac88567aSHyon Kim 	    dnode = topo_list_next(dnode)) {
951*ac88567aSHyon Kim 		if (strstr(dnode->ddn_dpath, sasaddr_str) != NULL) {
952*ac88567aSHyon Kim 			found = B_TRUE;
953*ac88567aSHyon Kim 			break;
954*ac88567aSHyon Kim 		}
955*ac88567aSHyon Kim 	}
956*ac88567aSHyon Kim 
957*ac88567aSHyon Kim 	if (!found) {
958*ac88567aSHyon Kim 		topo_mod_dprintf(mod,
959*ac88567aSHyon Kim 		    "ses_set_expander_props: Failed to find matching "
960*ac88567aSHyon Kim 		    "devinfo node for Exapnder SAS address %s",
961*ac88567aSHyon Kim 		    SES_EXP_PROP_SAS_ADDR);
962*ac88567aSHyon Kim 		/* continue on to get storage group props. */
963*ac88567aSHyon Kim 	} else {
964*ac88567aSHyon Kim 		/* create/set the devfs-path and devid in the io group */
965*ac88567aSHyon Kim 		if (topo_pgroup_create(tnode, &io_pgroup, &err) != 0) {
966*ac88567aSHyon Kim 			topo_mod_dprintf(mod, "ses_set_expander_props: "
967*ac88567aSHyon Kim 			    "create io error %s\n", topo_strerror(err));
968*ac88567aSHyon Kim 			goto error;
969*ac88567aSHyon Kim 		} else {
970*ac88567aSHyon Kim 			if (topo_prop_set_string(tnode, TOPO_PGROUP_IO,
971*ac88567aSHyon Kim 			    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE,
972*ac88567aSHyon Kim 			    dnode->ddn_dpath, &err) != 0) {
973*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
974*ac88567aSHyon Kim 				    "set dev error %s\n", topo_strerror(err));
975*ac88567aSHyon Kim 			}
976*ac88567aSHyon Kim 			if (topo_prop_set_string(tnode, TOPO_PGROUP_IO,
977*ac88567aSHyon Kim 			    TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE,
978*ac88567aSHyon Kim 			    dnode->ddn_devid, &err) != 0) {
979*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
980*ac88567aSHyon Kim 				    "set devid error %s\n", topo_strerror(err));
981*ac88567aSHyon Kim 			}
982*ac88567aSHyon Kim 			if (dnode->ddn_ppath_count != 0 &&
983*ac88567aSHyon Kim 			    topo_prop_set_string_array(tnode, TOPO_PGROUP_IO,
984*ac88567aSHyon Kim 			    TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE,
985*ac88567aSHyon Kim 			    (const char **)dnode->ddn_ppath,
986*ac88567aSHyon Kim 			    dnode->ddn_ppath_count, &err) != 0) {
987*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
988*ac88567aSHyon Kim 				    "set phys-path error %s\n",
989*ac88567aSHyon Kim 				    topo_strerror(err));
990*ac88567aSHyon Kim 			}
991*ac88567aSHyon Kim 		}
992*ac88567aSHyon Kim 	}
993*ac88567aSHyon Kim 
994*ac88567aSHyon Kim 	/* create the storage group */
995*ac88567aSHyon Kim 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
996*ac88567aSHyon Kim 		topo_mod_dprintf(mod, "ses_set_expander_props: "
997*ac88567aSHyon Kim 		    "create storage error %s\n", topo_strerror(err));
998*ac88567aSHyon Kim 		goto error;
999*ac88567aSHyon Kim 	} else {
1000*ac88567aSHyon Kim 		/* set the SAS address prop of the expander. */
1001*ac88567aSHyon Kim 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1002*ac88567aSHyon Kim 		    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str,
1003*ac88567aSHyon Kim 		    &err) != 0) {
1004*ac88567aSHyon Kim 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1005*ac88567aSHyon Kim 			    "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1006*ac88567aSHyon Kim 			    topo_strerror(err));
1007*ac88567aSHyon Kim 		}
1008*ac88567aSHyon Kim 
1009*ac88567aSHyon Kim 		/* Get the phy information for the expander */
1010*ac88567aSHyon Kim 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1011*ac88567aSHyon Kim 		    &phylist, &pcount) != 0) {
1012*ac88567aSHyon Kim 			topo_mod_dprintf(mod,
1013*ac88567aSHyon Kim 			    "Failed to get prop %s.", SES_SAS_PROP_PHYS);
1014*ac88567aSHyon Kim 		} else {
1015*ac88567aSHyon Kim 			/*
1016*ac88567aSHyon Kim 			 * For each phy, get the connector element index and
1017*ac88567aSHyon Kim 			 * stores into connector element index array.
1018*ac88567aSHyon Kim 			 */
1019*ac88567aSHyon Kim 			*phycount = pcount;
1020*ac88567aSHyon Kim 			for (i = 0; i < pcount; i++) {
1021*ac88567aSHyon Kim 				if (nvlist_lookup_uint64(phylist[i],
1022*ac88567aSHyon Kim 				    SES_PROP_CE_IDX, &connidx) == 0) {
1023*ac88567aSHyon Kim 					if (connidx != 0xff) {
1024*ac88567aSHyon Kim 						connlist[i] = connidx;
1025*ac88567aSHyon Kim 					} else {
1026*ac88567aSHyon Kim 						connlist[i] = -1;
1027*ac88567aSHyon Kim 					}
1028*ac88567aSHyon Kim 				} else {
1029*ac88567aSHyon Kim 					/* Fail to get the index. set to -1. */
1030*ac88567aSHyon Kim 					connlist[i] = -1;
1031*ac88567aSHyon Kim 				}
1032*ac88567aSHyon Kim 			}
1033*ac88567aSHyon Kim 
1034*ac88567aSHyon Kim 			/* set the phy count prop of the expander. */
1035*ac88567aSHyon Kim 			if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE,
1036*ac88567aSHyon Kim 			    TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount,
1037*ac88567aSHyon Kim 			    &err) != 0) {
1038*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1039*ac88567aSHyon Kim 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1040*ac88567aSHyon Kim 				    topo_strerror(err));
1041*ac88567aSHyon Kim 			}
1042*ac88567aSHyon Kim 
1043*ac88567aSHyon Kim 			/*
1044*ac88567aSHyon Kim 			 * set the connector element index of
1045*ac88567aSHyon Kim 			 * the expander phys.
1046*ac88567aSHyon Kim 			 */
1047*ac88567aSHyon Kim 		}
1048*ac88567aSHyon Kim 
1049*ac88567aSHyon Kim 		/* populate other misc storage group properties */
1050*ac88567aSHyon Kim 		if (found) {
1051*ac88567aSHyon Kim 			if (dnode->ddn_mfg && (topo_prop_set_string(tnode,
1052*ac88567aSHyon Kim 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER,
1053*ac88567aSHyon Kim 			    TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) {
1054*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1055*ac88567aSHyon Kim 				    "set mfg error %s\n", topo_strerror(err));
1056*ac88567aSHyon Kim 			}
1057*ac88567aSHyon Kim 
1058*ac88567aSHyon Kim 			if (dnode->ddn_model && (topo_prop_set_string(tnode,
1059*ac88567aSHyon Kim 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL,
1060*ac88567aSHyon Kim 			    TOPO_PROP_IMMUTABLE,
1061*ac88567aSHyon Kim 			    dnode->ddn_model, &err) != 0)) {
1062*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1063*ac88567aSHyon Kim 				    "set model error %s\n", topo_strerror(err));
1064*ac88567aSHyon Kim 			}
1065*ac88567aSHyon Kim 
1066*ac88567aSHyon Kim 			if (dnode->ddn_serial && (topo_prop_set_string(tnode,
1067*ac88567aSHyon Kim 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM,
1068*ac88567aSHyon Kim 			    TOPO_PROP_IMMUTABLE,
1069*ac88567aSHyon Kim 			    dnode->ddn_serial, &err) != 0)) {
1070*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1071*ac88567aSHyon Kim 				    "set serial error %s\n",
1072*ac88567aSHyon Kim 				    topo_strerror(err));
1073*ac88567aSHyon Kim 			}
1074*ac88567aSHyon Kim 
1075*ac88567aSHyon Kim 			if (dnode->ddn_firm && (topo_prop_set_string(tnode,
1076*ac88567aSHyon Kim 			    TOPO_PGROUP_STORAGE,
1077*ac88567aSHyon Kim 			    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
1078*ac88567aSHyon Kim 			    dnode->ddn_firm, &err) != 0)) {
1079*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1080*ac88567aSHyon Kim 				    "set firm error %s\n", topo_strerror(err));
1081*ac88567aSHyon Kim 			}
1082*ac88567aSHyon Kim 		}
1083*ac88567aSHyon Kim 	}
1084*ac88567aSHyon Kim 
1085940d71d2Seschrock 	return (0);
1086940d71d2Seschrock 
1087940d71d2Seschrock error:
1088940d71d2Seschrock 	nvlist_free(auth);
1089940d71d2Seschrock 	nvlist_free(fmri);
1090940d71d2Seschrock 	return (-1);
1091940d71d2Seschrock }
1092940d71d2Seschrock 
1093*ac88567aSHyon Kim /*
1094*ac88567aSHyon Kim  * Create SAS expander specific props.
1095*ac88567aSHyon Kim  */
1096*ac88567aSHyon Kim /*ARGSUSED*/
1097*ac88567aSHyon Kim static int
1098*ac88567aSHyon Kim ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1099*ac88567aSHyon Kim     tnode_t *tnode, int64_t phy_mask)
1100*ac88567aSHyon Kim {
1101*ac88567aSHyon Kim 	ses_node_t *np = snp->sen_node;
1102*ac88567aSHyon Kim 	topo_mod_t *mod = sdp->sed_mod;
1103*ac88567aSHyon Kim 	nvlist_t *props;
1104*ac88567aSHyon Kim 	int err, i;
1105*ac88567aSHyon Kim 	uint64_t conntype;
1106*ac88567aSHyon Kim 	char phymask_str[17], *conntype_str;
1107*ac88567aSHyon Kim 	boolean_t   found;
1108*ac88567aSHyon Kim 
1109*ac88567aSHyon Kim 	props = ses_node_props(np);
1110*ac88567aSHyon Kim 
1111*ac88567aSHyon Kim 	/*
1112*ac88567aSHyon Kim 	 * convert phy mask to string.
1113*ac88567aSHyon Kim 	 */
1114*ac88567aSHyon Kim 	(void) snprintf(phymask_str, 17, "%llx", phy_mask);
1115*ac88567aSHyon Kim 
1116*ac88567aSHyon Kim 	/* create the storage group */
1117*ac88567aSHyon Kim 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1118*ac88567aSHyon Kim 		topo_mod_dprintf(mod, "ses_set_expander_props: "
1119*ac88567aSHyon Kim 		    "create storage error %s\n", topo_strerror(err));
1120*ac88567aSHyon Kim 		return (-1);
1121*ac88567aSHyon Kim 	} else {
1122*ac88567aSHyon Kim 		/* set the SAS address prop of the expander. */
1123*ac88567aSHyon Kim 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1124*ac88567aSHyon Kim 		    TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE,
1125*ac88567aSHyon Kim 		    phymask_str, &err) != 0) {
1126*ac88567aSHyon Kim 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1127*ac88567aSHyon Kim 			    "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
1128*ac88567aSHyon Kim 			    topo_strerror(err));
1129*ac88567aSHyon Kim 		}
1130*ac88567aSHyon Kim 
1131*ac88567aSHyon Kim 		/* Get the connector type information for the expander */
1132*ac88567aSHyon Kim 		if (nvlist_lookup_uint64(props,
1133*ac88567aSHyon Kim 		    SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
1134*ac88567aSHyon Kim 			topo_mod_dprintf(mod, "Failed to get prop %s.",
1135*ac88567aSHyon Kim 			    TOPO_STORAGE_SAS_PHY_MASK);
1136*ac88567aSHyon Kim 		} else {
1137*ac88567aSHyon Kim 			found = B_FALSE;
1138*ac88567aSHyon Kim 			for (i = 0; ; i++) {
1139*ac88567aSHyon Kim 				if (sas_connector_type_list[i].type ==
1140*ac88567aSHyon Kim 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1141*ac88567aSHyon Kim 					break;
1142*ac88567aSHyon Kim 				}
1143*ac88567aSHyon Kim 				if (sas_connector_type_list[i].type ==
1144*ac88567aSHyon Kim 				    conntype) {
1145*ac88567aSHyon Kim 					conntype_str =
1146*ac88567aSHyon Kim 					    sas_connector_type_list[i].name;
1147*ac88567aSHyon Kim 					found = B_TRUE;
1148*ac88567aSHyon Kim 					break;
1149*ac88567aSHyon Kim 				}
1150*ac88567aSHyon Kim 			}
1151*ac88567aSHyon Kim 
1152*ac88567aSHyon Kim 			if (!found) {
1153*ac88567aSHyon Kim 				if (conntype <
1154*ac88567aSHyon Kim 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1155*ac88567aSHyon Kim 					conntype_str =
1156*ac88567aSHyon Kim 					    SAS_CONNECTOR_TYPE_RESERVED;
1157*ac88567aSHyon Kim 				} else {
1158*ac88567aSHyon Kim 					conntype_str =
1159*ac88567aSHyon Kim 					    SAS_CONNECTOR_TYPE_NOT_DEFINED;
1160*ac88567aSHyon Kim 				}
1161*ac88567aSHyon Kim 			}
1162*ac88567aSHyon Kim 
1163*ac88567aSHyon Kim 			/* set the phy count prop of the expander. */
1164*ac88567aSHyon Kim 			if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1165*ac88567aSHyon Kim 			    TOPO_STORAGE_SAS_CONNECTOR_TYPE,
1166*ac88567aSHyon Kim 			    TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) {
1167*ac88567aSHyon Kim 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1168*ac88567aSHyon Kim 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1169*ac88567aSHyon Kim 				    topo_strerror(err));
1170*ac88567aSHyon Kim 			}
1171*ac88567aSHyon Kim 		}
1172*ac88567aSHyon Kim 	}
1173*ac88567aSHyon Kim 
1174*ac88567aSHyon Kim 	return (0);
1175*ac88567aSHyon Kim }
1176*ac88567aSHyon Kim 
1177*ac88567aSHyon Kim /*
1178*ac88567aSHyon Kim  * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1179*ac88567aSHyon Kim  * nodes.
1180*ac88567aSHyon Kim  */
1181*ac88567aSHyon Kim /*ARGSUSED*/
1182*ac88567aSHyon Kim static int
1183*ac88567aSHyon Kim ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1184*ac88567aSHyon Kim     tnode_t *pnode, ses_enum_chassis_t *cp,
1185*ac88567aSHyon Kim     boolean_t dorange)
1186*ac88567aSHyon Kim {
1187*ac88567aSHyon Kim 	topo_mod_t *mod = sdp->sed_mod;
1188*ac88567aSHyon Kim 	tnode_t	*exptn, *contn;
1189*ac88567aSHyon Kim 	boolean_t found;
1190*ac88567aSHyon Kim 	sas_connector_phy_data_t connectors[64] = {NULL};
1191*ac88567aSHyon Kim 	uint64_t max;
1192*ac88567aSHyon Kim 	ses_enum_node_t *ctlsnp, *xsnp, *consnp;
1193*ac88567aSHyon Kim 	ses_node_t *np = snp->sen_node;
1194*ac88567aSHyon Kim 	nvlist_t *props, *psprops;
1195*ac88567aSHyon Kim 	uint64_t index, psindex, conindex, psstatus, i, j, count;
1196*ac88567aSHyon Kim 	int64_t cidxlist[256] = {NULL};
1197*ac88567aSHyon Kim 	int phycount;
1198*ac88567aSHyon Kim 
1199*ac88567aSHyon Kim 	props = ses_node_props(np);
1200*ac88567aSHyon Kim 
1201*ac88567aSHyon Kim 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
1202*ac88567aSHyon Kim 	    &index) != 0)
1203*ac88567aSHyon Kim 		return (-1);
1204*ac88567aSHyon Kim 
1205*ac88567aSHyon Kim 	/*
1206*ac88567aSHyon Kim 	 * For SES constroller node, check to see if there are
1207*ac88567aSHyon Kim 	 * associated SAS expanders.
1208*ac88567aSHyon Kim 	 */
1209*ac88567aSHyon Kim 	found = B_FALSE;
1210*ac88567aSHyon Kim 	max = 0;
1211*ac88567aSHyon Kim 	for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL;
1212*ac88567aSHyon Kim 	    ctlsnp = topo_list_next(ctlsnp)) {
1213*ac88567aSHyon Kim 		if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) {
1214*ac88567aSHyon Kim 			found = B_TRUE;
1215*ac88567aSHyon Kim 			if (ctlsnp->sen_instance > max)
1216*ac88567aSHyon Kim 				max = ctlsnp->sen_instance;
1217*ac88567aSHyon Kim 		}
1218*ac88567aSHyon Kim 	}
1219*ac88567aSHyon Kim 
1220*ac88567aSHyon Kim 	/*
1221*ac88567aSHyon Kim 	 * No SAS expander found notthing to process.
1222*ac88567aSHyon Kim 	 */
1223*ac88567aSHyon Kim 	if (!found)
1224*ac88567aSHyon Kim 		return (0);
1225*ac88567aSHyon Kim 
1226*ac88567aSHyon Kim 	topo_mod_dprintf(mod, "%s Controller %d: creating "
1227*ac88567aSHyon Kim 	    "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER);
1228*ac88567aSHyon Kim 
1229*ac88567aSHyon Kim 	/*
1230*ac88567aSHyon Kim 	 * The max number represent the number of elements
1231*ac88567aSHyon Kim 	 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1232*ac88567aSHyon Kim 	 * of SET_ET_SAS_EXPANDER type element.
1233*ac88567aSHyon Kim 	 *
1234*ac88567aSHyon Kim 	 * There may be multiple ESC Electronics element(controllers)
1235*ac88567aSHyon Kim 	 * within JBOD(typicall two for redundancy) and SAS expander
1236*ac88567aSHyon Kim 	 * elements are associated with only one of them.  We are
1237*ac88567aSHyon Kim 	 * still creating the range based max number here.
1238*ac88567aSHyon Kim 	 * That will cover the case that all expanders are associated
1239*ac88567aSHyon Kim 	 * with one SES controller.
1240*ac88567aSHyon Kim 	 */
1241*ac88567aSHyon Kim 	if (dorange && topo_node_range_create(mod, pnode,
1242*ac88567aSHyon Kim 	    SASEXPANDER, 0, max) != 0) {
1243*ac88567aSHyon Kim 		topo_mod_dprintf(mod,
1244*ac88567aSHyon Kim 		    "topo_node_create_range() failed: %s",
1245*ac88567aSHyon Kim 		    topo_mod_errmsg(mod));
1246*ac88567aSHyon Kim 		return (-1);
1247*ac88567aSHyon Kim 	}
1248*ac88567aSHyon Kim 
1249*ac88567aSHyon Kim 	/*
1250*ac88567aSHyon Kim 	 * Search exapnders with the parent index matching with
1251*ac88567aSHyon Kim 	 * ESC Electronics element index.
1252*ac88567aSHyon Kim 	 * Note the index used here is a global index across
1253*ac88567aSHyon Kim 	 * SES elements.
1254*ac88567aSHyon Kim 	 */
1255*ac88567aSHyon Kim 	for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL;
1256*ac88567aSHyon Kim 	    xsnp = topo_list_next(xsnp)) {
1257*ac88567aSHyon Kim 		if (xsnp->sen_type == SES_ET_SAS_EXPANDER) {
1258*ac88567aSHyon Kim 			/*
1259*ac88567aSHyon Kim 			 * get the parent ESC controller.
1260*ac88567aSHyon Kim 			 */
1261*ac88567aSHyon Kim 			psprops = ses_node_props(xsnp->sen_node);
1262*ac88567aSHyon Kim 			if (nvlist_lookup_uint64(psprops,
1263*ac88567aSHyon Kim 			    SES_PROP_STATUS_CODE, &psstatus) == 0) {
1264*ac88567aSHyon Kim 				if (psstatus == SES_ESC_NOT_INSTALLED) {
1265*ac88567aSHyon Kim 					/*
1266*ac88567aSHyon Kim 					 * Not installed.
1267*ac88567aSHyon Kim 					 * Don't create a ndoe.
1268*ac88567aSHyon Kim 					 */
1269*ac88567aSHyon Kim 					continue;
1270*ac88567aSHyon Kim 				}
1271*ac88567aSHyon Kim 			} else {
1272*ac88567aSHyon Kim 				/*
1273*ac88567aSHyon Kim 				 * The element should have status code.
1274*ac88567aSHyon Kim 				 * If not there is no way to find
1275*ac88567aSHyon Kim 				 * out if the expander element exist or
1276*ac88567aSHyon Kim 				 * not.
1277*ac88567aSHyon Kim 				 */
1278*ac88567aSHyon Kim 				continue;
1279*ac88567aSHyon Kim 			}
1280*ac88567aSHyon Kim 
1281*ac88567aSHyon Kim 			/* Get the physical parent index to compare. */
1282*ac88567aSHyon Kim 			if (nvlist_lookup_uint64(psprops,
1283*ac88567aSHyon Kim 			    LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
1284*ac88567aSHyon Kim 				if (index == psindex) {
1285*ac88567aSHyon Kim 		/* indentation moved forward */
1286*ac88567aSHyon Kim 		/*
1287*ac88567aSHyon Kim 		 * Handle basic node information of SAS expander
1288*ac88567aSHyon Kim 		 * element - binding to parent node and
1289*ac88567aSHyon Kim 		 * allocating FMRI...
1290*ac88567aSHyon Kim 		 */
1291*ac88567aSHyon Kim 		if (ses_create_generic(sdp, xsnp, pnode, SASEXPANDER,
1292*ac88567aSHyon Kim 		    "SAS-EXPANDER", &exptn) != 0)
1293*ac88567aSHyon Kim 			continue;
1294*ac88567aSHyon Kim 		/*
1295*ac88567aSHyon Kim 		 * Now handle SAS expander unique portion of node creation.
1296*ac88567aSHyon Kim 		 * The max nubmer of the phy count is 256 since SES-2
1297*ac88567aSHyon Kim 		 * defines as 1 byte field.  The cidxlist has the same
1298*ac88567aSHyon Kim 		 * number of elements.
1299*ac88567aSHyon Kim 		 *
1300*ac88567aSHyon Kim 		 * We use size 64 array to store the connectors.
1301*ac88567aSHyon Kim 		 * Typically a connectors associated with 4 phys so that
1302*ac88567aSHyon Kim 		 * matches with the max number of connecters associated
1303*ac88567aSHyon Kim 		 * with an expander.
1304*ac88567aSHyon Kim 		 * The phy count goes up to 38 for Sun supported
1305*ac88567aSHyon Kim 		 * JBOD.
1306*ac88567aSHyon Kim 		 */
1307*ac88567aSHyon Kim 		memset(cidxlist, 0, sizeof (int64_t) * 64);
1308*ac88567aSHyon Kim 		if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount,
1309*ac88567aSHyon Kim 		    cidxlist) != 0) {
1310*ac88567aSHyon Kim 			/*
1311*ac88567aSHyon Kim 			 * error on getting specific prop failed.
1312*ac88567aSHyon Kim 			 * continue on.  Note that the node is
1313*ac88567aSHyon Kim 			 * left bound.
1314*ac88567aSHyon Kim 			 */
1315*ac88567aSHyon Kim 			continue;
1316*ac88567aSHyon Kim 		}
1317*ac88567aSHyon Kim 
1318*ac88567aSHyon Kim 		/*
1319*ac88567aSHyon Kim 		 * count represetns the number of connectors discovered so far.
1320*ac88567aSHyon Kim 		 */
1321*ac88567aSHyon Kim 		count = 0;
1322*ac88567aSHyon Kim 		memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64);
1323*ac88567aSHyon Kim 		for (i = 0; i < phycount; i++) {
1324*ac88567aSHyon Kim 			if (cidxlist[i] != -1) {
1325*ac88567aSHyon Kim 				/* connector index is valid. */
1326*ac88567aSHyon Kim 				for (j = 0; j < count; j++) {
1327*ac88567aSHyon Kim 					if (connectors[j].index ==
1328*ac88567aSHyon Kim 					    cidxlist[i]) {
1329*ac88567aSHyon Kim 						/*
1330*ac88567aSHyon Kim 						 * Just update phy mask.
1331*ac88567aSHyon Kim 						 * The postion for connector
1332*ac88567aSHyon Kim 						 * index lists(cidxlist index)
1333*ac88567aSHyon Kim 						 * is set.
1334*ac88567aSHyon Kim 						 */
1335*ac88567aSHyon Kim 						connectors[j].phy_mask =
1336*ac88567aSHyon Kim 						    connectors[j].phy_mask |
1337*ac88567aSHyon Kim 						    (1ULL << i);
1338*ac88567aSHyon Kim 						break;
1339*ac88567aSHyon Kim 					}
1340*ac88567aSHyon Kim 				}
1341*ac88567aSHyon Kim 				/*
1342*ac88567aSHyon Kim 				 * If j and count matche a  new connector
1343*ac88567aSHyon Kim 				 * index is found.
1344*ac88567aSHyon Kim 				 */
1345*ac88567aSHyon Kim 				if (j == count) {
1346*ac88567aSHyon Kim 					/* add a new index and phy mask. */
1347*ac88567aSHyon Kim 					connectors[count].index = cidxlist[i];
1348*ac88567aSHyon Kim 					connectors[count].phy_mask =
1349*ac88567aSHyon Kim 					    connectors[count].phy_mask |
1350*ac88567aSHyon Kim 					    (1ULL << i);
1351*ac88567aSHyon Kim 					count++;
1352*ac88567aSHyon Kim 				}
1353*ac88567aSHyon Kim 			}
1354*ac88567aSHyon Kim 		}
1355*ac88567aSHyon Kim 
1356*ac88567aSHyon Kim 		/*
1357*ac88567aSHyon Kim 		 * create range for the connector nodes.
1358*ac88567aSHyon Kim 		 * The class index of the ses connector element
1359*ac88567aSHyon Kim 		 * is set as the instance nubmer for the node.
1360*ac88567aSHyon Kim 		 * Even though one expander may not have all connectors
1361*ac88567aSHyon Kim 		 * are associated with we are creating the range with
1362*ac88567aSHyon Kim 		 * max possible instance number.
1363*ac88567aSHyon Kim 		 */
1364*ac88567aSHyon Kim 		found = B_FALSE;
1365*ac88567aSHyon Kim 		max = 0;
1366*ac88567aSHyon Kim 		for (consnp = topo_list_next(&cp->sec_nodes);
1367*ac88567aSHyon Kim 		    consnp != NULL; consnp = topo_list_next(consnp)) {
1368*ac88567aSHyon Kim 			if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1369*ac88567aSHyon Kim 				psprops = ses_node_props(consnp->sen_node);
1370*ac88567aSHyon Kim 				found = B_TRUE;
1371*ac88567aSHyon Kim 				if (consnp->sen_instance > max)
1372*ac88567aSHyon Kim 					max = consnp->sen_instance;
1373*ac88567aSHyon Kim 			}
1374*ac88567aSHyon Kim 		}
1375*ac88567aSHyon Kim 
1376*ac88567aSHyon Kim 		/*
1377*ac88567aSHyon Kim 		 * No SAS connector found nothing to process.
1378*ac88567aSHyon Kim 		 */
1379*ac88567aSHyon Kim 		if (!found)
1380*ac88567aSHyon Kim 			return (0);
1381*ac88567aSHyon Kim 
1382*ac88567aSHyon Kim 		if (dorange && topo_node_range_create(mod, exptn,
1383*ac88567aSHyon Kim 		    RECEPTACLE, 0, max) != 0) {
1384*ac88567aSHyon Kim 			topo_mod_dprintf(mod,
1385*ac88567aSHyon Kim 			    "topo_node_create_range() failed: %s",
1386*ac88567aSHyon Kim 			    topo_mod_errmsg(mod));
1387*ac88567aSHyon Kim 			return (-1);
1388*ac88567aSHyon Kim 		}
1389*ac88567aSHyon Kim 
1390*ac88567aSHyon Kim 		/* search matching connector element using the index. */
1391*ac88567aSHyon Kim 		for (i = 0; i < count; i++) {
1392*ac88567aSHyon Kim 			found = B_FALSE;
1393*ac88567aSHyon Kim 			for (consnp = topo_list_next(&cp->sec_nodes);
1394*ac88567aSHyon Kim 			    consnp != NULL; consnp = topo_list_next(consnp)) {
1395*ac88567aSHyon Kim 				if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1396*ac88567aSHyon Kim 					psprops = ses_node_props(
1397*ac88567aSHyon Kim 					    consnp->sen_node);
1398*ac88567aSHyon Kim 					/*
1399*ac88567aSHyon Kim 					 * Get the physical parent index to
1400*ac88567aSHyon Kim 					 * compare.
1401*ac88567aSHyon Kim 					 * The connector elements are children
1402*ac88567aSHyon Kim 					 * of ESC Electronics element even
1403*ac88567aSHyon Kim 					 * though we enumerate them under
1404*ac88567aSHyon Kim 					 * an expander in libtopo.
1405*ac88567aSHyon Kim 					 */
1406*ac88567aSHyon Kim 					if (nvlist_lookup_uint64(psprops,
1407*ac88567aSHyon Kim 					    SES_PROP_ELEMENT_ONLY_INDEX,
1408*ac88567aSHyon Kim 					    &conindex) == 0) {
1409*ac88567aSHyon Kim 						if (conindex ==
1410*ac88567aSHyon Kim 						    connectors[i].index) {
1411*ac88567aSHyon Kim 							found = B_TRUE;
1412*ac88567aSHyon Kim 							break;
1413*ac88567aSHyon Kim 						}
1414*ac88567aSHyon Kim 					}
1415*ac88567aSHyon Kim 				}
1416*ac88567aSHyon Kim 			}
1417*ac88567aSHyon Kim 
1418*ac88567aSHyon Kim 			/* now create a libtopo node. */
1419*ac88567aSHyon Kim 			if (found) {
1420*ac88567aSHyon Kim 				/* Create generic props. */
1421*ac88567aSHyon Kim 				if (ses_create_generic(sdp, consnp, exptn,
1422*ac88567aSHyon Kim 				    RECEPTACLE, "RECEPTACLE", &contn) !=
1423*ac88567aSHyon Kim 				    0) {
1424*ac88567aSHyon Kim 					continue;
1425*ac88567aSHyon Kim 				}
1426*ac88567aSHyon Kim 				/* Create connector specific props. */
1427*ac88567aSHyon Kim 				if (ses_set_connector_props(sdp, consnp,
1428*ac88567aSHyon Kim 				    contn, connectors[i].phy_mask) != 0) {
1429*ac88567aSHyon Kim 					continue;
1430*ac88567aSHyon Kim 				}
1431*ac88567aSHyon Kim 			}
1432*ac88567aSHyon Kim 		}
1433*ac88567aSHyon Kim 		/* end indentation change */
1434*ac88567aSHyon Kim 				}
1435*ac88567aSHyon Kim 			}
1436*ac88567aSHyon Kim 		}
1437*ac88567aSHyon Kim 	}
1438*ac88567aSHyon Kim 
1439*ac88567aSHyon Kim 	return (0);
1440*ac88567aSHyon Kim }
1441*ac88567aSHyon Kim 
1442*ac88567aSHyon Kim /*
1443*ac88567aSHyon Kim  * Instantiate any protocol specific portion of a node.
1444*ac88567aSHyon Kim  */
1445*ac88567aSHyon Kim /*ARGSUSED*/
1446*ac88567aSHyon Kim static int
1447*ac88567aSHyon Kim ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1448*ac88567aSHyon Kim     tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp,
1449*ac88567aSHyon Kim     boolean_t dorange)
1450*ac88567aSHyon Kim {
1451*ac88567aSHyon Kim 
1452*ac88567aSHyon Kim 	if (type == SES_ET_ESC_ELECTRONICS) {
1453*ac88567aSHyon Kim 		/* create SAS specific children(expanders and connectors. */
1454*ac88567aSHyon Kim 		return (ses_create_esc_sasspecific(sdp, snp, pnode, cp,
1455*ac88567aSHyon Kim 		    dorange));
1456*ac88567aSHyon Kim 	}
1457*ac88567aSHyon Kim 
1458*ac88567aSHyon Kim 	return (0);
1459*ac88567aSHyon Kim }
1460*ac88567aSHyon Kim 
1461940d71d2Seschrock /*
1462940d71d2Seschrock  * Instantiate any children of a given type.
1463940d71d2Seschrock  */
1464940d71d2Seschrock static int
1465940d71d2Seschrock ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
1466d91236feSeschrock     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
1467d91236feSeschrock     boolean_t dorange)
1468940d71d2Seschrock {
1469940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1470940d71d2Seschrock 	boolean_t found;
1471940d71d2Seschrock 	uint64_t max;
1472940d71d2Seschrock 	ses_enum_node_t *snp;
1473*ac88567aSHyon Kim 	tnode_t	*tn;
1474940d71d2Seschrock 
1475940d71d2Seschrock 	/*
1476940d71d2Seschrock 	 * First go through and count how many matching nodes we have.
1477940d71d2Seschrock 	 */
1478940d71d2Seschrock 	max = 0;
1479940d71d2Seschrock 	found = B_FALSE;
1480940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1481940d71d2Seschrock 	    snp = topo_list_next(snp)) {
1482940d71d2Seschrock 		if (snp->sen_type == type) {
1483940d71d2Seschrock 			found = B_TRUE;
1484940d71d2Seschrock 			if (snp->sen_instance > max)
1485940d71d2Seschrock 				max = snp->sen_instance;
1486940d71d2Seschrock 		}
1487940d71d2Seschrock 	}
1488940d71d2Seschrock 
1489940d71d2Seschrock 	/*
1490940d71d2Seschrock 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
1491940d71d2Seschrock 	 * Since we map both of these to 'disk', if an enclosure does this, we
1492940d71d2Seschrock 	 * just ignore the array elements.
1493940d71d2Seschrock 	 */
1494940d71d2Seschrock 	if (!found ||
1495940d71d2Seschrock 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
1496940d71d2Seschrock 		return (0);
1497940d71d2Seschrock 
1498940d71d2Seschrock 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
149953dbcc59SSundeep Panicker 	    cp->sec_csn, max + 1, nodename);
1500940d71d2Seschrock 
1501d91236feSeschrock 	if (dorange && topo_node_range_create(mod, pnode,
1502940d71d2Seschrock 	    nodename, 0, max) != 0) {
1503940d71d2Seschrock 		topo_mod_dprintf(mod,
1504940d71d2Seschrock 		    "topo_node_create_range() failed: %s",
1505940d71d2Seschrock 		    topo_mod_errmsg(mod));
1506940d71d2Seschrock 		return (-1);
1507940d71d2Seschrock 	}
1508940d71d2Seschrock 
1509940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1510940d71d2Seschrock 	    snp = topo_list_next(snp)) {
1511940d71d2Seschrock 		if (snp->sen_type == type) {
1512940d71d2Seschrock 			if (ses_create_generic(sdp, snp, pnode,
1513*ac88567aSHyon Kim 			    nodename, defaultlabel, &tn) != 0)
1514940d71d2Seschrock 				return (-1);
1515*ac88567aSHyon Kim 			/*
1516*ac88567aSHyon Kim 			 * For some SES element there may be protocol specific
1517*ac88567aSHyon Kim 			 * information to process.   Here we are processing
1518*ac88567aSHyon Kim 			 * the association between enclosure controller and
1519*ac88567aSHyon Kim 			 * SAS expanders.
1520*ac88567aSHyon Kim 			 */
1521*ac88567aSHyon Kim 			if (type == SES_ET_ESC_ELECTRONICS) {
1522*ac88567aSHyon Kim 				/* create SAS expander node */
1523*ac88567aSHyon Kim 				if (ses_create_protocol_specific(sdp, snp,
1524*ac88567aSHyon Kim 				    tn, type, cp, dorange) != 0) {
1525*ac88567aSHyon Kim 					return (-1);
1526*ac88567aSHyon Kim 				}
1527*ac88567aSHyon Kim 			}
1528*ac88567aSHyon Kim 
1529940d71d2Seschrock 		}
1530940d71d2Seschrock 	}
1531940d71d2Seschrock 
1532940d71d2Seschrock 	return (0);
1533940d71d2Seschrock }
1534940d71d2Seschrock 
153553dbcc59SSundeep Panicker /*
153653dbcc59SSundeep Panicker  * Instantiate a new subchassis instance in the topology.
153753dbcc59SSundeep Panicker  */
153853dbcc59SSundeep Panicker static int
153953dbcc59SSundeep Panicker ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
154053dbcc59SSundeep Panicker     ses_enum_chassis_t *scp)
154153dbcc59SSundeep Panicker {
154253dbcc59SSundeep Panicker 	topo_mod_t *mod = sdp->sed_mod;
154353dbcc59SSundeep Panicker 	tnode_t *tn;
154453dbcc59SSundeep Panicker 	nvlist_t *props;
154553dbcc59SSundeep Panicker 	nvlist_t *auth = NULL, *fmri = NULL;
154653dbcc59SSundeep Panicker 	uint64_t instance = scp->sec_instance;
154753dbcc59SSundeep Panicker 	char *desc;
154853dbcc59SSundeep Panicker 	char label[128];
154953dbcc59SSundeep Panicker 	char **paths;
155053dbcc59SSundeep Panicker 	int i, err;
155153dbcc59SSundeep Panicker 	ses_enum_target_t *stp;
155253dbcc59SSundeep Panicker 	int ret = -1;
155353dbcc59SSundeep Panicker 
155453dbcc59SSundeep Panicker 	/*
155553dbcc59SSundeep Panicker 	 * Copy authority information from parent enclosure node
155653dbcc59SSundeep Panicker 	 */
155753dbcc59SSundeep Panicker 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
155853dbcc59SSundeep Panicker 		goto error;
155953dbcc59SSundeep Panicker 
156053dbcc59SSundeep Panicker 	/*
156153dbcc59SSundeep Panicker 	 * Record the subchassis serial number in the FMRI.
156253dbcc59SSundeep Panicker 	 * For now, we assume that logical id is the subchassis serial number.
156353dbcc59SSundeep Panicker 	 * If this assumption changes in future, then the following
156453dbcc59SSundeep Panicker 	 * piece of code will need to be updated via an RFE.
156553dbcc59SSundeep Panicker 	 */
156653dbcc59SSundeep Panicker 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
1567*ac88567aSHyon Kim 	    SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL,
1568*ac88567aSHyon Kim 	    NULL)) == NULL) {
156953dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
157053dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
157153dbcc59SSundeep Panicker 		goto error;
157253dbcc59SSundeep Panicker 	}
157353dbcc59SSundeep Panicker 
157453dbcc59SSundeep Panicker 	if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
157553dbcc59SSundeep Panicker 	    instance, fmri)) == NULL) {
157653dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
157753dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
157853dbcc59SSundeep Panicker 		goto error;
157953dbcc59SSundeep Panicker 	}
158053dbcc59SSundeep Panicker 
158153dbcc59SSundeep Panicker 	props = ses_node_props(scp->sec_enclosure);
158253dbcc59SSundeep Panicker 
158353dbcc59SSundeep Panicker 	/*
158453dbcc59SSundeep Panicker 	 * Look for the subchassis label in the following order:
158553dbcc59SSundeep Panicker 	 *	<ses-description>
158653dbcc59SSundeep Panicker 	 *	<ses-class-description> <instance>
158753dbcc59SSundeep Panicker 	 *	<default-type-label> <instance>
158853dbcc59SSundeep Panicker 	 *
158953dbcc59SSundeep Panicker 	 * For subchassis, the default label is "SUBCHASSIS"
159053dbcc59SSundeep Panicker 	 */
159153dbcc59SSundeep Panicker 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
159253dbcc59SSundeep Panicker 	    desc[0] == '\0') {
159353dbcc59SSundeep Panicker 		if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
159453dbcc59SSundeep Panicker 		    &desc) == 0 && desc[0] != '\0')
159553dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label), "%s %llu", desc,
159653dbcc59SSundeep Panicker 			    instance);
159753dbcc59SSundeep Panicker 		else
159853dbcc59SSundeep Panicker 			(void) snprintf(label, sizeof (label),
159953dbcc59SSundeep Panicker 			    "SUBCHASSIS %llu", instance);
160053dbcc59SSundeep Panicker 		desc = label;
160153dbcc59SSundeep Panicker 	}
160253dbcc59SSundeep Panicker 
160353dbcc59SSundeep Panicker 	if (topo_node_label_set(tn, desc, &err) != 0)
160453dbcc59SSundeep Panicker 		goto error;
160553dbcc59SSundeep Panicker 
1606*ac88567aSHyon Kim 	if (ses_set_standard_props(mod, NULL, tn, NULL,
160753dbcc59SSundeep Panicker 	    ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
160853dbcc59SSundeep Panicker 		goto error;
160953dbcc59SSundeep Panicker 
1610*ac88567aSHyon Kim 	/*
1611*ac88567aSHyon Kim 	 * Set the 'chassis-type' property for this subchassis.  This is either
1612*ac88567aSHyon Kim 	 * 'ses-class-description' or 'subchassis'.
1613*ac88567aSHyon Kim 	 */
1614*ac88567aSHyon Kim 	if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0)
1615*ac88567aSHyon Kim 		desc = "subchassis";
1616*ac88567aSHyon Kim 
1617*ac88567aSHyon Kim 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
1618*ac88567aSHyon Kim 	    TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) {
1619*ac88567aSHyon Kim 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
1620*ac88567aSHyon Kim 		    TOPO_PROP_CHASSIS_TYPE, topo_strerror(err));
1621*ac88567aSHyon Kim 		goto error;
1622*ac88567aSHyon Kim 	}
1623*ac88567aSHyon Kim 
162453dbcc59SSundeep Panicker 	/*
162553dbcc59SSundeep Panicker 	 * For enclosures, we want to include all possible targets (for upgrade
162653dbcc59SSundeep Panicker 	 * purposes).
162753dbcc59SSundeep Panicker 	 */
162853dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
162953dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
163053dbcc59SSundeep Panicker 		;
163153dbcc59SSundeep Panicker 
163253dbcc59SSundeep Panicker 	verify(i != 0);
163353dbcc59SSundeep Panicker 	paths = alloca(i * sizeof (char *));
163453dbcc59SSundeep Panicker 
163553dbcc59SSundeep Panicker 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
163653dbcc59SSundeep Panicker 	    stp = topo_list_next(stp), i++)
163753dbcc59SSundeep Panicker 		paths[i] = stp->set_devpath;
163853dbcc59SSundeep Panicker 
163953dbcc59SSundeep Panicker 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
164053dbcc59SSundeep Panicker 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
164153dbcc59SSundeep Panicker 	    i, &err) != 0) {
164253dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
164353dbcc59SSundeep Panicker 		    TOPO_PROP_PATHS, topo_strerror(err));
164453dbcc59SSundeep Panicker 		goto error;
164553dbcc59SSundeep Panicker 	}
164653dbcc59SSundeep Panicker 
164753dbcc59SSundeep Panicker 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
164853dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
164953dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
165053dbcc59SSundeep Panicker 		goto error;
165153dbcc59SSundeep Panicker 	}
165253dbcc59SSundeep Panicker 
165353dbcc59SSundeep Panicker 	/*
165453dbcc59SSundeep Panicker 	 * Create the nodes for controllers and bays.
165553dbcc59SSundeep Panicker 	 */
165653dbcc59SSundeep Panicker 	if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
165753dbcc59SSundeep Panicker 	    CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
165853dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
165953dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0 ||
166053dbcc59SSundeep Panicker 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
166153dbcc59SSundeep Panicker 	    BAY, "BAY", scp, B_TRUE) != 0)
166253dbcc59SSundeep Panicker 		goto error;
166353dbcc59SSundeep Panicker 
166453dbcc59SSundeep Panicker 	ret = 0;
166553dbcc59SSundeep Panicker 
166653dbcc59SSundeep Panicker error:
166753dbcc59SSundeep Panicker 	nvlist_free(auth);
166853dbcc59SSundeep Panicker 	nvlist_free(fmri);
166953dbcc59SSundeep Panicker 	return (ret);
167053dbcc59SSundeep Panicker }
167153dbcc59SSundeep Panicker 
1672940d71d2Seschrock /*
1673940d71d2Seschrock  * Instantiate a new chassis instance in the topology.
1674940d71d2Seschrock  */
1675940d71d2Seschrock static int
1676940d71d2Seschrock ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
1677940d71d2Seschrock {
1678940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
1679940d71d2Seschrock 	nvlist_t *props;
1680940d71d2Seschrock 	char *raw_manufacturer, *raw_model, *raw_revision;
1681940d71d2Seschrock 	char *manufacturer = NULL, *model = NULL, *product = NULL;
1682940d71d2Seschrock 	char *revision = NULL;
1683940d71d2Seschrock 	char *serial;
16846efb64caSEric Schrock 	char **paths;
1685940d71d2Seschrock 	size_t prodlen;
1686940d71d2Seschrock 	tnode_t *tn;
1687940d71d2Seschrock 	nvlist_t *fmri = NULL, *auth = NULL;
1688940d71d2Seschrock 	int ret = -1;
1689940d71d2Seschrock 	ses_enum_node_t *snp;
16906efb64caSEric Schrock 	ses_enum_target_t *stp;
169153dbcc59SSundeep Panicker 	ses_enum_chassis_t *scp;
16926efb64caSEric Schrock 	int i, err;
169353dbcc59SSundeep Panicker 	uint64_t sc_count = 0;
1694940d71d2Seschrock 
1695d91236feSeschrock 	/*
1696d91236feSeschrock 	 * Ignore any internal enclosures.
1697d91236feSeschrock 	 */
1698d91236feSeschrock 	if (cp->sec_internal)
1699d91236feSeschrock 		return (0);
1700d91236feSeschrock 
1701940d71d2Seschrock 	/*
1702940d71d2Seschrock 	 * Check to see if there are any devices presennt in the chassis.  If
1703940d71d2Seschrock 	 * not, ignore the chassis alltogether.  This is most useful for
1704940d71d2Seschrock 	 * ignoring internal HBAs that present a SES target but don't actually
1705940d71d2Seschrock 	 * manage any of the devices.
1706940d71d2Seschrock 	 */
1707940d71d2Seschrock 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1708940d71d2Seschrock 	    snp = topo_list_next(snp)) {
1709940d71d2Seschrock 		if (snp->sen_type == SES_ET_DEVICE ||
1710940d71d2Seschrock 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
1711940d71d2Seschrock 			break;
1712940d71d2Seschrock 	}
1713940d71d2Seschrock 
1714940d71d2Seschrock 	if (snp == NULL)
1715940d71d2Seschrock 		return (0);
1716940d71d2Seschrock 
1717940d71d2Seschrock 	props = ses_node_props(cp->sec_enclosure);
1718940d71d2Seschrock 
1719940d71d2Seschrock 	/*
1720940d71d2Seschrock 	 * We use the following property mappings:
1721940d71d2Seschrock 	 *
1722940d71d2Seschrock 	 * 	manufacturer		vendor-id
1723940d71d2Seschrock 	 * 	model			product-id
1724940d71d2Seschrock 	 * 	serial-number		libses-chassis-serial
1725940d71d2Seschrock 	 */
1726940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
1727940d71d2Seschrock 	    &raw_manufacturer) == 0);
1728940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
1729940d71d2Seschrock 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
1730940d71d2Seschrock 	    &raw_revision) == 0);
1731940d71d2Seschrock 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
1732940d71d2Seschrock 
1733940d71d2Seschrock 	/*
1734940d71d2Seschrock 	 * To construct the authority information, we 'clean' each string by
1735940d71d2Seschrock 	 * removing any offensive characters and trimmming whitespace.  For the
1736940d71d2Seschrock 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
1737940d71d2Seschrock 	 * also take the numerical serial number and convert it to a string.
1738940d71d2Seschrock 	 */
1739940d71d2Seschrock 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
1740940d71d2Seschrock 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
1741940d71d2Seschrock 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
1742940d71d2Seschrock 		goto error;
1743940d71d2Seschrock 	}
1744940d71d2Seschrock 
1745940d71d2Seschrock 	prodlen = strlen(manufacturer) + strlen(model) + 2;
1746940d71d2Seschrock 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
1747940d71d2Seschrock 		goto error;
1748940d71d2Seschrock 
1749940d71d2Seschrock 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
1750940d71d2Seschrock 
1751940d71d2Seschrock 	/*
1752940d71d2Seschrock 	 * Construct the topo node and bind it to our parent.
1753940d71d2Seschrock 	 */
1754940d71d2Seschrock 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
1755940d71d2Seschrock 		goto error;
1756940d71d2Seschrock 
1757940d71d2Seschrock 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
1758940d71d2Seschrock 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
1759940d71d2Seschrock 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1760940d71d2Seschrock 		goto error;
1761940d71d2Seschrock 	}
1762940d71d2Seschrock 
1763940d71d2Seschrock 	/*
1764940d71d2Seschrock 	 * We pass NULL for the parent FMRI because there is no resource
1765940d71d2Seschrock 	 * associated with it.  For the toplevel enclosure, we leave the
1766940d71d2Seschrock 	 * serial/part/revision portions empty, which are reserved for
1767940d71d2Seschrock 	 * individual components within the chassis.
1768940d71d2Seschrock 	 */
1769940d71d2Seschrock 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
1770940d71d2Seschrock 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
1771940d71d2Seschrock 	    model, revision, serial)) == NULL) {
1772940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1773940d71d2Seschrock 		    topo_mod_errmsg(mod));
1774940d71d2Seschrock 		goto error;
1775940d71d2Seschrock 	}
1776940d71d2Seschrock 
1777940d71d2Seschrock 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
1778940d71d2Seschrock 	    cp->sec_instance, fmri)) == NULL) {
1779940d71d2Seschrock 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1780940d71d2Seschrock 		    topo_mod_errmsg(mod));
1781940d71d2Seschrock 		goto error;
1782940d71d2Seschrock 	}
1783940d71d2Seschrock 
1784940d71d2Seschrock 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
1785940d71d2Seschrock 		topo_mod_dprintf(mod,
1786940d71d2Seschrock 		    "topo_method_register() failed: %s",
1787940d71d2Seschrock 		    topo_mod_errmsg(mod));
1788940d71d2Seschrock 		goto error;
1789940d71d2Seschrock 	}
1790940d71d2Seschrock 
1791*ac88567aSHyon Kim 	if (ses_set_standard_props(mod, NULL, tn, auth,
1792940d71d2Seschrock 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
1793940d71d2Seschrock 		goto error;
1794940d71d2Seschrock 
17956efb64caSEric Schrock 	/*
17966efb64caSEric Schrock 	 * For enclosures, we want to include all possible targets (for upgrade
17976efb64caSEric Schrock 	 * purposes).
17986efb64caSEric Schrock 	 */
17996efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
18006efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
18016efb64caSEric Schrock 		;
18026efb64caSEric Schrock 
18036efb64caSEric Schrock 	verify(i != 0);
18046efb64caSEric Schrock 	paths = alloca(i * sizeof (char *));
18056efb64caSEric Schrock 
18066efb64caSEric Schrock 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
18076efb64caSEric Schrock 	    stp = topo_list_next(stp), i++)
18086efb64caSEric Schrock 		paths[i] = stp->set_devpath;
18096efb64caSEric Schrock 
18106efb64caSEric Schrock 
18116efb64caSEric Schrock 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
18126efb64caSEric Schrock 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
18136efb64caSEric Schrock 	    i, &err) != 0) {
18146efb64caSEric Schrock 		topo_mod_dprintf(mod,
18156efb64caSEric Schrock 		    "failed to create property %s: %s\n",
18166efb64caSEric Schrock 		    TOPO_PROP_PATHS, topo_strerror(err));
18176efb64caSEric Schrock 		goto error;
18186efb64caSEric Schrock 	}
18196efb64caSEric Schrock 
1820940d71d2Seschrock 	/*
1821*ac88567aSHyon Kim 	 * Create the nodes for power supplies, fans, controllers and devices.
1822*ac88567aSHyon Kim 	 * Note that SAS exopander nodes and connector nodes are handled
1823*ac88567aSHyon Kim 	 * through protocol specific processing of controllers.
1824940d71d2Seschrock 	 */
1825940d71d2Seschrock 	if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
1826d91236feSeschrock 	    PSU, "PSU", cp, B_TRUE) != 0 ||
1827940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_COOLING,
1828d91236feSeschrock 	    FAN, "FAN", cp, B_TRUE) != 0 ||
1829940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
1830d91236feSeschrock 	    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
1831940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
1832d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0 ||
1833940d71d2Seschrock 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
1834d91236feSeschrock 	    BAY, "BAY", cp, B_TRUE) != 0)
1835940d71d2Seschrock 		goto error;
1836940d71d2Seschrock 
1837*ac88567aSHyon Kim 	if (cp->sec_maxinstance >= 0 &&
1838*ac88567aSHyon Kim 	    (topo_node_range_create(mod, tn, SUBCHASSIS, 0,
1839*ac88567aSHyon Kim 	    cp->sec_maxinstance) != 0)) {
184053dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
184153dbcc59SSundeep Panicker 		    topo_mod_errmsg(mod));
184253dbcc59SSundeep Panicker 		goto error;
184353dbcc59SSundeep Panicker 	}
184453dbcc59SSundeep Panicker 
184553dbcc59SSundeep Panicker 	for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
184653dbcc59SSundeep Panicker 	    scp = topo_list_next(scp)) {
184753dbcc59SSundeep Panicker 
184853dbcc59SSundeep Panicker 		if (ses_create_subchassis(sdp, tn, scp) != 0)
184953dbcc59SSundeep Panicker 			goto error;
185053dbcc59SSundeep Panicker 
185153dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "created Subchassis node with "
1852*ac88567aSHyon Kim 		    "instance %u\nand target (%s) under Chassis with CSN %s",
1853*ac88567aSHyon Kim 		    scp->sec_instance, scp->sec_target->set_devpath,
1854*ac88567aSHyon Kim 		    cp->sec_csn);
185553dbcc59SSundeep Panicker 
185653dbcc59SSundeep Panicker 		sc_count++;
185753dbcc59SSundeep Panicker 	}
185853dbcc59SSundeep Panicker 
185953dbcc59SSundeep Panicker 	topo_mod_dprintf(mod, "%s: created %llu %s nodes",
186053dbcc59SSundeep Panicker 	    cp->sec_csn, sc_count, SUBCHASSIS);
186153dbcc59SSundeep Panicker 
18626efb64caSEric Schrock 	cp->sec_target->set_refcount++;
18636efb64caSEric Schrock 	topo_node_setspecific(tn, cp->sec_target);
1864d91236feSeschrock 
1865940d71d2Seschrock 	ret = 0;
1866940d71d2Seschrock error:
1867940d71d2Seschrock 	topo_mod_strfree(mod, manufacturer);
1868940d71d2Seschrock 	topo_mod_strfree(mod, model);
1869940d71d2Seschrock 	topo_mod_strfree(mod, revision);
1870940d71d2Seschrock 	topo_mod_strfree(mod, product);
1871940d71d2Seschrock 
1872940d71d2Seschrock 	nvlist_free(fmri);
1873940d71d2Seschrock 	nvlist_free(auth);
1874940d71d2Seschrock 	return (ret);
1875940d71d2Seschrock }
1876940d71d2Seschrock 
1877d91236feSeschrock /*
1878d91236feSeschrock  * Create a bay node explicitly enumerated via XML.
1879d91236feSeschrock  */
1880d91236feSeschrock static int
1881d91236feSeschrock ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
1882d91236feSeschrock {
1883d91236feSeschrock 	topo_mod_t *mod = sdp->sed_mod;
1884d91236feSeschrock 	ses_enum_chassis_t *cp;
1885d91236feSeschrock 
1886d91236feSeschrock 	/*
1887d91236feSeschrock 	 * Iterate over chassis looking for an internal enclosure.  This
1888d91236feSeschrock 	 * property is set via a vendor-specific plugin, and there should only
1889d91236feSeschrock 	 * ever be a single internal chassis in a system.
1890d91236feSeschrock 	 */
1891d91236feSeschrock 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1892d91236feSeschrock 	    cp = topo_list_next(cp)) {
1893d91236feSeschrock 		if (cp->sec_internal)
1894d91236feSeschrock 			break;
1895d91236feSeschrock 	}
1896d91236feSeschrock 
1897d91236feSeschrock 	if (cp == NULL) {
1898d91236feSeschrock 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
1899d91236feSeschrock 		return (-1);
1900d91236feSeschrock 	}
1901d91236feSeschrock 
1902d91236feSeschrock 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
1903d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0 ||
1904d91236feSeschrock 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
1905d91236feSeschrock 	    BAY, "BAY", cp, B_FALSE) != 0)
1906d91236feSeschrock 		return (-1);
1907d91236feSeschrock 
1908d91236feSeschrock 	return (0);
1909d91236feSeschrock }
191053dbcc59SSundeep Panicker 
191153dbcc59SSundeep Panicker /*
191253dbcc59SSundeep Panicker  * Initialize chassis or subchassis.
191353dbcc59SSundeep Panicker  */
191453dbcc59SSundeep Panicker static int
191553dbcc59SSundeep Panicker ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
191653dbcc59SSundeep Panicker     ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
1917*ac88567aSHyon Kim     uint64_t subchassis, ses_chassis_type_e flags)
191853dbcc59SSundeep Panicker {
191953dbcc59SSundeep Panicker 	boolean_t internal, ident;
192053dbcc59SSundeep Panicker 
192153dbcc59SSundeep Panicker 	assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
192253dbcc59SSundeep Panicker 	    SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
192353dbcc59SSundeep Panicker 
1924*ac88567aSHyon Kim 	assert(cp != NULL);
1925*ac88567aSHyon Kim 	assert(np != NULL);
1926*ac88567aSHyon Kim 	assert(props != NULL);
192753dbcc59SSundeep Panicker 
192853dbcc59SSundeep Panicker 	if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
192953dbcc59SSundeep Panicker 		assert(pcp != NULL);
193053dbcc59SSundeep Panicker 
1931*ac88567aSHyon Kim 	topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)",
1932*ac88567aSHyon Kim 	    sdp->sed_name, subchassis, flags);
193353dbcc59SSundeep Panicker 
193453dbcc59SSundeep Panicker 	if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
193553dbcc59SSundeep Panicker 
193653dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "new chassis/subchassis");
193753dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
193853dbcc59SSundeep Panicker 		    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
193953dbcc59SSundeep Panicker 			cp->sec_internal = internal;
194053dbcc59SSundeep Panicker 
194153dbcc59SSundeep Panicker 		cp->sec_enclosure = np;
194253dbcc59SSundeep Panicker 		cp->sec_target = sdp->sed_target;
194353dbcc59SSundeep Panicker 
194453dbcc59SSundeep Panicker 		if (flags & SES_NEW_CHASSIS) {
1945*ac88567aSHyon Kim 			if (!cp->sec_internal)
1946*ac88567aSHyon Kim 				cp->sec_instance = sdp->sed_instance++;
194753dbcc59SSundeep Panicker 			topo_list_append(&sdp->sed_chassis, cp);
194853dbcc59SSundeep Panicker 		} else {
1949*ac88567aSHyon Kim 			if (subchassis != NO_SUBCHASSIS)
1950*ac88567aSHyon Kim 				cp->sec_instance = subchassis;
1951*ac88567aSHyon Kim 			else
1952*ac88567aSHyon Kim 				cp->sec_instance = pcp->sec_scinstance++;
1953*ac88567aSHyon Kim 
1954*ac88567aSHyon Kim 			if (cp->sec_instance > pcp->sec_maxinstance)
1955*ac88567aSHyon Kim 				pcp->sec_maxinstance = cp->sec_instance;
1956*ac88567aSHyon Kim 
195753dbcc59SSundeep Panicker 			topo_list_append(&pcp->sec_subchassis, cp);
195853dbcc59SSundeep Panicker 		}
195953dbcc59SSundeep Panicker 
196053dbcc59SSundeep Panicker 	} else {
196153dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "dup chassis/subchassis");
196253dbcc59SSundeep Panicker 		if (nvlist_lookup_boolean_value(props,
196353dbcc59SSundeep Panicker 		    SES_PROP_IDENT, &ident) == 0) {
196453dbcc59SSundeep Panicker 			topo_mod_dprintf(mod,  "overriding enclosure node");
196553dbcc59SSundeep Panicker 
196653dbcc59SSundeep Panicker 			cp->sec_enclosure = np;
196753dbcc59SSundeep Panicker 			cp->sec_target = sdp->sed_target;
196853dbcc59SSundeep Panicker 		}
196953dbcc59SSundeep Panicker 	}
197053dbcc59SSundeep Panicker 
197153dbcc59SSundeep Panicker 	topo_list_append(&cp->sec_targets, sdp->sed_target);
197253dbcc59SSundeep Panicker 	sdp->sed_current = cp;
197353dbcc59SSundeep Panicker 
197453dbcc59SSundeep Panicker 	return (0);
197553dbcc59SSundeep Panicker }
197653dbcc59SSundeep Panicker 
1977940d71d2Seschrock /*
1978940d71d2Seschrock  * Gather nodes from the current SES target into our chassis list, merging the
1979940d71d2Seschrock  * results if necessary.
1980940d71d2Seschrock  */
1981940d71d2Seschrock static ses_walk_action_t
1982940d71d2Seschrock ses_enum_gather(ses_node_t *np, void *data)
1983940d71d2Seschrock {
1984940d71d2Seschrock 	nvlist_t *props = ses_node_props(np);
1985940d71d2Seschrock 	ses_enum_data_t *sdp = data;
1986940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
198753dbcc59SSundeep Panicker 	ses_enum_chassis_t *cp, *scp;
1988940d71d2Seschrock 	ses_enum_node_t *snp;
19890b32bb8bSEric Schrock 	ses_alt_node_t *sap;
1990940d71d2Seschrock 	char *csn;
1991940d71d2Seschrock 	uint64_t instance, type;
1992d91236feSeschrock 	uint64_t prevstatus, status;
199353dbcc59SSundeep Panicker 	boolean_t report;
1994*ac88567aSHyon Kim 	uint64_t subchassis = NO_SUBCHASSIS;
1995940d71d2Seschrock 
1996940d71d2Seschrock 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
1997940d71d2Seschrock 		/*
1998940d71d2Seschrock 		 * If we have already identified the chassis for this target,
1999940d71d2Seschrock 		 * then this is a secondary enclosure and we should ignore it,
2000940d71d2Seschrock 		 * along with the rest of the tree (since this is depth-first).
2001940d71d2Seschrock 		 */
2002940d71d2Seschrock 		if (sdp->sed_current != NULL)
2003940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
2004940d71d2Seschrock 
2005940d71d2Seschrock 		/*
2006940d71d2Seschrock 		 * Go through the list of chassis we have seen so far and see
2007940d71d2Seschrock 		 * if this serial number matches one of the known values.
200853dbcc59SSundeep Panicker 		 * If so, check whether this enclosure is a subchassis.
2009940d71d2Seschrock 		 */
2010940d71d2Seschrock 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
2011940d71d2Seschrock 		    &csn) != 0)
2012940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
2013940d71d2Seschrock 
2014*ac88567aSHyon Kim 		(void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
2015*ac88567aSHyon Kim 		    &subchassis);
201653dbcc59SSundeep Panicker 
201753dbcc59SSundeep Panicker 		topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
2018*ac88567aSHyon Kim 		    "CSN (%s), subchassis (%llu)", sdp->sed_name, csn,
2019*ac88567aSHyon Kim 		    subchassis);
202053dbcc59SSundeep Panicker 
202153dbcc59SSundeep Panicker 		/*
202253dbcc59SSundeep Panicker 		 * We need to determine whether this enclosure node
202353dbcc59SSundeep Panicker 		 * represents a chassis or a subchassis. Since we may
202453dbcc59SSundeep Panicker 		 * receive the enclosure nodes in a non-deterministic
202553dbcc59SSundeep Panicker 		 * manner, we need to account for all possible combinations:
202653dbcc59SSundeep Panicker 		 *	1. Chassis for the current CSN has not yet been
202753dbcc59SSundeep Panicker 		 *	   allocated
202853dbcc59SSundeep Panicker 		 *		1.1 This is a new chassis:
202953dbcc59SSundeep Panicker 		 *			allocate and instantiate the chassis
203053dbcc59SSundeep Panicker 		 *		1.2 This is a new subchassis:
203153dbcc59SSundeep Panicker 		 *			allocate a placeholder chassis
203253dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
203353dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
203453dbcc59SSundeep Panicker 		 *	2. Chassis for the current CSN has been allocated
203553dbcc59SSundeep Panicker 		 *		2.1 This is a duplicate chassis enclosure
203653dbcc59SSundeep Panicker 		 *			check whether to override old chassis
203753dbcc59SSundeep Panicker 		 *			append to chassis' target list
203853dbcc59SSundeep Panicker 		 *		2.2 Only placeholder chassis exists
203953dbcc59SSundeep Panicker 		 *			fill in the chassis fields
204053dbcc59SSundeep Panicker 		 *		2.3 This is a new subchassis
204153dbcc59SSundeep Panicker 		 *			allocate and instantiate the subchassis
204253dbcc59SSundeep Panicker 		 *			link the subchassis to the chassis
204353dbcc59SSundeep Panicker 		 *		2.4 This is a duplicate subchassis enclosure
204453dbcc59SSundeep Panicker 		 *			 check whether to override old chassis
204553dbcc59SSundeep Panicker 		 *			 append to chassis' target list
204653dbcc59SSundeep Panicker 		 */
204753dbcc59SSundeep Panicker 
2048940d71d2Seschrock 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
204953dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
205053dbcc59SSundeep Panicker 			if (strcmp(cp->sec_csn, csn) == 0)
2051940d71d2Seschrock 				break;
2052940d71d2Seschrock 
2053940d71d2Seschrock 		if (cp == NULL) {
205453dbcc59SSundeep Panicker 			/* 1. Haven't seen a chassis with this CSN before */
2055940d71d2Seschrock 
2056940d71d2Seschrock 			if ((cp = topo_mod_zalloc(mod,
2057940d71d2Seschrock 			    sizeof (ses_enum_chassis_t))) == NULL)
2058940d71d2Seschrock 				goto error;
2059940d71d2Seschrock 
2060*ac88567aSHyon Kim 			cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
2061*ac88567aSHyon Kim 			cp->sec_maxinstance = -1;
2062940d71d2Seschrock 			cp->sec_csn = csn;
206353dbcc59SSundeep Panicker 
2064*ac88567aSHyon Kim 			if (subchassis == NO_SUBCHASSIS) {
206553dbcc59SSundeep Panicker 				/* 1.1 This is a new chassis */
206653dbcc59SSundeep Panicker 
206753dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
2068*ac88567aSHyon Kim 				    "chassis with CSN %s", sdp->sed_name, csn);
206953dbcc59SSundeep Panicker 
207053dbcc59SSundeep Panicker 				if (ses_init_chassis(mod, sdp, NULL, cp,
2071*ac88567aSHyon Kim 				    np, props, NO_SUBCHASSIS,
2072*ac88567aSHyon Kim 				    SES_NEW_CHASSIS) < 0)
207353dbcc59SSundeep Panicker 					goto error;
207453dbcc59SSundeep Panicker 			} else {
207553dbcc59SSundeep Panicker 				/* 1.2 This is a new subchassis */
207653dbcc59SSundeep Panicker 
207753dbcc59SSundeep Panicker 				topo_mod_dprintf(mod, "%s: Initialize new "
2078*ac88567aSHyon Kim 				    "subchassis with CSN %s and index %llu",
2079*ac88567aSHyon Kim 				    sdp->sed_name, csn, subchassis);
208053dbcc59SSundeep Panicker 
208153dbcc59SSundeep Panicker 				if ((scp = topo_mod_zalloc(mod,
208253dbcc59SSundeep Panicker 				    sizeof (ses_enum_chassis_t))) == NULL)
208353dbcc59SSundeep Panicker 					goto error;
208453dbcc59SSundeep Panicker 
208553dbcc59SSundeep Panicker 				scp->sec_csn = csn;
208653dbcc59SSundeep Panicker 
2087*ac88567aSHyon Kim 				if (ses_init_chassis(mod, sdp, cp, scp, np,
2088*ac88567aSHyon Kim 				    props, subchassis, SES_NEW_SUBCHASSIS) < 0)
208953dbcc59SSundeep Panicker 					goto error;
209053dbcc59SSundeep Panicker 			}
20916efb64caSEric Schrock 		} else {
2092*ac88567aSHyon Kim 			/*
2093*ac88567aSHyon Kim 			 * We have a chassis or subchassis with this CSN.  If
2094*ac88567aSHyon Kim 			 * it's a chassis, we must check to see whether it is
2095*ac88567aSHyon Kim 			 * a placeholder previously created because we found a
2096*ac88567aSHyon Kim 			 * subchassis with this CSN.  We will know that because
2097*ac88567aSHyon Kim 			 * the sec_target value will not be set; it is set only
2098*ac88567aSHyon Kim 			 * in ses_init_chassis().  In that case, initialise it
2099*ac88567aSHyon Kim 			 * as a new chassis; otherwise, it's a duplicate and we
2100*ac88567aSHyon Kim 			 * need to append only.
2101*ac88567aSHyon Kim 			 */
2102*ac88567aSHyon Kim 			if (subchassis == NO_SUBCHASSIS) {
2103*ac88567aSHyon Kim 				if (cp->sec_target != NULL) {
210453dbcc59SSundeep Panicker 					/* 2.1 This is a duplicate chassis */
210553dbcc59SSundeep Panicker 
210653dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
2107*ac88567aSHyon Kim 					    "duplicate chassis with CSN (%s)",
2108*ac88567aSHyon Kim 					    sdp->sed_name, csn);
210953dbcc59SSundeep Panicker 
211053dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, NULL, cp,
2111*ac88567aSHyon Kim 					    np, props, NO_SUBCHASSIS,
211253dbcc59SSundeep Panicker 					    SES_DUP_CHASSIS) < 0)
211353dbcc59SSundeep Panicker 						goto error;
211453dbcc59SSundeep Panicker 				} else {
2115*ac88567aSHyon Kim 					/* Placeholder chassis - init it up */
211653dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize"
2117*ac88567aSHyon Kim 					    "placeholder chassis with CSN %s",
2118*ac88567aSHyon Kim 					    sdp->sed_name, csn);
211953dbcc59SSundeep Panicker 
2120*ac88567aSHyon Kim 					if (ses_init_chassis(mod, sdp, NULL,
2121*ac88567aSHyon Kim 					    cp, np, props, NO_SUBCHASSIS,
212253dbcc59SSundeep Panicker 					    SES_NEW_CHASSIS) < 0)
212353dbcc59SSundeep Panicker 						goto error;
212453dbcc59SSundeep Panicker 
212553dbcc59SSundeep Panicker 				}
212653dbcc59SSundeep Panicker 			} else {
212753dbcc59SSundeep Panicker 				/* This is a subchassis */
212853dbcc59SSundeep Panicker 
212953dbcc59SSundeep Panicker 				for (scp = topo_list_next(&cp->sec_subchassis);
213053dbcc59SSundeep Panicker 				    scp != NULL; scp = topo_list_next(scp))
2131*ac88567aSHyon Kim 					if (scp->sec_instance == subchassis)
213253dbcc59SSundeep Panicker 						break;
213353dbcc59SSundeep Panicker 
213453dbcc59SSundeep Panicker 				if (scp == NULL) {
213553dbcc59SSundeep Panicker 					/* 2.3 This is a new subchassis */
213653dbcc59SSundeep Panicker 
213753dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Initialize "
213853dbcc59SSundeep Panicker 					    "new subchassis with CSN (%s) "
213953dbcc59SSundeep Panicker 					    "and LID (%s)",
2140*ac88567aSHyon Kim 					    sdp->sed_name, csn);
214153dbcc59SSundeep Panicker 
214253dbcc59SSundeep Panicker 					if ((scp = topo_mod_zalloc(mod,
214353dbcc59SSundeep Panicker 					    sizeof (ses_enum_chassis_t)))
214453dbcc59SSundeep Panicker 					    == NULL)
214553dbcc59SSundeep Panicker 						goto error;
214653dbcc59SSundeep Panicker 
214753dbcc59SSundeep Panicker 					scp->sec_csn = csn;
214853dbcc59SSundeep Panicker 
214953dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
2150*ac88567aSHyon Kim 					    np, props, subchassis,
215153dbcc59SSundeep Panicker 					    SES_NEW_SUBCHASSIS) < 0)
215253dbcc59SSundeep Panicker 						goto error;
215353dbcc59SSundeep Panicker 				} else {
215453dbcc59SSundeep Panicker 					/* 2.4 This is a duplicate subchassis */
215553dbcc59SSundeep Panicker 
215653dbcc59SSundeep Panicker 					topo_mod_dprintf(mod, "%s: Append "
215753dbcc59SSundeep Panicker 					    "duplicate subchassis with "
2158*ac88567aSHyon Kim 					    "CSN (%s)", sdp->sed_name, csn);
215953dbcc59SSundeep Panicker 
216053dbcc59SSundeep Panicker 					if (ses_init_chassis(mod, sdp, cp, scp,
2161*ac88567aSHyon Kim 					    np, props, subchassis,
216253dbcc59SSundeep Panicker 					    SES_DUP_SUBCHASSIS) < 0)
216353dbcc59SSundeep Panicker 						goto error;
216453dbcc59SSundeep Panicker 				}
21656efb64caSEric Schrock 			}
2166940d71d2Seschrock 		}
2167940d71d2Seschrock 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
2168940d71d2Seschrock 		/*
2169940d71d2Seschrock 		 * If we haven't yet seen an enclosure node and identified the
2170940d71d2Seschrock 		 * current chassis, something is very wrong; bail out.
2171940d71d2Seschrock 		 */
2172940d71d2Seschrock 		if (sdp->sed_current == NULL)
2173940d71d2Seschrock 			return (SES_WALK_ACTION_TERMINATE);
2174940d71d2Seschrock 
2175940d71d2Seschrock 		/*
2176940d71d2Seschrock 		 * If this isn't one of the element types we care about, then
2177940d71d2Seschrock 		 * ignore it.
2178940d71d2Seschrock 		 */
2179940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
2180940d71d2Seschrock 		    &type) == 0);
2181940d71d2Seschrock 		if (type != SES_ET_DEVICE &&
2182940d71d2Seschrock 		    type != SES_ET_ARRAY_DEVICE &&
2183940d71d2Seschrock 		    type != SES_ET_COOLING &&
2184940d71d2Seschrock 		    type != SES_ET_POWER_SUPPLY &&
2185*ac88567aSHyon Kim 		    type != SES_ET_ESC_ELECTRONICS &&
2186*ac88567aSHyon Kim 		    type != SES_ET_SAS_EXPANDER &&
2187*ac88567aSHyon Kim 		    type != SES_ET_SAS_CONNECTOR)
2188940d71d2Seschrock 			return (SES_WALK_ACTION_CONTINUE);
2189940d71d2Seschrock 
2190940d71d2Seschrock 		/*
2191940d71d2Seschrock 		 * Get the current instance number and see if we already know
2192940d71d2Seschrock 		 * about this element.  If so, it means we have multiple paths
2193940d71d2Seschrock 		 * to the same elements, and we should ignore the current path.
2194940d71d2Seschrock 		 */
2195940d71d2Seschrock 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
2196940d71d2Seschrock 		    &instance) == 0);
2197940d71d2Seschrock 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
2198940d71d2Seschrock 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
2199940d71d2Seschrock 			    &instance);
2200940d71d2Seschrock 
2201940d71d2Seschrock 		cp = sdp->sed_current;
2202940d71d2Seschrock 
2203940d71d2Seschrock 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2204940d71d2Seschrock 		    snp = topo_list_next(snp)) {
2205940d71d2Seschrock 			if (snp->sen_type == type &&
2206940d71d2Seschrock 			    snp->sen_instance == instance)
2207d91236feSeschrock 				break;
2208d91236feSeschrock 		}
2209d91236feSeschrock 
2210d91236feSeschrock 		/*
2211d91236feSeschrock 		 * We prefer the new element under the following circumstances:
2212d91236feSeschrock 		 *
2213d91236feSeschrock 		 * - The currently known element's status is unknown or not
2214d91236feSeschrock 		 *   available, but the new element has a known status.  This
2215d91236feSeschrock 		 *   occurs if a given element is only available through a
2216d91236feSeschrock 		 *   particular target.
2217d91236feSeschrock 		 *
2218d91236feSeschrock 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
2219d91236feSeschrock 		 *   property is set.  This allows us to get reliable firmware
2220d91236feSeschrock 		 *   revision information from the enclosure node.
2221d91236feSeschrock 		 */
2222d91236feSeschrock 		if (snp != NULL) {
2223d91236feSeschrock 			if (nvlist_lookup_uint64(
2224d91236feSeschrock 			    ses_node_props(snp->sen_node),
2225d91236feSeschrock 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
2226d91236feSeschrock 				prevstatus = SES_ESC_UNSUPPORTED;
2227d91236feSeschrock 			if (nvlist_lookup_uint64(
2228d91236feSeschrock 			    props, SES_PROP_STATUS_CODE, &status) != 0)
2229d91236feSeschrock 				status = SES_ESC_UNSUPPORTED;
2230d91236feSeschrock 			if (nvlist_lookup_boolean_value(
2231d91236feSeschrock 			    props, SES_PROP_REPORT, &report) != 0)
2232d91236feSeschrock 				report = B_FALSE;
2233d91236feSeschrock 
2234d91236feSeschrock 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
2235d91236feSeschrock 			    !SES_STATUS_UNAVAIL(status)) ||
2236d91236feSeschrock 			    (type == SES_ET_ESC_ELECTRONICS &&
2237d91236feSeschrock 			    report)) {
2238d91236feSeschrock 				snp->sen_node = np;
2239d91236feSeschrock 				snp->sen_target = sdp->sed_target;
2240d91236feSeschrock 			}
2241d91236feSeschrock 
22420b32bb8bSEric Schrock 			if ((sap = topo_mod_zalloc(mod,
22430b32bb8bSEric Schrock 			    sizeof (ses_alt_node_t))) == NULL)
22440b32bb8bSEric Schrock 				goto error;
22450b32bb8bSEric Schrock 
22460b32bb8bSEric Schrock 			sap->san_node = np;
22470b32bb8bSEric Schrock 			topo_list_append(&snp->sen_alt_nodes, sap);
22480b32bb8bSEric Schrock 
2249d91236feSeschrock 			return (SES_WALK_ACTION_CONTINUE);
2250940d71d2Seschrock 		}
2251940d71d2Seschrock 
2252940d71d2Seschrock 		if ((snp = topo_mod_zalloc(mod,
2253940d71d2Seschrock 		    sizeof (ses_enum_node_t))) == NULL)
2254940d71d2Seschrock 			goto error;
2255940d71d2Seschrock 
22560b32bb8bSEric Schrock 		if ((sap = topo_mod_zalloc(mod,
22570b32bb8bSEric Schrock 		    sizeof (ses_alt_node_t))) == NULL) {
22580b32bb8bSEric Schrock 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
22590b32bb8bSEric Schrock 			goto error;
22600b32bb8bSEric Schrock 		}
22610b32bb8bSEric Schrock 
2262940d71d2Seschrock 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
2263940d71d2Seschrock 		    sdp->sed_name, type, instance);
2264940d71d2Seschrock 		snp->sen_node = np;
2265940d71d2Seschrock 		snp->sen_type = type;
2266940d71d2Seschrock 		snp->sen_instance = instance;
2267940d71d2Seschrock 		snp->sen_target = sdp->sed_target;
22680b32bb8bSEric Schrock 		sap->san_node = np;
22690b32bb8bSEric Schrock 		topo_list_append(&snp->sen_alt_nodes, sap);
2270940d71d2Seschrock 		topo_list_append(&cp->sec_nodes, snp);
2271940d71d2Seschrock 
2272940d71d2Seschrock 		if (type == SES_ET_DEVICE)
2273940d71d2Seschrock 			cp->sec_hasdev = B_TRUE;
2274940d71d2Seschrock 	}
2275940d71d2Seschrock 
2276940d71d2Seschrock 	return (SES_WALK_ACTION_CONTINUE);
2277940d71d2Seschrock 
2278940d71d2Seschrock error:
2279940d71d2Seschrock 	sdp->sed_errno = -1;
2280940d71d2Seschrock 	return (SES_WALK_ACTION_TERMINATE);
2281940d71d2Seschrock }
2282940d71d2Seschrock 
2283940d71d2Seschrock static int
2284940d71d2Seschrock ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
2285940d71d2Seschrock {
2286940d71d2Seschrock 	topo_mod_t *mod = sdp->sed_mod;
2287940d71d2Seschrock 	DIR *dir;
2288940d71d2Seschrock 	struct dirent *dp;
2289940d71d2Seschrock 	char path[PATH_MAX];
2290940d71d2Seschrock 	ses_enum_target_t *stp;
2291940d71d2Seschrock 	int err = -1;
2292940d71d2Seschrock 
2293940d71d2Seschrock 	/*
2294940d71d2Seschrock 	 * Open the SES target directory and iterate over any available
2295940d71d2Seschrock 	 * targets.
2296940d71d2Seschrock 	 */
2297940d71d2Seschrock 	if ((dir = opendir(dirpath)) == NULL) {
2298940d71d2Seschrock 		/*
2299940d71d2Seschrock 		 * If the SES target directory does not exist, then return as if
2300940d71d2Seschrock 		 * there are no active targets.
2301940d71d2Seschrock 		 */
2302940d71d2Seschrock 		topo_mod_dprintf(mod, "failed to open ses "
2303940d71d2Seschrock 		    "directory '%s'", dirpath);
2304940d71d2Seschrock 		return (0);
2305940d71d2Seschrock 	}
2306940d71d2Seschrock 
2307940d71d2Seschrock 	while ((dp = readdir(dir)) != NULL) {
2308940d71d2Seschrock 		if (strcmp(dp->d_name, ".") == 0 ||
2309940d71d2Seschrock 		    strcmp(dp->d_name, "..") == 0)
2310940d71d2Seschrock 			continue;
2311940d71d2Seschrock 
2312940d71d2Seschrock 		/*
2313940d71d2Seschrock 		 * Create a new target instance and take a snapshot.
2314940d71d2Seschrock 		 */
2315940d71d2Seschrock 		if ((stp = topo_mod_zalloc(mod,
2316940d71d2Seschrock 		    sizeof (ses_enum_target_t))) == NULL)
2317940d71d2Seschrock 			goto error;
2318940d71d2Seschrock 
23190b32bb8bSEric Schrock 		(void) pthread_mutex_init(&stp->set_lock, NULL);
23200b32bb8bSEric Schrock 
2321940d71d2Seschrock 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
2322940d71d2Seschrock 		    dp->d_name);
2323940d71d2Seschrock 
2324940d71d2Seschrock 		/*
2325940d71d2Seschrock 		 * We keep track of the SES device path and export it on a
2326940d71d2Seschrock 		 * per-node basis to allow higher level software to get to the
2327940d71d2Seschrock 		 * corresponding SES state.
2328940d71d2Seschrock 		 */
2329940d71d2Seschrock 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
2330940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
2331940d71d2Seschrock 			goto error;
2332940d71d2Seschrock 		}
2333940d71d2Seschrock 
2334940d71d2Seschrock 		if ((stp->set_target =
2335940d71d2Seschrock 		    ses_open(LIBSES_VERSION, path)) == NULL) {
2336940d71d2Seschrock 			topo_mod_dprintf(mod, "failed to open ses target "
2337940d71d2Seschrock 			    "'%s': %s", dp->d_name, ses_errmsg());
2338940d71d2Seschrock 			topo_mod_strfree(mod, stp->set_devpath);
2339940d71d2Seschrock 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
2340940d71d2Seschrock 			continue;
2341940d71d2Seschrock 		}
2342940d71d2Seschrock 
2343940d71d2Seschrock 		stp->set_refcount = 1;
2344940d71d2Seschrock 		sdp->sed_target = stp;
2345940d71d2Seschrock 		stp->set_snap = ses_snap_hold(stp->set_target);
2346525b85dbSEric Schrock 		stp->set_snaptime = gethrtime();
2347940d71d2Seschrock 
2348940d71d2Seschrock 		/*
2349940d71d2Seschrock 		 * Enumerate over all SES elements and merge them into the
2350940d71d2Seschrock 		 * correct ses_enum_chassis_t.
2351940d71d2Seschrock 		 */
2352940d71d2Seschrock 		sdp->sed_current = NULL;
2353940d71d2Seschrock 		sdp->sed_errno = 0;
2354940d71d2Seschrock 		sdp->sed_name = dp->d_name;
2355940d71d2Seschrock 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
2356940d71d2Seschrock 
2357940d71d2Seschrock 		if (sdp->sed_errno != 0)
2358940d71d2Seschrock 			goto error;
2359940d71d2Seschrock 	}
2360940d71d2Seschrock 
2361940d71d2Seschrock 	err = 0;
2362940d71d2Seschrock error:
2363940d71d2Seschrock 	closedir(dir);
2364940d71d2Seschrock 	return (err);
2365940d71d2Seschrock }
2366940d71d2Seschrock 
2367940d71d2Seschrock static void
2368940d71d2Seschrock ses_release(topo_mod_t *mod, tnode_t *tn)
2369940d71d2Seschrock {
2370940d71d2Seschrock 	ses_enum_target_t *stp;
2371940d71d2Seschrock 
2372940d71d2Seschrock 	if ((stp = topo_node_getspecific(tn)) != NULL)
2373940d71d2Seschrock 		ses_target_free(mod, stp);
2374940d71d2Seschrock }
2375940d71d2Seschrock 
2376940d71d2Seschrock /*ARGSUSED*/
2377940d71d2Seschrock static int
2378940d71d2Seschrock ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
2379940d71d2Seschrock     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
2380940d71d2Seschrock {
2381940d71d2Seschrock 	ses_enum_chassis_t *cp;
2382d91236feSeschrock 	ses_enum_data_t *data;
2383940d71d2Seschrock 
2384940d71d2Seschrock 	/*
2385940d71d2Seschrock 	 * Check to make sure we're being invoked sensibly, and that we're not
2386940d71d2Seschrock 	 * being invoked as part of a post-processing step.
2387940d71d2Seschrock 	 */
2388d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
2389940d71d2Seschrock 		return (0);
2390940d71d2Seschrock 
2391940d71d2Seschrock 	/*
2392d91236feSeschrock 	 * If this is the first time we've called our enumeration method, then
2393d91236feSeschrock 	 * gather information about any available enclosures.
2394940d71d2Seschrock 	 */
2395d91236feSeschrock 	if ((data = topo_mod_getspecific(mod)) == NULL) {
2396d91236feSeschrock 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
2397d91236feSeschrock 		    NULL)
2398d91236feSeschrock 			return (-1);
2399940d71d2Seschrock 
2400d91236feSeschrock 		data->sed_mod = mod;
2401d91236feSeschrock 		topo_mod_setspecific(mod, data);
2402d91236feSeschrock 
2403*ac88567aSHyon Kim 		if (dev_list_gather(mod, &data->sed_devs) != 0)
2404d91236feSeschrock 			goto error;
2405d91236feSeschrock 
2406d91236feSeschrock 		/*
2407d91236feSeschrock 		 * We search both the ses(7D) and sgen(7D) locations, so we are
2408d91236feSeschrock 		 * independent of any particular driver class bindings.
2409d91236feSeschrock 		 */
2410d91236feSeschrock 		if (ses_process_dir("/dev/es", data) != 0 ||
2411d91236feSeschrock 		    ses_process_dir("/dev/scsi/ses", data) != 0)
2412d91236feSeschrock 			goto error;
2413d91236feSeschrock 	}
2414d91236feSeschrock 
2415d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
2416d91236feSeschrock 		/*
2417d91236feSeschrock 		 * This is a request to enumerate external enclosures.  Go
2418d91236feSeschrock 		 * through all the targets and create chassis nodes where
2419d91236feSeschrock 		 * necessary.
2420d91236feSeschrock 		 */
2421d91236feSeschrock 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
2422d91236feSeschrock 		    cp = topo_list_next(cp)) {
2423d91236feSeschrock 			if (ses_create_chassis(data, rnode, cp) != 0)
2424d91236feSeschrock 				goto error;
2425d91236feSeschrock 		}
2426d91236feSeschrock 	} else {
2427d91236feSeschrock 		/*
2428d91236feSeschrock 		 * This is a request to enumerate a specific bay underneath the
2429d91236feSeschrock 		 * root chassis (for internal disks).
2430d91236feSeschrock 		 */
2431d91236feSeschrock 		if (ses_create_bays(data, rnode) != 0)
2432940d71d2Seschrock 			goto error;
2433940d71d2Seschrock 	}
2434940d71d2Seschrock 
2435d91236feSeschrock 	/*
2436d91236feSeschrock 	 * This is a bit of a kludge.  In order to allow internal disks to be
2437d91236feSeschrock 	 * enumerated and share snapshot-specific information with the external
2438d91236feSeschrock 	 * enclosure enumeration, we rely on the fact that we will be invoked
2439d91236feSeschrock 	 * for the 'ses-enclosure' node last.
2440d91236feSeschrock 	 */
2441d91236feSeschrock 	if (strcmp(name, SES_ENCLOSURE) == 0) {
244253dbcc59SSundeep Panicker 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
244353dbcc59SSundeep Panicker 		    cp = topo_list_next(cp))
244453dbcc59SSundeep Panicker 			ses_data_free(data, cp);
244553dbcc59SSundeep Panicker 		ses_data_free(data, NULL);
2446d91236feSeschrock 		topo_mod_setspecific(mod, NULL);
2447d91236feSeschrock 	}
2448940d71d2Seschrock 	return (0);
2449940d71d2Seschrock 
2450940d71d2Seschrock error:
245153dbcc59SSundeep Panicker 	for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
245253dbcc59SSundeep Panicker 	    cp = topo_list_next(cp))
245353dbcc59SSundeep Panicker 		ses_data_free(data, cp);
245453dbcc59SSundeep Panicker 	ses_data_free(data, NULL);
2455d91236feSeschrock 	topo_mod_setspecific(mod, NULL);
2456940d71d2Seschrock 	return (-1);
2457940d71d2Seschrock }
2458940d71d2Seschrock 
2459940d71d2Seschrock static const topo_modops_t ses_ops =
2460940d71d2Seschrock 	{ ses_enum, ses_release };
2461940d71d2Seschrock 
2462940d71d2Seschrock static topo_modinfo_t ses_info =
2463940d71d2Seschrock 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
2464940d71d2Seschrock 
2465940d71d2Seschrock /*ARGSUSED*/
2466940d71d2Seschrock int
2467940d71d2Seschrock _topo_init(topo_mod_t *mod, topo_version_t version)
2468940d71d2Seschrock {
2469940d71d2Seschrock 	if (getenv("TOPOSESDEBUG") != NULL)
2470940d71d2Seschrock 		topo_mod_setdebug(mod);
2471940d71d2Seschrock 
2472940d71d2Seschrock 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
2473940d71d2Seschrock 	    SES_ENCLOSURE);
2474940d71d2Seschrock 
2475940d71d2Seschrock 	return (topo_mod_register(mod, &ses_info, TOPO_VERSION));
2476940d71d2Seschrock }
2477940d71d2Seschrock 
2478940d71d2Seschrock void
2479940d71d2Seschrock _topo_fini(topo_mod_t *mod)
2480940d71d2Seschrock {
2481940d71d2Seschrock 	topo_mod_unregister(mod);
2482940d71d2Seschrock }
2483