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