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