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