xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c (revision e2336878c3b2087bcf5c52436847f37afaec8666)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright 2019 Joyent, Inc.
27  * Copyright 2019 by Western Digital Corporation
28  */
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <strings.h>
34 #include <limits.h>
35 #include <alloca.h>
36 #include <errno.h>
37 #include <libnvpair.h>
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/fm/protocol.h>
41 #include <fm/libtopo.h>
42 #include <fm/topo_mod.h>
43 #include <libipmi.h>
44 
45 #define	BUFSZ	128
46 
47 #define	BAY_PRESENT_LED_MASK	0x01
48 
49 /*
50  * The largest possible SDR ID length is 2^5+1
51  */
52 #define	MAX_ID_LEN	33
53 
54 #define	TOPO_METH_IPMI_PLATFORM_MESSAGE_VERSION	0
55 #define	TOPO_METH_IPMI_READING_VERSION		0
56 #define	TOPO_METH_IPMI_STATE_VERSION		0
57 #define	TOPO_METH_IPMI_MODE_VERSION		0
58 #define	TOPO_METH_X4500_MODE_VERSION		0
59 #define	TOPO_METH_BAY_LOCATE_VERSION		0
60 #define	TOPO_METH_BAY_MODE_VERSION		0
61 #define	TOPO_METH_CHASSIS_SERVICE_VERSION	0
62 #define	TOPO_METH_IPMI_ENTITY_VERSION		0
63 #define	TOPO_METH_DIMM_IPMI_ENTITY_VERSION	0
64 #define	TOPO_METH_CHASSIS_IDENT_VERSION		0
65 
66 static int fac_prov_ipmi_enum(topo_mod_t *, tnode_t *, const char *,
67     topo_instance_t, topo_instance_t, void *, void *);
68 
69 /*
70  * IPMI facility provider methods
71  */
72 static int ipmi_sensor_enum(topo_mod_t *, tnode_t *, topo_version_t,
73     nvlist_t *, nvlist_t **);
74 static int ipmi_entity(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
75     nvlist_t **);
76 static int dimm_ipmi_entity(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
77     nvlist_t **);
78 static int cs_ipmi_entity(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
79     nvlist_t **);
80 static int ipmi_platform_message(topo_mod_t *, tnode_t *, topo_version_t,
81     nvlist_t *, nvlist_t **);
82 static int ipmi_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t,
83     nvlist_t *, nvlist_t **);
84 static int ipmi_sensor_state(topo_mod_t *, tnode_t *, topo_version_t,
85     nvlist_t *, nvlist_t **);
86 static int ipmi_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
87     nvlist_t *, nvlist_t **);
88 static int bay_locate_mode(topo_mod_t *, tnode_t *, topo_version_t,
89     nvlist_t *, nvlist_t **);
90 static int x4500_present_mode(topo_mod_t *, tnode_t *, topo_version_t,
91     nvlist_t *, nvlist_t **);
92 static int bay_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
93     nvlist_t *, nvlist_t **);
94 static int chassis_service_mode(topo_mod_t *, tnode_t *, topo_version_t,
95     nvlist_t *, nvlist_t **);
96 static int chassis_ident_mode(topo_mod_t *, tnode_t *, topo_version_t,
97     nvlist_t *, nvlist_t **);
98 
99 const topo_modops_t ipmi_ops = { fac_prov_ipmi_enum, NULL };
100 
101 const topo_modinfo_t ipmi_info =
102 	{ "IPMI facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION,
103 	&ipmi_ops };
104 
105 static const topo_method_t ipmi_node_methods[] = {
106 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
107 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_enum },
108 	{ TOPO_METH_IPMI_ENTITY, TOPO_PROP_METH_DESC,
109 	    TOPO_METH_IPMI_ENTITY_VERSION,
110 	    TOPO_STABILITY_INTERNAL, ipmi_entity },
111 	{ "dimm_ipmi_entity", TOPO_PROP_METH_DESC,
112 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
113 	    TOPO_STABILITY_INTERNAL, dimm_ipmi_entity },
114 	{ "cs_ipmi_entity", TOPO_PROP_METH_DESC,
115 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
116 	    TOPO_STABILITY_INTERNAL, cs_ipmi_entity },
117 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
118 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
119 	    topo_method_sensor_failure },
120 	{ NULL }
121 };
122 
123 static const topo_method_t ipmi_fac_methods[] = {
124 	{ "ipmi_platform_message", TOPO_PROP_METH_DESC,
125 	    TOPO_METH_IPMI_PLATFORM_MESSAGE_VERSION,
126 	    TOPO_STABILITY_INTERNAL, ipmi_platform_message },
127 	{ "ipmi_sensor_reading", TOPO_PROP_METH_DESC,
128 	    TOPO_METH_IPMI_READING_VERSION,
129 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_reading },
130 	{ "ipmi_sensor_state", TOPO_PROP_METH_DESC,
131 	    TOPO_METH_IPMI_STATE_VERSION,
132 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_state },
133 	{ "ipmi_indicator_mode", TOPO_PROP_METH_DESC,
134 	    TOPO_METH_IPMI_MODE_VERSION,
135 	    TOPO_STABILITY_INTERNAL, ipmi_indicator_mode },
136 	{ "bay_locate_mode", TOPO_PROP_METH_DESC,
137 	    TOPO_METH_BAY_LOCATE_VERSION,
138 	    TOPO_STABILITY_INTERNAL, bay_locate_mode },
139 	{ "bay_indicator_mode", TOPO_PROP_METH_DESC,
140 	    TOPO_METH_BAY_MODE_VERSION,
141 	    TOPO_STABILITY_INTERNAL, bay_indicator_mode },
142 	{ "chassis_service_mode", TOPO_PROP_METH_DESC,
143 	    TOPO_METH_CHASSIS_SERVICE_VERSION,
144 	    TOPO_STABILITY_INTERNAL, chassis_service_mode },
145 	{ "chassis_ident_mode", TOPO_PROP_METH_DESC,
146 	    TOPO_METH_CHASSIS_SERVICE_VERSION,
147 	    TOPO_STABILITY_INTERNAL, chassis_ident_mode },
148 	{ "x4500_present_mode", TOPO_PROP_METH_DESC,
149 	    TOPO_METH_CHASSIS_SERVICE_VERSION,
150 	    TOPO_STABILITY_INTERNAL, x4500_present_mode },
151 	{ TOPO_METH_IPMI_ENTITY, TOPO_PROP_METH_DESC,
152 	    TOPO_METH_IPMI_ENTITY_VERSION,
153 	    TOPO_STABILITY_INTERNAL, ipmi_entity },
154 	{ "dimm_ipmi_entity", TOPO_PROP_METH_DESC,
155 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
156 	    TOPO_STABILITY_INTERNAL, dimm_ipmi_entity },
157 	{ "cs_ipmi_entity", TOPO_PROP_METH_DESC,
158 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
159 	    TOPO_STABILITY_INTERNAL, dimm_ipmi_entity },
160 	{ NULL }
161 };
162 
163 struct entity_info {
164 	uint32_t ei_id;
165 	uint32_t ei_inst;
166 	topo_mod_t *ei_mod;
167 	tnode_t *ei_node;
168 	char **ei_list;
169 	uint_t ei_listsz;
170 };
171 
172 struct sensor_data {
173 	char sd_entity_ref[MAX_ID_LEN];
174 	uint8_t sd_units;
175 	uint32_t sd_stype;
176 	uint32_t sd_rtype;
177 	char *sd_class;
178 	ipmi_sdr_full_sensor_t *sd_fs_sdr;
179 };
180 
181 /*ARGSUSED*/
182 int
183 _topo_init(topo_mod_t *mod, topo_version_t version)
184 {
185 	if (getenv("TOPOFACIPMIDEBUG") != NULL)
186 		topo_mod_setdebug(mod);
187 
188 	return (topo_mod_register(mod, &ipmi_info, TOPO_VERSION));
189 }
190 
191 void
192 _topo_fini(topo_mod_t *mod)
193 {
194 	topo_mod_unregister(mod);
195 }
196 
197 /*
198  * Some platforms (most notably G1/2N) use the 'platform event message' command
199  * to manipulate disk fault LEDs over IPMI, but uses the standard sensor
200  * reading to read the value.  This method implements this alternative
201  * interface for these platforms.
202  */
203 /*ARGSUSED*/
204 static int
205 ipmi_platform_message(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
206     nvlist_t *in, nvlist_t **out)
207 {
208 	char *entity_ref;
209 	ipmi_sdr_compact_sensor_t *csp;
210 	ipmi_handle_t *hdl;
211 	int err, ret;
212 	uint32_t mode;
213 	nvlist_t *pargs, *nvl;
214 	ipmi_platform_event_message_t pem;
215 	ipmi_sensor_reading_t *reading;
216 
217 	if (vers > TOPO_METH_IPMI_PLATFORM_MESSAGE_VERSION)
218 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
219 
220 	/*
221 	 * Get an IPMI handle and then lookup the generic device locator sensor
222 	 * data record referenced by the entity_ref prop val
223 	 */
224 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
225 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
226 		return (-1);
227 	}
228 
229 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
230 	    &entity_ref, &err) != 0) {
231 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
232 		    "(%s)", topo_strerror(err));
233 		topo_mod_ipmi_rele(mod);
234 		return (-1);
235 	}
236 
237 	if ((csp = ipmi_sdr_lookup_compact_sensor(hdl, entity_ref)) == NULL) {
238 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
239 		    entity_ref, ipmi_errmsg(hdl));
240 		topo_mod_strfree(mod, entity_ref);
241 		topo_mod_ipmi_rele(mod);
242 		return (-1);
243 	}
244 
245 	/*
246 	 * Now look for a private argument list to figure out whether we're
247 	 * doing a get or a set operation, and then do it.
248 	 */
249 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
250 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
251 		/*
252 		 * Set the LED mode
253 		 */
254 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
255 		    &mode)) != 0) {
256 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
257 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
258 			topo_mod_strfree(mod, entity_ref);
259 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
260 			topo_mod_ipmi_rele(mod);
261 			return (-1);
262 		}
263 
264 		if (mode != TOPO_LED_STATE_OFF &&
265 		    mode != TOPO_LED_STATE_ON) {
266 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
267 			    mode);
268 			topo_mod_strfree(mod, entity_ref);
269 			(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
270 			topo_mod_ipmi_rele(mod);
271 			return (-1);
272 		}
273 
274 		pem.ipem_sensor_type = csp->is_cs_type;
275 		pem.ipem_sensor_num = csp->is_cs_number;
276 		pem.ipem_event_type = csp->is_cs_reading_type;
277 
278 		/*
279 		 * The spec states that any values between 0x20 and 0x29 are
280 		 * legitimate for "system software".  However, some versions of
281 		 * Sun's ILOM rejects messages over /dev/ipmi0 with a generator
282 		 * of 0x20, so we use 0x21 instead.
283 		 */
284 		pem.ipem_generator = 0x21;
285 		pem.ipem_event_dir = 0;
286 		pem.ipem_rev = 0x04;
287 		if (mode == TOPO_LED_STATE_ON)
288 			pem.ipem_event_data[0] = 1;
289 		else
290 			pem.ipem_event_data[0] = 0;
291 		pem.ipem_event_data[1] = 0xff;
292 		pem.ipem_event_data[2] = 0xff;
293 
294 		if (ipmi_event_platform_message(hdl, &pem) < 0) {
295 			topo_mod_dprintf(mod, "Failed to set LED mode for %s "
296 			    "(%s)\n", entity_ref, ipmi_errmsg(hdl));
297 			topo_mod_strfree(mod, entity_ref);
298 			topo_mod_ipmi_rele(mod);
299 			return (-1);
300 		}
301 	} else {
302 		/*
303 		 * Get the LED mode
304 		 */
305 		if ((reading = ipmi_get_sensor_reading(hdl, csp->is_cs_number))
306 		    == NULL) {
307 			topo_mod_dprintf(mod, "Failed to get sensor reading "
308 			    "for sensor %s: %s\n", entity_ref,
309 			    ipmi_errmsg(hdl));
310 			topo_mod_strfree(mod, entity_ref);
311 			topo_mod_ipmi_rele(mod);
312 			return (-1);
313 		}
314 
315 		if (reading->isr_state &
316 		    TOPO_SENSOR_STATE_GENERIC_STATE_ASSERTED)
317 			mode = TOPO_LED_STATE_ON;
318 		else
319 			mode = TOPO_LED_STATE_OFF;
320 	}
321 	topo_mod_strfree(mod, entity_ref);
322 
323 	topo_mod_ipmi_rele(mod);
324 
325 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
326 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
327 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
328 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, mode) != 0) {
329 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
330 		nvlist_free(nvl);
331 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
332 	}
333 	*out = nvl;
334 
335 	return (0);
336 }
337 
338 /*ARGSUSED*/
339 static int
340 ipmi_sensor_state(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
341     nvlist_t *in, nvlist_t **out)
342 {
343 	char **entity_refs, *sensor_class;
344 	uint_t nelems;
345 	ipmi_sdr_t *sdr = NULL;
346 	ipmi_sensor_reading_t *reading;
347 	ipmi_handle_t *hdl;
348 	int err, i;
349 	uint8_t sensor_num;
350 	uint32_t e_id, e_inst, state;
351 	ipmi_sdr_full_sensor_t *fsensor;
352 	ipmi_sdr_compact_sensor_t *csensor;
353 	nvlist_t *nvl;
354 	boolean_t found_sdr = B_FALSE;
355 	tnode_t *pnode;
356 
357 	if (vers > TOPO_METH_IPMI_STATE_VERSION)
358 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
359 
360 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
361 	    &entity_refs, &nelems, &err) != 0) {
362 		topo_mod_dprintf(mod, "%s: Failed to lookup entity_ref "
363 		    "property (%s)", __func__, topo_strerror(err));
364 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
365 	}
366 
367 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
368 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
369 		topo_mod_strfreev(mod, entity_refs, nelems);
370 		return (-1);
371 	}
372 
373 	pnode = topo_node_parent(node);
374 	if (topo_prop_get_uint32(pnode, TOPO_PGROUP_IPMI,
375 	    TOPO_PROP_IPMI_ENTITY_ID, &e_id, &err) != 0 ||
376 	    topo_prop_get_uint32(pnode, TOPO_PGROUP_IPMI,
377 	    TOPO_PROP_IPMI_ENTITY_INST, &e_inst, &err) != 0) {
378 		e_id = IPMI_ET_UNSPECIFIED;
379 		e_inst = 0;
380 	}
381 
382 	for (i = 0; i < nelems; i++) {
383 		if ((sdr = ipmi_sdr_lookup_precise(hdl, entity_refs[i],
384 		    (uint8_t)e_id, (uint8_t)e_inst)) != NULL) {
385 			found_sdr = B_TRUE;
386 			break;
387 		} else
388 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
389 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
390 	}
391 
392 	if (! found_sdr) {
393 		topo_mod_strfreev(mod, entity_refs, nelems);
394 		topo_mod_ipmi_rele(mod);
395 		return (-1);
396 	}
397 
398 	switch (sdr->is_type) {
399 		case IPMI_SDR_TYPE_FULL_SENSOR:
400 			fsensor = (ipmi_sdr_full_sensor_t *)sdr->is_record;
401 			sensor_num = fsensor->is_fs_number;
402 			break;
403 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
404 			csensor = (ipmi_sdr_compact_sensor_t *)sdr->is_record;
405 			sensor_num = csensor->is_cs_number;
406 			break;
407 		default:
408 			topo_mod_dprintf(mod, "%s does not refer to a full or "
409 			    "compact SDR\n", entity_refs[i]);
410 			topo_mod_ipmi_rele(mod);
411 			topo_mod_strfreev(mod, entity_refs, nelems);
412 			return (-1);
413 	}
414 	if ((reading = ipmi_get_sensor_reading(hdl, sensor_num))
415 	    == NULL) {
416 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
417 		    "%s, sensor_num=%d (%s)\n", entity_refs[i], sensor_num,
418 		    ipmi_errmsg(hdl));
419 		topo_mod_strfreev(mod, entity_refs, nelems);
420 		topo_mod_ipmi_rele(mod);
421 		return (-1);
422 	}
423 	if (reading->isr_state_unavailable) {
424 		topo_mod_dprintf(mod, "Unavailable sensor %s, sensor_num=%d\n",
425 		    entity_refs[i], sensor_num);
426 		topo_mod_strfreev(mod, entity_refs, nelems);
427 		topo_mod_ipmi_rele(mod);
428 		return (-1);
429 	}
430 	topo_mod_strfreev(mod, entity_refs, nelems);
431 	topo_mod_ipmi_rele(mod);
432 
433 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
434 	    &sensor_class, &err) != 0) {
435 		topo_mod_dprintf(mod, "Failed to lookup prop %s/%s on node %s ",
436 		    "(%s)", TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
437 		    topo_node_name(node), topo_strerror(err));
438 		return (topo_mod_seterrno(mod, EMOD_UKNOWN_ENUM));
439 	}
440 	/*
441 	 * Mask off bits that are marked as reserved in the IPMI spec.
442 	 * For threshold sensors, bits 6:7 are reserved.
443 	 * For discrete sensors, bit 15 is reserved.
444 	 */
445 	state = reading->isr_state;
446 	if (strcmp(sensor_class, TOPO_SENSOR_CLASS_THRESHOLD) == 0)
447 		state = state & 0x3F;
448 	else
449 		state = state & 0x7FFF;
450 
451 	topo_mod_strfree(mod, sensor_class);
452 
453 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
454 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
455 	    TOPO_SENSOR_STATE) != 0 ||
456 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
457 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, state) != 0) {
458 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
459 		nvlist_free(nvl);
460 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
461 	}
462 	*out = nvl;
463 
464 	return (0);
465 }
466 
467 /*ARGSUSED*/
468 static int
469 ipmi_sensor_reading(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
470     nvlist_t *in, nvlist_t **out)
471 {
472 	char **entity_refs, reading_str[BUFSZ];
473 	uint_t nelems;
474 	int err = 0, i;
475 	ipmi_sdr_t *sdr = NULL;
476 	ipmi_sdr_full_sensor_t *fsensor;
477 	ipmi_sensor_reading_t  *reading;
478 	double conv_reading;
479 	ipmi_handle_t *hdl;
480 	nvlist_t *nvl;
481 	boolean_t found_sdr = B_FALSE;
482 	uint8_t sensor_num;
483 	uint32_t e_id, e_inst;
484 	tnode_t *pnode;
485 
486 	if (vers > TOPO_METH_IPMI_READING_VERSION)
487 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
488 
489 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
490 	    &entity_refs, &nelems, &err) != 0) {
491 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
492 		    "(%s)", topo_strerror(err));
493 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
494 	}
495 
496 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
497 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
498 		topo_mod_strfreev(mod, entity_refs, nelems);
499 		return (-1);
500 	}
501 
502 	pnode = topo_node_parent(node);
503 	if (topo_prop_get_uint32(pnode, TOPO_PGROUP_IPMI,
504 	    TOPO_PROP_IPMI_ENTITY_ID, &e_id, &err) != 0 ||
505 	    topo_prop_get_uint32(pnode, TOPO_PGROUP_IPMI,
506 	    TOPO_PROP_IPMI_ENTITY_INST, &e_inst, &err) != 0) {
507 		e_id = IPMI_ET_UNSPECIFIED;
508 		e_inst = 0;
509 	}
510 
511 	for (i = 0; i < nelems; i++) {
512 		if ((sdr = ipmi_sdr_lookup_precise(hdl, entity_refs[i],
513 		    (uint8_t)e_id, (uint8_t)e_inst)) != NULL) {
514 			found_sdr = B_TRUE;
515 			break;
516 		} else
517 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
518 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
519 	}
520 
521 	if (! found_sdr) {
522 		topo_mod_strfreev(mod, entity_refs, nelems);
523 		topo_mod_ipmi_rele(mod);
524 		return (-1);
525 	}
526 	switch (sdr->is_type) {
527 		case IPMI_SDR_TYPE_FULL_SENSOR:
528 			fsensor = (ipmi_sdr_full_sensor_t *)sdr->is_record;
529 			sensor_num = fsensor->is_fs_number;
530 			break;
531 		default:
532 			topo_mod_dprintf(mod, "%s does not refer to a full "
533 			    "sensor SDR\n", entity_refs[i]);
534 			topo_mod_ipmi_rele(mod);
535 			topo_mod_strfreev(mod, entity_refs, nelems);
536 			return (-1);
537 	}
538 
539 	if ((reading = ipmi_get_sensor_reading(hdl, sensor_num)) == NULL) {
540 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
541 		    "%s, sensor_num=%d (%s)\n", entity_refs[i],
542 		    sensor_num, ipmi_errmsg(hdl));
543 		topo_mod_strfreev(mod, entity_refs, nelems);
544 		topo_mod_ipmi_rele(mod);
545 		return (-1);
546 	}
547 	topo_mod_ipmi_rele(mod);
548 
549 	if (ipmi_sdr_conv_reading(fsensor, reading->isr_reading, &conv_reading)
550 	    != 0) {
551 		topo_mod_dprintf(mod, "Failed to convert sensor reading for "
552 		    "sensor %s (%s)\n", entity_refs[i], ipmi_errmsg(hdl));
553 		topo_mod_strfreev(mod, entity_refs, nelems);
554 		return (-1);
555 	}
556 	topo_mod_strfreev(mod, entity_refs, nelems);
557 
558 	(void) snprintf(reading_str, BUFSZ, "%f", conv_reading);
559 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
560 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
561 	    TOPO_SENSOR_READING) != 0 ||
562 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
563 	    nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, conv_reading) != 0) {
564 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
565 		nvlist_free(nvl);
566 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
567 	}
568 	*out = nvl;
569 
570 	return (0);
571 }
572 
573 static int
574 ipmi_indicator_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
575     nvlist_t *in, nvlist_t **out)
576 {
577 	char **entity_refs;
578 	uint_t nelems;
579 	ipmi_sdr_generic_locator_t *gdl = NULL;
580 	ipmi_handle_t *hdl;
581 	int err, ret, i;
582 	uint8_t ledmode;
583 	uint32_t mode_in;
584 	nvlist_t *pargs, *nvl;
585 	boolean_t found_sdr = B_FALSE;
586 
587 	if (vers > TOPO_METH_IPMI_MODE_VERSION)
588 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
589 
590 	/*
591 	 * Get an IPMI handle and then lookup the generic device locator sensor
592 	 * data record referenced by the entity_ref prop val
593 	 */
594 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
595 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
596 		return (-1);
597 	}
598 
599 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
600 	    &entity_refs, &nelems, &err) != 0) {
601 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
602 		    "(%s)", topo_strerror(err));
603 		topo_mod_ipmi_rele(mod);
604 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
605 	}
606 
607 	for (i = 0; i < nelems; i++) {
608 		if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_refs[i]))
609 		    != NULL) {
610 			found_sdr = B_TRUE;
611 			break;
612 		} else
613 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
614 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
615 	}
616 
617 	if (! found_sdr) {
618 		topo_mod_strfreev(mod, entity_refs, nelems);
619 		topo_mod_ipmi_rele(mod);
620 		return (-1);
621 	}
622 
623 	/*
624 	 * Now look for a private argument list to figure out whether we're
625 	 * doing a get or a set operation, and then do it.
626 	 */
627 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
628 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
629 		/*
630 		 * Set the LED mode
631 		 */
632 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
633 		    &mode_in)) != 0) {
634 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
635 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
636 			topo_mod_strfreev(mod, entity_refs, nelems);
637 			topo_mod_ipmi_rele(mod);
638 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
639 		}
640 		if (mode_in != TOPO_LED_STATE_OFF &&
641 		    mode_in != TOPO_LED_STATE_ON) {
642 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
643 			    mode_in);
644 			topo_mod_strfreev(mod, entity_refs, nelems);
645 			topo_mod_ipmi_rele(mod);
646 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
647 		}
648 		ledmode = (uint8_t)mode_in;
649 		if (ipmi_sunoem_led_set(hdl, gdl, ledmode) < 0) {
650 			topo_mod_dprintf(mod, "%s: Failed to set LED mode for "
651 			    "%s (%s) to %s\n", __func__, entity_refs[i],
652 			    ipmi_errmsg(hdl), ledmode ? "ON" : "OFF");
653 			topo_mod_strfreev(mod, entity_refs, nelems);
654 			topo_mod_ipmi_rele(mod);
655 			return (-1);
656 		}
657 	} else {
658 		/*
659 		 * Get the LED mode
660 		 */
661 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
662 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
663 			    "%s (%s)\n", __func__, entity_refs[i],
664 			    ipmi_errmsg(hdl));
665 			topo_mod_strfreev(mod, entity_refs, nelems);
666 			topo_mod_ipmi_rele(mod);
667 			return (-1);
668 		}
669 	}
670 	topo_mod_strfreev(mod, entity_refs, nelems);
671 	topo_mod_ipmi_rele(mod);
672 
673 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
674 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
675 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
676 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
677 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
678 		nvlist_free(nvl);
679 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
680 	}
681 	*out = nvl;
682 
683 	return (0);
684 }
685 
686 /*
687  * On most Sun platforms there is no seperate locate LED for the drive bays.
688  * This propmethod simulates a locate LED by blinking the ok2rm LED.
689  *
690  * LED control is through a the Sun OEM led/get commands.  This propmethod can
691  * work on X4500/X4540 with ILOM 2.x and on
692  * X4140/X4240/X4440/X4500/X4540/X4150/X4250 and X4450 platforms with ILOM 3.x.
693  */
694 static int
695 bay_locate_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
696     nvlist_t *in, nvlist_t **out)
697 {
698 	char **entity_refs;
699 	uint_t nelems;
700 	ipmi_sdr_generic_locator_t *gdl = NULL;
701 	ipmi_handle_t *hdl;
702 	int err, ret, i;
703 	uint8_t ledmode;
704 	uint32_t mode_in;
705 	nvlist_t *pargs, *nvl;
706 	boolean_t found_sdr = B_FALSE;
707 
708 	if (vers > TOPO_METH_BAY_LOCATE_VERSION)
709 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
710 
711 	/*
712 	 * Get an IPMI handle and then lookup the generic device locator sensor
713 	 * data record referenced by the entity_ref prop val
714 	 */
715 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
716 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
717 		return (-1);
718 	}
719 
720 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
721 	    &entity_refs, &nelems, &err) != 0) {
722 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
723 		    "(%s)", topo_strerror(err));
724 		topo_mod_ipmi_rele(mod);
725 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
726 	}
727 
728 	for (i = 0; i < nelems; i++) {
729 		if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_refs[i]))
730 		    != NULL) {
731 			found_sdr = B_TRUE;
732 			break;
733 		} else
734 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
735 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
736 	}
737 
738 	if (! found_sdr) {
739 		topo_mod_strfreev(mod, entity_refs, nelems);
740 		topo_mod_ipmi_rele(mod);
741 		return (-1);
742 	}
743 
744 	/*
745 	 * Now look for a private argument list to figure out whether we're
746 	 * doing a get or a set operation, and then do it.
747 	 */
748 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
749 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
750 		/*
751 		 * Set the LED mode
752 		 */
753 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
754 		    &mode_in)) != 0) {
755 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
756 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
757 			topo_mod_strfreev(mod, entity_refs, nelems);
758 			topo_mod_ipmi_rele(mod);
759 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
760 		}
761 		if (mode_in != TOPO_LED_STATE_OFF &&
762 		    mode_in != TOPO_LED_STATE_ON) {
763 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
764 			    mode_in);
765 			topo_mod_strfreev(mod, entity_refs, nelems);
766 			topo_mod_ipmi_rele(mod);
767 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
768 		}
769 		if (mode_in == TOPO_LED_STATE_ON)
770 			ledmode = IPMI_SUNOEM_LED_MODE_FAST;
771 		else
772 			ledmode = IPMI_SUNOEM_LED_MODE_OFF;
773 		if (ipmi_sunoem_led_set(hdl, gdl, ledmode) < 0) {
774 			topo_mod_dprintf(mod, "Failed to set LED mode for %s "
775 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
776 			topo_mod_strfreev(mod, entity_refs, nelems);
777 			topo_mod_ipmi_rele(mod);
778 			return (-1);
779 		}
780 	} else {
781 		/*
782 		 * Get the LED mode
783 		 */
784 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
785 			topo_mod_dprintf(mod, "Failed to get LED mode for %s "
786 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
787 			topo_mod_strfreev(mod, entity_refs, nelems);
788 			topo_mod_ipmi_rele(mod);
789 			return (-1);
790 		}
791 	}
792 	topo_mod_strfreev(mod, entity_refs, nelems);
793 	topo_mod_ipmi_rele(mod);
794 
795 	if (ledmode == IPMI_SUNOEM_LED_MODE_SLOW ||
796 	    ledmode == IPMI_SUNOEM_LED_MODE_FAST)
797 		ledmode = TOPO_LED_STATE_ON;
798 	else
799 		ledmode = TOPO_LED_STATE_OFF;
800 
801 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
802 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
803 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
804 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
805 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
806 		nvlist_free(nvl);
807 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
808 	}
809 	*out = nvl;
810 
811 	return (0);
812 }
813 
814 /*
815  * This is a method for the "mode" property that is specific for the ok2rm and
816  * service drive bay LED's on the X4500/X4540 platforms running ILOM 2.x and
817  * for X4140/X4240/X4440/X4500/X4540/X4150/X4250 and X4450 platforms running
818  * ILOM 3.x.
819  *
820  * For ILOM 2.x, the LED's are controlled by a Sun OEM led set command
821  *
822  * For ILOM 3.x platforms the LED's are controlled by sending a platform event
823  * message for the appropriate DBP/HDD##/STATE compact SDR.
824  *
825  * For both ILOM 2 and ILOM 3, the current LED mode can be obtained by a
826  * Sun OEM led get command.
827  */
828 static int
829 bay_indicator_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
830     nvlist_t *in, nvlist_t **out)
831 {
832 	char **entity_refs;
833 	uint_t nelems;
834 	ipmi_sdr_compact_sensor_t *cs = NULL;
835 	ipmi_sdr_generic_locator_t *gdl = NULL;
836 	ipmi_deviceid_t *sp_devid;
837 	ipmi_platform_event_message_t pem;
838 	ipmi_handle_t *hdl;
839 	int err, ret, i;
840 	uint32_t type, ledmode;
841 	uint8_t mode_in, ev_off;
842 	nvlist_t *pargs, *nvl;
843 	boolean_t found_sdr = B_FALSE;
844 
845 	if (vers > TOPO_METH_BAY_MODE_VERSION)
846 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
847 
848 	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
849 	    &type, &err) != 0) {
850 		topo_mod_dprintf(mod, "Failed to lookup %s property "
851 		    "(%s)", TOPO_FACILITY_TYPE, topo_strerror(err));
852 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
853 	}
854 	switch (type) {
855 	case (TOPO_LED_TYPE_SERVICE):
856 		ev_off = 0x01;
857 		break;
858 	case (TOPO_LED_TYPE_OK2RM):
859 		ev_off = 0x03;
860 		break;
861 	default:
862 		topo_mod_dprintf(mod, "Invalid LED type: 0x%x\n", type);
863 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
864 	}
865 
866 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
867 	    &entity_refs, &nelems, &err) != 0) {
868 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
869 		    "(%s)", topo_strerror(err));
870 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
871 	}
872 
873 	/*
874 	 * Figure out whether the SP is running ILOM 2.x or ILOM 3.x
875 	 */
876 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
877 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
878 		topo_mod_strfreev(mod, entity_refs, nelems);
879 		return (-1);
880 	}
881 
882 	if ((sp_devid = ipmi_get_deviceid(hdl)) == NULL) {
883 		topo_mod_dprintf(mod, "%s: GET DEVICEID command failed (%s)\n",
884 		    __func__, ipmi_errmsg(hdl));
885 		topo_mod_strfreev(mod, entity_refs, nelems);
886 		topo_mod_ipmi_rele(mod);
887 		return (-1);
888 	}
889 
890 	/*
891 	 * Now lookup the propmethod argument list and figure out whether we're
892 	 * doing a get or a set operation, and then do it.
893 	 */
894 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
895 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
896 		/*
897 		 * Set the LED mode
898 		 */
899 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
900 		    &ledmode)) != 0) {
901 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
902 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
903 			topo_mod_strfreev(mod, entity_refs, nelems);
904 			topo_mod_ipmi_rele(mod);
905 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
906 		}
907 
908 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
909 		    ledmode ? "ON" : "OFF");
910 
911 		if (sp_devid->id_firm_major == 2) {
912 			for (i = 0; i < nelems; i++) {
913 				if ((gdl = ipmi_sdr_lookup_generic(hdl,
914 				    entity_refs[i])) != NULL) {
915 					found_sdr = B_TRUE;
916 					break;
917 				} else
918 					topo_mod_dprintf(mod,
919 					    "Failed to lookup SDR for %s(%s)\n",
920 					    entity_refs[i], ipmi_errmsg(hdl));
921 			}
922 
923 			if (! found_sdr) {
924 				topo_mod_strfreev(mod, entity_refs, nelems);
925 				topo_mod_ipmi_rele(mod);
926 				return (-1);
927 			}
928 
929 			if (ipmi_sunoem_led_set(hdl, gdl, (uint8_t)ledmode)
930 			    < 0) {
931 				topo_mod_dprintf(mod,
932 				    "Failed to set LED mode for %s (%s)\n",
933 				    entity_refs[i], ipmi_errmsg(hdl));
934 				topo_mod_strfreev(mod, entity_refs, nelems);
935 				topo_mod_ipmi_rele(mod);
936 				return (-1);
937 			}
938 		} else {
939 			for (i = 0; i < nelems; i++) {
940 				if ((cs = ipmi_sdr_lookup_compact_sensor(hdl,
941 				    entity_refs[i])) != NULL) {
942 					found_sdr = B_TRUE;
943 					break;
944 				} else
945 					topo_mod_dprintf(mod,
946 					    "Failed to lookup SDR for %s(%s)\n",
947 					    entity_refs[i], ipmi_errmsg(hdl));
948 			}
949 
950 			if (! found_sdr) {
951 				topo_mod_strfreev(mod, entity_refs, nelems);
952 				topo_mod_ipmi_rele(mod);
953 				return (-1);
954 			}
955 
956 			pem.ipem_generator = IPMI_SEL_SYSTEM;
957 			pem.ipem_rev = IPMI_EV_REV15;
958 			pem.ipem_sensor_type = IPMI_ST_BAY;
959 			pem.ipem_sensor_num = cs->is_cs_number;
960 			pem.ipem_event_type =  IPMI_RT_SPECIFIC;
961 			if (ledmode == TOPO_LED_STATE_ON)
962 				pem.ipem_event_dir = 0;
963 			else
964 				pem.ipem_event_dir = 1;
965 
966 			pem.ipem_event_data[0] = ev_off;
967 			pem.ipem_event_data[1] = 0xff;
968 			pem.ipem_event_data[2] = 0xff;
969 
970 			if (ipmi_event_platform_message(hdl, &pem) != 0) {
971 				topo_mod_dprintf(mod, "%s: Failed to send "
972 				    "platform event mesg for %s (%s)\n",
973 				    __func__, entity_refs[i], ipmi_errmsg(hdl));
974 				topo_mod_strfreev(mod, entity_refs, nelems);
975 				topo_mod_ipmi_rele(mod);
976 				return (-1);
977 			}
978 		}
979 	} else {
980 		/*
981 		 * Get the LED mode
982 		 */
983 		for (i = 0; i < nelems; i++) {
984 			if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_refs[i]))
985 			    != NULL) {
986 				found_sdr = B_TRUE;
987 				break;
988 			} else
989 				topo_mod_dprintf(mod, "%s: Failed to lookup "
990 				    "SDR for %s (%s)\n", __func__,
991 				    entity_refs[i], ipmi_errmsg(hdl));
992 		}
993 
994 		if (! found_sdr) {
995 			topo_mod_strfreev(mod, entity_refs, nelems);
996 			topo_mod_ipmi_rele(mod);
997 			return (-1);
998 		}
999 		if (ipmi_sunoem_led_get(hdl, gdl, &mode_in) < 0) {
1000 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
1001 			    "%s (%s)\n", __func__, entity_refs[i],
1002 			    ipmi_errmsg(hdl));
1003 			topo_mod_strfreev(mod, entity_refs, nelems);
1004 			topo_mod_ipmi_rele(mod);
1005 			return (-1);
1006 		}
1007 		ledmode = mode_in;
1008 	}
1009 	topo_mod_strfreev(mod, entity_refs, nelems);
1010 	topo_mod_ipmi_rele(mod);
1011 
1012 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1013 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
1014 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
1015 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
1016 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1017 		nvlist_free(nvl);
1018 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1019 	}
1020 	*out = nvl;
1021 	return (0);
1022 }
1023 
1024 /*
1025  * This propmethod is for controlling the present LED on the drive bays for
1026  * the X4500 platform.
1027  */
1028 static int
1029 x4500_present_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1030     nvlist_t *in, nvlist_t **out)
1031 {
1032 	char **entity_refs;
1033 	uint_t nelems;
1034 	ipmi_sdr_compact_sensor_t *cs = NULL;
1035 	ipmi_set_sensor_reading_t sr_out = { 0 };
1036 	ipmi_handle_t *hdl;
1037 	int err, ret, i;
1038 	uint32_t ledmode;
1039 	nvlist_t *pargs, *nvl;
1040 	boolean_t found_sdr = B_FALSE;
1041 
1042 	if (vers > TOPO_METH_X4500_MODE_VERSION)
1043 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1044 
1045 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
1046 	    &entity_refs, &nelems, &err) != 0) {
1047 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
1048 		    "(%s)", topo_strerror(err));
1049 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1050 	}
1051 
1052 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
1053 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
1054 		topo_mod_strfreev(mod, entity_refs, nelems);
1055 		return (-1);
1056 	}
1057 	for (i = 0; i < nelems; i++) {
1058 		if ((cs = ipmi_sdr_lookup_compact_sensor(hdl, entity_refs[i]))
1059 		    != NULL) {
1060 			found_sdr = B_TRUE;
1061 			break;
1062 		} else
1063 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
1064 			    "(%s)\n", entity_refs[i],
1065 			    ipmi_errmsg(hdl));
1066 	}
1067 
1068 	if (! found_sdr) {
1069 		topo_mod_strfreev(mod, entity_refs, nelems);
1070 		topo_mod_ipmi_rele(mod);
1071 		return (-1);
1072 	}
1073 
1074 	/*
1075 	 * Now lookup the propmethod argument list and figure out whether we're
1076 	 * doing a get or a set operation, and then do it.
1077 	 */
1078 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
1079 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
1080 		/*
1081 		 * Set the LED mode
1082 		 */
1083 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
1084 		    &ledmode)) != 0) {
1085 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
1086 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
1087 			topo_mod_strfreev(mod, entity_refs, nelems);
1088 			topo_mod_ipmi_rele(mod);
1089 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1090 		}
1091 
1092 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
1093 		    ledmode ? "ON" : "OFF");
1094 
1095 		if (ledmode == TOPO_LED_STATE_OFF) {
1096 			sr_out.iss_deassert_state = BAY_PRESENT_LED_MASK;
1097 			sr_out.iss_deassrt_op = IPMI_SENSOR_OP_SET;
1098 		} else if (ledmode == TOPO_LED_STATE_ON) {
1099 			sr_out.iss_assert_state = BAY_PRESENT_LED_MASK;
1100 			sr_out.iss_assert_op = IPMI_SENSOR_OP_SET;
1101 		} else {
1102 			topo_mod_dprintf(mod, "%s: Invalid LED mode: "
1103 			    "%d\n", __func__, ledmode);
1104 			topo_mod_strfreev(mod, entity_refs, nelems);
1105 			topo_mod_ipmi_rele(mod);
1106 			return (-1);
1107 		}
1108 		sr_out.iss_id = cs->is_cs_number;
1109 		topo_mod_dprintf(mod, "Setting LED mode (mask=0x%x)\n",
1110 		    BAY_PRESENT_LED_MASK);
1111 		if (ipmi_set_sensor_reading(hdl, &sr_out) != 0) {
1112 			topo_mod_dprintf(mod, "%s: Failed to set "
1113 			    "sensor reading for %s (%s)\n", __func__,
1114 			    entity_refs[i], ipmi_errmsg(hdl));
1115 			topo_mod_strfreev(mod, entity_refs, nelems);
1116 			topo_mod_ipmi_rele(mod);
1117 			return (-1);
1118 		}
1119 	} else {
1120 		/*
1121 		 * Get the LED mode
1122 		 */
1123 		ipmi_sensor_reading_t *sr_in;
1124 
1125 		topo_mod_dprintf(mod, "Getting LED mode\n");
1126 		if ((sr_in = ipmi_get_sensor_reading(hdl, cs->is_cs_number))
1127 		    == NULL) {
1128 			topo_mod_dprintf(mod, "Failed to get sensor reading "
1129 			    "for sensor %s (sensor num: %d) (error: %s)\n",
1130 			    entity_refs[i], cs->is_cs_number, ipmi_errmsg(hdl));
1131 			topo_mod_strfreev(mod, entity_refs, nelems);
1132 			topo_mod_ipmi_rele(mod);
1133 			return (-1);
1134 		}
1135 		if (sr_in->isr_state & (uint16_t)BAY_PRESENT_LED_MASK)
1136 			ledmode = TOPO_LED_STATE_ON;
1137 		else
1138 			ledmode = TOPO_LED_STATE_OFF;
1139 	}
1140 	topo_mod_strfreev(mod, entity_refs, nelems);
1141 	topo_mod_ipmi_rele(mod);
1142 
1143 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1144 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
1145 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
1146 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
1147 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1148 		nvlist_free(nvl);
1149 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1150 	}
1151 	*out = nvl;
1152 	return (0);
1153 }
1154 
1155 /*
1156  * This is a property method for controlling the chassis service LED on
1157  * ILOM 3.x based platforms.
1158  */
1159 static int
1160 chassis_service_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1161     nvlist_t *in, nvlist_t **out)
1162 {
1163 	char **entity_refs;
1164 	uint_t nelems;
1165 	ipmi_sdr_generic_locator_t *gdl = NULL;
1166 	ipmi_deviceid_t *sp_devid;
1167 	ipmi_platform_event_message_t pem;
1168 	ipmi_handle_t *hdl;
1169 	int err, ret, i;
1170 	uint8_t ledmode;
1171 	uint32_t mode_in;
1172 	nvlist_t *pargs, *nvl;
1173 	boolean_t found_sdr = B_FALSE;
1174 
1175 	if (vers > TOPO_METH_CHASSIS_SERVICE_VERSION)
1176 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1177 
1178 	/*
1179 	 * Get an IPMI handle and then lookup the generic device locator record
1180 	 * referenced by the entity_ref prop val
1181 	 */
1182 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
1183 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
1184 		return (-1);
1185 	}
1186 
1187 	if (topo_prop_get_string_array(node, TOPO_PGROUP_FACILITY, "entity_ref",
1188 	    &entity_refs, &nelems, &err) != 0) {
1189 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
1190 		    "(%s)", topo_strerror(err));
1191 		topo_mod_ipmi_rele(mod);
1192 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1193 	}
1194 
1195 	for (i = 0; i < nelems; i++) {
1196 		if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_refs[i]))
1197 		    != NULL) {
1198 			found_sdr = B_TRUE;
1199 			break;
1200 		} else
1201 			topo_mod_dprintf(mod, "Failed to lookup SDR for %s "
1202 			    "(%s)\n", entity_refs[i], ipmi_errmsg(hdl));
1203 	}
1204 
1205 	if (! found_sdr) {
1206 		topo_mod_strfreev(mod, entity_refs, nelems);
1207 		topo_mod_ipmi_rele(mod);
1208 		return (-1);
1209 	}
1210 
1211 	/*
1212 	 * Now lookup the propmethod argument list and figure out whether we're
1213 	 * doing a get or a set operation, and then do it.
1214 	 */
1215 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
1216 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
1217 		/*
1218 		 * Set the LED mode
1219 		 */
1220 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
1221 		    &mode_in)) != 0) {
1222 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
1223 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
1224 			topo_mod_strfreev(mod, entity_refs, nelems);
1225 			topo_mod_ipmi_rele(mod);
1226 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1227 		}
1228 
1229 		/*
1230 		 * Determine which IPMI mechanism to use to set the LED mode
1231 		 * based on whether the SP is running ILOM 2 or later.
1232 		 */
1233 		if ((sp_devid = ipmi_get_deviceid(hdl)) == NULL) {
1234 			topo_mod_dprintf(mod, "%s: GET DEVICEID command failed "
1235 			"(%s)\n", __func__, ipmi_errmsg(hdl));
1236 			topo_mod_strfreev(mod, entity_refs, nelems);
1237 			topo_mod_ipmi_rele(mod);
1238 			return (-1);
1239 		}
1240 
1241 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
1242 		    mode_in ? "ON" : "OFF");
1243 
1244 		if (sp_devid->id_firm_major == 2) {
1245 			if (mode_in != TOPO_LED_STATE_OFF &&
1246 			    mode_in != TOPO_LED_STATE_ON) {
1247 				topo_mod_dprintf(mod, "Invalid property value: "
1248 				    "%d\n", mode_in);
1249 				topo_mod_strfreev(mod, entity_refs, nelems);
1250 				topo_mod_ipmi_rele(mod);
1251 				return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1252 			}
1253 			if (ipmi_sunoem_led_set(hdl, gdl, (uint8_t)mode_in)
1254 			    < 0) {
1255 				topo_mod_dprintf(mod, "Failed to set LED mode "
1256 				    "for %s (%s)\n", entity_refs[i],
1257 				    ipmi_errmsg(hdl));
1258 				topo_mod_strfreev(mod, entity_refs, nelems);
1259 				topo_mod_ipmi_rele(mod);
1260 				return (-1);
1261 			}
1262 		} else {
1263 			pem.ipem_generator = IPMI_SEL_SYSTEM;
1264 			pem.ipem_rev = IPMI_EV_REV15;
1265 			pem.ipem_sensor_type = IPMI_ST_SYSTEM;
1266 			pem.ipem_sensor_num = 0x00;
1267 			pem.ipem_event_type =  IPMI_RT_SPECIFIC;
1268 			if (mode_in == TOPO_LED_STATE_ON)
1269 				pem.ipem_event_dir = 0;
1270 			else
1271 				pem.ipem_event_dir = 1;
1272 
1273 			pem.ipem_event_data[0] = 0x02;
1274 			pem.ipem_event_data[1] = 0xff;
1275 			pem.ipem_event_data[2] = 0xff;
1276 
1277 			topo_mod_dprintf(mod, "Sending platform event\n");
1278 			if (ipmi_event_platform_message(hdl, &pem) != 0) {
1279 				topo_mod_dprintf(mod, "%s: Failed to send "
1280 				    "platform event mesg for sensor 0 (%s)\n",
1281 				    __func__, ipmi_errmsg(hdl));
1282 				topo_mod_strfreev(mod, entity_refs, nelems);
1283 				topo_mod_ipmi_rele(mod);
1284 				return (-1);
1285 			}
1286 		}
1287 	} else {
1288 		/*
1289 		 * Get the LED mode
1290 		 */
1291 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
1292 			topo_mod_dprintf(mod, "%s: Failed to get LED mode for "
1293 			    "%s (%s)\n", __func__, entity_refs[i],
1294 			    ipmi_errmsg(hdl));
1295 			topo_mod_strfreev(mod, entity_refs, nelems);
1296 			topo_mod_ipmi_rele(mod);
1297 			return (-1);
1298 		}
1299 	}
1300 	topo_mod_strfreev(mod, entity_refs, nelems);
1301 	topo_mod_ipmi_rele(mod);
1302 
1303 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1304 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
1305 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
1306 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
1307 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1308 		nvlist_free(nvl);
1309 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1310 	}
1311 	*out = nvl;
1312 	return (0);
1313 }
1314 
1315 /*
1316  * This is a property method for controlling the chassis identify LED using
1317  * generic IPMI mechanisms.
1318  */
1319 /*ARGSUSED*/
1320 static int
1321 chassis_ident_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1322     nvlist_t *in, nvlist_t **out)
1323 {
1324 	ipmi_handle_t *hdl;
1325 	int ret;
1326 	uint32_t modeval;
1327 	boolean_t assert_ident;
1328 	nvlist_t *pargs, *nvl;
1329 	ipmi_chassis_status_t *chs;
1330 
1331 	if (vers > TOPO_METH_CHASSIS_IDENT_VERSION)
1332 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1333 
1334 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
1335 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
1336 		return (-1);
1337 	}
1338 
1339 	/*
1340 	 * Now lookup the propmethod argument list and figure out whether we're
1341 	 * doing a get or a set operation, and then do it.
1342 	 */
1343 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
1344 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
1345 		/*
1346 		 * Set the LED mode
1347 		 */
1348 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
1349 		    &modeval)) != 0) {
1350 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
1351 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
1352 			topo_mod_ipmi_rele(mod);
1353 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1354 		}
1355 
1356 		assert_ident = modeval ? B_TRUE : B_FALSE;
1357 		topo_mod_dprintf(mod, "%s: Setting LED mode to %s\n", __func__,
1358 		    assert_ident ? "ON" : "OFF");
1359 		if (ipmi_chassis_identify(hdl, assert_ident) != 0) {
1360 			topo_mod_ipmi_rele(mod);
1361 			return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
1362 		}
1363 
1364 	} else {
1365 		/*
1366 		 * Get the LED mode
1367 		 */
1368 		if ((chs = ipmi_chassis_status(hdl)) == NULL ||
1369 		    !chs->ichs_identify_supported) {
1370 			free(chs);
1371 			topo_mod_ipmi_rele(mod);
1372 			return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
1373 		}
1374 		/*
1375 		 * ichs_identify_state is a 2-bit value with the following
1376 		 * semantics:
1377 		 * 0 - ident is off
1378 		 * 1 - ident is temporarily on
1379 		 * 2 - ident is indefinitely on
1380 		 * 3 - reserved
1381 		 */
1382 		switch (chs->ichs_identify_state) {
1383 		case 0:
1384 			modeval = TOPO_LED_STATE_OFF;
1385 			break;
1386 		case 1:
1387 		case 2:
1388 			modeval = TOPO_LED_STATE_ON;
1389 			break;
1390 		default:
1391 			free(chs);
1392 			topo_mod_ipmi_rele(mod);
1393 			return (topo_mod_seterrno(mod, EMOD_UNKNOWN));
1394 		}
1395 		free(chs);
1396 	}
1397 	topo_mod_ipmi_rele(mod);
1398 
1399 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1400 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
1401 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
1402 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, modeval) != 0) {
1403 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1404 		nvlist_free(nvl);
1405 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1406 	}
1407 	*out = nvl;
1408 	return (0);
1409 }
1410 
1411 #define	ISBITSET(MASK, BIT)	((MASK & BIT) == BIT)
1412 
1413 struct sensor_thresh {
1414 	uint8_t	sthr_threshbit;
1415 	const char *sthr_propname;
1416 	uint8_t sthr_threshoff;
1417 };
1418 
1419 static const struct sensor_thresh threshset[] = {
1420 	{ IPMI_SENSOR_THRESHOLD_LOWER_NONCRIT, TOPO_PROP_THRESHOLD_LNC,
1421 	    offsetof(ipmi_sensor_thresholds_t, ithr_lower_noncrit) },
1422 	{ IPMI_SENSOR_THRESHOLD_LOWER_CRIT, TOPO_PROP_THRESHOLD_LCR,
1423 	    offsetof(ipmi_sensor_thresholds_t, ithr_lower_crit) },
1424 	{ IPMI_SENSOR_THRESHOLD_LOWER_NONRECOV, TOPO_PROP_THRESHOLD_LNR,
1425 	    offsetof(ipmi_sensor_thresholds_t, ithr_lower_nonrec) },
1426 	{ IPMI_SENSOR_THRESHOLD_UPPER_NONCRIT, TOPO_PROP_THRESHOLD_UNC,
1427 	    offsetof(ipmi_sensor_thresholds_t, ithr_upper_noncrit) },
1428 	{ IPMI_SENSOR_THRESHOLD_UPPER_CRIT, TOPO_PROP_THRESHOLD_UCR,
1429 	    offsetof(ipmi_sensor_thresholds_t, ithr_upper_crit) },
1430 	{ IPMI_SENSOR_THRESHOLD_UPPER_NONRECOV, TOPO_PROP_THRESHOLD_UNR,
1431 	    offsetof(ipmi_sensor_thresholds_t, ithr_upper_nonrec) }
1432 };
1433 
1434 static uint_t num_thresholds =
1435     sizeof (threshset) / sizeof (struct sensor_thresh);
1436 
1437 static int
1438 set_thresh_prop(topo_mod_t *mod, tnode_t *fnode, ipmi_sdr_full_sensor_t *fs,
1439     uint8_t raw_thresh, const struct sensor_thresh *thresh)
1440 {
1441 	int err;
1442 	double conv_thresh;
1443 
1444 	if (ipmi_sdr_conv_reading(fs, raw_thresh, &conv_thresh) != 0) {
1445 		topo_mod_dprintf(mod, "Failed to convert threshold %s on node "
1446 		    "%s", thresh->sthr_propname, topo_node_name(fnode));
1447 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1448 	}
1449 	if (topo_prop_set_double(fnode, TOPO_PGROUP_FACILITY,
1450 	    thresh->sthr_propname, TOPO_PROP_IMMUTABLE, conv_thresh, &err) !=
1451 	    0) {
1452 		topo_mod_dprintf(mod, "Failed to set property %s on node %s "
1453 		    "(%s)", thresh->sthr_propname, topo_node_name(fnode),
1454 		    topo_strerror(err));
1455 		return (topo_mod_seterrno(mod, err));
1456 	}
1457 	return (0);
1458 }
1459 
1460 static int
1461 make_sensor_node(topo_mod_t *mod, tnode_t *pnode, struct sensor_data *sd,
1462     ipmi_handle_t *hdl)
1463 {
1464 	int err, ret, i;
1465 	tnode_t *fnode;
1466 	char *ftype = "sensor", facname[MAX_ID_LEN], **entity_refs;
1467 	topo_pgroup_info_t pgi;
1468 	nvlist_t *arg_nvl = NULL;
1469 	ipmi_sensor_thresholds_t thresh = { 0 };
1470 	uint8_t mask;
1471 
1472 	/*
1473 	 * Some platforms have '/' characters in the IPMI entity name, but '/'
1474 	 * has a special meaning for FMRI's so we change them to '.' before
1475 	 * binding the node into the topology.
1476 	 */
1477 	(void) strcpy(facname, sd->sd_entity_ref);
1478 	for (i = 0; facname[i]; i++)
1479 		if (facname[i] == '/')
1480 			facname[i] = '.';
1481 
1482 	if ((fnode = topo_node_facbind(mod, pnode, facname, ftype)) == NULL) {
1483 		topo_mod_dprintf(mod, "Failed to bind facility node: %s\n",
1484 		    facname);
1485 		/* errno set */
1486 		return (-1);
1487 	}
1488 
1489 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
1490 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1491 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1492 	pgi.tpi_version = 1;
1493 	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
1494 		if (err != ETOPO_PROP_DEFD) {
1495 			topo_mod_dprintf(mod,  "pgroups create failure: %s\n",
1496 			    topo_strerror(err));
1497 			topo_node_unbind(fnode);
1498 			return (topo_mod_seterrno(mod, err));
1499 		}
1500 	}
1501 	if (topo_method_register(mod, fnode, ipmi_fac_methods) < 0) {
1502 		topo_mod_dprintf(mod, "make_fac_node: "
1503 		    "failed to register facility methods");
1504 		topo_node_unbind(fnode);
1505 		/* errno set */
1506 		return (-1);
1507 	}
1508 	/*
1509 	 * For both threshold and discrete sensors we set up a propmethod for
1510 	 * getting the sensor state and properties to hold the entity ref,
1511 	 * sensor class and sensor type.
1512 	 */
1513 	if ((entity_refs = topo_mod_alloc(mod, sizeof (char *))) == NULL)
1514 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1515 
1516 	entity_refs[0] = topo_mod_strdup(mod, sd->sd_entity_ref);
1517 
1518 	if (topo_prop_set_string_array(fnode, TOPO_PGROUP_FACILITY,
1519 	    "entity_ref", TOPO_PROP_IMMUTABLE, (const char **)entity_refs, 1,
1520 	    &err) != 0) {
1521 		topo_mod_dprintf(mod, "%s: Failed to set entity_ref property "
1522 		    "on node: %s=%d (%s)\n", __func__, topo_node_name(fnode),
1523 		    topo_node_instance(fnode), topo_strerror(err));
1524 		topo_mod_strfreev(mod, entity_refs, 1);
1525 		return (topo_mod_seterrno(mod, err));
1526 	}
1527 	topo_mod_strfreev(mod, entity_refs, 1);
1528 
1529 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
1530 	    TOPO_PROP_IMMUTABLE, sd->sd_class, &err) != 0) {
1531 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
1532 		    "%s=%d (%s)\n", TOPO_SENSOR_CLASS, topo_node_name(fnode),
1533 		    topo_node_instance(fnode), topo_strerror(err));
1534 		return (topo_mod_seterrno(mod, err));
1535 	}
1536 	if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
1537 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, sd->sd_stype, &err) != 0) {
1538 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
1539 		    "%s=%d (%s)\n", TOPO_FACILITY_TYPE, topo_node_name(fnode),
1540 		    topo_node_instance(fnode), topo_strerror(err));
1541 		return (topo_mod_seterrno(mod, err));
1542 	}
1543 	if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0) {
1544 		topo_node_unbind(fnode);
1545 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1546 	}
1547 
1548 	if ((ret = nvlist_add_string(arg_nvl, "ipmi_entity", sd->sd_entity_ref))
1549 	    != 0) {
1550 		topo_mod_dprintf(mod, "Failed build arg nvlist (%s)\n",
1551 		    strerror(ret));
1552 		nvlist_free(arg_nvl);
1553 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1554 	}
1555 
1556 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
1557 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ipmi_sensor_state", arg_nvl,
1558 	    &err) != 0) {
1559 		topo_mod_dprintf(mod, "Failed to register %s propmeth on fac "
1560 		    "node %s (%s)\n", TOPO_SENSOR_STATE, topo_node_name(fnode),
1561 		    topo_strerror(err));
1562 		nvlist_free(arg_nvl);
1563 		return (topo_mod_seterrno(mod, err));
1564 	}
1565 
1566 	/*
1567 	 * If it's a discrete sensor then we're done.  For threshold sensors,
1568 	 * there are additional properties to set up.
1569 	 */
1570 	if (strcmp(sd->sd_class, TOPO_SENSOR_CLASS_THRESHOLD) != 0) {
1571 		nvlist_free(arg_nvl);
1572 		return (0);
1573 	}
1574 
1575 	/*
1576 	 * Create properties to expose the analog sensor reading, the unit
1577 	 * type and the upper and lower thresholds, if available.
1578 	 */
1579 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
1580 	    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE, "ipmi_sensor_reading",
1581 	    arg_nvl, &err) != 0) {
1582 		topo_mod_dprintf(mod, "Failed to register %s propmeth on fac "
1583 		    "node %s (%s)\n", TOPO_SENSOR_READING,
1584 		    topo_node_name(fnode), topo_strerror(err));
1585 		nvlist_free(arg_nvl);
1586 		return (topo_mod_seterrno(mod, err));
1587 	}
1588 	if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
1589 	    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sd->sd_units, &err) != 0) {
1590 		topo_mod_dprintf(mod, "Failed to set units property on node "
1591 		    "%s (%s)\n", topo_node_name(fnode), topo_strerror(err));
1592 		nvlist_free(arg_nvl);
1593 		return (topo_mod_seterrno(mod, err));
1594 	}
1595 	nvlist_free(arg_nvl);
1596 
1597 	/*
1598 	 * It is possible (though unusual) for a compact sensor record to
1599 	 * represent a threshold sensor.  However, due to how
1600 	 * ipmi_sdr_conv_reading() is currently implemented, we only support
1601 	 * gathering threshold readings on sensors enumerated from Full Sensor
1602 	 * Records.
1603 	 */
1604 	if (sd->sd_fs_sdr == NULL)
1605 		return (0);
1606 
1607 	if (ipmi_get_sensor_thresholds(hdl, &thresh,
1608 	    sd->sd_fs_sdr->is_fs_number) != 0) {
1609 		topo_mod_dprintf(mod, "Failed to get sensor thresholds for "
1610 		    "node %s (%s)\n", topo_node_name(fnode), ipmi_errmsg(hdl));
1611 		return (topo_mod_seterrno(mod, EMOD_PARTIAL_ENUM));
1612 	}
1613 
1614 	/*
1615 	 * The IPMI Get Sensor Thresholds command returns a bitmask describing
1616 	 * which of the 3 upper and lower thresholds are readable.  Iterate
1617 	 * through those and create a topo property for each threshold that is
1618 	 * readable.
1619 	 */
1620 	mask = thresh.ithr_readable_mask;
1621 	for (i = 0; i < num_thresholds; i++) {
1622 		if (!ISBITSET(mask, threshset[i].sthr_threshbit))
1623 			continue;
1624 
1625 		if (set_thresh_prop(mod, fnode, sd->sd_fs_sdr,
1626 		    *(uint8_t *)((char *)&thresh +
1627 		    threshset[i].sthr_threshoff), &threshset[i]) != 0) {
1628 			/* errno set */
1629 			return (-1);
1630 		}
1631 	}
1632 	return (0);
1633 }
1634 
1635 static boolean_t
1636 seq_search(char *key, char **list, uint_t nelem)
1637 {
1638 	for (int i = 0; i < nelem; i++)
1639 		if (strcmp(key, list[i]) == 0)
1640 			return (B_TRUE);
1641 	return (B_FALSE);
1642 }
1643 
1644 /* ARGSUSED */
1645 static int
1646 sdr_callback(ipmi_handle_t *hdl, const char *id, ipmi_sdr_t *sdr, void *data)
1647 {
1648 	uint8_t sensor_entity, sensor_inst;
1649 	int sensor_idlen;
1650 	ipmi_sdr_full_sensor_t *f_sensor = NULL;
1651 	ipmi_sdr_compact_sensor_t *c_sensor = NULL;
1652 	struct sensor_data sd;
1653 	struct entity_info *ei = (struct entity_info *)data;
1654 
1655 	switch (sdr->is_type) {
1656 		case IPMI_SDR_TYPE_FULL_SENSOR:
1657 			f_sensor =
1658 			    (ipmi_sdr_full_sensor_t *)sdr->is_record;
1659 			sensor_entity = f_sensor->is_fs_entity_id;
1660 			sensor_inst = f_sensor->is_fs_entity_instance;
1661 			sensor_idlen = f_sensor->is_fs_idlen;
1662 			(void) strncpy(sd.sd_entity_ref,
1663 			    f_sensor->is_fs_idstring,
1664 			    f_sensor->is_fs_idlen);
1665 			sd.sd_entity_ref[sensor_idlen] = '\0';
1666 			sd.sd_units = f_sensor->is_fs_unit2;
1667 			sd.sd_stype = f_sensor->is_fs_type;
1668 			sd.sd_rtype = f_sensor->is_fs_reading_type;
1669 			sd.sd_fs_sdr = f_sensor;
1670 			break;
1671 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
1672 			c_sensor =
1673 			    (ipmi_sdr_compact_sensor_t *)sdr->is_record;
1674 			sensor_entity = c_sensor->is_cs_entity_id;
1675 			sensor_inst = c_sensor->is_cs_entity_instance;
1676 			sensor_idlen = c_sensor->is_cs_idlen;
1677 			(void) strncpy(sd.sd_entity_ref,
1678 			    c_sensor->is_cs_idstring,
1679 			    sensor_idlen);
1680 			sd.sd_entity_ref[sensor_idlen] = '\0';
1681 			sd.sd_units = c_sensor->is_cs_unit2;
1682 			sd.sd_stype = c_sensor->is_cs_type;
1683 			sd.sd_rtype = c_sensor->is_cs_reading_type;
1684 			sd.sd_fs_sdr = NULL;
1685 			break;
1686 		default:
1687 			return (0);
1688 	}
1689 	if (sd.sd_rtype == IPMI_RT_THRESHOLD)
1690 		sd.sd_class = TOPO_SENSOR_CLASS_THRESHOLD;
1691 	else
1692 		sd.sd_class = TOPO_SENSOR_CLASS_DISCRETE;
1693 
1694 	/*
1695 	 * We offset the threshold and generic sensor reading types by 0x100
1696 	 */
1697 	if (sd.sd_rtype >= 0x1 && sd.sd_rtype <= 0xc)
1698 		sd.sd_stype = sd.sd_rtype + 0x100;
1699 
1700 	if ((ei->ei_list != NULL && seq_search(sd.sd_entity_ref,
1701 	    ei->ei_list, ei->ei_listsz) == B_TRUE) ||
1702 	    (sensor_entity == ei->ei_id && sensor_inst == ei->ei_inst)) {
1703 
1704 		if (make_sensor_node(ei->ei_mod, ei->ei_node, &sd, hdl) != 0) {
1705 			topo_mod_dprintf(ei->ei_mod, "Failed to create sensor "
1706 			    "node for %s\n", sd.sd_entity_ref);
1707 			if (topo_mod_errno(ei->ei_mod) != EMOD_NODE_DUP)
1708 				return (-1);
1709 		}
1710 	}
1711 	return (0);
1712 }
1713 
1714 static int
1715 get_entity_info(topo_mod_t *mod, tnode_t *node, ipmi_handle_t *hdl,
1716     struct entity_info *ei)
1717 {
1718 	char **entity_refs;
1719 	int err;
1720 	uint_t nelems;
1721 	ipmi_sdr_t *ref_sdr;
1722 	ipmi_sdr_full_sensor_t *fsensor;
1723 	ipmi_sdr_compact_sensor_t *csensor;
1724 	ipmi_sdr_fru_locator_t *floc;
1725 	ipmi_sdr_generic_locator_t *gloc;
1726 	boolean_t found_sdr = B_FALSE;
1727 
1728 	/*
1729 	 * Use the entity ref to lookup the SDR, which will have the entity ID
1730 	 * and instance.
1731 	 */
1732 	if (topo_prop_get_string_array(node, TOPO_PGROUP_IPMI,
1733 	    "entity_ref", &entity_refs, &nelems, &err) != 0) {
1734 		topo_mod_dprintf(mod, "%s: Failed to lookup entity_ref "
1735 		    "property on %s=%d (%s)\n", __func__, topo_node_name(node),
1736 		    topo_node_instance(node), topo_strerror(err));
1737 		topo_mod_ipmi_rele(mod);
1738 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1739 	}
1740 
1741 	for (int i = 0; i < nelems; i++) {
1742 		if ((ref_sdr = ipmi_sdr_lookup(hdl, entity_refs[i])) != NULL) {
1743 			found_sdr = B_TRUE;
1744 			break;
1745 		} else
1746 			topo_mod_dprintf(mod, "%s: Failed to lookup SDR for %s "
1747 			    "(%s)\n", __func__, entity_refs[i],
1748 			    ipmi_errmsg(hdl));
1749 	}
1750 	topo_mod_strfreev(mod, entity_refs, nelems);
1751 	if (! found_sdr) {
1752 		topo_mod_ipmi_rele(mod);
1753 		return (-1);
1754 	}
1755 
1756 	switch (ref_sdr->is_type) {
1757 		case IPMI_SDR_TYPE_FULL_SENSOR:
1758 			fsensor = (ipmi_sdr_full_sensor_t *)ref_sdr->is_record;
1759 			ei->ei_id = fsensor->is_fs_entity_id;
1760 			ei->ei_inst = fsensor->is_fs_entity_instance;
1761 			break;
1762 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
1763 			csensor
1764 			    = (ipmi_sdr_compact_sensor_t *)ref_sdr->is_record;
1765 			ei->ei_id = csensor->is_cs_entity_id;
1766 			ei->ei_inst = csensor->is_cs_entity_instance;
1767 			break;
1768 		case IPMI_SDR_TYPE_FRU_LOCATOR:
1769 			floc = (ipmi_sdr_fru_locator_t *)ref_sdr->is_record;
1770 			ei->ei_id = floc->is_fl_entity;
1771 			ei->ei_inst = floc->is_fl_instance;
1772 			break;
1773 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
1774 			gloc = (ipmi_sdr_generic_locator_t *)ref_sdr->is_record;
1775 			ei->ei_id = gloc->is_gl_entity;
1776 			ei->ei_inst = gloc->is_gl_instance;
1777 			break;
1778 		default:
1779 			topo_mod_dprintf(mod, "Failed to determine entity id "
1780 			    "and instance\n", ipmi_errmsg(hdl));
1781 			topo_mod_ipmi_rele(mod);
1782 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1783 	}
1784 	return (0);
1785 }
1786 
1787 /* ARGSUSED */
1788 static int
1789 ipmi_sensor_enum(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1790     nvlist_t *in, nvlist_t **out)
1791 {
1792 	int err, ret = -1;
1793 	struct entity_info ei = {0};
1794 	ipmi_handle_t *hdl;
1795 
1796 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
1797 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
1798 		return (-1);
1799 	}
1800 
1801 	/*
1802 	 * For cases where the records in the SDR are hopelessly broken, then
1803 	 * we'll resort to hardcoding a list of sensor entities that should be
1804 	 * bound to this particular node.  Otherwise, we'll first check if the
1805 	 * properties for the associated IPMI entity id and instance exist.  If
1806 	 * not, we check for a property referencing an IPMI entity name on which
1807 	 * we can lookup the entity ID and instance.  If none of the above pans
1808 	 * out, then we bail out.
1809 	 */
1810 	if (topo_prop_get_string_array(node, TOPO_PGROUP_IPMI,
1811 	    TOPO_PROP_IPMI_ENTITY_LIST, &ei.ei_list, &ei.ei_listsz, &err)
1812 	    != 0 && (topo_prop_get_uint32(node, TOPO_PGROUP_IPMI,
1813 	    TOPO_PROP_IPMI_ENTITY_ID, &ei.ei_id, &err) != 0 ||
1814 	    topo_prop_get_uint32(node, TOPO_PGROUP_IPMI,
1815 	    TOPO_PROP_IPMI_ENTITY_INST, &ei.ei_inst, &err) != 0)) {
1816 		if (get_entity_info(mod, node, hdl, &ei) != 0)
1817 			goto out;
1818 	}
1819 	ei.ei_node = node;
1820 	ei.ei_mod = mod;
1821 
1822 	/*
1823 	 * Now iterate through all of the full and compact sensor data records
1824 	 * and create a sensor facility node for each record that matches our
1825 	 * entity ID and instance
1826 	 */
1827 	if ((ret = ipmi_sdr_iter(hdl, sdr_callback, &ei)) != 0) {
1828 		topo_mod_dprintf(mod, "ipmi_sdr_iter() failed\n");
1829 	}
1830 out:
1831 	topo_mod_ipmi_rele(mod);
1832 	if (ei.ei_list != NULL)
1833 		topo_mod_strfreev(mod, ei.ei_list, ei.ei_listsz);
1834 
1835 	return (ret);
1836 }
1837 
1838 static int
1839 ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1840     nvlist_t *in, nvlist_t **out)
1841 {
1842 	char **fmtarr, **entity_refs, buf[BUFSZ];
1843 	tnode_t *refnode;
1844 	uint_t nelems;
1845 	int ret, inst1, inst2;
1846 	uint32_t offset, nparams;
1847 	nvlist_t *args, *nvl;
1848 
1849 	if (vers > TOPO_METH_IPMI_ENTITY_VERSION)
1850 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
1851 
1852 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
1853 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
1854 		    strerror(ret));
1855 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1856 	}
1857 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
1858 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
1859 		    strerror(ret));
1860 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1861 	}
1862 	if ((ret = nvlist_lookup_uint32(args, "nparams", &nparams)) != 0) {
1863 		topo_mod_dprintf(mod, "Failed to lookup 'nparams' arg (%s)\n",
1864 		    strerror(ret));
1865 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1866 	}
1867 	if (nvlist_lookup_string_array(args, "format", &fmtarr, &nelems) != 0) {
1868 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
1869 		    strerror(errno));
1870 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1871 	}
1872 
1873 	if ((entity_refs = topo_mod_alloc(mod, (nelems * sizeof (char *))))
1874 	    == NULL)
1875 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1876 
1877 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1878 		refnode = topo_node_parent(node);
1879 	else
1880 		refnode = node;
1881 
1882 	for (int i = 0; i < nelems; i++) {
1883 		switch (nparams) {
1884 		case 1:
1885 			/* LINTED: E_SEC_PRINTF_VAR_FMT */
1886 			(void) snprintf(buf, BUFSZ, fmtarr[i],
1887 			    (topo_node_instance(refnode) + offset));
1888 			break;
1889 		case 2:
1890 			inst1 = topo_node_instance(topo_node_parent(refnode))
1891 			    + offset;
1892 			inst2 = topo_node_instance(refnode) + offset;
1893 			/* LINTED: E_SEC_PRINTF_VAR_FMT */
1894 			(void) snprintf(buf, BUFSZ, fmtarr[i], inst1, inst2);
1895 			break;
1896 		default:
1897 			topo_mod_dprintf(mod, "Invalid 'nparams' argval (%d)\n",
1898 			    nparams);
1899 			topo_mod_strfreev(mod, entity_refs, nelems);
1900 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1901 		}
1902 		entity_refs[i] = topo_mod_strdup(mod, buf);
1903 	}
1904 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1905 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1906 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE,
1907 	    TOPO_TYPE_STRING_ARRAY) != 0 ||
1908 	    nvlist_add_string_array(nvl, TOPO_PROP_VAL_VAL, entity_refs,
1909 	    nelems) != 0) {
1910 
1911 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1912 		topo_mod_strfreev(mod, entity_refs, nelems);
1913 		nvlist_free(nvl);
1914 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1915 	}
1916 	topo_mod_strfreev(mod, entity_refs, nelems);
1917 	*out = nvl;
1918 
1919 	return (0);
1920 }
1921 
1922 /* ARGSUSED */
1923 static int
1924 dimm_ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1925     nvlist_t *in, nvlist_t **out)
1926 {
1927 	char **fmtarr, **entity_refs, buf[BUFSZ];
1928 	tnode_t *chip, *dimm;
1929 	int ret;
1930 	uint_t nelems;
1931 	uint32_t offset;
1932 	nvlist_t *args, *nvl;
1933 
1934 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
1935 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
1936 		    strerror(ret));
1937 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1938 	}
1939 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
1940 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
1941 		    strerror(ret));
1942 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1943 	}
1944 	if (nvlist_lookup_string_array(args, "format", &fmtarr, &nelems) != 0) {
1945 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
1946 		    strerror(errno));
1947 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1948 	}
1949 
1950 	if ((entity_refs = topo_mod_alloc(mod, (nelems * sizeof (char *))))
1951 	    == NULL)
1952 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1953 
1954 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1955 		dimm = topo_node_parent(node);
1956 	else
1957 		dimm = node;
1958 
1959 	chip = topo_node_parent(topo_node_parent(dimm));
1960 
1961 	for (int i = 0; i < nelems; i++) {
1962 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
1963 		(void) snprintf(buf, BUFSZ, fmtarr[i], topo_node_instance(chip),
1964 		    (topo_node_instance(dimm) + offset));
1965 		entity_refs[i] = topo_mod_strdup(mod, buf);
1966 	}
1967 
1968 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1969 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1970 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE,
1971 	    TOPO_TYPE_STRING_ARRAY) != 0 ||
1972 	    nvlist_add_string_array(nvl, TOPO_PROP_VAL_VAL, entity_refs, nelems)
1973 	    != 0) {
1974 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1975 		topo_mod_strfreev(mod, entity_refs, nelems);
1976 		nvlist_free(nvl);
1977 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1978 	}
1979 	topo_mod_strfreev(mod, entity_refs, nelems);
1980 	*out = nvl;
1981 
1982 	return (0);
1983 }
1984 
1985 /* ARGSUSED */
1986 static int
1987 cs_ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1988     nvlist_t *in, nvlist_t **out)
1989 {
1990 	char **fmtarr, **entity_refs, buf[BUFSZ];
1991 	tnode_t *chip, *chan, *cs;
1992 	int ret, dimm_num;
1993 	uint_t nelems;
1994 	uint32_t offset;
1995 	nvlist_t *args, *nvl;
1996 
1997 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
1998 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
1999 		    strerror(ret));
2000 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2001 	}
2002 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
2003 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
2004 		    strerror(ret));
2005 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2006 	}
2007 	if (nvlist_lookup_string_array(args, "format", &fmtarr, &nelems) != 0) {
2008 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
2009 		    strerror(errno));
2010 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
2011 	}
2012 
2013 	if ((entity_refs = topo_mod_alloc(mod, (nelems * sizeof (char *))))
2014 	    == NULL)
2015 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2016 
2017 	if (topo_node_flags(node) & TOPO_NODE_FACILITY) {
2018 		cs = topo_node_parent(node);
2019 		chip = topo_node_parent(topo_node_parent(topo_node_parent(cs)));
2020 		chan = topo_node_parent(cs);
2021 
2022 		dimm_num = topo_node_instance(cs) - (topo_node_instance(cs) % 2)
2023 		    + topo_node_instance(cs) + offset;
2024 	} else {
2025 		cs = node;
2026 		chip = topo_node_parent(topo_node_parent(topo_node_parent(cs)));
2027 		chan = topo_node_parent(cs);
2028 
2029 		dimm_num = topo_node_instance(cs) - (topo_node_instance(cs) % 2)
2030 		    + topo_node_instance(chan) + offset;
2031 	}
2032 
2033 	for (int i = 0; i < nelems; i++) {
2034 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
2035 		(void) snprintf(buf, BUFSZ, fmtarr[i], topo_node_instance(chip),
2036 		    dimm_num);
2037 		entity_refs[i] = topo_mod_strdup(mod, buf);
2038 	}
2039 
2040 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
2041 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
2042 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE,
2043 	    TOPO_TYPE_STRING_ARRAY) != 0 ||
2044 	    nvlist_add_string_array(nvl, TOPO_PROP_VAL_VAL, entity_refs, nelems)
2045 	    != 0) {
2046 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
2047 		topo_mod_strfreev(mod, entity_refs, nelems);
2048 		nvlist_free(nvl);
2049 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
2050 	}
2051 	topo_mod_strfreev(mod, entity_refs, nelems);
2052 	*out = nvl;
2053 
2054 	return (0);
2055 }
2056 
2057 /*ARGSUSED*/
2058 static int
2059 fac_prov_ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
2060     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
2061 {
2062 	topo_pgroup_info_t pgi;
2063 	int err;
2064 
2065 	if (topo_node_flags(rnode) == TOPO_NODE_DEFAULT) {
2066 		pgi.tpi_name = TOPO_PGROUP_IPMI;
2067 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
2068 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
2069 		pgi.tpi_version = 1;
2070 		if (topo_pgroup_create(rnode, &pgi, &err) != 0) {
2071 			if (err != ETOPO_PROP_DEFD) {
2072 				topo_mod_dprintf(mod,
2073 				    "pgroups create failure: %s\n",
2074 				    topo_strerror(err));
2075 				return (-1);
2076 			}
2077 		}
2078 		if (topo_method_register(mod, rnode, ipmi_node_methods) != 0) {
2079 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
2080 			    "topo_method_register() failed: %s",
2081 			    topo_mod_errmsg(mod));
2082 			return (-1);
2083 		}
2084 	} else {
2085 		if (topo_method_register(mod, rnode, ipmi_fac_methods) != 0) {
2086 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
2087 			    "topo_method_register() failed: %s",
2088 			    topo_mod_errmsg(mod));
2089 			return (-1);
2090 		}
2091 	}
2092 	return (0);
2093 }
2094