xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ses/ses.c (revision 904e51f67bfac9f3ec88d9254757474c448808eb)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <alloca.h>
27 #include <dirent.h>
28 #include <devid.h>
29 #include <fm/libdiskstatus.h>
30 #include <inttypes.h>
31 #include <pthread.h>
32 #include <strings.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/dkio.h>
36 #include <sys/fm/protocol.h>
37 #include <sys/libdevid.h>
38 #include <sys/scsi/scsi_types.h>
39 #include <sys/byteorder.h>
40 
41 #include "disk.h"
42 #include "ses.h"
43 
44 #define	SES_VERSION	1
45 
46 #define	SES_STARTING_SUBCHASSIS 256	/* valid subchassis IDs are uint8_t */
47 #define	NO_SUBCHASSIS	((uint64_t)-1)
48 
49 static int ses_snap_freq = 250;		/* in milliseconds */
50 
51 #define	SES_STATUS_UNAVAIL(s)	\
52 	((s) == SES_ESC_UNSUPPORTED || (s) >= SES_ESC_NOT_INSTALLED)
53 
54 /*
55  * Because multiple SES targets can be part of a single chassis, we construct
56  * our own hierarchy that takes this into account.  These SES targets may refer
57  * to the same devices (multiple paths) or to different devices (managing
58  * different portions of the space).  We arrange things into a
59  * ses_enum_enclosure_t, which contains a set of ses targets, and a list of all
60  * nodes found so far.
61  */
62 typedef struct ses_alt_node {
63 	topo_list_t		san_link;
64 	ses_node_t		*san_node;
65 } ses_alt_node_t;
66 
67 typedef struct ses_enum_node {
68 	topo_list_t		sen_link;
69 	ses_node_t		*sen_node;
70 	topo_list_t		sen_alt_nodes;
71 	uint64_t		sen_type;
72 	uint64_t		sen_instance;
73 	ses_enum_target_t	*sen_target;
74 } ses_enum_node_t;
75 
76 typedef struct ses_enum_chassis {
77 	topo_list_t		sec_link;
78 	topo_list_t		sec_subchassis;
79 	topo_list_t		sec_nodes;
80 	topo_list_t		sec_targets;
81 	const char		*sec_csn;
82 	ses_node_t		*sec_enclosure;
83 	ses_enum_target_t	*sec_target;
84 	topo_instance_t		sec_instance;
85 	topo_instance_t		sec_scinstance;
86 	topo_instance_t		sec_maxinstance;
87 	boolean_t		sec_hasdev;
88 	boolean_t		sec_internal;
89 } ses_enum_chassis_t;
90 
91 typedef struct ses_enum_data {
92 	topo_list_t		sed_devs;
93 	topo_list_t		sed_chassis;
94 	ses_enum_chassis_t	*sed_current;
95 	ses_enum_target_t	*sed_target;
96 	int			sed_errno;
97 	char			*sed_name;
98 	topo_mod_t		*sed_mod;
99 	topo_instance_t		sed_instance;
100 } ses_enum_data_t;
101 
102 typedef struct sas_connector_phy_data {
103 	uint64_t    index;
104 	uint64_t    phy_mask;
105 } sas_connector_phy_data_t;
106 
107 typedef struct sas_connector_type {
108 	uint64_t    type;
109 	char	    *name;
110 } sas_connector_type_t;
111 
112 static const sas_connector_type_t sas_connector_type_list[] = {
113 	{   0x0, "Information unknown"  },
114 	{   0x1, "External SAS 4x receptacle (see SAS-2 and SFF-8470)"	},
115 	{   0x2, "Exteranl Mini SAS 4x receptacle (see SAS-2 and SFF-8088)" },
116 	{   0xF, "Vendor-specific external connector"	},
117 	{   0x10, "Internal wide SAS 4i plug (see SAS-2 and SFF-8484)"	},
118 	{   0x11,
119 	"Internal wide Mini SAS 4i receptacle (see SAS-2 and SFF-8087)"	},
120 	{   0x20, "Internal SAS Drive receptacle (see SAS-2 and SFF-8482)"  },
121 	{   0x21, "Internal SATA host plug (see SAS-2 and SATA-2)"  },
122 	{   0x22, "Internal SAS Drive plug (see SAS-2 and SFF-8482)"	},
123 	{   0x23, "Internal SATA device plug (see SAS-2 and SATA-2)"	},
124 	{   0x2F, "Internal SAS virtual connector"  },
125 	{   0x3F, "Vendor-specific internal connector"	},
126 	{   0x70, "Other Vendor-specific connector"	},
127 	{   0x71, "Other Vendor-specific connector"	},
128 	{   0x72, "Other Vendor-specific connector"	},
129 	{   0x73, "Other Vendor-specific connector"	},
130 	{   0x74, "Other Vendor-specific connector"	},
131 	{   0x75, "Other Vendor-specific connector"	},
132 	{   0x76, "Other Vendor-specific connector"	},
133 	{   0x77, "Other Vendor-specific connector"	},
134 	{   0x78, "Other Vendor-specific connector"	},
135 	{   0x79, "Other Vendor-specific connector"	},
136 	{   0x7A, "Other Vendor-specific connector"	},
137 	{   0x7B, "Other Vendor-specific connector"	},
138 	{   0x7C, "Other Vendor-specific connector"	},
139 	{   0x7D, "Other Vendor-specific connector"	},
140 	{   0x7E, "Other Vendor-specific connector"	},
141 	{   0x7F, "Other Vendor-specific connector"	},
142 	{   0x80, "Not Defined"	}
143 };
144 
145 #define	SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED  0x80
146 #define	SAS_CONNECTOR_TYPE_NOT_DEFINED \
147 	"Connector type not definedi by SES-2 standard"
148 #define	SAS_CONNECTOR_TYPE_RESERVED \
149 	"Connector type reserved by SES-2 standard"
150 
151 typedef enum {
152 	SES_NEW_CHASSIS		= 0x1,
153 	SES_NEW_SUBCHASSIS	= 0x2,
154 	SES_DUP_CHASSIS		= 0x4,
155 	SES_DUP_SUBCHASSIS	= 0x8
156 } ses_chassis_type_e;
157 
158 static const topo_pgroup_info_t io_pgroup = {
159 	TOPO_PGROUP_IO,
160 	TOPO_STABILITY_PRIVATE,
161 	TOPO_STABILITY_PRIVATE,
162 	1
163 };
164 
165 static const topo_pgroup_info_t storage_pgroup = {
166 	TOPO_PGROUP_STORAGE,
167 	TOPO_STABILITY_PRIVATE,
168 	TOPO_STABILITY_PRIVATE,
169 	1
170 };
171 
172 static int ses_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
173     nvlist_t **);
174 static int ses_contains(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
175     nvlist_t **);
176 
177 static const topo_method_t ses_component_methods[] = {
178 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
179 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ses_present },
180 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
181 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
182 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
183 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
184 	    topo_method_sensor_failure },
185 	{ NULL }
186 };
187 
188 static const topo_method_t ses_bay_methods[] = {
189 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
190 	    TOPO_STABILITY_INTERNAL, ses_node_enum_facility },
191 	{ NULL }
192 };
193 
194 static const topo_method_t ses_enclosure_methods[] = {
195 	{ TOPO_METH_CONTAINS, TOPO_METH_CONTAINS_DESC,
196 	    TOPO_METH_CONTAINS_VERSION, TOPO_STABILITY_INTERNAL, ses_contains },
197 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
198 	    TOPO_STABILITY_INTERNAL, ses_enc_enum_facility },
199 	{ NULL }
200 };
201 
202 static void
203 ses_target_free(topo_mod_t *mod, ses_enum_target_t *stp)
204 {
205 	if (--stp->set_refcount == 0) {
206 		ses_snap_rele(stp->set_snap);
207 		ses_close(stp->set_target);
208 		topo_mod_strfree(mod, stp->set_devpath);
209 		topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
210 	}
211 }
212 
213 static void
214 ses_data_free(ses_enum_data_t *sdp, ses_enum_chassis_t *pcp)
215 {
216 	topo_mod_t *mod = sdp->sed_mod;
217 	ses_enum_chassis_t *cp;
218 	ses_enum_node_t *np;
219 	ses_enum_target_t *tp;
220 	ses_alt_node_t *ap;
221 	topo_list_t *cpl;
222 
223 
224 	if (pcp != NULL)
225 		cpl = &pcp->sec_subchassis;
226 	else
227 		cpl = &sdp->sed_chassis;
228 
229 	while ((cp = topo_list_next(cpl)) != NULL) {
230 		topo_list_delete(cpl, cp);
231 
232 		while ((np = topo_list_next(&cp->sec_nodes)) != NULL) {
233 			while ((ap = topo_list_next(&np->sen_alt_nodes)) !=
234 			    NULL) {
235 				topo_list_delete(&np->sen_alt_nodes, ap);
236 				topo_mod_free(mod, ap, sizeof (ses_alt_node_t));
237 			}
238 			topo_list_delete(&cp->sec_nodes, np);
239 			topo_mod_free(mod, np, sizeof (ses_enum_node_t));
240 		}
241 
242 		while ((tp = topo_list_next(&cp->sec_targets)) != NULL) {
243 			topo_list_delete(&cp->sec_targets, tp);
244 			ses_target_free(mod, tp);
245 		}
246 
247 		topo_mod_free(mod, cp, sizeof (ses_enum_chassis_t));
248 	}
249 
250 	if (pcp == NULL) {
251 		dev_list_free(mod, &sdp->sed_devs);
252 		topo_mod_free(mod, sdp, sizeof (ses_enum_data_t));
253 	}
254 }
255 
256 /*
257  * For enclosure nodes, we have a special contains method.  By default, the hc
258  * walker will compare the node name and instance number to determine if an
259  * FMRI matches.  For enclosures where the enumeration order is impossible to
260  * predict, we instead use the chassis-id as a unique identifier, and ignore
261  * the instance number.
262  */
263 static int
264 fmri_contains(topo_mod_t *mod, nvlist_t *nv1, nvlist_t *nv2)
265 {
266 	uint8_t v1, v2;
267 	nvlist_t **hcp1, **hcp2;
268 	int err, i;
269 	uint_t nhcp1, nhcp2;
270 	nvlist_t *a1, *a2;
271 	char *c1, *c2;
272 	int mindepth;
273 
274 	if (nvlist_lookup_uint8(nv1, FM_VERSION, &v1) != 0 ||
275 	    nvlist_lookup_uint8(nv2, FM_VERSION, &v2) != 0 ||
276 	    v1 > FM_HC_SCHEME_VERSION || v2 > FM_HC_SCHEME_VERSION)
277 		return (topo_mod_seterrno(mod, EMOD_FMRI_VERSION));
278 
279 	err = nvlist_lookup_nvlist_array(nv1, FM_FMRI_HC_LIST, &hcp1, &nhcp1);
280 	err |= nvlist_lookup_nvlist_array(nv2, FM_FMRI_HC_LIST, &hcp2, &nhcp2);
281 	if (err != 0)
282 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
283 
284 	/*
285 	 * If the chassis-id doesn't match, then these FMRIs are not
286 	 * equivalent.  If one of the FMRIs doesn't have a chassis ID, then we
287 	 * have no choice but to fall back to the instance ID.
288 	 */
289 	if (nvlist_lookup_nvlist(nv1, FM_FMRI_AUTHORITY, &a1) == 0 &&
290 	    nvlist_lookup_nvlist(nv2, FM_FMRI_AUTHORITY, &a2) == 0 &&
291 	    nvlist_lookup_string(a1, FM_FMRI_AUTH_CHASSIS, &c1) == 0 &&
292 	    nvlist_lookup_string(a2, FM_FMRI_AUTH_CHASSIS, &c2) == 0) {
293 		if (strcmp(c1, c2) != 0)
294 			return (0);
295 
296 		mindepth = 1;
297 	} else {
298 		mindepth = 0;
299 	}
300 
301 	if (nhcp2 < nhcp1)
302 		return (0);
303 
304 	for (i = 0; i < nhcp1; i++) {
305 		char *nm1 = NULL;
306 		char *nm2 = NULL;
307 		char *id1 = NULL;
308 		char *id2 = NULL;
309 
310 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_NAME, &nm1);
311 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_NAME, &nm2);
312 		(void) nvlist_lookup_string(hcp1[i], FM_FMRI_HC_ID, &id1);
313 		(void) nvlist_lookup_string(hcp2[i], FM_FMRI_HC_ID, &id2);
314 		if (nm1 == NULL || nm2 == NULL || id1 == NULL || id2 == NULL)
315 			return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
316 
317 		if (strcmp(nm1, nm2) == 0 &&
318 		    (i < mindepth || strcmp(id1, id2) == 0))
319 			continue;
320 
321 		return (0);
322 	}
323 
324 	return (1);
325 }
326 
327 /*ARGSUSED*/
328 static int
329 ses_contains(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
330     nvlist_t *in, nvlist_t **out)
331 {
332 	int ret;
333 	nvlist_t *nv1, *nv2;
334 
335 	if (version > TOPO_METH_CONTAINS_VERSION)
336 		return (topo_mod_seterrno(mod, EMOD_VER_NEW));
337 
338 	if (nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, &nv1) != 0 ||
339 	    nvlist_lookup_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, &nv2) != 0)
340 		return (topo_mod_seterrno(mod, EMOD_METHOD_INVAL));
341 
342 	ret = fmri_contains(mod, nv1, nv2);
343 	if (ret < 0)
344 		return (-1);
345 
346 	if (topo_mod_nvalloc(mod, out, NV_UNIQUE_NAME) == 0) {
347 		if (nvlist_add_uint32(*out, TOPO_METH_CONTAINS_RET,
348 		    ret) == 0)
349 			return (0);
350 		else
351 			nvlist_free(*out);
352 	}
353 
354 	return (-1);
355 
356 }
357 
358 /*
359  * Return a current instance of the node.  This is somewhat complicated because
360  * we need to take a new snapshot in order to get the new data, but we don't
361  * want to be constantly taking SES snapshots if the consumer is going to do a
362  * series of queries.  So we adopt the strategy of assuming that the SES state
363  * is not going to be rapidly changing, and limit our snapshot frequency to
364  * some defined bounds.
365  */
366 ses_node_t *
367 ses_node_lock(topo_mod_t *mod, tnode_t *tn)
368 {
369 	ses_enum_target_t *tp = topo_node_getspecific(tn);
370 	hrtime_t now;
371 	ses_snap_t *snap;
372 	int err;
373 	uint64_t nodeid;
374 	ses_node_t *np;
375 
376 	if (tp == NULL) {
377 		(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
378 		return (NULL);
379 	}
380 
381 	(void) pthread_mutex_lock(&tp->set_lock);
382 
383 	/*
384 	 * Determine if we need to take a new snapshot.
385 	 */
386 	now = gethrtime();
387 
388 	if (now - tp->set_snaptime > (ses_snap_freq * 1000 * 1000) &&
389 	    (snap = ses_snap_new(tp->set_target)) != NULL) {
390 		if (ses_snap_generation(snap) !=
391 		    ses_snap_generation(tp->set_snap)) {
392 			/*
393 			 * If we find ourselves in this situation, we're in
394 			 * trouble.  The generation count has changed, which
395 			 * indicates that our current topology is out of date.
396 			 * But we need to consult the new topology in order to
397 			 * determine presence at this moment in time.  We can't
398 			 * go back and change the topo snapshot in situ, so
399 			 * we'll just have to fail the call in this unlikely
400 			 * scenario.
401 			 */
402 			ses_snap_rele(snap);
403 			(void) topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP);
404 			(void) pthread_mutex_unlock(&tp->set_lock);
405 			return (NULL);
406 		} else {
407 			ses_snap_rele(tp->set_snap);
408 			tp->set_snap = snap;
409 		}
410 		tp->set_snaptime = gethrtime();
411 	}
412 
413 	snap = tp->set_snap;
414 
415 	verify(topo_prop_get_uint64(tn, TOPO_PGROUP_SES,
416 	    TOPO_PROP_NODE_ID, &nodeid, &err) == 0);
417 	verify((np = ses_node_lookup(snap, nodeid)) != NULL);
418 
419 	return (np);
420 }
421 
422 /*ARGSUSED*/
423 void
424 ses_node_unlock(topo_mod_t *mod, tnode_t *tn)
425 {
426 	ses_enum_target_t *tp = topo_node_getspecific(tn);
427 
428 	verify(tp != NULL);
429 
430 	(void) pthread_mutex_unlock(&tp->set_lock);
431 }
432 
433 /*
434  * Determine if the element is present.
435  */
436 /*ARGSUSED*/
437 static int
438 ses_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
439     nvlist_t *in, nvlist_t **out)
440 {
441 	boolean_t present;
442 	ses_node_t *np;
443 	nvlist_t *props, *nvl;
444 	uint64_t status;
445 
446 	if ((np = ses_node_lock(mod, tn)) == NULL)
447 		return (-1);
448 
449 	verify((props = ses_node_props(np)) != NULL);
450 	verify(nvlist_lookup_uint64(props,
451 	    SES_PROP_STATUS_CODE, &status) == 0);
452 
453 	ses_node_unlock(mod, tn);
454 
455 	present = (status != SES_ESC_NOT_INSTALLED);
456 
457 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
458 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
459 
460 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET,
461 	    present) != 0) {
462 		nvlist_free(nvl);
463 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
464 	}
465 
466 	*out = nvl;
467 
468 	return (0);
469 }
470 
471 /*
472  * Sets standard properties for a ses node (enclosure, bay, controller
473  * or expander).
474  * This includes setting the FRU, as well as setting the
475  * authority information.  When  the fru topo node(frutn) is not NULL
476  * its resouce should be used as FRU.
477  */
478 static int
479 ses_set_standard_props(topo_mod_t *mod, tnode_t *frutn, tnode_t *tn,
480     nvlist_t *auth, uint64_t nodeid, const char *path)
481 {
482 	int err;
483 	char *product, *chassis;
484 	nvlist_t *fmri;
485 	topo_pgroup_info_t pgi;
486 
487 	/*
488 	 * Set the authority explicitly if specified.
489 	 */
490 	if (auth) {
491 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT,
492 		    &product) == 0);
493 		verify(nvlist_lookup_string(auth, FM_FMRI_AUTH_CHASSIS,
494 		    &chassis) == 0);
495 		if (topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
496 		    FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, product,
497 		    &err) != 0 ||
498 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
499 		    FM_FMRI_AUTH_CHASSIS, TOPO_PROP_IMMUTABLE, chassis,
500 		    &err) != 0 ||
501 		    topo_prop_set_string(tn, FM_FMRI_AUTHORITY,
502 		    FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, "",
503 		    &err) != 0) {
504 			topo_mod_dprintf(mod, "failed to add authority "
505 			    "properties: %s\n", topo_strerror(err));
506 			return (topo_mod_seterrno(mod, err));
507 		}
508 	}
509 
510 	/*
511 	 * Copy the resource and set that as the FRU.
512 	 */
513 	if (frutn != NULL) {
514 		if (topo_node_resource(frutn, &fmri, &err) != 0) {
515 			topo_mod_dprintf(mod,
516 			    "topo_node_resource() failed : %s\n",
517 			    topo_strerror(err));
518 			return (topo_mod_seterrno(mod, err));
519 		}
520 	} else {
521 		if (topo_node_resource(tn, &fmri, &err) != 0) {
522 			topo_mod_dprintf(mod,
523 			    "topo_node_resource() failed : %s\n",
524 			    topo_strerror(err));
525 			return (topo_mod_seterrno(mod, err));
526 		}
527 	}
528 
529 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
530 		topo_mod_dprintf(mod,
531 		    "topo_node_fru_set() failed : %s\n",
532 		    topo_strerror(err));
533 		nvlist_free(fmri);
534 		return (topo_mod_seterrno(mod, err));
535 	}
536 
537 	nvlist_free(fmri);
538 
539 	/*
540 	 * Set the SES-specific properties so that consumers can query
541 	 * additional information about the particular SES element.
542 	 */
543 	pgi.tpi_name = TOPO_PGROUP_SES;
544 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
545 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
546 	pgi.tpi_version = TOPO_VERSION;
547 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
548 		topo_mod_dprintf(mod, "failed to create propgroup "
549 		    "%s: %s\n", TOPO_PGROUP_SES, topo_strerror(err));
550 		return (-1);
551 	}
552 
553 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
554 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
555 	    nodeid, &err) != 0) {
556 		topo_mod_dprintf(mod,
557 		    "failed to create property %s: %s\n",
558 		    TOPO_PROP_NODE_ID, topo_strerror(err));
559 		return (-1);
560 	}
561 
562 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
563 	    TOPO_PROP_TARGET_PATH, TOPO_PROP_IMMUTABLE,
564 	    path, &err) != 0) {
565 		topo_mod_dprintf(mod,
566 		    "failed to create property %s: %s\n",
567 		    TOPO_PROP_TARGET_PATH, topo_strerror(err));
568 		return (-1);
569 	}
570 
571 	return (0);
572 }
573 
574 /*
575  * Callback to add a disk to a given bay.  We first check the status-code to
576  * determine if a disk is present, ignoring those that aren't in an appropriate
577  * state.  We then scan the parent bay node's SAS address array to determine
578  * possible attached SAS addresses.  We create a disk node if the disk is not
579  * SAS or the SES target does not support the necessary pages for this; if we
580  * find the SAS address, we create a disk node and also correlate it with
581  * the corresponding Solaris device node to fill in the rest of the data.
582  */
583 static int
584 ses_create_disk(ses_enum_data_t *sdp, tnode_t *pnode, nvlist_t *props)
585 {
586 	topo_mod_t *mod = sdp->sed_mod;
587 	uint64_t status;
588 	nvlist_t **sas;
589 	uint_t s, nsas;
590 	char **paths;
591 	int err, ret;
592 	tnode_t *child = NULL;
593 
594 	/*
595 	 * Skip devices that are not in a present (and possibly damaged) state.
596 	 */
597 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
598 		return (0);
599 
600 	if (status != SES_ESC_UNSUPPORTED &&
601 	    status != SES_ESC_OK &&
602 	    status != SES_ESC_CRITICAL &&
603 	    status != SES_ESC_NONCRITICAL &&
604 	    status != SES_ESC_UNRECOVERABLE &&
605 	    status != SES_ESC_NO_ACCESS)
606 		return (0);
607 
608 	topo_mod_dprintf(mod, "found attached disk");
609 
610 	/*
611 	 * Create the disk range.
612 	 */
613 	if (topo_node_range_create(mod, pnode, DISK, 0, 0) != 0) {
614 		topo_mod_dprintf(mod,
615 		    "topo_node_create_range() failed: %s",
616 		    topo_mod_errmsg(mod));
617 		return (-1);
618 	}
619 
620 	/*
621 	 * Look through all SAS addresses and attempt to correlate them to a
622 	 * known Solaris device.  If we don't find a matching node, then we
623 	 * don't enumerate the disk node.
624 	 */
625 	if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
626 	    &sas, &nsas) != 0)
627 		return (0);
628 
629 	if (topo_prop_get_string_array(pnode, TOPO_PGROUP_SES,
630 	    TOPO_PROP_SAS_ADDR, &paths, &nsas, &err) != 0)
631 		return (0);
632 
633 	err = 0;
634 
635 	for (s = 0; s < nsas; s++) {
636 		ret = disk_declare_addr(mod, pnode, &sdp->sed_devs, paths[s],
637 		    &child);
638 		if (ret == 0) {
639 			break;
640 		} else if (ret < 0) {
641 			err = -1;
642 			break;
643 		}
644 	}
645 
646 	if (s == nsas)
647 		disk_declare_non_enumerated(mod, pnode, &child);
648 
649 	/* copy sas_addresses (target-ports) from parent (with 'w'added) */
650 	if (child != NULL) {
651 		int i;
652 		char **tports;
653 		uint64_t wwn;
654 
655 		tports = topo_mod_zalloc(mod, sizeof (char *) * nsas);
656 		if (tports != NULL) {
657 			for (i = 0; i < nsas; i++) {
658 				if (scsi_wwnstr_to_wwn(paths[i], &wwn) !=
659 				    DDI_SUCCESS)
660 					break;
661 				tports[i] = scsi_wwn_to_wwnstr(wwn, 1, NULL);
662 				if (tports[i] == NULL)
663 					break;
664 			}
665 			/* if they all worked then create the property */
666 			if (i == nsas)
667 				(void) topo_prop_set_string_array(child,
668 				    TOPO_PGROUP_STORAGE,
669 				    TOPO_STORAGE_TARGET_PORT_L0IDS,
670 				    TOPO_PROP_IMMUTABLE, (const char **)tports,
671 				    nsas, &err);
672 
673 			for (i = 0; i < nsas; i++)
674 				if (tports[i] != NULL)
675 					scsi_free_wwnstr(tports[i]);
676 			topo_mod_free(mod, tports, sizeof (char *) * nsas);
677 		}
678 	}
679 
680 	for (s = 0; s < nsas; s++)
681 		topo_mod_free(mod, paths[s], strlen(paths[s]) + 1);
682 	topo_mod_free(mod, paths, nsas * sizeof (char *));
683 
684 	return (err);
685 }
686 
687 static int
688 ses_add_bay_props(topo_mod_t *mod, tnode_t *tn, ses_enum_node_t *snp)
689 {
690 	ses_alt_node_t *ap;
691 	ses_node_t *np;
692 	nvlist_t *props;
693 
694 	nvlist_t **phys;
695 	uint_t i, j, n_phys, all_phys = 0;
696 	char **paths;
697 	uint64_t addr;
698 	size_t len;
699 	int terr, err = -1;
700 
701 	for (ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
702 	    ap = topo_list_next(ap)) {
703 		np = ap->san_node;
704 		props = ses_node_props(np);
705 
706 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
707 		    &phys, &n_phys) != 0)
708 			continue;
709 
710 		all_phys += n_phys;
711 	}
712 
713 	if (all_phys == 0)
714 		return (0);
715 
716 	if ((paths = topo_mod_zalloc(mod, all_phys * sizeof (char *))) == NULL)
717 		return (-1);
718 
719 	for (i = 0, ap = topo_list_next(&snp->sen_alt_nodes); ap != NULL;
720 	    ap = topo_list_next(ap)) {
721 		np = ap->san_node;
722 		props = ses_node_props(np);
723 
724 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
725 		    &phys, &n_phys) != 0)
726 			continue;
727 
728 		for (j = 0; j < n_phys; j++) {
729 			if (nvlist_lookup_uint64(phys[j], SES_SAS_PROP_ADDR,
730 			    &addr) != 0)
731 				continue;
732 
733 			len = snprintf(NULL, 0, "%016llx", addr) + 1;
734 			if ((paths[i] = topo_mod_alloc(mod, len)) == NULL)
735 				goto error;
736 
737 			(void) snprintf(paths[i], len, "%016llx", addr);
738 
739 			++i;
740 		}
741 	}
742 
743 	err = topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
744 	    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE,
745 	    (const char **)paths, i, &terr);
746 	if (err != 0)
747 		err = topo_mod_seterrno(mod, terr);
748 
749 error:
750 	for (i = 0; i < all_phys && paths[i] != NULL; i++)
751 		topo_mod_free(mod, paths[i], strlen(paths[i]) + 1);
752 	topo_mod_free(mod, paths, all_phys * sizeof (char *));
753 
754 	return (err);
755 }
756 
757 /*
758  * Callback to create a basic node (bay, psu, fan, or controller and expander).
759  */
760 static int
761 ses_create_generic(ses_enum_data_t *sdp, ses_enum_node_t *snp,
762     tnode_t *pnode, const char *nodename, const char *labelname, tnode_t **node)
763 {
764 	ses_node_t *np = snp->sen_node;
765 	ses_node_t *parent;
766 	uint64_t instance = snp->sen_instance;
767 	topo_mod_t *mod = sdp->sed_mod;
768 	nvlist_t *props, *aprops;
769 	nvlist_t *auth = NULL, *fmri = NULL;
770 	tnode_t *tn = NULL, *frutn = NULL;
771 	char label[128];
772 	int err;
773 	char *part = NULL, *serial = NULL, *revision = NULL;
774 	char *desc;
775 	boolean_t report;
776 
777 	props = ses_node_props(np);
778 
779 	(void) nvlist_lookup_string(props, LIBSES_PROP_PART, &part);
780 	(void) nvlist_lookup_string(props, LIBSES_PROP_SERIAL, &serial);
781 
782 	topo_mod_dprintf(mod, "adding %s %llu", nodename, instance);
783 
784 	/*
785 	 * Create the node.  The interesting information is all copied from the
786 	 * parent enclosure node, so there is not much to do.
787 	 */
788 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
789 		goto error;
790 
791 	/*
792 	 * We want to report revision information for the controller nodes, but
793 	 * we do not get per-element revision information.  However, we do have
794 	 * revision information for the entire enclosure, and we can use the
795 	 * 'reported-via' property to know that this controller corresponds to
796 	 * the given revision information.  This means we cannot get revision
797 	 * information for targets we are not explicitly connected to, but
798 	 * there is little we can do about the situation.
799 	 */
800 	if (strcmp(nodename, CONTROLLER) == 0 &&
801 	    nvlist_lookup_boolean_value(props, SES_PROP_REPORT, &report) == 0 &&
802 	    report) {
803 		for (parent = ses_node_parent(np); parent != NULL;
804 		    parent = ses_node_parent(parent)) {
805 			if (ses_node_type(parent) == SES_NODE_ENCLOSURE) {
806 				(void) nvlist_lookup_string(
807 				    ses_node_props(parent),
808 				    SES_EN_PROP_REV, &revision);
809 				break;
810 			}
811 		}
812 	}
813 
814 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
815 	    nodename, (topo_instance_t)instance, NULL, auth, part, revision,
816 	    serial)) == NULL) {
817 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
818 		    topo_mod_errmsg(mod));
819 		goto error;
820 	}
821 
822 	if ((tn = topo_node_bind(mod, pnode, nodename,
823 	    instance, fmri)) == NULL) {
824 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
825 		    topo_mod_errmsg(mod));
826 		goto error;
827 	}
828 
829 	/*
830 	 * For the node label, we look for the following in order:
831 	 *
832 	 * 	<ses-description>
833 	 * 	<ses-class-description> <instance>
834 	 * 	<default-type-label> <instance>
835 	 */
836 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
837 	    desc[0] == '\0') {
838 		parent = ses_node_parent(np);
839 		aprops = ses_node_props(parent);
840 		if (nvlist_lookup_string(aprops, SES_PROP_CLASS_DESCRIPTION,
841 		    &desc) != 0 || desc[0] == '\0')
842 			desc = (char *)labelname;
843 		(void) snprintf(label, sizeof (label), "%s %llu", desc,
844 		    instance);
845 		desc = label;
846 	}
847 
848 	if (topo_node_label_set(tn, desc, &err) != 0)
849 		goto error;
850 
851 	/*
852 	 * For an expander node, set the FRU to its parent(controller).
853 	 * For a connector node, set the FRU to its grand parent(controller).
854 	 */
855 	if (strcmp(nodename, SASEXPANDER) == 0) {
856 		frutn = pnode;
857 	} else if (strcmp(nodename, RECEPTACLE) == 0) {
858 		frutn = topo_node_parent(pnode);
859 	}
860 
861 	if (ses_set_standard_props(mod, frutn, tn, NULL, ses_node_id(np),
862 	    snp->sen_target->set_devpath) != 0)
863 		goto error;
864 
865 	if (strcmp(nodename, BAY) == 0) {
866 		if (ses_add_bay_props(mod, tn, snp) != 0)
867 			goto error;
868 
869 		if (ses_create_disk(sdp, tn, props) != 0)
870 			goto error;
871 
872 		if (topo_method_register(mod, tn, ses_bay_methods) != 0) {
873 			topo_mod_dprintf(mod,
874 			    "topo_method_register() failed: %s",
875 			    topo_mod_errmsg(mod));
876 			goto error;
877 		}
878 	} else if ((strcmp(nodename, FAN) == 0) ||
879 	    (strcmp(nodename, PSU) == 0) ||
880 	    (strcmp(nodename, CONTROLLER) == 0)) {
881 		/*
882 		 * Only fan, psu, and controller nodes have a 'present' method.
883 		 * Bay nodes are always present, and disk nodes are present by
884 		 * virtue of being enumerated and SAS expander nodes and
885 		 * SAS connector nodes are also always present once
886 		 * the parent controller is found.
887 		 */
888 		if (topo_method_register(mod, tn, ses_component_methods) != 0) {
889 			topo_mod_dprintf(mod,
890 			    "topo_method_register() failed: %s",
891 			    topo_mod_errmsg(mod));
892 			goto error;
893 		}
894 
895 	}
896 
897 	snp->sen_target->set_refcount++;
898 	topo_node_setspecific(tn, snp->sen_target);
899 
900 	nvlist_free(auth);
901 	nvlist_free(fmri);
902 	if (node != NULL) *node = tn;
903 	return (0);
904 
905 error:
906 	nvlist_free(auth);
907 	nvlist_free(fmri);
908 	return (-1);
909 }
910 
911 /*
912  * Create SAS expander specific props.
913  */
914 /*ARGSUSED*/
915 static int
916 ses_set_expander_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
917     tnode_t *ptnode, tnode_t *tnode, int *phycount, int64_t *connlist)
918 {
919 	ses_node_t *np = snp->sen_node;
920 	topo_mod_t *mod = sdp->sed_mod;
921 	nvlist_t *auth = NULL, *fmri = NULL;
922 	nvlist_t *props, **phylist;
923 	int err, i;
924 	uint_t pcount;
925 	uint64_t sasaddr, connidx;
926 	char sasaddr_str[17];
927 	boolean_t found = B_FALSE;
928 	dev_di_node_t *dnode;
929 
930 	props = ses_node_props(np);
931 
932 	/*
933 	 * the uninstalled expander is not enumerated by checking
934 	 * the element status code.  No present present' method provided.
935 	 */
936 	/*
937 	 * Get the Expander SAS address.  It should exist.
938 	 */
939 	if (nvlist_lookup_uint64(props, SES_EXP_PROP_SAS_ADDR,
940 	    &sasaddr) != 0) {
941 		topo_mod_dprintf(mod,
942 		    "Failed to get prop %s.", SES_EXP_PROP_SAS_ADDR);
943 		goto error;
944 	}
945 
946 	(void) sprintf(sasaddr_str, "%llx", sasaddr);
947 
948 	/* search matching dev_di_node. */
949 	for (dnode = topo_list_next(&sdp->sed_devs); dnode != NULL;
950 	    dnode = topo_list_next(dnode)) {
951 		if (strstr(dnode->ddn_dpath, sasaddr_str) != NULL) {
952 			found = B_TRUE;
953 			break;
954 		}
955 	}
956 
957 	if (!found) {
958 		topo_mod_dprintf(mod,
959 		    "ses_set_expander_props: Failed to find matching "
960 		    "devinfo node for Exapnder SAS address %s",
961 		    SES_EXP_PROP_SAS_ADDR);
962 		/* continue on to get storage group props. */
963 	} else {
964 		/* create/set the devfs-path and devid in the io group */
965 		if (topo_pgroup_create(tnode, &io_pgroup, &err) != 0) {
966 			topo_mod_dprintf(mod, "ses_set_expander_props: "
967 			    "create io error %s\n", topo_strerror(err));
968 			goto error;
969 		} else {
970 			if (topo_prop_set_string(tnode, TOPO_PGROUP_IO,
971 			    TOPO_IO_DEV_PATH, TOPO_PROP_IMMUTABLE,
972 			    dnode->ddn_dpath, &err) != 0) {
973 				topo_mod_dprintf(mod, "ses_set_expander_props: "
974 				    "set dev error %s\n", topo_strerror(err));
975 			}
976 			if (topo_prop_set_string(tnode, TOPO_PGROUP_IO,
977 			    TOPO_IO_DEVID, TOPO_PROP_IMMUTABLE,
978 			    dnode->ddn_devid, &err) != 0) {
979 				topo_mod_dprintf(mod, "ses_set_expander_props: "
980 				    "set devid error %s\n", topo_strerror(err));
981 			}
982 			if (dnode->ddn_ppath_count != 0 &&
983 			    topo_prop_set_string_array(tnode, TOPO_PGROUP_IO,
984 			    TOPO_IO_PHYS_PATH, TOPO_PROP_IMMUTABLE,
985 			    (const char **)dnode->ddn_ppath,
986 			    dnode->ddn_ppath_count, &err) != 0) {
987 				topo_mod_dprintf(mod, "ses_set_expander_props: "
988 				    "set phys-path error %s\n",
989 				    topo_strerror(err));
990 			}
991 		}
992 	}
993 
994 	/* create the storage group */
995 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
996 		topo_mod_dprintf(mod, "ses_set_expander_props: "
997 		    "create storage error %s\n", topo_strerror(err));
998 		goto error;
999 	} else {
1000 		/* set the SAS address prop of the expander. */
1001 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1002 		    TOPO_PROP_SAS_ADDR, TOPO_PROP_IMMUTABLE, sasaddr_str,
1003 		    &err) != 0) {
1004 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1005 			    "set %S error %s\n", TOPO_PROP_SAS_ADDR,
1006 			    topo_strerror(err));
1007 		}
1008 
1009 		/* Get the phy information for the expander */
1010 		if (nvlist_lookup_nvlist_array(props, SES_SAS_PROP_PHYS,
1011 		    &phylist, &pcount) != 0) {
1012 			topo_mod_dprintf(mod,
1013 			    "Failed to get prop %s.", SES_SAS_PROP_PHYS);
1014 		} else {
1015 			/*
1016 			 * For each phy, get the connector element index and
1017 			 * stores into connector element index array.
1018 			 */
1019 			*phycount = pcount;
1020 			for (i = 0; i < pcount; i++) {
1021 				if (nvlist_lookup_uint64(phylist[i],
1022 				    SES_PROP_CE_IDX, &connidx) == 0) {
1023 					if (connidx != 0xff) {
1024 						connlist[i] = connidx;
1025 					} else {
1026 						connlist[i] = -1;
1027 					}
1028 				} else {
1029 					/* Fail to get the index. set to -1. */
1030 					connlist[i] = -1;
1031 				}
1032 			}
1033 
1034 			/* set the phy count prop of the expander. */
1035 			if (topo_prop_set_uint64(tnode, TOPO_PGROUP_STORAGE,
1036 			    TOPO_PROP_PHY_COUNT, TOPO_PROP_IMMUTABLE, pcount,
1037 			    &err) != 0) {
1038 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1039 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1040 				    topo_strerror(err));
1041 			}
1042 
1043 			/*
1044 			 * set the connector element index of
1045 			 * the expander phys.
1046 			 */
1047 		}
1048 
1049 		/* populate other misc storage group properties */
1050 		if (found) {
1051 			if (dnode->ddn_mfg && (topo_prop_set_string(tnode,
1052 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MANUFACTURER,
1053 			    TOPO_PROP_IMMUTABLE, dnode->ddn_mfg, &err) != 0)) {
1054 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1055 				    "set mfg error %s\n", topo_strerror(err));
1056 			}
1057 
1058 			if (dnode->ddn_model && (topo_prop_set_string(tnode,
1059 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_MODEL,
1060 			    TOPO_PROP_IMMUTABLE,
1061 			    dnode->ddn_model, &err) != 0)) {
1062 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1063 				    "set model error %s\n", topo_strerror(err));
1064 			}
1065 
1066 			if (dnode->ddn_serial && (topo_prop_set_string(tnode,
1067 			    TOPO_PGROUP_STORAGE, TOPO_STORAGE_SERIAL_NUM,
1068 			    TOPO_PROP_IMMUTABLE,
1069 			    dnode->ddn_serial, &err) != 0)) {
1070 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1071 				    "set serial error %s\n",
1072 				    topo_strerror(err));
1073 			}
1074 
1075 			if (dnode->ddn_firm && (topo_prop_set_string(tnode,
1076 			    TOPO_PGROUP_STORAGE,
1077 			    TOPO_STORAGE_FIRMWARE_REV, TOPO_PROP_IMMUTABLE,
1078 			    dnode->ddn_firm, &err) != 0)) {
1079 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1080 				    "set firm error %s\n", topo_strerror(err));
1081 			}
1082 		}
1083 	}
1084 
1085 	return (0);
1086 
1087 error:
1088 	nvlist_free(auth);
1089 	nvlist_free(fmri);
1090 	return (-1);
1091 }
1092 
1093 /*
1094  * Create SAS expander specific props.
1095  */
1096 /*ARGSUSED*/
1097 static int
1098 ses_set_connector_props(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1099     tnode_t *tnode, int64_t phy_mask)
1100 {
1101 	ses_node_t *np = snp->sen_node;
1102 	topo_mod_t *mod = sdp->sed_mod;
1103 	nvlist_t *props;
1104 	int err, i;
1105 	uint64_t conntype;
1106 	char phymask_str[17], *conntype_str;
1107 	boolean_t   found;
1108 
1109 	props = ses_node_props(np);
1110 
1111 	/*
1112 	 * convert phy mask to string.
1113 	 */
1114 	(void) snprintf(phymask_str, 17, "%llx", phy_mask);
1115 
1116 	/* create the storage group */
1117 	if (topo_pgroup_create(tnode, &storage_pgroup, &err) != 0) {
1118 		topo_mod_dprintf(mod, "ses_set_expander_props: "
1119 		    "create storage error %s\n", topo_strerror(err));
1120 		return (-1);
1121 	} else {
1122 		/* set the SAS address prop of the expander. */
1123 		if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1124 		    TOPO_STORAGE_SAS_PHY_MASK, TOPO_PROP_IMMUTABLE,
1125 		    phymask_str, &err) != 0) {
1126 			topo_mod_dprintf(mod, "ses_set_expander_props: "
1127 			    "set %S error %s\n", TOPO_STORAGE_SAS_PHY_MASK,
1128 			    topo_strerror(err));
1129 		}
1130 
1131 		/* Get the connector type information for the expander */
1132 		if (nvlist_lookup_uint64(props,
1133 		    SES_SC_PROP_CONNECTOR_TYPE, &conntype) != 0) {
1134 			topo_mod_dprintf(mod, "Failed to get prop %s.",
1135 			    TOPO_STORAGE_SAS_PHY_MASK);
1136 		} else {
1137 			found = B_FALSE;
1138 			for (i = 0; ; i++) {
1139 				if (sas_connector_type_list[i].type ==
1140 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1141 					break;
1142 				}
1143 				if (sas_connector_type_list[i].type ==
1144 				    conntype) {
1145 					conntype_str =
1146 					    sas_connector_type_list[i].name;
1147 					found = B_TRUE;
1148 					break;
1149 				}
1150 			}
1151 
1152 			if (!found) {
1153 				if (conntype <
1154 				    SAS_CONNECTOR_TYPE_CODE_NOT_DEFINED) {
1155 					conntype_str =
1156 					    SAS_CONNECTOR_TYPE_RESERVED;
1157 				} else {
1158 					conntype_str =
1159 					    SAS_CONNECTOR_TYPE_NOT_DEFINED;
1160 				}
1161 			}
1162 
1163 			/* set the phy count prop of the expander. */
1164 			if (topo_prop_set_string(tnode, TOPO_PGROUP_STORAGE,
1165 			    TOPO_STORAGE_SAS_CONNECTOR_TYPE,
1166 			    TOPO_PROP_IMMUTABLE, conntype_str, &err) != 0) {
1167 				topo_mod_dprintf(mod, "ses_set_expander_props: "
1168 				    "set %S error %s\n", TOPO_PROP_PHY_COUNT,
1169 				    topo_strerror(err));
1170 			}
1171 		}
1172 	}
1173 
1174 	return (0);
1175 }
1176 
1177 /*
1178  * Instantiate SAS expander nodes for a given ESC Electronics node(controller)
1179  * nodes.
1180  */
1181 /*ARGSUSED*/
1182 static int
1183 ses_create_esc_sasspecific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1184     tnode_t *pnode, ses_enum_chassis_t *cp,
1185     boolean_t dorange)
1186 {
1187 	topo_mod_t *mod = sdp->sed_mod;
1188 	tnode_t	*exptn, *contn;
1189 	boolean_t found;
1190 	sas_connector_phy_data_t connectors[64] = {NULL};
1191 	uint64_t max;
1192 	ses_enum_node_t *ctlsnp, *xsnp, *consnp;
1193 	ses_node_t *np = snp->sen_node;
1194 	nvlist_t *props, *psprops;
1195 	uint64_t index, psindex, conindex, psstatus, i, j, count;
1196 	int64_t cidxlist[256] = {NULL};
1197 	int phycount;
1198 
1199 	props = ses_node_props(np);
1200 
1201 	if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_ONLY_INDEX,
1202 	    &index) != 0)
1203 		return (-1);
1204 
1205 	/*
1206 	 * For SES constroller node, check to see if there are
1207 	 * associated SAS expanders.
1208 	 */
1209 	found = B_FALSE;
1210 	max = 0;
1211 	for (ctlsnp = topo_list_next(&cp->sec_nodes); ctlsnp != NULL;
1212 	    ctlsnp = topo_list_next(ctlsnp)) {
1213 		if (ctlsnp->sen_type == SES_ET_SAS_EXPANDER) {
1214 			found = B_TRUE;
1215 			if (ctlsnp->sen_instance > max)
1216 				max = ctlsnp->sen_instance;
1217 		}
1218 	}
1219 
1220 	/*
1221 	 * No SAS expander found notthing to process.
1222 	 */
1223 	if (!found)
1224 		return (0);
1225 
1226 	topo_mod_dprintf(mod, "%s Controller %d: creating "
1227 	    "%llu %s nodes", cp->sec_csn, index, max + 1, SASEXPANDER);
1228 
1229 	/*
1230 	 * The max number represent the number of elements
1231 	 * deducted from the highest SES_PROP_ELEMENT_CLASS_INDEX
1232 	 * of SET_ET_SAS_EXPANDER type element.
1233 	 *
1234 	 * There may be multiple ESC Electronics element(controllers)
1235 	 * within JBOD(typicall two for redundancy) and SAS expander
1236 	 * elements are associated with only one of them.  We are
1237 	 * still creating the range based max number here.
1238 	 * That will cover the case that all expanders are associated
1239 	 * with one SES controller.
1240 	 */
1241 	if (dorange && topo_node_range_create(mod, pnode,
1242 	    SASEXPANDER, 0, max) != 0) {
1243 		topo_mod_dprintf(mod,
1244 		    "topo_node_create_range() failed: %s",
1245 		    topo_mod_errmsg(mod));
1246 		return (-1);
1247 	}
1248 
1249 	/*
1250 	 * Search exapnders with the parent index matching with
1251 	 * ESC Electronics element index.
1252 	 * Note the index used here is a global index across
1253 	 * SES elements.
1254 	 */
1255 	for (xsnp = topo_list_next(&cp->sec_nodes); xsnp != NULL;
1256 	    xsnp = topo_list_next(xsnp)) {
1257 		if (xsnp->sen_type == SES_ET_SAS_EXPANDER) {
1258 			/*
1259 			 * get the parent ESC controller.
1260 			 */
1261 			psprops = ses_node_props(xsnp->sen_node);
1262 			if (nvlist_lookup_uint64(psprops,
1263 			    SES_PROP_STATUS_CODE, &psstatus) == 0) {
1264 				if (psstatus == SES_ESC_NOT_INSTALLED) {
1265 					/*
1266 					 * Not installed.
1267 					 * Don't create a ndoe.
1268 					 */
1269 					continue;
1270 				}
1271 			} else {
1272 				/*
1273 				 * The element should have status code.
1274 				 * If not there is no way to find
1275 				 * out if the expander element exist or
1276 				 * not.
1277 				 */
1278 				continue;
1279 			}
1280 
1281 			/* Get the physical parent index to compare. */
1282 			if (nvlist_lookup_uint64(psprops,
1283 			    LIBSES_PROP_PHYS_PARENT, &psindex) == 0) {
1284 				if (index == psindex) {
1285 		/* indentation moved forward */
1286 		/*
1287 		 * Handle basic node information of SAS expander
1288 		 * element - binding to parent node and
1289 		 * allocating FMRI...
1290 		 */
1291 		if (ses_create_generic(sdp, xsnp, pnode, SASEXPANDER,
1292 		    "SAS-EXPANDER", &exptn) != 0)
1293 			continue;
1294 		/*
1295 		 * Now handle SAS expander unique portion of node creation.
1296 		 * The max nubmer of the phy count is 256 since SES-2
1297 		 * defines as 1 byte field.  The cidxlist has the same
1298 		 * number of elements.
1299 		 *
1300 		 * We use size 64 array to store the connectors.
1301 		 * Typically a connectors associated with 4 phys so that
1302 		 * matches with the max number of connecters associated
1303 		 * with an expander.
1304 		 * The phy count goes up to 38 for Sun supported
1305 		 * JBOD.
1306 		 */
1307 		memset(cidxlist, 0, sizeof (int64_t) * 64);
1308 		if (ses_set_expander_props(sdp, xsnp, pnode, exptn, &phycount,
1309 		    cidxlist) != 0) {
1310 			/*
1311 			 * error on getting specific prop failed.
1312 			 * continue on.  Note that the node is
1313 			 * left bound.
1314 			 */
1315 			continue;
1316 		}
1317 
1318 		/*
1319 		 * count represetns the number of connectors discovered so far.
1320 		 */
1321 		count = 0;
1322 		memset(connectors, 0, sizeof (sas_connector_phy_data_t) * 64);
1323 		for (i = 0; i < phycount; i++) {
1324 			if (cidxlist[i] != -1) {
1325 				/* connector index is valid. */
1326 				for (j = 0; j < count; j++) {
1327 					if (connectors[j].index ==
1328 					    cidxlist[i]) {
1329 						/*
1330 						 * Just update phy mask.
1331 						 * The postion for connector
1332 						 * index lists(cidxlist index)
1333 						 * is set.
1334 						 */
1335 						connectors[j].phy_mask =
1336 						    connectors[j].phy_mask |
1337 						    (1ULL << i);
1338 						break;
1339 					}
1340 				}
1341 				/*
1342 				 * If j and count matche a  new connector
1343 				 * index is found.
1344 				 */
1345 				if (j == count) {
1346 					/* add a new index and phy mask. */
1347 					connectors[count].index = cidxlist[i];
1348 					connectors[count].phy_mask =
1349 					    connectors[count].phy_mask |
1350 					    (1ULL << i);
1351 					count++;
1352 				}
1353 			}
1354 		}
1355 
1356 		/*
1357 		 * create range for the connector nodes.
1358 		 * The class index of the ses connector element
1359 		 * is set as the instance nubmer for the node.
1360 		 * Even though one expander may not have all connectors
1361 		 * are associated with we are creating the range with
1362 		 * max possible instance number.
1363 		 */
1364 		found = B_FALSE;
1365 		max = 0;
1366 		for (consnp = topo_list_next(&cp->sec_nodes);
1367 		    consnp != NULL; consnp = topo_list_next(consnp)) {
1368 			if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1369 				psprops = ses_node_props(consnp->sen_node);
1370 				found = B_TRUE;
1371 				if (consnp->sen_instance > max)
1372 					max = consnp->sen_instance;
1373 			}
1374 		}
1375 
1376 		/*
1377 		 * No SAS connector found nothing to process.
1378 		 */
1379 		if (!found)
1380 			return (0);
1381 
1382 		if (dorange && topo_node_range_create(mod, exptn,
1383 		    RECEPTACLE, 0, max) != 0) {
1384 			topo_mod_dprintf(mod,
1385 			    "topo_node_create_range() failed: %s",
1386 			    topo_mod_errmsg(mod));
1387 			return (-1);
1388 		}
1389 
1390 		/* search matching connector element using the index. */
1391 		for (i = 0; i < count; i++) {
1392 			found = B_FALSE;
1393 			for (consnp = topo_list_next(&cp->sec_nodes);
1394 			    consnp != NULL; consnp = topo_list_next(consnp)) {
1395 				if (consnp->sen_type == SES_ET_SAS_CONNECTOR) {
1396 					psprops = ses_node_props(
1397 					    consnp->sen_node);
1398 					/*
1399 					 * Get the physical parent index to
1400 					 * compare.
1401 					 * The connector elements are children
1402 					 * of ESC Electronics element even
1403 					 * though we enumerate them under
1404 					 * an expander in libtopo.
1405 					 */
1406 					if (nvlist_lookup_uint64(psprops,
1407 					    SES_PROP_ELEMENT_ONLY_INDEX,
1408 					    &conindex) == 0) {
1409 						if (conindex ==
1410 						    connectors[i].index) {
1411 							found = B_TRUE;
1412 							break;
1413 						}
1414 					}
1415 				}
1416 			}
1417 
1418 			/* now create a libtopo node. */
1419 			if (found) {
1420 				/* Create generic props. */
1421 				if (ses_create_generic(sdp, consnp, exptn,
1422 				    RECEPTACLE, "RECEPTACLE", &contn) !=
1423 				    0) {
1424 					continue;
1425 				}
1426 				/* Create connector specific props. */
1427 				if (ses_set_connector_props(sdp, consnp,
1428 				    contn, connectors[i].phy_mask) != 0) {
1429 					continue;
1430 				}
1431 			}
1432 		}
1433 		/* end indentation change */
1434 				}
1435 			}
1436 		}
1437 	}
1438 
1439 	return (0);
1440 }
1441 
1442 /*
1443  * Instantiate any protocol specific portion of a node.
1444  */
1445 /*ARGSUSED*/
1446 static int
1447 ses_create_protocol_specific(ses_enum_data_t *sdp, ses_enum_node_t *snp,
1448     tnode_t *pnode, uint64_t type, ses_enum_chassis_t *cp,
1449     boolean_t dorange)
1450 {
1451 
1452 	if (type == SES_ET_ESC_ELECTRONICS) {
1453 		/* create SAS specific children(expanders and connectors. */
1454 		return (ses_create_esc_sasspecific(sdp, snp, pnode, cp,
1455 		    dorange));
1456 	}
1457 
1458 	return (0);
1459 }
1460 
1461 /*
1462  * Instantiate any children of a given type.
1463  */
1464 static int
1465 ses_create_children(ses_enum_data_t *sdp, tnode_t *pnode, uint64_t type,
1466     const char *nodename, const char *defaultlabel, ses_enum_chassis_t *cp,
1467     boolean_t dorange)
1468 {
1469 	topo_mod_t *mod = sdp->sed_mod;
1470 	boolean_t found;
1471 	uint64_t max;
1472 	ses_enum_node_t *snp;
1473 	tnode_t	*tn;
1474 
1475 	/*
1476 	 * First go through and count how many matching nodes we have.
1477 	 */
1478 	max = 0;
1479 	found = B_FALSE;
1480 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1481 	    snp = topo_list_next(snp)) {
1482 		if (snp->sen_type == type) {
1483 			found = B_TRUE;
1484 			if (snp->sen_instance > max)
1485 				max = snp->sen_instance;
1486 		}
1487 	}
1488 
1489 	/*
1490 	 * No enclosure should export both DEVICE and ARRAY_DEVICE elements.
1491 	 * Since we map both of these to 'disk', if an enclosure does this, we
1492 	 * just ignore the array elements.
1493 	 */
1494 	if (!found ||
1495 	    (type == SES_ET_ARRAY_DEVICE && cp->sec_hasdev))
1496 		return (0);
1497 
1498 	topo_mod_dprintf(mod, "%s: creating %llu %s nodes",
1499 	    cp->sec_csn, max + 1, nodename);
1500 
1501 	if (dorange && topo_node_range_create(mod, pnode,
1502 	    nodename, 0, max) != 0) {
1503 		topo_mod_dprintf(mod,
1504 		    "topo_node_create_range() failed: %s",
1505 		    topo_mod_errmsg(mod));
1506 		return (-1);
1507 	}
1508 
1509 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1510 	    snp = topo_list_next(snp)) {
1511 		if (snp->sen_type == type) {
1512 			if (ses_create_generic(sdp, snp, pnode,
1513 			    nodename, defaultlabel, &tn) != 0)
1514 				return (-1);
1515 			/*
1516 			 * For some SES element there may be protocol specific
1517 			 * information to process.   Here we are processing
1518 			 * the association between enclosure controller and
1519 			 * SAS expanders.
1520 			 */
1521 			if (type == SES_ET_ESC_ELECTRONICS) {
1522 				/* create SAS expander node */
1523 				if (ses_create_protocol_specific(sdp, snp,
1524 				    tn, type, cp, dorange) != 0) {
1525 					return (-1);
1526 				}
1527 			}
1528 
1529 		}
1530 	}
1531 
1532 	return (0);
1533 }
1534 
1535 /*
1536  * Instantiate a new subchassis instance in the topology.
1537  */
1538 static int
1539 ses_create_subchassis(ses_enum_data_t *sdp, tnode_t *pnode,
1540     ses_enum_chassis_t *scp)
1541 {
1542 	topo_mod_t *mod = sdp->sed_mod;
1543 	tnode_t *tn;
1544 	nvlist_t *props;
1545 	nvlist_t *auth = NULL, *fmri = NULL;
1546 	uint64_t instance = scp->sec_instance;
1547 	char *desc;
1548 	char label[128];
1549 	char **paths;
1550 	int i, err;
1551 	ses_enum_target_t *stp;
1552 	int ret = -1;
1553 
1554 	/*
1555 	 * Copy authority information from parent enclosure node
1556 	 */
1557 	if ((auth = topo_mod_auth(mod, pnode)) == NULL)
1558 		goto error;
1559 
1560 	/*
1561 	 * Record the subchassis serial number in the FMRI.
1562 	 * For now, we assume that logical id is the subchassis serial number.
1563 	 * If this assumption changes in future, then the following
1564 	 * piece of code will need to be updated via an RFE.
1565 	 */
1566 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
1567 	    SUBCHASSIS, (topo_instance_t)instance, NULL, auth, NULL, NULL,
1568 	    NULL)) == NULL) {
1569 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1570 		    topo_mod_errmsg(mod));
1571 		goto error;
1572 	}
1573 
1574 	if ((tn = topo_node_bind(mod, pnode, SUBCHASSIS,
1575 	    instance, fmri)) == NULL) {
1576 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1577 		    topo_mod_errmsg(mod));
1578 		goto error;
1579 	}
1580 
1581 	props = ses_node_props(scp->sec_enclosure);
1582 
1583 	/*
1584 	 * Look for the subchassis label in the following order:
1585 	 *	<ses-description>
1586 	 *	<ses-class-description> <instance>
1587 	 *	<default-type-label> <instance>
1588 	 *
1589 	 * For subchassis, the default label is "SUBCHASSIS"
1590 	 */
1591 	if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION, &desc) != 0 ||
1592 	    desc[0] == '\0') {
1593 		if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION,
1594 		    &desc) == 0 && desc[0] != '\0')
1595 			(void) snprintf(label, sizeof (label), "%s %llu", desc,
1596 			    instance);
1597 		else
1598 			(void) snprintf(label, sizeof (label),
1599 			    "SUBCHASSIS %llu", instance);
1600 		desc = label;
1601 	}
1602 
1603 	if (topo_node_label_set(tn, desc, &err) != 0)
1604 		goto error;
1605 
1606 	if (ses_set_standard_props(mod, NULL, tn, NULL,
1607 	    ses_node_id(scp->sec_enclosure), scp->sec_target->set_devpath) != 0)
1608 		goto error;
1609 
1610 	/*
1611 	 * Set the 'chassis-type' property for this subchassis.  This is either
1612 	 * 'ses-class-description' or 'subchassis'.
1613 	 */
1614 	if (nvlist_lookup_string(props, SES_PROP_CLASS_DESCRIPTION, &desc) != 0)
1615 		desc = "subchassis";
1616 
1617 	if (topo_prop_set_string(tn, TOPO_PGROUP_SES,
1618 	    TOPO_PROP_CHASSIS_TYPE, TOPO_PROP_IMMUTABLE, desc, &err) != 0) {
1619 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
1620 		    TOPO_PROP_CHASSIS_TYPE, topo_strerror(err));
1621 		goto error;
1622 	}
1623 
1624 	/*
1625 	 * For enclosures, we want to include all possible targets (for upgrade
1626 	 * purposes).
1627 	 */
1628 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
1629 	    stp = topo_list_next(stp), i++)
1630 		;
1631 
1632 	verify(i != 0);
1633 	paths = alloca(i * sizeof (char *));
1634 
1635 	for (i = 0, stp = topo_list_next(&scp->sec_targets); stp != NULL;
1636 	    stp = topo_list_next(stp), i++)
1637 		paths[i] = stp->set_devpath;
1638 
1639 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
1640 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
1641 	    i, &err) != 0) {
1642 		topo_mod_dprintf(mod, "failed to create property %s: %s\n",
1643 		    TOPO_PROP_PATHS, topo_strerror(err));
1644 		goto error;
1645 	}
1646 
1647 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
1648 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
1649 		    topo_mod_errmsg(mod));
1650 		goto error;
1651 	}
1652 
1653 	/*
1654 	 * Create the nodes for controllers and bays.
1655 	 */
1656 	if (ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
1657 	    CONTROLLER, "CONTROLLER", scp, B_TRUE) != 0 ||
1658 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
1659 	    BAY, "BAY", scp, B_TRUE) != 0 ||
1660 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
1661 	    BAY, "BAY", scp, B_TRUE) != 0)
1662 		goto error;
1663 
1664 	ret = 0;
1665 
1666 error:
1667 	nvlist_free(auth);
1668 	nvlist_free(fmri);
1669 	return (ret);
1670 }
1671 
1672 /*
1673  * Instantiate a new chassis instance in the topology.
1674  */
1675 static int
1676 ses_create_chassis(ses_enum_data_t *sdp, tnode_t *pnode, ses_enum_chassis_t *cp)
1677 {
1678 	topo_mod_t *mod = sdp->sed_mod;
1679 	nvlist_t *props;
1680 	char *raw_manufacturer, *raw_model, *raw_revision;
1681 	char *manufacturer = NULL, *model = NULL, *product = NULL;
1682 	char *revision = NULL;
1683 	char *serial;
1684 	char **paths;
1685 	size_t prodlen;
1686 	tnode_t *tn;
1687 	nvlist_t *fmri = NULL, *auth = NULL;
1688 	int ret = -1;
1689 	ses_enum_node_t *snp;
1690 	ses_enum_target_t *stp;
1691 	ses_enum_chassis_t *scp;
1692 	int i, err;
1693 	uint64_t sc_count = 0;
1694 
1695 	/*
1696 	 * Ignore any internal enclosures.
1697 	 */
1698 	if (cp->sec_internal)
1699 		return (0);
1700 
1701 	/*
1702 	 * Check to see if there are any devices presennt in the chassis.  If
1703 	 * not, ignore the chassis alltogether.  This is most useful for
1704 	 * ignoring internal HBAs that present a SES target but don't actually
1705 	 * manage any of the devices.
1706 	 */
1707 	for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
1708 	    snp = topo_list_next(snp)) {
1709 		if (snp->sen_type == SES_ET_DEVICE ||
1710 		    snp->sen_type == SES_ET_ARRAY_DEVICE)
1711 			break;
1712 	}
1713 
1714 	if (snp == NULL)
1715 		return (0);
1716 
1717 	props = ses_node_props(cp->sec_enclosure);
1718 
1719 	/*
1720 	 * We use the following property mappings:
1721 	 *
1722 	 * 	manufacturer		vendor-id
1723 	 * 	model			product-id
1724 	 * 	serial-number		libses-chassis-serial
1725 	 */
1726 	verify(nvlist_lookup_string(props, SES_EN_PROP_VID,
1727 	    &raw_manufacturer) == 0);
1728 	verify(nvlist_lookup_string(props, SES_EN_PROP_PID, &raw_model) == 0);
1729 	verify(nvlist_lookup_string(props, SES_EN_PROP_REV,
1730 	    &raw_revision) == 0);
1731 	verify(nvlist_lookup_string(props, LIBSES_EN_PROP_CSN, &serial) == 0);
1732 
1733 	/*
1734 	 * To construct the authority information, we 'clean' each string by
1735 	 * removing any offensive characters and trimmming whitespace.  For the
1736 	 * 'product-id', we use a concatenation of 'manufacturer-model'.  We
1737 	 * also take the numerical serial number and convert it to a string.
1738 	 */
1739 	if ((manufacturer = disk_auth_clean(mod, raw_manufacturer)) == NULL ||
1740 	    (model = disk_auth_clean(mod, raw_model)) == NULL ||
1741 	    (revision = disk_auth_clean(mod, raw_revision)) == NULL) {
1742 		goto error;
1743 	}
1744 
1745 	prodlen = strlen(manufacturer) + strlen(model) + 2;
1746 	if ((product = topo_mod_alloc(mod, prodlen)) == NULL)
1747 		goto error;
1748 
1749 	(void) snprintf(product, prodlen, "%s-%s", manufacturer, model);
1750 
1751 	/*
1752 	 * Construct the topo node and bind it to our parent.
1753 	 */
1754 	if (topo_mod_nvalloc(mod, &auth, NV_UNIQUE_NAME) != 0)
1755 		goto error;
1756 
1757 	if (nvlist_add_string(auth, FM_FMRI_AUTH_PRODUCT, product) != 0 ||
1758 	    nvlist_add_string(auth, FM_FMRI_AUTH_CHASSIS, serial) != 0) {
1759 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
1760 		goto error;
1761 	}
1762 
1763 	/*
1764 	 * We pass NULL for the parent FMRI because there is no resource
1765 	 * associated with it.  For the toplevel enclosure, we leave the
1766 	 * serial/part/revision portions empty, which are reserved for
1767 	 * individual components within the chassis.
1768 	 */
1769 	if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION,
1770 	    SES_ENCLOSURE, cp->sec_instance, NULL, auth,
1771 	    model, revision, serial)) == NULL) {
1772 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
1773 		    topo_mod_errmsg(mod));
1774 		goto error;
1775 	}
1776 
1777 	if ((tn = topo_node_bind(mod, pnode, SES_ENCLOSURE,
1778 	    cp->sec_instance, fmri)) == NULL) {
1779 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
1780 		    topo_mod_errmsg(mod));
1781 		goto error;
1782 	}
1783 
1784 	if (topo_method_register(mod, tn, ses_enclosure_methods) != 0) {
1785 		topo_mod_dprintf(mod,
1786 		    "topo_method_register() failed: %s",
1787 		    topo_mod_errmsg(mod));
1788 		goto error;
1789 	}
1790 
1791 	if (ses_set_standard_props(mod, NULL, tn, auth,
1792 	    ses_node_id(cp->sec_enclosure), cp->sec_target->set_devpath) != 0)
1793 		goto error;
1794 
1795 	/*
1796 	 * For enclosures, we want to include all possible targets (for upgrade
1797 	 * purposes).
1798 	 */
1799 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
1800 	    stp = topo_list_next(stp), i++)
1801 		;
1802 
1803 	verify(i != 0);
1804 	paths = alloca(i * sizeof (char *));
1805 
1806 	for (i = 0, stp = topo_list_next(&cp->sec_targets); stp != NULL;
1807 	    stp = topo_list_next(stp), i++)
1808 		paths[i] = stp->set_devpath;
1809 
1810 
1811 	if (topo_prop_set_string_array(tn, TOPO_PGROUP_SES,
1812 	    TOPO_PROP_PATHS, TOPO_PROP_IMMUTABLE, (const char **)paths,
1813 	    i, &err) != 0) {
1814 		topo_mod_dprintf(mod,
1815 		    "failed to create property %s: %s\n",
1816 		    TOPO_PROP_PATHS, topo_strerror(err));
1817 		goto error;
1818 	}
1819 
1820 	/*
1821 	 * Create the nodes for power supplies, fans, controllers and devices.
1822 	 * Note that SAS exopander nodes and connector nodes are handled
1823 	 * through protocol specific processing of controllers.
1824 	 */
1825 	if (ses_create_children(sdp, tn, SES_ET_POWER_SUPPLY,
1826 	    PSU, "PSU", cp, B_TRUE) != 0 ||
1827 	    ses_create_children(sdp, tn, SES_ET_COOLING,
1828 	    FAN, "FAN", cp, B_TRUE) != 0 ||
1829 	    ses_create_children(sdp, tn, SES_ET_ESC_ELECTRONICS,
1830 	    CONTROLLER, "CONTROLLER", cp, B_TRUE) != 0 ||
1831 	    ses_create_children(sdp, tn, SES_ET_DEVICE,
1832 	    BAY, "BAY", cp, B_TRUE) != 0 ||
1833 	    ses_create_children(sdp, tn, SES_ET_ARRAY_DEVICE,
1834 	    BAY, "BAY", cp, B_TRUE) != 0)
1835 		goto error;
1836 
1837 	if (cp->sec_maxinstance >= 0 &&
1838 	    (topo_node_range_create(mod, tn, SUBCHASSIS, 0,
1839 	    cp->sec_maxinstance) != 0)) {
1840 		topo_mod_dprintf(mod, "topo_node_create_range() failed: %s",
1841 		    topo_mod_errmsg(mod));
1842 		goto error;
1843 	}
1844 
1845 	for (scp = topo_list_next(&cp->sec_subchassis); scp != NULL;
1846 	    scp = topo_list_next(scp)) {
1847 
1848 		if (ses_create_subchassis(sdp, tn, scp) != 0)
1849 			goto error;
1850 
1851 		topo_mod_dprintf(mod, "created Subchassis node with "
1852 		    "instance %u\nand target (%s) under Chassis with CSN %s",
1853 		    scp->sec_instance, scp->sec_target->set_devpath,
1854 		    cp->sec_csn);
1855 
1856 		sc_count++;
1857 	}
1858 
1859 	topo_mod_dprintf(mod, "%s: created %llu %s nodes",
1860 	    cp->sec_csn, sc_count, SUBCHASSIS);
1861 
1862 	cp->sec_target->set_refcount++;
1863 	topo_node_setspecific(tn, cp->sec_target);
1864 
1865 	ret = 0;
1866 error:
1867 	topo_mod_strfree(mod, manufacturer);
1868 	topo_mod_strfree(mod, model);
1869 	topo_mod_strfree(mod, revision);
1870 	topo_mod_strfree(mod, product);
1871 
1872 	nvlist_free(fmri);
1873 	nvlist_free(auth);
1874 	return (ret);
1875 }
1876 
1877 /*
1878  * Create a bay node explicitly enumerated via XML.
1879  */
1880 static int
1881 ses_create_bays(ses_enum_data_t *sdp, tnode_t *pnode)
1882 {
1883 	topo_mod_t *mod = sdp->sed_mod;
1884 	ses_enum_chassis_t *cp;
1885 
1886 	/*
1887 	 * Iterate over chassis looking for an internal enclosure.  This
1888 	 * property is set via a vendor-specific plugin, and there should only
1889 	 * ever be a single internal chassis in a system.
1890 	 */
1891 	for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
1892 	    cp = topo_list_next(cp)) {
1893 		if (cp->sec_internal)
1894 			break;
1895 	}
1896 
1897 	if (cp == NULL) {
1898 		topo_mod_dprintf(mod, "failed to find internal chassis\n");
1899 		return (-1);
1900 	}
1901 
1902 	if (ses_create_children(sdp, pnode, SES_ET_DEVICE,
1903 	    BAY, "BAY", cp, B_FALSE) != 0 ||
1904 	    ses_create_children(sdp, pnode, SES_ET_ARRAY_DEVICE,
1905 	    BAY, "BAY", cp, B_FALSE) != 0)
1906 		return (-1);
1907 
1908 	return (0);
1909 }
1910 
1911 /*
1912  * Initialize chassis or subchassis.
1913  */
1914 static int
1915 ses_init_chassis(topo_mod_t *mod, ses_enum_data_t *sdp, ses_enum_chassis_t *pcp,
1916     ses_enum_chassis_t *cp, ses_node_t *np, nvlist_t *props,
1917     uint64_t subchassis, ses_chassis_type_e flags)
1918 {
1919 	boolean_t internal, ident;
1920 
1921 	assert((flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS |
1922 	    SES_DUP_CHASSIS | SES_DUP_SUBCHASSIS)) != 0);
1923 
1924 	assert(cp != NULL);
1925 	assert(np != NULL);
1926 	assert(props != NULL);
1927 
1928 	if (flags & (SES_NEW_SUBCHASSIS | SES_DUP_SUBCHASSIS))
1929 		assert(pcp != NULL);
1930 
1931 	topo_mod_dprintf(mod, "ses_init_chassis: %s: index %llu, flags (%d)",
1932 	    sdp->sed_name, subchassis, flags);
1933 
1934 	if (flags & (SES_NEW_CHASSIS | SES_NEW_SUBCHASSIS)) {
1935 
1936 		topo_mod_dprintf(mod, "new chassis/subchassis");
1937 		if (nvlist_lookup_boolean_value(props,
1938 		    LIBSES_EN_PROP_INTERNAL, &internal) == 0)
1939 			cp->sec_internal = internal;
1940 
1941 		cp->sec_enclosure = np;
1942 		cp->sec_target = sdp->sed_target;
1943 
1944 		if (flags & SES_NEW_CHASSIS) {
1945 			if (!cp->sec_internal)
1946 				cp->sec_instance = sdp->sed_instance++;
1947 			topo_list_append(&sdp->sed_chassis, cp);
1948 		} else {
1949 			if (subchassis != NO_SUBCHASSIS)
1950 				cp->sec_instance = subchassis;
1951 			else
1952 				cp->sec_instance = pcp->sec_scinstance++;
1953 
1954 			if (cp->sec_instance > pcp->sec_maxinstance)
1955 				pcp->sec_maxinstance = cp->sec_instance;
1956 
1957 			topo_list_append(&pcp->sec_subchassis, cp);
1958 		}
1959 
1960 	} else {
1961 		topo_mod_dprintf(mod, "dup chassis/subchassis");
1962 		if (nvlist_lookup_boolean_value(props,
1963 		    SES_PROP_IDENT, &ident) == 0) {
1964 			topo_mod_dprintf(mod,  "overriding enclosure node");
1965 
1966 			cp->sec_enclosure = np;
1967 			cp->sec_target = sdp->sed_target;
1968 		}
1969 	}
1970 
1971 	topo_list_append(&cp->sec_targets, sdp->sed_target);
1972 	sdp->sed_current = cp;
1973 
1974 	return (0);
1975 }
1976 
1977 /*
1978  * Gather nodes from the current SES target into our chassis list, merging the
1979  * results if necessary.
1980  */
1981 static ses_walk_action_t
1982 ses_enum_gather(ses_node_t *np, void *data)
1983 {
1984 	nvlist_t *props = ses_node_props(np);
1985 	ses_enum_data_t *sdp = data;
1986 	topo_mod_t *mod = sdp->sed_mod;
1987 	ses_enum_chassis_t *cp, *scp;
1988 	ses_enum_node_t *snp;
1989 	ses_alt_node_t *sap;
1990 	char *csn;
1991 	uint64_t instance, type;
1992 	uint64_t prevstatus, status;
1993 	boolean_t report;
1994 	uint64_t subchassis = NO_SUBCHASSIS;
1995 
1996 	if (ses_node_type(np) == SES_NODE_ENCLOSURE) {
1997 		/*
1998 		 * If we have already identified the chassis for this target,
1999 		 * then this is a secondary enclosure and we should ignore it,
2000 		 * along with the rest of the tree (since this is depth-first).
2001 		 */
2002 		if (sdp->sed_current != NULL)
2003 			return (SES_WALK_ACTION_TERMINATE);
2004 
2005 		/*
2006 		 * Go through the list of chassis we have seen so far and see
2007 		 * if this serial number matches one of the known values.
2008 		 * If so, check whether this enclosure is a subchassis.
2009 		 */
2010 		if (nvlist_lookup_string(props, LIBSES_EN_PROP_CSN,
2011 		    &csn) != 0)
2012 			return (SES_WALK_ACTION_TERMINATE);
2013 
2014 		(void) nvlist_lookup_uint64(props, LIBSES_EN_PROP_SUBCHASSIS_ID,
2015 		    &subchassis);
2016 
2017 		topo_mod_dprintf(mod, "ses_enum_gather: Enclosure Node (%s) "
2018 		    "CSN (%s), subchassis (%llu)", sdp->sed_name, csn,
2019 		    subchassis);
2020 
2021 		/*
2022 		 * We need to determine whether this enclosure node
2023 		 * represents a chassis or a subchassis. Since we may
2024 		 * receive the enclosure nodes in a non-deterministic
2025 		 * manner, we need to account for all possible combinations:
2026 		 *	1. Chassis for the current CSN has not yet been
2027 		 *	   allocated
2028 		 *		1.1 This is a new chassis:
2029 		 *			allocate and instantiate the chassis
2030 		 *		1.2 This is a new subchassis:
2031 		 *			allocate a placeholder chassis
2032 		 *			allocate and instantiate the subchassis
2033 		 *			link the subchassis to the chassis
2034 		 *	2. Chassis for the current CSN has been allocated
2035 		 *		2.1 This is a duplicate chassis enclosure
2036 		 *			check whether to override old chassis
2037 		 *			append to chassis' target list
2038 		 *		2.2 Only placeholder chassis exists
2039 		 *			fill in the chassis fields
2040 		 *		2.3 This is a new subchassis
2041 		 *			allocate and instantiate the subchassis
2042 		 *			link the subchassis to the chassis
2043 		 *		2.4 This is a duplicate subchassis enclosure
2044 		 *			 check whether to override old chassis
2045 		 *			 append to chassis' target list
2046 		 */
2047 
2048 		for (cp = topo_list_next(&sdp->sed_chassis); cp != NULL;
2049 		    cp = topo_list_next(cp))
2050 			if (strcmp(cp->sec_csn, csn) == 0)
2051 				break;
2052 
2053 		if (cp == NULL) {
2054 			/* 1. Haven't seen a chassis with this CSN before */
2055 
2056 			if ((cp = topo_mod_zalloc(mod,
2057 			    sizeof (ses_enum_chassis_t))) == NULL)
2058 				goto error;
2059 
2060 			cp->sec_scinstance = SES_STARTING_SUBCHASSIS;
2061 			cp->sec_maxinstance = -1;
2062 			cp->sec_csn = csn;
2063 
2064 			if (subchassis == NO_SUBCHASSIS) {
2065 				/* 1.1 This is a new chassis */
2066 
2067 				topo_mod_dprintf(mod, "%s: Initialize new "
2068 				    "chassis with CSN %s", sdp->sed_name, csn);
2069 
2070 				if (ses_init_chassis(mod, sdp, NULL, cp,
2071 				    np, props, NO_SUBCHASSIS,
2072 				    SES_NEW_CHASSIS) < 0)
2073 					goto error;
2074 			} else {
2075 				/* 1.2 This is a new subchassis */
2076 
2077 				topo_mod_dprintf(mod, "%s: Initialize new "
2078 				    "subchassis with CSN %s and index %llu",
2079 				    sdp->sed_name, csn, subchassis);
2080 
2081 				if ((scp = topo_mod_zalloc(mod,
2082 				    sizeof (ses_enum_chassis_t))) == NULL)
2083 					goto error;
2084 
2085 				scp->sec_csn = csn;
2086 
2087 				if (ses_init_chassis(mod, sdp, cp, scp, np,
2088 				    props, subchassis, SES_NEW_SUBCHASSIS) < 0)
2089 					goto error;
2090 			}
2091 		} else {
2092 			/*
2093 			 * We have a chassis or subchassis with this CSN.  If
2094 			 * it's a chassis, we must check to see whether it is
2095 			 * a placeholder previously created because we found a
2096 			 * subchassis with this CSN.  We will know that because
2097 			 * the sec_target value will not be set; it is set only
2098 			 * in ses_init_chassis().  In that case, initialise it
2099 			 * as a new chassis; otherwise, it's a duplicate and we
2100 			 * need to append only.
2101 			 */
2102 			if (subchassis == NO_SUBCHASSIS) {
2103 				if (cp->sec_target != NULL) {
2104 					/* 2.1 This is a duplicate chassis */
2105 
2106 					topo_mod_dprintf(mod, "%s: Append "
2107 					    "duplicate chassis with CSN (%s)",
2108 					    sdp->sed_name, csn);
2109 
2110 					if (ses_init_chassis(mod, sdp, NULL, cp,
2111 					    np, props, NO_SUBCHASSIS,
2112 					    SES_DUP_CHASSIS) < 0)
2113 						goto error;
2114 				} else {
2115 					/* Placeholder chassis - init it up */
2116 					topo_mod_dprintf(mod, "%s: Initialize"
2117 					    "placeholder chassis with CSN %s",
2118 					    sdp->sed_name, csn);
2119 
2120 					if (ses_init_chassis(mod, sdp, NULL,
2121 					    cp, np, props, NO_SUBCHASSIS,
2122 					    SES_NEW_CHASSIS) < 0)
2123 						goto error;
2124 
2125 				}
2126 			} else {
2127 				/* This is a subchassis */
2128 
2129 				for (scp = topo_list_next(&cp->sec_subchassis);
2130 				    scp != NULL; scp = topo_list_next(scp))
2131 					if (scp->sec_instance == subchassis)
2132 						break;
2133 
2134 				if (scp == NULL) {
2135 					/* 2.3 This is a new subchassis */
2136 
2137 					topo_mod_dprintf(mod, "%s: Initialize "
2138 					    "new subchassis with CSN (%s) "
2139 					    "and LID (%s)",
2140 					    sdp->sed_name, csn);
2141 
2142 					if ((scp = topo_mod_zalloc(mod,
2143 					    sizeof (ses_enum_chassis_t)))
2144 					    == NULL)
2145 						goto error;
2146 
2147 					scp->sec_csn = csn;
2148 
2149 					if (ses_init_chassis(mod, sdp, cp, scp,
2150 					    np, props, subchassis,
2151 					    SES_NEW_SUBCHASSIS) < 0)
2152 						goto error;
2153 				} else {
2154 					/* 2.4 This is a duplicate subchassis */
2155 
2156 					topo_mod_dprintf(mod, "%s: Append "
2157 					    "duplicate subchassis with "
2158 					    "CSN (%s)", sdp->sed_name, csn);
2159 
2160 					if (ses_init_chassis(mod, sdp, cp, scp,
2161 					    np, props, subchassis,
2162 					    SES_DUP_SUBCHASSIS) < 0)
2163 						goto error;
2164 				}
2165 			}
2166 		}
2167 	} else if (ses_node_type(np) == SES_NODE_ELEMENT) {
2168 		/*
2169 		 * If we haven't yet seen an enclosure node and identified the
2170 		 * current chassis, something is very wrong; bail out.
2171 		 */
2172 		if (sdp->sed_current == NULL)
2173 			return (SES_WALK_ACTION_TERMINATE);
2174 
2175 		/*
2176 		 * If this isn't one of the element types we care about, then
2177 		 * ignore it.
2178 		 */
2179 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE,
2180 		    &type) == 0);
2181 		if (type != SES_ET_DEVICE &&
2182 		    type != SES_ET_ARRAY_DEVICE &&
2183 		    type != SES_ET_COOLING &&
2184 		    type != SES_ET_POWER_SUPPLY &&
2185 		    type != SES_ET_ESC_ELECTRONICS &&
2186 		    type != SES_ET_SAS_EXPANDER &&
2187 		    type != SES_ET_SAS_CONNECTOR)
2188 			return (SES_WALK_ACTION_CONTINUE);
2189 
2190 		/*
2191 		 * Get the current instance number and see if we already know
2192 		 * about this element.  If so, it means we have multiple paths
2193 		 * to the same elements, and we should ignore the current path.
2194 		 */
2195 		verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
2196 		    &instance) == 0);
2197 		if (type == SES_ET_DEVICE || type == SES_ET_ARRAY_DEVICE)
2198 			(void) nvlist_lookup_uint64(props, SES_PROP_BAY_NUMBER,
2199 			    &instance);
2200 
2201 		cp = sdp->sed_current;
2202 
2203 		for (snp = topo_list_next(&cp->sec_nodes); snp != NULL;
2204 		    snp = topo_list_next(snp)) {
2205 			if (snp->sen_type == type &&
2206 			    snp->sen_instance == instance)
2207 				break;
2208 		}
2209 
2210 		/*
2211 		 * We prefer the new element under the following circumstances:
2212 		 *
2213 		 * - The currently known element's status is unknown or not
2214 		 *   available, but the new element has a known status.  This
2215 		 *   occurs if a given element is only available through a
2216 		 *   particular target.
2217 		 *
2218 		 * - This is an ESC_ELECTRONICS element, and the 'reported-via'
2219 		 *   property is set.  This allows us to get reliable firmware
2220 		 *   revision information from the enclosure node.
2221 		 */
2222 		if (snp != NULL) {
2223 			if (nvlist_lookup_uint64(
2224 			    ses_node_props(snp->sen_node),
2225 			    SES_PROP_STATUS_CODE, &prevstatus) != 0)
2226 				prevstatus = SES_ESC_UNSUPPORTED;
2227 			if (nvlist_lookup_uint64(
2228 			    props, SES_PROP_STATUS_CODE, &status) != 0)
2229 				status = SES_ESC_UNSUPPORTED;
2230 			if (nvlist_lookup_boolean_value(
2231 			    props, SES_PROP_REPORT, &report) != 0)
2232 				report = B_FALSE;
2233 
2234 			if ((SES_STATUS_UNAVAIL(prevstatus) &&
2235 			    !SES_STATUS_UNAVAIL(status)) ||
2236 			    (type == SES_ET_ESC_ELECTRONICS &&
2237 			    report)) {
2238 				snp->sen_node = np;
2239 				snp->sen_target = sdp->sed_target;
2240 			}
2241 
2242 			if ((sap = topo_mod_zalloc(mod,
2243 			    sizeof (ses_alt_node_t))) == NULL)
2244 				goto error;
2245 
2246 			sap->san_node = np;
2247 			topo_list_append(&snp->sen_alt_nodes, sap);
2248 
2249 			return (SES_WALK_ACTION_CONTINUE);
2250 		}
2251 
2252 		if ((snp = topo_mod_zalloc(mod,
2253 		    sizeof (ses_enum_node_t))) == NULL)
2254 			goto error;
2255 
2256 		if ((sap = topo_mod_zalloc(mod,
2257 		    sizeof (ses_alt_node_t))) == NULL) {
2258 			topo_mod_free(mod, snp, sizeof (ses_enum_node_t));
2259 			goto error;
2260 		}
2261 
2262 		topo_mod_dprintf(mod, "%s: adding node (%llu, %llu)",
2263 		    sdp->sed_name, type, instance);
2264 		snp->sen_node = np;
2265 		snp->sen_type = type;
2266 		snp->sen_instance = instance;
2267 		snp->sen_target = sdp->sed_target;
2268 		sap->san_node = np;
2269 		topo_list_append(&snp->sen_alt_nodes, sap);
2270 		topo_list_append(&cp->sec_nodes, snp);
2271 
2272 		if (type == SES_ET_DEVICE)
2273 			cp->sec_hasdev = B_TRUE;
2274 	}
2275 
2276 	return (SES_WALK_ACTION_CONTINUE);
2277 
2278 error:
2279 	sdp->sed_errno = -1;
2280 	return (SES_WALK_ACTION_TERMINATE);
2281 }
2282 
2283 static int
2284 ses_process_dir(const char *dirpath, ses_enum_data_t *sdp)
2285 {
2286 	topo_mod_t *mod = sdp->sed_mod;
2287 	DIR *dir;
2288 	struct dirent *dp;
2289 	char path[PATH_MAX];
2290 	ses_enum_target_t *stp;
2291 	int err = -1;
2292 
2293 	/*
2294 	 * Open the SES target directory and iterate over any available
2295 	 * targets.
2296 	 */
2297 	if ((dir = opendir(dirpath)) == NULL) {
2298 		/*
2299 		 * If the SES target directory does not exist, then return as if
2300 		 * there are no active targets.
2301 		 */
2302 		topo_mod_dprintf(mod, "failed to open ses "
2303 		    "directory '%s'", dirpath);
2304 		return (0);
2305 	}
2306 
2307 	while ((dp = readdir(dir)) != NULL) {
2308 		if (strcmp(dp->d_name, ".") == 0 ||
2309 		    strcmp(dp->d_name, "..") == 0)
2310 			continue;
2311 
2312 		/*
2313 		 * Create a new target instance and take a snapshot.
2314 		 */
2315 		if ((stp = topo_mod_zalloc(mod,
2316 		    sizeof (ses_enum_target_t))) == NULL)
2317 			goto error;
2318 
2319 		(void) pthread_mutex_init(&stp->set_lock, NULL);
2320 
2321 		(void) snprintf(path, sizeof (path), "%s/%s", dirpath,
2322 		    dp->d_name);
2323 
2324 		/*
2325 		 * We keep track of the SES device path and export it on a
2326 		 * per-node basis to allow higher level software to get to the
2327 		 * corresponding SES state.
2328 		 */
2329 		if ((stp->set_devpath = topo_mod_strdup(mod, path)) == NULL) {
2330 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
2331 			goto error;
2332 		}
2333 
2334 		if ((stp->set_target =
2335 		    ses_open(LIBSES_VERSION, path)) == NULL) {
2336 			topo_mod_dprintf(mod, "failed to open ses target "
2337 			    "'%s': %s", dp->d_name, ses_errmsg());
2338 			topo_mod_strfree(mod, stp->set_devpath);
2339 			topo_mod_free(mod, stp, sizeof (ses_enum_target_t));
2340 			continue;
2341 		}
2342 
2343 		stp->set_refcount = 1;
2344 		sdp->sed_target = stp;
2345 		stp->set_snap = ses_snap_hold(stp->set_target);
2346 		stp->set_snaptime = gethrtime();
2347 
2348 		/*
2349 		 * Enumerate over all SES elements and merge them into the
2350 		 * correct ses_enum_chassis_t.
2351 		 */
2352 		sdp->sed_current = NULL;
2353 		sdp->sed_errno = 0;
2354 		sdp->sed_name = dp->d_name;
2355 		(void) ses_walk(stp->set_snap, ses_enum_gather, sdp);
2356 
2357 		if (sdp->sed_errno != 0)
2358 			goto error;
2359 	}
2360 
2361 	err = 0;
2362 error:
2363 	closedir(dir);
2364 	return (err);
2365 }
2366 
2367 static void
2368 ses_release(topo_mod_t *mod, tnode_t *tn)
2369 {
2370 	ses_enum_target_t *stp;
2371 
2372 	if ((stp = topo_node_getspecific(tn)) != NULL)
2373 		ses_target_free(mod, stp);
2374 }
2375 
2376 /*ARGSUSED*/
2377 static int
2378 ses_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
2379     topo_instance_t min, topo_instance_t max, void *arg, void *notused)
2380 {
2381 	ses_enum_chassis_t *cp;
2382 	ses_enum_data_t *data;
2383 
2384 	/*
2385 	 * Check to make sure we're being invoked sensibly, and that we're not
2386 	 * being invoked as part of a post-processing step.
2387 	 */
2388 	if (strcmp(name, SES_ENCLOSURE) != 0 && strcmp(name, BAY) != 0)
2389 		return (0);
2390 
2391 	/*
2392 	 * If this is the first time we've called our enumeration method, then
2393 	 * gather information about any available enclosures.
2394 	 */
2395 	if ((data = topo_mod_getspecific(mod)) == NULL) {
2396 		if ((data = topo_mod_zalloc(mod, sizeof (ses_enum_data_t))) ==
2397 		    NULL)
2398 			return (-1);
2399 
2400 		data->sed_mod = mod;
2401 		topo_mod_setspecific(mod, data);
2402 
2403 		if (dev_list_gather(mod, &data->sed_devs) != 0)
2404 			goto error;
2405 
2406 		/*
2407 		 * We search both the ses(7D) and sgen(7D) locations, so we are
2408 		 * independent of any particular driver class bindings.
2409 		 */
2410 		if (ses_process_dir("/dev/es", data) != 0 ||
2411 		    ses_process_dir("/dev/scsi/ses", data) != 0)
2412 			goto error;
2413 	}
2414 
2415 	if (strcmp(name, SES_ENCLOSURE) == 0) {
2416 		/*
2417 		 * This is a request to enumerate external enclosures.  Go
2418 		 * through all the targets and create chassis nodes where
2419 		 * necessary.
2420 		 */
2421 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
2422 		    cp = topo_list_next(cp)) {
2423 			if (ses_create_chassis(data, rnode, cp) != 0)
2424 				goto error;
2425 		}
2426 	} else {
2427 		/*
2428 		 * This is a request to enumerate a specific bay underneath the
2429 		 * root chassis (for internal disks).
2430 		 */
2431 		if (ses_create_bays(data, rnode) != 0)
2432 			goto error;
2433 	}
2434 
2435 	/*
2436 	 * This is a bit of a kludge.  In order to allow internal disks to be
2437 	 * enumerated and share snapshot-specific information with the external
2438 	 * enclosure enumeration, we rely on the fact that we will be invoked
2439 	 * for the 'ses-enclosure' node last.
2440 	 */
2441 	if (strcmp(name, SES_ENCLOSURE) == 0) {
2442 		for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
2443 		    cp = topo_list_next(cp))
2444 			ses_data_free(data, cp);
2445 		ses_data_free(data, NULL);
2446 		topo_mod_setspecific(mod, NULL);
2447 	}
2448 	return (0);
2449 
2450 error:
2451 	for (cp = topo_list_next(&data->sed_chassis); cp != NULL;
2452 	    cp = topo_list_next(cp))
2453 		ses_data_free(data, cp);
2454 	ses_data_free(data, NULL);
2455 	topo_mod_setspecific(mod, NULL);
2456 	return (-1);
2457 }
2458 
2459 static const topo_modops_t ses_ops =
2460 	{ ses_enum, ses_release };
2461 
2462 static topo_modinfo_t ses_info =
2463 	{ SES_ENCLOSURE, FM_FMRI_SCHEME_HC, SES_VERSION, &ses_ops };
2464 
2465 /*ARGSUSED*/
2466 int
2467 _topo_init(topo_mod_t *mod, topo_version_t version)
2468 {
2469 	if (getenv("TOPOSESDEBUG") != NULL)
2470 		topo_mod_setdebug(mod);
2471 
2472 	topo_mod_dprintf(mod, "initializing %s enumerator\n",
2473 	    SES_ENCLOSURE);
2474 
2475 	return (topo_mod_register(mod, &ses_info, TOPO_VERSION));
2476 }
2477 
2478 void
2479 _topo_fini(topo_mod_t *mod)
2480 {
2481 	topo_mod_unregister(mod);
2482 }
2483