xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/ses/ses_facility.c (revision 74e7dc986c89efca1f2e4451c7a572e05e4a6e4f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Facility node support for SES enclosures.  We support the following facility
29  * nodes, based on the node type:
30  *
31  * 	bay
32  * 		indicator=ident
33  * 		indicator=fail
34  * 		indicator=ok2rm
35  * 		sensor=fault
36  *
37  * 	controller
38  * 		indicator=ident
39  * 		indicator=fail
40  *
41  * 	fan
42  * 		indicator=ident
43  * 		indicator=fail
44  * 		sensor=speed
45  * 		sensor=fault
46  *
47  * 	psu
48  * 		indicator=ident
49  * 		indicator=fail
50  * 		sensor=status
51  *
52  * 	ses-enclosure
53  * 		indicator=ident
54  * 		indicator=fail
55  * 		sensor=fault
56  * 		sensor=<name>	(temperature)
57  * 		sensor=<name>	(voltage)
58  * 		sensor=<name>	(current)
59  *
60  * Most of these are handled by a single method that supports getting and
61  * setting boolean properties on the node.  The fan speed sensor requires a
62  * special handler, while the analog enclosure sensors all have similar
63  * behavior and can be grouped together using a common method.
64  */
65 
66 #include "ses.h"
67 #include "disk.h"
68 
69 #include <string.h>
70 
71 static int ses_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
72     nvlist_t *, nvlist_t **);
73 static int ses_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t,
74     nvlist_t *, nvlist_t **);
75 static int ses_sensor_state(topo_mod_t *, tnode_t *, topo_version_t,
76     nvlist_t *, nvlist_t **);
77 static int ses_psu_state(topo_mod_t *, tnode_t *, topo_version_t,
78     nvlist_t *, nvlist_t **);
79 
80 #define	SES_SUPP_WARN_UNDER	0x01
81 #define	SES_SUPP_WARN_OVER	0x02
82 #define	SES_SUPP_CRIT_UNDER	0x04
83 #define	SES_SUPP_CRIT_OVER	0x08
84 
85 typedef struct ses_sensor_desc {
86 	int		sd_type;
87 	int		sd_units;
88 	const char	*sd_propname;
89 	double		sd_multiplier;
90 } ses_sensor_desc_t;
91 
92 #define	TOPO_METH_SES_MODE_VERSION	0
93 #define	TOPO_METH_SES_READING_VERSION	0
94 #define	TOPO_METH_SES_STATE_VERSION	0
95 #define	TOPO_METH_SES_PSU_VERSION	0
96 
97 #define	TOPO_METH_SES_READING_PROP	"propname"
98 #define	TOPO_METH_SES_READING_MULT	"multiplier"
99 
100 #define	TOPO_METH_SES_STATE_PROP	"propname"
101 
102 #define	TOPO_METH_SES_MODE_PROP		"property-name"
103 #define	TOPO_METH_SES_MODE_ALTPROP	"alternate-property"
104 
105 static const topo_method_t ses_indicator_methods[] = {
106 	{ "ses_indicator_mode", TOPO_PROP_METH_DESC,
107 	    TOPO_METH_SES_MODE_VERSION, TOPO_STABILITY_INTERNAL,
108 	    ses_indicator_mode }
109 };
110 
111 static const topo_method_t ses_sensor_methods[] = {
112 	{ "ses_sensor_reading", TOPO_PROP_METH_DESC,
113 	    TOPO_METH_SES_READING_VERSION, TOPO_STABILITY_INTERNAL,
114 	    ses_sensor_reading },
115 	{ "ses_sensor_state", TOPO_PROP_METH_DESC,
116 	    TOPO_METH_SES_STATE_VERSION, TOPO_STABILITY_INTERNAL,
117 	    ses_sensor_state },
118 	{ "ses_psu_state", TOPO_PROP_METH_DESC,
119 	    TOPO_METH_SES_PSU_VERSION, TOPO_STABILITY_INTERNAL,
120 	    ses_psu_state },
121 };
122 
123 /*
124  * Get or set an indicator.  This method is invoked with arguments indicating
125  * the property to query to retrieve the value.  Some elements (enclosures and
126  * devices) support a request property that is distinct from an array-detected
127  * property.  Either of these conditions will result in the indicator being
128  * lit, so we have to check both properties.
129  */
130 static int
131 ses_indicator_mode(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
132     nvlist_t *in, nvlist_t **out)
133 {
134 	ses_node_t *np;
135 	nvlist_t *args, *pargs, *props;
136 	char *propname, *altprop;
137 	uint32_t mode;
138 	boolean_t current, altcurrent;
139 	nvlist_t *nvl;
140 
141 	if (vers > TOPO_METH_SES_MODE_VERSION)
142 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
143 
144 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
145 	    nvlist_lookup_string(args, TOPO_METH_SES_MODE_PROP,
146 	    &propname) != 0) {
147 		topo_mod_dprintf(mod, "invalid arguments to 'mode' method\n");
148 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
149 	}
150 
151 	if (nvlist_lookup_string(args, TOPO_METH_SES_MODE_ALTPROP,
152 	    &altprop) != 0)
153 		altprop = NULL;
154 
155 	if ((np = ses_node_get(mod, tn)) == NULL) {
156 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
157 		    "method\n");
158 		return (-1);
159 	}
160 	verify((props = ses_node_props(np)) != NULL);
161 
162 	if (nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0 &&
163 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
164 		/* set operation */
165 		if (nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
166 		    &mode) != 0) {
167 			topo_mod_dprintf(mod, "invalid type for indicator "
168 			    "mode property");
169 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
170 		}
171 
172 		if (mode != TOPO_LED_STATE_OFF && mode != TOPO_LED_STATE_ON) {
173 			topo_mod_dprintf(mod, "invalid indicator mode %d\n",
174 			    mode);
175 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
176 		}
177 
178 		nvl = NULL;
179 		if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
180 		    nvlist_add_boolean_value(nvl, propname,
181 		    mode == TOPO_LED_STATE_ON ? B_TRUE : B_FALSE) != 0) {
182 			nvlist_free(nvl);
183 			return (topo_mod_seterrno(mod, EMOD_NOMEM));
184 		}
185 
186 		if (ses_node_ctl(np, SES_CTL_OP_SETPROP, nvl) != 0) {
187 			topo_mod_dprintf(mod, "failed to set indicator: %s\n",
188 			    ses_errmsg());
189 			nvlist_free(nvl);
190 			return (-1);
191 		}
192 
193 		nvlist_free(nvl);
194 	} else {
195 		/* get operation */
196 		if (nvlist_lookup_boolean_value(props,
197 		    propname, &current) != 0) {
198 			topo_mod_dprintf(mod, "failed to lookup %s in node "
199 			    "properties\n", propname);
200 			return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
201 		}
202 
203 		if (altprop != NULL && nvlist_lookup_boolean_value(props,
204 		    altprop, &altcurrent) == 0)
205 			current |= altcurrent;
206 
207 		mode = current ? TOPO_LED_STATE_ON : TOPO_LED_STATE_OFF;
208 	}
209 
210 	nvl = NULL;
211 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
212 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
213 	    TOPO_LED_MODE) != 0 ||
214 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
215 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) {
216 		nvlist_free(nvl);
217 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
218 	}
219 
220 	*out = nvl;
221 	return (0);
222 }
223 
224 /*
225  * Read the given sensor value.  This just looks up the value in the node
226  * properties, and multiplies by a fixed value (determined when the method is
227  * instantiated).
228  */
229 static int
230 ses_sensor_reading(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
231     nvlist_t *in, nvlist_t **out)
232 {
233 	ses_node_t *np;
234 	nvlist_t *args, *props;
235 	char *prop;
236 	double raw, multiplier;
237 	uint64_t current;
238 	int64_t scurrent;
239 	nvlist_t *nvl;
240 
241 	if (vers > TOPO_METH_SES_MODE_VERSION)
242 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
243 
244 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0 ||
245 	    nvlist_lookup_string(args, TOPO_METH_SES_READING_PROP,
246 	    &prop) != 0) {
247 		topo_mod_dprintf(mod,
248 		    "invalid arguments to 'reading' method\n");
249 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
250 	}
251 
252 	if (nvlist_lookup_double(args, TOPO_METH_SES_READING_MULT,
253 	    &multiplier) != 0)
254 		multiplier = 1;
255 
256 	if ((np = ses_node_get(mod, tn)) == NULL) {
257 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
258 		    "method\n");
259 		return (-1);
260 	}
261 	verify((props = ses_node_props(np)) != NULL);
262 
263 	if (nvlist_lookup_uint64(props, prop, &current) == 0) {
264 		raw = (double)current;
265 	} else if (nvlist_lookup_int64(props, prop, &scurrent) == 0) {
266 		raw = (double)scurrent;
267 	} else {
268 		topo_mod_dprintf(mod, "failed to lookup %s in node "
269 		    "properties\n", prop);
270 		return (topo_mod_seterrno(mod, EMOD_METHOD_NOTSUP));
271 	}
272 
273 	nvl = NULL;
274 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
275 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
276 	    TOPO_SENSOR_READING) != 0 ||
277 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
278 	    nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, raw * multiplier) != 0) {
279 		nvlist_free(nvl);
280 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
281 	}
282 
283 	*out = nvl;
284 	return (0);
285 }
286 
287 /*
288  * Returns the current sensor state.  This can be invoked for one of two
289  * different types of sensors: threshold or discrete sensors.  For discrete
290  * sensors, we expect a name of a boolean property and indicate
291  * asserted/deasserted based on that.  For threshold sensors, we check for the
292  * standard warning/critical properties and translate that into the appropriate
293  * topo state.
294  */
295 /*ARGSUSED*/
296 static int
297 ses_sensor_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
298     nvlist_t *in, nvlist_t **out)
299 {
300 	nvlist_t *nvl, *args, *props;
301 	boolean_t value, asserted;
302 	uint64_t status;
303 	uint32_t state;
304 	ses_node_t *np;
305 	char *prop;
306 
307 	if ((np = ses_node_get(mod, tn)) == NULL) {
308 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
309 		    "method\n");
310 		return (-1);
311 	}
312 	verify((props = ses_node_props(np)) != NULL);
313 
314 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
315 		topo_mod_dprintf(mod,
316 		    "invalid arguments to 'state' method\n");
317 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
318 	}
319 
320 	if (nvlist_lookup_uint64(props, SES_PROP_STATUS_CODE, &status) != 0)
321 		status = SES_ESC_UNSUPPORTED;
322 
323 	state = 0;
324 	if (nvlist_lookup_string(args, TOPO_METH_SES_STATE_PROP,
325 	    &prop) == 0) {
326 		/* discrete (fault) sensor */
327 
328 		asserted = B_FALSE;
329 		if (nvlist_lookup_boolean_value(props, prop, &value) == 0 &&
330 		    value)
331 			asserted = B_TRUE;
332 		if (status == SES_ESC_UNRECOVERABLE ||
333 		    status == SES_ESC_CRITICAL)
334 			asserted = B_TRUE;
335 
336 		if (asserted)
337 			state |= TOPO_SENSOR_STATE_GENERIC_STATE_ASSERTED;
338 		else
339 			state |= TOPO_SENSOR_STATE_GENERIC_STATE_DEASSERTED;
340 	} else {
341 		/* threshold sensor */
342 		if (nvlist_lookup_boolean_value(props,
343 		    SES_PROP_WARN_UNDER, &value) == 0 && value)
344 			state |=
345 			    TOPO_SENSOR_THRESH_LOWER_NONCRIT_GOING_HIGH;
346 		if (nvlist_lookup_boolean_value(props,
347 		    SES_PROP_WARN_OVER, &value) == 0 && value)
348 			state |=
349 			    TOPO_SENSOR_THRESH_UPPER_NONCRIT_GOING_HIGH;
350 		if (nvlist_lookup_boolean_value(props,
351 		    SES_PROP_CRIT_UNDER, &value) == 0 && value)
352 			state |=
353 			    TOPO_SENSOR_THRESH_LOWER_CRIT_GOING_HIGH;
354 		if (nvlist_lookup_boolean_value(props,
355 		    SES_PROP_CRIT_OVER, &value) == 0 && value)
356 			state |=
357 			    TOPO_SENSOR_THRESH_UPPER_CRIT_GOING_HIGH;
358 	}
359 
360 	nvl = NULL;
361 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
362 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
363 	    TOPO_SENSOR_STATE) != 0 ||
364 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
365 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) {
366 		nvlist_free(nvl);
367 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
368 	}
369 
370 	*out = nvl;
371 	return (0);
372 }
373 
374 /*
375  * Read the status of a PSU.  This is such a specialized operation that it has
376  * its own method instead of trying to piggyback on ses_sensor_state().  We
377  * use the following mapping to get to the standard topo power supply states:
378  *
379  *	acfail		-> INPUT_LOST
380  *	dcfail		-> INPUT_LOST
381  *	undervoltage	-> INPUT_RANGE
382  *	overvoltage	-> INPUT_RANGE_PRES
383  *	overcurrent	-> INPUT_RANGE_PRES
384  *	overtemp	-> (none)
385  *
386  * If we ever have a need for reading overtemp, we can expand the topo
387  * representation for power supplies, but at the moment this seems unnecessary.
388  */
389 /*ARGSUSED*/
390 static int
391 ses_psu_state(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
392     nvlist_t *in, nvlist_t **out)
393 {
394 	nvlist_t *nvl, *props;
395 	boolean_t value;
396 	uint32_t state;
397 	ses_node_t *np;
398 
399 	if ((np = ses_node_get(mod, tn)) == NULL) {
400 		topo_mod_dprintf(mod, "failed to lookup ses node in 'mode' "
401 		    "method\n");
402 		return (-1);
403 	}
404 	verify((props = ses_node_props(np)) != NULL);
405 
406 	state = 0;
407 	if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_FAIL,
408 	    &value) == 0 && value) ||
409 	    (nvlist_lookup_boolean_value(props, SES_PSU_PROP_AC_FAIL,
410 	    &value) == 0 && value))
411 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_LOST;
412 
413 	if (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_UNDER_VOLTAGE,
414 	    &value) == 0 && value)
415 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE;
416 
417 	if ((nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_VOLTAGE,
418 	    &value) == 0 && value) ||
419 	    (nvlist_lookup_boolean_value(props, SES_PSU_PROP_DC_OVER_CURRENT,
420 	    &value) == 0 && value))
421 		state |= TOPO_SENSOR_STATE_POWER_SUPPLY_INPUT_RANGE_PRES;
422 
423 	nvl = NULL;
424 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
425 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
426 	    TOPO_SENSOR_STATE) != 0 ||
427 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
428 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) {
429 		nvlist_free(nvl);
430 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
431 	}
432 
433 	*out = nvl;
434 	return (0);
435 }
436 
437 /*
438  * Create a facility node, either a sensor or an indicator.
439  */
440 static tnode_t *
441 ses_add_fac_common(topo_mod_t *mod, tnode_t *pnode, const char *name,
442     const char *type, uint64_t nodeid)
443 {
444 	tnode_t *tn;
445 	topo_pgroup_info_t pgi;
446 	int err;
447 	ses_enum_target_t *stp = topo_node_getspecific(pnode);
448 
449 	if ((tn = topo_node_facbind(mod, pnode, name, type)) == NULL) {
450 		topo_mod_dprintf(mod, "failed to bind facility node %s\n",
451 		    name);
452 		return (NULL);
453 	}
454 
455 	stp->set_refcount++;
456 	topo_node_setspecific(tn, stp);
457 
458 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
459 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
460 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
461 	pgi.tpi_version = 1;
462 
463 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
464 		topo_mod_dprintf(mod, "failed to create facility property "
465 		    "group: %s\n", topo_strerror(err));
466 		topo_node_unbind(tn);
467 		return (NULL);
468 	}
469 
470 	/*
471 	 * We need the node-id property for each facility node.
472 	 */
473 	pgi.tpi_name = TOPO_PGROUP_SES;
474 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
475 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
476 	pgi.tpi_version = TOPO_VERSION;
477 
478 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
479 		topo_mod_dprintf(mod, "failed to create ses property "
480 		    "group: %s\n", topo_strerror(err));
481 		topo_node_unbind(tn);
482 		return (NULL);
483 	}
484 
485 	if (topo_prop_set_uint64(tn, TOPO_PGROUP_SES,
486 	    TOPO_PROP_NODE_ID, TOPO_PROP_IMMUTABLE,
487 	    nodeid, &err) != 0) {
488 		topo_mod_dprintf(mod,
489 		    "failed to create property %s: %s\n",
490 		    TOPO_PROP_NODE_ID, topo_strerror(err));
491 		topo_node_unbind(tn);
492 		return (NULL);
493 	}
494 
495 	return (tn);
496 }
497 
498 /*
499  * Add an indicator.  This can be represented by a single property, or by the
500  * union of two elements when SES is capable of distinguishing between
501  * requested failure and detected failure.
502  */
503 static int
504 ses_add_indicator(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
505     int type, const char *name, const char *propname, const char *altprop)
506 {
507 	tnode_t *tn;
508 	int err;
509 	nvlist_t *nvl;
510 
511 	/* create facility node and add methods */
512 	if ((tn = ses_add_fac_common(mod, pnode, name,
513 	    TOPO_FAC_TYPE_INDICATOR, nodeid)) == NULL)
514 		return (-1);
515 
516 	if (topo_method_register(mod, tn, ses_indicator_methods) < 0) {
517 		topo_mod_dprintf(mod, "failed to register facility methods\n");
518 		topo_node_unbind(tn);
519 		return (-1);
520 	}
521 
522 	/* set standard properties */
523 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
524 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, type, &err) != 0) {
525 		topo_mod_dprintf(mod,
526 		    "failed to set facility node properties: %s\n",
527 		    topo_strerror(err));
528 		topo_node_unbind(tn);
529 		return (-1);
530 	}
531 
532 	/* 'mode' property */
533 	nvl = NULL;
534 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
535 	    nvlist_add_string(nvl, TOPO_METH_SES_MODE_PROP,
536 	    propname) != 0 ||
537 	    (altprop != NULL && nvlist_add_string(nvl,
538 	    TOPO_METH_SES_MODE_ALTPROP, altprop) != 0)) {
539 		nvlist_free(nvl);
540 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
541 		topo_node_unbind(tn);
542 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
543 	}
544 
545 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
546 	    TOPO_LED_MODE, TOPO_TYPE_UINT32, "ses_indicator_mode",
547 	    nvl, &err) != 0) {
548 		nvlist_free(nvl);
549 		topo_mod_dprintf(mod, "failed to register reading method: %s\n",
550 		    topo_strerror(err));
551 		return (-1);
552 	}
553 
554 	if (topo_prop_setmutable(tn, TOPO_PGROUP_FACILITY,
555 	    TOPO_LED_MODE, &err) != 0) {
556 		nvlist_free(nvl);
557 		topo_mod_dprintf(mod, "failed to set property as mutable: %s\n",
558 		    topo_strerror(err));
559 		return (-1);
560 	}
561 
562 	nvlist_free(nvl);
563 	return (0);
564 }
565 
566 static tnode_t *
567 ses_add_sensor_common(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
568     const char *name, const char *class, int type)
569 {
570 	tnode_t *tn;
571 	int err;
572 
573 	/* create facility node and add methods */
574 	if ((tn = ses_add_fac_common(mod, pnode, name,
575 	    TOPO_FAC_TYPE_SENSOR, nodeid)) == NULL)
576 		return (NULL);
577 
578 	if (topo_method_register(mod, tn, ses_sensor_methods) < 0) {
579 		topo_mod_dprintf(mod, "failed to register facility methods\n");
580 		topo_node_unbind(tn);
581 		return (NULL);
582 	}
583 
584 	/* set standard properties */
585 	if (topo_prop_set_string(tn, TOPO_PGROUP_FACILITY,
586 	    TOPO_SENSOR_CLASS, TOPO_PROP_IMMUTABLE,
587 	    class, &err) != 0 ||
588 	    topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
589 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE,
590 	    type, &err) != 0) {
591 		topo_mod_dprintf(mod,
592 		    "failed to set facility node properties: %s\n",
593 		    topo_strerror(err));
594 		topo_node_unbind(tn);
595 		return (NULL);
596 	}
597 
598 	return (tn);
599 }
600 
601 /*
602  * Add an analog (threshold) sensor to the enclosure.  This is used for fan
603  * speed, voltage, current, and temperature sensors.
604  */
605 static int
606 ses_add_sensor(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
607     const char *name, const ses_sensor_desc_t *sdp)
608 {
609 	tnode_t *tn;
610 	int err;
611 	nvlist_t *nvl;
612 
613 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
614 	    TOPO_SENSOR_CLASS_THRESHOLD, sdp->sd_type)) == NULL)
615 		return (-1);
616 
617 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_FACILITY,
618 	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sdp->sd_units, &err) != 0) {
619 		topo_mod_dprintf(mod,
620 		    "failed to set facility node properties: %s\n",
621 		    topo_strerror(err));
622 		topo_node_unbind(tn);
623 		return (-1);
624 	}
625 
626 	/* 'reading' property */
627 	nvl = NULL;
628 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
629 	    nvlist_add_string(nvl, TOPO_METH_SES_READING_PROP,
630 	    sdp->sd_propname) != 0 ||
631 	    (sdp->sd_multiplier != 0 &&
632 	    nvlist_add_double(nvl, TOPO_METH_SES_READING_MULT,
633 	    sdp->sd_multiplier) != 0)) {
634 		nvlist_free(nvl);
635 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
636 		topo_node_unbind(tn);
637 		return (-1);
638 	}
639 
640 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
641 	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ses_sensor_reading",
642 	    nvl, &err) != 0) {
643 		nvlist_free(nvl);
644 		topo_mod_dprintf(mod, "failed to register reading method: %s\n",
645 		    topo_strerror(err));
646 		return (-1);
647 	}
648 
649 	nvlist_free(nvl);
650 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
651 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
652 		topo_node_unbind(tn);
653 		return (-1);
654 	}
655 
656 	/* 'state' property */
657 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
658 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state",
659 	    nvl, &err) != 0) {
660 		nvlist_free(nvl);
661 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
662 		    topo_strerror(err));
663 		return (-1);
664 	}
665 
666 	nvlist_free(nvl);
667 	return (0);
668 }
669 
670 /*
671  * Add a discrete sensor for simple boolean values.  This is used to indicate
672  * externally-detected failures for fans, bays, and enclosures.
673  */
674 static int
675 ses_add_discrete(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid,
676     const char *name, const char *prop)
677 {
678 	tnode_t *tn;
679 	int err;
680 	nvlist_t *nvl;
681 
682 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, name,
683 	    TOPO_SENSOR_CLASS_DISCRETE,
684 	    TOPO_SENSOR_TYPE_GENERIC_STATE)) == NULL)
685 		return (-1);
686 
687 	nvl = NULL;
688 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
689 	    nvlist_add_string(nvl, TOPO_METH_SES_STATE_PROP, prop) != 0) {
690 		nvlist_free(nvl);
691 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
692 		topo_node_unbind(tn);
693 		return (-1);
694 	}
695 
696 	/* 'state' property */
697 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
698 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_sensor_state",
699 	    nvl, &err) != 0) {
700 		nvlist_free(nvl);
701 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
702 		    topo_strerror(err));
703 		return (-1);
704 	}
705 
706 	nvlist_free(nvl);
707 	return (0);
708 }
709 
710 /*ARGSUSED*/
711 static int
712 ses_add_psu_status(topo_mod_t *mod, tnode_t *pnode, uint64_t nodeid)
713 {
714 	tnode_t *tn;
715 	int err;
716 	nvlist_t *nvl;
717 
718 	/* create facility node and add methods */
719 	if ((tn = ses_add_sensor_common(mod, pnode, nodeid, "status",
720 	    TOPO_SENSOR_CLASS_DISCRETE,
721 	    TOPO_SENSOR_TYPE_POWER_SUPPLY)) == NULL)
722 		return (-1);
723 
724 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0) {
725 		nvlist_free(nvl);
726 		topo_mod_dprintf(mod, "failed to setup method arguments\n");
727 		topo_node_unbind(tn);
728 		return (-1);
729 	}
730 
731 	/* 'state' property */
732 	if (topo_prop_method_register(tn, TOPO_PGROUP_FACILITY,
733 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ses_psu_state",
734 	    nvl, &err) != 0) {
735 		nvlist_free(nvl);
736 		topo_mod_dprintf(mod, "failed to register state method: %s\n",
737 		    topo_strerror(err));
738 		return (-1);
739 	}
740 
741 	nvlist_free(nvl);
742 	return (0);
743 }
744 
745 /*ARGSUSED*/
746 int
747 ses_node_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
748     nvlist_t *in, nvlist_t **out)
749 {
750 	ses_node_t *np;
751 	nvlist_t *props;
752 	uint64_t type, nodeid;
753 	ses_sensor_desc_t sd = { 0 };
754 
755 	if ((np = ses_node_get(mod, tn)) == NULL)
756 		return (-1);
757 
758 	assert(ses_node_type(np) == SES_NODE_ELEMENT);
759 	nodeid = ses_node_id(np);
760 	verify((props = ses_node_props(np)) != NULL);
761 	verify(nvlist_lookup_uint64(props, SES_PROP_ELEMENT_TYPE, &type) == 0);
762 
763 	if (type != SES_ET_DEVICE && type != SES_ET_ARRAY_DEVICE &&
764 	    type != SES_ET_COOLING && type != SES_ET_POWER_SUPPLY)
765 		return (0);
766 
767 	/*
768 	 * Every element supports an 'ident' indicator.  All elements also
769 	 * support a 'fail' indicator, but the properties used to represent
770 	 * this condition differs between elements.
771 	 */
772 	if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
773 	    SES_PROP_IDENT, NULL) != 0)
774 		return (-1);
775 
776 	switch (type) {
777 	case SES_ET_DEVICE:
778 	case SES_ET_ARRAY_DEVICE:
779 		/*
780 		 * Disks support an additional 'ok2rm' indicator, as well as
781 		 * externally detected 'fail' sensor.
782 		 */
783 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
784 		    "fail", SES_DEV_PROP_FAULT_RQSTD,
785 		    SES_DEV_PROP_FAULT_SENSED) != 0 ||
786 		    ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_OK2RM,
787 		    "ok2rm", SES_PROP_RMV, SES_PROP_RMV) != 0 ||
788 		    ses_add_discrete(mod, tn, nodeid, "fault",
789 		    SES_DEV_PROP_FAULT_SENSED) != 0)
790 			return (-1);
791 		break;
792 
793 	case SES_ET_COOLING:
794 		/*
795 		 * Add the fan speed sensor, and a discrete sensor for
796 		 * detecting failure.
797 		 */
798 		sd.sd_type = TOPO_SENSOR_TYPE_THRESHOLD_STATE;
799 		sd.sd_units = TOPO_SENSOR_UNITS_RPM;
800 		sd.sd_propname = SES_COOLING_PROP_FAN_SPEED;
801 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
802 		    "fail", SES_PROP_FAIL, NULL) != 0 ||
803 		    ses_add_sensor(mod, tn, nodeid, "speed", &sd) != 0 ||
804 		    ses_add_discrete(mod, tn, nodeid, "fault",
805 		    SES_PROP_OFF) != 0)
806 			return (-1);
807 		break;
808 
809 	case SES_ET_POWER_SUPPLY:
810 		/*
811 		 * For power supplies, we have a number of different sensors:
812 		 * acfail, dcfail, overtemp, undervoltate, overvoltage,
813 		 * and overcurrent.  Rather than expose these all as individual
814 		 * sensors, we lump them together into a 'status' sensor of
815 		 * type TOPO_SENSOR_TYPE_POWER_SUPPLY and export the
816 		 * appropriate status flags as defined by the libtopo standard.
817 		 */
818 		if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE,
819 		    "fail", SES_PROP_FAIL, NULL) != 0)
820 			return (-1);
821 
822 		if (ses_add_psu_status(mod, tn, nodeid) != 0)
823 			return (-1);
824 		break;
825 
826 	default:
827 		return (0);
828 	}
829 
830 	return (0);
831 }
832 
833 /*
834  * Add enclosure-wide sensors (temperature, voltage, and current) beneath the
835  * given aggregate.
836  */
837 static int
838 ses_add_enclosure_sensors(topo_mod_t *mod, tnode_t *tn, ses_node_t *agg,
839     uint64_t type)
840 {
841 	ses_node_t *child;
842 	const char *defaultname;
843 	char *desc, *name;
844 	char rawname[64];
845 	nvlist_t *props, *aprops;
846 	uint64_t index, nodeid;
847 	ses_sensor_desc_t sd = { 0 };
848 	size_t len;
849 
850 	switch (type) {
851 	case SES_ET_TEMPERATURE_SENSOR:
852 		sd.sd_type = TOPO_SENSOR_TYPE_TEMP;
853 		sd.sd_units = TOPO_SENSOR_UNITS_DEGREES_C;
854 		sd.sd_propname = SES_TEMP_PROP_TEMP;
855 		defaultname = "temperature";
856 		break;
857 
858 	case SES_ET_VOLTAGE_SENSOR:
859 		sd.sd_type = TOPO_SENSOR_TYPE_VOLTAGE;
860 		sd.sd_units = TOPO_SENSOR_UNITS_VOLTS;
861 		sd.sd_propname = SES_VS_PROP_VOLTAGE_MV;
862 		sd.sd_multiplier = 0.001;
863 		defaultname = "voltage";
864 		break;
865 
866 	case SES_ET_CURRENT_SENSOR:
867 		sd.sd_type = TOPO_SENSOR_TYPE_CURRENT;
868 		sd.sd_units = TOPO_SENSOR_UNITS_AMPS;
869 		sd.sd_propname = SES_CS_PROP_CURRENT_MA;
870 		sd.sd_multiplier = 0.001;
871 		defaultname = "current";
872 		break;
873 
874 	default:
875 		return (0);
876 	}
877 
878 	aprops = ses_node_props(agg);
879 
880 	for (child = ses_node_child(agg); child != NULL;
881 	    child = ses_node_sibling(child)) {
882 		/*
883 		 * The only tricky part here is getting the name for the
884 		 * sensor, where we follow the algorithm of the standard
885 		 * elements.
886 		 */
887 		props = ses_node_props(child);
888 		nodeid = ses_node_id(child);
889 		if (nvlist_lookup_uint64(props, SES_PROP_ELEMENT_CLASS_INDEX,
890 		    &index) != 0)
891 			continue;
892 
893 		if (nvlist_lookup_string(props, SES_PROP_DESCRIPTION,
894 		    &desc) == 0 && desc[0] != '\0') {
895 			(void) strlcpy(rawname, desc, sizeof (rawname));
896 		} else {
897 			if (nvlist_lookup_string(aprops,
898 			    SES_PROP_CLASS_DESCRIPTION, &desc) != 0 ||
899 			    desc[0] == '\0')
900 				desc = (char *)defaultname;
901 
902 			len = strlen(desc);
903 			while (len > 0 && desc[len - 1] == ' ')
904 				len--;
905 
906 			(void) snprintf(rawname, sizeof (rawname),
907 			    "%.*s %llu", len, desc, index);
908 		}
909 
910 		if ((name = disk_auth_clean(mod, rawname)) == NULL)
911 			return (-1);
912 
913 		if (ses_add_sensor(mod, tn, nodeid, name, &sd) != 0) {
914 			topo_mod_strfree(mod, name);
915 			return (-1);
916 		}
917 
918 		topo_mod_strfree(mod, name);
919 	}
920 
921 	return (0);
922 }
923 
924 /*ARGSUSED*/
925 int
926 ses_enc_enum_facility(topo_mod_t *mod, tnode_t *tn, topo_version_t vers,
927     nvlist_t *in, nvlist_t **out)
928 {
929 	ses_node_t *np, *agg;
930 	nvlist_t *aprops;
931 	uint64_t type, nodeid;
932 
933 	if ((np = ses_node_get(mod, tn)) == NULL)
934 		return (-1);
935 
936 	assert(ses_node_type(np) == SES_NODE_ENCLOSURE);
937 	nodeid = ses_node_id(np);
938 
939 	/*
940 	 * 'ident' and 'fail' LEDs, and 'fault' sensor.
941 	 */
942 	if (ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_LOCATE, "ident",
943 	    SES_PROP_IDENT, NULL) != 0 ||
944 	    ses_add_indicator(mod, tn, nodeid, TOPO_LED_TYPE_SERVICE, "fail",
945 	    SES_PROP_FAIL_REQ, SES_PROP_FAIL) != 0 ||
946 	    ses_add_discrete(mod, tn, nodeid, "fault", SES_PROP_FAIL) != 0)
947 		return (-1);
948 
949 	/*
950 	 * Environmental sensors (temperature, voltage, current).  We have no
951 	 * way of knowing if any of these sensors correspond to a particular
952 	 * element, so we just attach them to the enclosure as a whole.  In the
953 	 * future, some vendor-specific libses plugin knowledge could let us
954 	 * make this correlation clearer.
955 	 */
956 	for (agg = ses_node_child(np); agg != NULL;
957 	    agg = ses_node_sibling(agg)) {
958 		if (ses_node_type(agg) != SES_NODE_AGGREGATE)
959 			continue;
960 
961 		verify((aprops = ses_node_props(agg)) != NULL);
962 		if (nvlist_lookup_uint64(aprops, SES_PROP_ELEMENT_TYPE,
963 		    &type) != 0)
964 			continue;
965 
966 		if (ses_add_enclosure_sensors(mod, tn, agg, type) != 0)
967 			return (-1);
968 	}
969 
970 	return (0);
971 }
972