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