xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c (revision 7fcedef2cb0c5f9b2168d8c757cf243facdec1e6)
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 #define	TOPO_PGROUP_IPMI	"ipmi"
44 
45 #define	THUMPER_PRESENT_LED_MASK	0x01
46 #define	THUMPER_SERVICE_LED_MASK	0x02
47 #define	THUMPER_OK2RM_LED_MASK		0x08
48 
49 /*
50  * The largest possible SDR ID length is 2^5+1
51  */
52 #define	MAX_ID_LEN	33
53 
54 #define	TOPO_METH_IPMI_READING_VERSION		0
55 #define	TOPO_METH_IPMI_STATE_VERSION		0
56 #define	TOPO_METH_IPMI_MODE_VERSION		0
57 #define	TOPO_METH_THUMPER_LOCATE_VERSION	0
58 #define	TOPO_METH_THUMPER_MODE_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 ipmi_sensor_reading(topo_mod_t *, tnode_t *, topo_version_t,
75     nvlist_t *, nvlist_t **);
76 static int ipmi_sensor_state(topo_mod_t *, tnode_t *, topo_version_t,
77     nvlist_t *, nvlist_t **);
78 static int ipmi_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
79     nvlist_t *, nvlist_t **);
80 static int thumper_locate_mode(topo_mod_t *, tnode_t *, topo_version_t,
81     nvlist_t *, nvlist_t **);
82 static int thumper_indicator_mode(topo_mod_t *, tnode_t *, topo_version_t,
83     nvlist_t *, nvlist_t **);
84 
85 const topo_modops_t ipmi_ops = { fac_prov_ipmi_enum, NULL };
86 
87 const topo_modinfo_t ipmi_info =
88 	{ "IPMI facility provider", FM_FMRI_SCHEME_HC, TOPO_VERSION,
89 	&ipmi_ops };
90 
91 static const topo_method_t ipmi_node_methods[] = {
92 	{ TOPO_METH_FAC_ENUM, TOPO_METH_FAC_ENUM_DESC, 0,
93 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_enum },
94 	{ TOPO_METH_IPMI_ENTITY, TOPO_PROP_METH_DESC,
95 	    TOPO_METH_IPMI_ENTITY_VERSION,
96 	    TOPO_STABILITY_INTERNAL, ipmi_entity },
97 	{ "dimm_ipmi_entity", TOPO_PROP_METH_DESC,
98 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
99 	    TOPO_STABILITY_INTERNAL, dimm_ipmi_entity },
100 	{ NULL }
101 };
102 
103 static const topo_method_t ipmi_fac_methods[] = {
104 	{ "ipmi_sensor_reading", TOPO_PROP_METH_DESC,
105 	    TOPO_METH_IPMI_READING_VERSION,
106 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_reading },
107 	{ "ipmi_sensor_state", TOPO_PROP_METH_DESC,
108 	    TOPO_METH_IPMI_STATE_VERSION,
109 	    TOPO_STABILITY_INTERNAL, ipmi_sensor_state },
110 	{ "ipmi_indicator_mode", TOPO_PROP_METH_DESC,
111 	    TOPO_METH_IPMI_MODE_VERSION,
112 	    TOPO_STABILITY_INTERNAL, ipmi_indicator_mode },
113 	{ "thumper_locate_mode", TOPO_PROP_METH_DESC,
114 	    TOPO_METH_THUMPER_LOCATE_VERSION,
115 	    TOPO_STABILITY_INTERNAL, thumper_locate_mode },
116 	{ "thumper_indicator_mode", TOPO_PROP_METH_DESC,
117 	    TOPO_METH_THUMPER_MODE_VERSION,
118 	    TOPO_STABILITY_INTERNAL, thumper_indicator_mode },
119 	{ TOPO_METH_IPMI_ENTITY, TOPO_PROP_METH_DESC,
120 	    TOPO_METH_IPMI_ENTITY_VERSION,
121 	    TOPO_STABILITY_INTERNAL, ipmi_entity },
122 	{ "dimm_ipmi_entity", TOPO_PROP_METH_DESC,
123 	    TOPO_METH_DIMM_IPMI_ENTITY_VERSION,
124 	    TOPO_STABILITY_INTERNAL, dimm_ipmi_entity },
125 	{ NULL }
126 };
127 
128 struct entity_info {
129 	uint32_t ei_id;
130 	uint32_t ei_inst;
131 	topo_mod_t *ei_mod;
132 	tnode_t *ei_node;
133 };
134 
135 struct sensor_data {
136 	char sd_entity_ref[MAX_ID_LEN];
137 	uint8_t sd_units;
138 	uint32_t sd_stype;
139 	uint32_t sd_rtype;
140 	char *sd_class;
141 };
142 
143 /*ARGSUSED*/
144 int
145 _topo_init(topo_mod_t *mod, topo_version_t version)
146 {
147 	if (getenv("TOPOFACIPMIDEBUG") != NULL)
148 		topo_mod_setdebug(mod);
149 
150 	return (topo_mod_register(mod, &ipmi_info, TOPO_VERSION));
151 }
152 
153 void
154 _topo_fini(topo_mod_t *mod)
155 {
156 	topo_mod_unregister(mod);
157 }
158 
159 static char *
160 get_fmtstr(topo_mod_t *mod, nvlist_t *in)
161 {
162 	char *fmtstr;
163 	nvlist_t *args;
164 
165 	if (nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args) != 0) {
166 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
167 		    strerror(errno));
168 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
169 		return (NULL);
170 	}
171 	if (nvlist_lookup_string(args, "format", &fmtstr) != 0) {
172 		topo_mod_dprintf(mod, "Failed to lookup 'format' arg (%s)\n",
173 		    strerror(errno));
174 		(void) topo_mod_seterrno(mod, EMOD_NVL_INVAL);
175 		return (NULL);
176 	}
177 	return (fmtstr);
178 }
179 
180 /*ARGSUSED*/
181 static int
182 ipmi_sensor_state(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
183     nvlist_t *in, nvlist_t **out)
184 {
185 	char *entity_ref;
186 	ipmi_sdr_t *sdr = NULL;
187 	ipmi_sensor_reading_t *reading;
188 	ipmi_handle_t *hdl;
189 	int err;
190 	uint8_t sensor_num;
191 	ipmi_sdr_full_sensor_t *fsensor;
192 	ipmi_sdr_compact_sensor_t *csensor;
193 	nvlist_t *nvl;
194 
195 	if (vers > TOPO_METH_IPMI_STATE_VERSION)
196 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
197 
198 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
199 	    &entity_ref, &err) != 0) {
200 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
201 		    "(%s)", topo_strerror(err));
202 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
203 	}
204 
205 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
206 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
207 		topo_mod_strfree(mod, entity_ref);
208 		return (-1);
209 	}
210 
211 	if ((sdr = ipmi_sdr_lookup(hdl, entity_ref)) == NULL) {
212 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
213 		    entity_ref, ipmi_errmsg(hdl));
214 		topo_mod_strfree(mod, entity_ref);
215 		topo_mod_ipmi_rele(mod);
216 		return (-1);
217 	}
218 
219 	switch (sdr->is_type) {
220 		case IPMI_SDR_TYPE_FULL_SENSOR:
221 			fsensor = (ipmi_sdr_full_sensor_t *)sdr->is_record;
222 			sensor_num = fsensor->is_fs_number;
223 			break;
224 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
225 			csensor = (ipmi_sdr_compact_sensor_t *)sdr->is_record;
226 			sensor_num = csensor->is_cs_number;
227 			break;
228 		default:
229 			topo_mod_dprintf(mod, "%s does not refer to a full or "
230 			    "compact SDR\n", entity_ref);
231 			topo_mod_strfree(mod, entity_ref);
232 			topo_mod_ipmi_rele(mod);
233 			return (-1);
234 	}
235 	if ((reading = ipmi_get_sensor_reading(hdl, sensor_num))
236 	    == NULL) {
237 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
238 		    "%s, sensor_num=%d (%s)\n", entity_ref, sensor_num,
239 		    ipmi_errmsg(hdl));
240 		topo_mod_strfree(mod, entity_ref);
241 		topo_mod_ipmi_rele(mod);
242 		return (-1);
243 	}
244 	topo_mod_strfree(mod, entity_ref);
245 	topo_mod_ipmi_rele(mod);
246 
247 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
248 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
249 	    TOPO_SENSOR_STATE) != 0 ||
250 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
251 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, reading->isr_state)
252 	    != 0) {
253 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
254 		nvlist_free(nvl);
255 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
256 	}
257 	*out = nvl;
258 
259 	return (0);
260 }
261 
262 /*ARGSUSED*/
263 static int
264 ipmi_sensor_reading(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
265     nvlist_t *in, nvlist_t **out)
266 {
267 	char *entity_ref, reading_str[BUFSZ];
268 	int err = 0;
269 	ipmi_sdr_full_sensor_t *sensor;
270 	ipmi_sensor_reading_t  *reading;
271 	double conv_reading;
272 	ipmi_handle_t *hdl;
273 	nvlist_t *nvl;
274 
275 	if (vers > TOPO_METH_IPMI_READING_VERSION)
276 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
277 
278 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
279 	    &entity_ref, &err) != 0) {
280 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
281 		    "(%s)", topo_strerror(err));
282 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
283 	}
284 
285 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
286 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
287 		topo_mod_strfree(mod, entity_ref);
288 		return (-1);
289 	}
290 
291 	if ((sensor = ipmi_sdr_lookup_full_sensor(hdl, entity_ref))
292 	    == NULL) {
293 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
294 		    entity_ref, ipmi_errmsg(hdl));
295 		topo_mod_strfree(mod, entity_ref);
296 		topo_mod_ipmi_rele(mod);
297 		return (-1);
298 	}
299 
300 	if ((reading = ipmi_get_sensor_reading(hdl, sensor->is_fs_number))
301 	    == NULL) {
302 		topo_mod_dprintf(mod, "Failed to get sensor reading for sensor "
303 		    "%s, sensor_num=%d (%s)\n", entity_ref,
304 		    sensor->is_fs_number, ipmi_errmsg(hdl));
305 		topo_mod_strfree(mod, entity_ref);
306 		topo_mod_ipmi_rele(mod);
307 		return (-1);
308 	}
309 	topo_mod_ipmi_rele(mod);
310 
311 	if (ipmi_sdr_conv_reading(sensor, reading->isr_reading, &conv_reading)
312 	    != 0) {
313 		topo_mod_dprintf(mod, "Failed to convert sensor reading for "
314 		    "sensor %s (%s)\n", entity_ref, ipmi_errmsg(hdl));
315 		topo_mod_strfree(mod, entity_ref);
316 		return (-1);
317 	}
318 	topo_mod_strfree(mod, entity_ref);
319 
320 	(void) snprintf(reading_str, BUFSZ, "%f", conv_reading);
321 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
322 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME,
323 	    TOPO_SENSOR_READING) != 0 ||
324 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_DOUBLE) != 0 ||
325 	    nvlist_add_double(nvl, TOPO_PROP_VAL_VAL, conv_reading) != 0) {
326 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
327 		nvlist_free(nvl);
328 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
329 	}
330 	*out = nvl;
331 
332 	return (0);
333 }
334 
335 static int
336 ipmi_indicator_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
337     nvlist_t *in, nvlist_t **out)
338 {
339 	char *entity_ref;
340 	ipmi_sdr_generic_locator_t *gdl = NULL;
341 	ipmi_handle_t *hdl;
342 	int err, ret;
343 	uint8_t ledmode;
344 	uint32_t mode_in;
345 	nvlist_t *pargs, *nvl;
346 
347 	if (vers > TOPO_METH_IPMI_MODE_VERSION)
348 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
349 
350 	/*
351 	 * Get an IPMI handle and then lookup the generic device locator sensor
352 	 * data record referenced by the entity_ref prop val
353 	 */
354 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
355 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
356 		return (-1);
357 	}
358 
359 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
360 	    &entity_ref, &err) != 0) {
361 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
362 		    "(%s)", topo_strerror(err));
363 		topo_mod_ipmi_rele(mod);
364 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
365 	}
366 
367 	if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_ref))
368 	    == NULL) {
369 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
370 		    entity_ref, ipmi_errmsg(hdl));
371 		topo_mod_strfree(mod, entity_ref);
372 		topo_mod_ipmi_rele(mod);
373 		return (-1);
374 	}
375 
376 	/*
377 	 * Now look for a private argument list to figure out whether we're
378 	 * doing a get or a set operation, and then do it.
379 	 */
380 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
381 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
382 		/*
383 		 * Set the LED mode
384 		 */
385 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
386 		    &mode_in)) != 0) {
387 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
388 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
389 			topo_mod_strfree(mod, entity_ref);
390 			topo_mod_ipmi_rele(mod);
391 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
392 		}
393 		if (mode_in != TOPO_LED_STATE_OFF &&
394 		    mode_in != TOPO_LED_STATE_ON) {
395 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
396 			    mode_in);
397 			topo_mod_strfree(mod, entity_ref);
398 			topo_mod_ipmi_rele(mod);
399 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
400 		}
401 		ledmode = (uint8_t)mode_in;
402 		topo_mod_dprintf(mod, "Setting LED mode to %s\n",
403 		    ledmode ? "ON" : "OFF");
404 		if (ipmi_sunoem_led_set(hdl, gdl, ledmode) < 0) {
405 			topo_mod_dprintf(mod, "Failed to set LED mode for %s "
406 			    "(%s)\n", entity_ref, ipmi_errmsg(hdl));
407 			topo_mod_strfree(mod, entity_ref);
408 			topo_mod_ipmi_rele(mod);
409 			return (-1);
410 		}
411 	} else {
412 		/*
413 		 * Get the LED mode
414 		 */
415 		topo_mod_dprintf(mod, "Getting LED mode\n");
416 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
417 			topo_mod_dprintf(mod, "Failed to get LED mode for %s "
418 			    "(%s)\n", entity_ref, ipmi_errmsg(hdl));
419 			topo_mod_strfree(mod, entity_ref);
420 			topo_mod_ipmi_rele(mod);
421 			return (-1);
422 		}
423 	}
424 	topo_mod_strfree(mod, entity_ref);
425 	topo_mod_ipmi_rele(mod);
426 
427 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
428 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
429 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
430 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
431 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
432 		nvlist_free(nvl);
433 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
434 	}
435 	*out = nvl;
436 
437 	return (0);
438 }
439 
440 /*
441  * On thumper platforms these is no seperate locate LED for the drive bays.
442  * Therefore we simulate a locate LED by blinking the ok2rm LED.
443  */
444 static int
445 thumper_locate_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
446     nvlist_t *in, nvlist_t **out)
447 {
448 	char *entity_ref;
449 	ipmi_sdr_generic_locator_t *gdl = NULL;
450 	ipmi_handle_t *hdl;
451 	int err, ret;
452 	uint8_t ledmode;
453 	uint32_t mode_in;
454 	nvlist_t *pargs, *nvl;
455 
456 	if (vers > TOPO_METH_THUMPER_LOCATE_VERSION)
457 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
458 
459 	/*
460 	 * Get an IPMI handle and then lookup the generic device locator sensor
461 	 * data record referenced by the entity_ref prop val
462 	 */
463 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
464 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
465 		return (-1);
466 	}
467 
468 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
469 	    &entity_ref, &err) != 0) {
470 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
471 		    "(%s)", topo_strerror(err));
472 		topo_mod_ipmi_rele(mod);
473 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
474 	}
475 
476 	if ((gdl = ipmi_sdr_lookup_generic(hdl, entity_ref))
477 	    == NULL) {
478 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
479 		    entity_ref, ipmi_errmsg(hdl));
480 		topo_mod_strfree(mod, entity_ref);
481 		topo_mod_ipmi_rele(mod);
482 		return (-1);
483 	}
484 
485 	/*
486 	 * Now look for a private argument list to figure out whether we're
487 	 * doing a get or a set operation, and then do it.
488 	 */
489 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
490 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
491 		/*
492 		 * Set the LED mode
493 		 */
494 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
495 		    &mode_in)) != 0) {
496 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
497 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
498 			topo_mod_strfree(mod, entity_ref);
499 			topo_mod_ipmi_rele(mod);
500 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
501 		}
502 		if (mode_in != TOPO_LED_STATE_OFF &&
503 		    mode_in != TOPO_LED_STATE_ON) {
504 			topo_mod_dprintf(mod, "Invalid property value: %d\n",
505 			    mode_in);
506 			topo_mod_strfree(mod, entity_ref);
507 			topo_mod_ipmi_rele(mod);
508 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
509 		}
510 		if (mode_in == TOPO_LED_STATE_ON)
511 			ledmode = IPMI_SUNOEM_LED_MODE_SLOW;
512 		else
513 			ledmode = (uint8_t)mode_in;
514 		if (ipmi_sunoem_led_set(hdl, gdl, ledmode) < 0) {
515 			topo_mod_dprintf(mod, "Failed to set LED mode for %s "
516 			    "(%s)\n", entity_ref, ipmi_errmsg(hdl));
517 			topo_mod_strfree(mod, entity_ref);
518 			topo_mod_ipmi_rele(mod);
519 			return (-1);
520 		}
521 	} else {
522 		/*
523 		 * Get the LED mode
524 		 */
525 		if (ipmi_sunoem_led_get(hdl, gdl, &ledmode) < 0) {
526 			topo_mod_dprintf(mod, "Failed to get LED mode for %s "
527 			    "(%s)\n", entity_ref, ipmi_errmsg(hdl));
528 			topo_mod_strfree(mod, entity_ref);
529 			topo_mod_ipmi_rele(mod);
530 			return (-1);
531 		}
532 	}
533 	topo_mod_strfree(mod, entity_ref);
534 	topo_mod_ipmi_rele(mod);
535 
536 	if (ledmode == IPMI_SUNOEM_LED_MODE_SLOW ||
537 	    ledmode == IPMI_SUNOEM_LED_MODE_FAST)
538 		ledmode = TOPO_LED_STATE_ON;
539 	else
540 		ledmode = TOPO_LED_STATE_OFF;
541 
542 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
543 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
544 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
545 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
546 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
547 		nvlist_free(nvl);
548 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
549 	}
550 	*out = nvl;
551 
552 	return (0);
553 }
554 
555 
556 /*
557  * This is a method for the "mode" property that is specific for the drive bay
558  * LED's on thumper platforms.  On thumper, the drive bay LED's are manipulated
559  * by asserting the right state bits in the hdd#.state compact SDR.
560  */
561 static int
562 thumper_indicator_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
563     nvlist_t *in, nvlist_t **out)
564 {
565 	char *entity_ref;
566 	ipmi_sdr_compact_sensor_t *cs = NULL;
567 	ipmi_handle_t *hdl;
568 	int err, ret;
569 	uint32_t mask, type, ledmode;
570 	nvlist_t *pargs, *nvl;
571 
572 	if (vers > TOPO_METH_THUMPER_MODE_VERSION)
573 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
574 
575 	/*
576 	 * Figure out which sensor state mask to use based on the indicator
577 	 * node's type prop val
578 	 */
579 	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
580 	    &type, &err) != 0) {
581 		topo_mod_dprintf(mod, "Failed to lookup %s property "
582 		    "(%s)", TOPO_FACILITY_TYPE, topo_strerror(err));
583 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
584 	}
585 	switch (type) {
586 	case (TOPO_LED_TYPE_SERVICE):
587 		mask = THUMPER_SERVICE_LED_MASK;
588 		break;
589 	case (TOPO_LED_TYPE_PRESENT):
590 		mask = THUMPER_PRESENT_LED_MASK;
591 		break;
592 	case (TOPO_LED_TYPE_OK2RM):
593 		mask = THUMPER_OK2RM_LED_MASK;
594 		break;
595 	default:
596 		topo_mod_dprintf(mod, "Invalid LED type: 0x%x\n", type);
597 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
598 	}
599 
600 	/*
601 	 * Get an IPMI handle and then lookup the compact sensor data record
602 	 * referenced by the entity_ref prop val
603 	 */
604 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
605 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
606 		return (-1);
607 	}
608 
609 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
610 	    &entity_ref, &err) != 0) {
611 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
612 		    "(%s)", topo_strerror(err));
613 		topo_mod_ipmi_rele(mod);
614 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
615 	}
616 
617 	if ((cs = ipmi_sdr_lookup_compact_sensor(hdl, entity_ref))
618 	    == NULL) {
619 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
620 		    entity_ref, ipmi_errmsg(hdl));
621 		topo_mod_strfree(mod, entity_ref);
622 		topo_mod_ipmi_rele(mod);
623 		return (-1);
624 	}
625 
626 	/*
627 	 * Now lookup the propmethod argument list and figure out whether we're
628 	 * doing a get or a set operation, and then do it.
629 	 */
630 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
631 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
632 		/*
633 		 * Set the LED mode
634 		 */
635 		ipmi_set_sensor_reading_t sr_out = { 0 };
636 
637 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
638 		    &ledmode)) != 0) {
639 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
640 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
641 			topo_mod_strfree(mod, entity_ref);
642 			topo_mod_ipmi_rele(mod);
643 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
644 		}
645 
646 		if (ledmode == TOPO_LED_STATE_OFF) {
647 			sr_out.iss_deassert_state = mask;
648 			sr_out.iss_deassrt_op = IPMI_SENSOR_OP_SET;
649 		} else if (ledmode == TOPO_LED_STATE_ON) {
650 			sr_out.iss_assert_state = mask;
651 			sr_out.iss_assert_op = IPMI_SENSOR_OP_SET;
652 		} else {
653 			topo_mod_dprintf(mod, "Invalid LED mode: %d 0x%x\n",
654 			    ledmode);
655 			topo_mod_strfree(mod, entity_ref);
656 			topo_mod_ipmi_rele(mod);
657 			return (-1);
658 		}
659 		sr_out.iss_id = cs->is_cs_number;
660 		topo_mod_dprintf(mod, "Setting LED mode (mask = 0x%x)\n", mask);
661 		if (ipmi_set_sensor_reading(hdl, &sr_out) != 0) {
662 			topo_mod_dprintf(mod, "Failed to set sensor reading "
663 			    "for sensor %s (%s)\n", entity_ref,
664 			    ipmi_errmsg(hdl));
665 			topo_mod_strfree(mod, entity_ref);
666 			topo_mod_ipmi_rele(mod);
667 			return (-1);
668 		}
669 	} else {
670 		/*
671 		 * Get the LED mode
672 		 */
673 		ipmi_sensor_reading_t *sr_in;
674 
675 		topo_mod_dprintf(mod, "Getting LED mode\n");
676 		if ((sr_in = ipmi_get_sensor_reading(hdl, cs->is_cs_number))
677 		    == NULL) {
678 			topo_mod_dprintf(mod, "Failed to get sensor reading "
679 			    "for sensor %s (sensor num: %d) (error: %s)\n",
680 			    entity_ref, cs->is_cs_number, ipmi_errmsg(hdl));
681 			topo_mod_strfree(mod, entity_ref);
682 			topo_mod_ipmi_rele(mod);
683 			return (-1);
684 		}
685 		if (sr_in->isr_state & (uint16_t)mask)
686 			ledmode = TOPO_LED_STATE_ON;
687 		else
688 			ledmode = TOPO_LED_STATE_OFF;
689 	}
690 	topo_mod_strfree(mod, entity_ref);
691 	topo_mod_ipmi_rele(mod);
692 
693 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
694 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
695 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
696 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
697 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
698 		nvlist_free(nvl);
699 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
700 	}
701 	*out = nvl;
702 	return (0);
703 }
704 
705 static int
706 make_sensor_node(topo_mod_t *mod, tnode_t *pnode, struct sensor_data *sd)
707 {
708 	int err, ret, i;
709 	tnode_t *fnode;
710 	char *ftype = "sensor", facname[MAX_ID_LEN];
711 	topo_pgroup_info_t pgi;
712 	nvlist_t *arg_nvl = NULL;
713 
714 	/*
715 	 * Some platforms have '/' characters in the IPMI entity name, but '/'
716 	 * has a special meaning for FMRI's so we change them to '.' before
717 	 * binding the node into the topology.
718 	 */
719 	(void) strcpy(facname, sd->sd_entity_ref);
720 	for (i = 0; facname[i]; i++)
721 		if (facname[i] == '/')
722 			facname[i] = '.';
723 
724 	if ((fnode = topo_node_facbind(mod, pnode, facname, ftype)) == NULL) {
725 		topo_mod_dprintf(mod, "Failed to bind facility node: %s\n",
726 		    facname);
727 		/* topo errno set */
728 		return (-1);
729 	}
730 
731 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
732 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
733 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
734 	pgi.tpi_version = 1;
735 	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
736 		if (err != ETOPO_PROP_DEFD) {
737 			topo_mod_dprintf(mod,  "pgroups create failure: %s\n",
738 			    topo_strerror(err));
739 			topo_node_unbind(fnode);
740 			return (-1);
741 		}
742 	}
743 	if (topo_method_register(mod, fnode, ipmi_fac_methods) < 0) {
744 		topo_mod_dprintf(mod, "make_fac_node: "
745 		    "failed to register facility methods");
746 		topo_node_unbind(fnode);
747 		return (-1);
748 	}
749 	/*
750 	 * For both threshold and discrete sensors we set up a propmethod for
751 	 * getting the sensor state and properties to hold the entity ref,
752 	 * sensor class and sensor type.
753 	 *
754 	 * Additionally, for analog sensors we set up a property method for
755 	 * getting the converted sensor reading and property for the base
756 	 * unit type
757 	 */
758 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, "entity_ref",
759 	    TOPO_PROP_IMMUTABLE, sd->sd_entity_ref, &err) != 0) {
760 		topo_mod_dprintf(mod, "Failed to set entity_ref property on "
761 		    "node: %s=%d (%s)\n", topo_node_name(fnode),
762 		    topo_node_instance(fnode), topo_strerror(err));
763 		return (-1);
764 	}
765 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
766 	    TOPO_PROP_IMMUTABLE, sd->sd_class, &err) != 0) {
767 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
768 		    "%s=%d (%s)\n", TOPO_SENSOR_CLASS, topo_node_name(fnode),
769 		    topo_node_instance(fnode), topo_strerror(err));
770 		return (-1);
771 	}
772 	if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
773 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, sd->sd_stype, &err) != 0) {
774 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
775 		    "%s=%d (%s)\n", TOPO_FACILITY_TYPE, topo_node_name(fnode),
776 		    topo_node_instance(fnode), topo_strerror(err));
777 		return (-1);
778 	}
779 	if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0) {
780 		topo_node_unbind(fnode);
781 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
782 	}
783 
784 	if ((ret = nvlist_add_string(arg_nvl, "ipmi_entity", sd->sd_entity_ref))
785 	    != 0) {
786 		topo_mod_dprintf(mod, "Failed build arg nvlist (%s)\n",
787 		    strerror(ret));
788 		nvlist_free(arg_nvl);
789 		return (-1);
790 	}
791 
792 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
793 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ipmi_sensor_state", arg_nvl,
794 	    &err) != 0) {
795 		topo_mod_dprintf(mod, "Failed to register %s propmeth on fac "
796 		    "node %s (%s)\n", TOPO_SENSOR_STATE, topo_node_name(fnode),
797 		    topo_strerror(err));
798 		nvlist_free(arg_nvl);
799 		return (-1);
800 	}
801 
802 	if (strcmp(sd->sd_class, TOPO_SENSOR_CLASS_THRESHOLD) == 0) {
803 		if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
804 		    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE,
805 		    "ipmi_sensor_reading", arg_nvl, &err) != 0) {
806 			topo_mod_dprintf(mod, "Failed to register %s propmeth "
807 			    "on fac node %s (%s)\n", TOPO_SENSOR_READING,
808 			    topo_node_name(fnode), topo_strerror(err));
809 			nvlist_free(arg_nvl);
810 			return (-1);
811 		}
812 		if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
813 		    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sd->sd_units, &err)
814 		    != 0) {
815 			topo_mod_dprintf(mod, "Failed to set units property on "
816 			    "node: %s (%s)\n", topo_node_name(fnode),
817 			    topo_strerror(err));
818 			nvlist_free(arg_nvl);
819 			return (-1);
820 		}
821 	}
822 	nvlist_free(arg_nvl);
823 	return (0);
824 }
825 
826 /* ARGSUSED */
827 static int
828 sdr_callback(ipmi_handle_t *hdl, const char *id, ipmi_sdr_t *sdr, void *data)
829 {
830 	uint8_t sensor_entity, sensor_inst;
831 	int sensor_idlen;
832 	ipmi_sdr_full_sensor_t *f_sensor = NULL;
833 	ipmi_sdr_compact_sensor_t *c_sensor = NULL;
834 	struct sensor_data sd;
835 	struct entity_info *ei = (struct entity_info *)data;
836 
837 	switch (sdr->is_type) {
838 		case IPMI_SDR_TYPE_FULL_SENSOR:
839 			f_sensor =
840 			    (ipmi_sdr_full_sensor_t *)sdr->is_record;
841 			sensor_entity = f_sensor->is_fs_entity_id;
842 			sensor_inst = f_sensor->is_fs_entity_instance;
843 			sensor_idlen = f_sensor->is_fs_idlen;
844 			(void) strncpy(sd.sd_entity_ref,
845 			    f_sensor->is_fs_idstring,
846 			    f_sensor->is_fs_idlen);
847 			sd.sd_entity_ref[sensor_idlen] = '\0';
848 			sd.sd_class = TOPO_SENSOR_CLASS_THRESHOLD;
849 			sd.sd_units = f_sensor->is_fs_unit2;
850 			sd.sd_stype = f_sensor->is_fs_type;
851 			sd.sd_rtype = f_sensor->is_fs_reading_type;
852 			break;
853 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
854 			c_sensor =
855 			    (ipmi_sdr_compact_sensor_t *)sdr->is_record;
856 			sensor_entity = c_sensor->is_cs_entity_id;
857 			sensor_inst = c_sensor->is_cs_entity_instance;
858 			sensor_idlen = c_sensor->is_cs_idlen;
859 			(void) strncpy(sd.sd_entity_ref,
860 			    c_sensor->is_cs_idstring,
861 			    sensor_idlen);
862 			sd.sd_entity_ref[sensor_idlen] = '\0';
863 			sd.sd_class = TOPO_SENSOR_CLASS_DISCRETE;
864 			sd.sd_units = c_sensor->is_cs_unit2;
865 			sd.sd_stype = c_sensor->is_cs_type;
866 			sd.sd_rtype = c_sensor->is_cs_reading_type;
867 			break;
868 		default:
869 			return (0);
870 	}
871 	/*
872 	 * We offset the threshold and generic sensor reading types by 0x100
873 	 */
874 	if (sd.sd_rtype >= 0x1 && sd.sd_rtype <= 0xc)
875 		sd.sd_stype = sd.sd_rtype + 0x100;
876 
877 	if ((sensor_entity == ei->ei_id) && (sensor_inst == ei->ei_inst))
878 		if (make_sensor_node(ei->ei_mod, ei->ei_node, &sd) != 0) {
879 			topo_mod_dprintf(ei->ei_mod, "Failed to create sensor "
880 			    "node for %s\n", sd.sd_entity_ref);
881 			if (topo_mod_errno(ei->ei_mod) != EMOD_NODE_DUP)
882 				return (-1);
883 		}
884 	return (0);
885 }
886 
887 /* ARGSUSED */
888 static int
889 ipmi_sensor_enum(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
890     nvlist_t *in, nvlist_t **out)
891 {
892 	char *entity_ref;
893 	int err;
894 	struct entity_info ei;
895 	ipmi_sdr_t *ref_sdr;
896 	ipmi_handle_t *hdl;
897 	ipmi_sdr_full_sensor_t *fsensor;
898 	ipmi_sdr_compact_sensor_t *csensor;
899 	ipmi_sdr_fru_locator_t *floc;
900 	ipmi_sdr_generic_locator_t *gloc;
901 
902 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
903 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
904 		return (-1);
905 	}
906 
907 	/*
908 	 * Use the entity ref to lookup the SDR, which will have the entity ID
909 	 * and instance.
910 	 */
911 	if (topo_prop_get_string(node, TOPO_PGROUP_IPMI,
912 	    "entity_ref", &entity_ref, &err) != 0) {
913 		topo_mod_dprintf(mod, "Failed to lookup entity_ref "
914 		    "property (%s)\n", topo_strerror(err));
915 		topo_mod_ipmi_rele(mod);
916 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
917 	}
918 
919 	topo_mod_dprintf(mod, "Looking up SDR for %s ...\n",
920 	    entity_ref);
921 	if ((ref_sdr = ipmi_sdr_lookup(hdl, entity_ref)) == NULL) {
922 		topo_mod_dprintf(mod, "Failed to lookup SDR (%s)\n",
923 		    ipmi_errmsg(hdl));
924 		topo_mod_strfree(mod, entity_ref);
925 		topo_mod_ipmi_rele(mod);
926 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
927 	}
928 	topo_mod_strfree(mod, entity_ref);
929 
930 	switch (ref_sdr->is_type) {
931 		case IPMI_SDR_TYPE_FULL_SENSOR:
932 			fsensor = (ipmi_sdr_full_sensor_t *)ref_sdr->is_record;
933 			ei.ei_id = fsensor->is_fs_entity_id;
934 			ei.ei_inst = fsensor->is_fs_entity_instance;
935 			break;
936 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
937 			csensor
938 			    = (ipmi_sdr_compact_sensor_t *)ref_sdr->is_record;
939 			ei.ei_id = csensor->is_cs_entity_id;
940 			ei.ei_inst = csensor->is_cs_entity_instance;
941 			break;
942 		case IPMI_SDR_TYPE_FRU_LOCATOR:
943 			floc = (ipmi_sdr_fru_locator_t *)ref_sdr->is_record;
944 			ei.ei_id = floc->is_fl_entity;
945 			ei.ei_inst = floc->is_fl_instance;
946 			break;
947 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
948 			gloc = (ipmi_sdr_generic_locator_t *)ref_sdr->is_record;
949 			ei.ei_id = gloc->is_gl_entity;
950 			ei.ei_inst = gloc->is_gl_instance;
951 			break;
952 		default:
953 			topo_mod_dprintf(mod, "Failed to determine entity id "
954 			    "and instance\n", ipmi_errmsg(hdl));
955 			topo_mod_ipmi_rele(mod);
956 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
957 	}
958 	topo_mod_dprintf(mod, "Entity ID = 0x%x, Entity Instance = 0x%x\n",
959 	    ei.ei_id, ei.ei_inst);
960 
961 	ei.ei_node = node;
962 	ei.ei_mod = mod;
963 
964 	/*
965 	 * Now iterate through all of the full and compact sensor data records
966 	 * and create a sensor facility node for each record that matches our
967 	 * entity ID and instance
968 	 */
969 	if (ipmi_sdr_iter(hdl, sdr_callback, &ei) != 0) {
970 		topo_mod_dprintf(mod, "ipmi_sdr_iter() failed\n");
971 		topo_mod_ipmi_rele(mod);
972 		return (-1);
973 	}
974 
975 	topo_mod_ipmi_rele(mod);
976 
977 	return (0);
978 }
979 
980 static int
981 ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
982     nvlist_t *in, nvlist_t **out)
983 {
984 	char *fmtstr, buf[BUFSZ];
985 	tnode_t *refnode;
986 	int ret, inst1, inst2;
987 	uint32_t offset, nparams;
988 	nvlist_t *args, *nvl;
989 
990 	if (vers > TOPO_METH_IPMI_ENTITY_VERSION)
991 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
992 
993 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
994 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
995 		    strerror(ret));
996 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
997 	}
998 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
999 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
1000 		    strerror(ret));
1001 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1002 	}
1003 	if ((ret = nvlist_lookup_uint32(args, "nparams", &nparams)) != 0) {
1004 		topo_mod_dprintf(mod, "Failed to lookup 'nparams' arg (%s)\n",
1005 		    strerror(ret));
1006 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1007 	}
1008 
1009 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
1010 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
1011 		/* topo errno already set */
1012 		return (-1);
1013 	}
1014 
1015 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1016 		refnode = topo_node_parent(node);
1017 	else
1018 		refnode = node;
1019 
1020 	switch (nparams) {
1021 	case 1:
1022 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
1023 		(void) snprintf(buf, BUFSZ, fmtstr,
1024 		    (topo_node_instance(refnode) + offset));
1025 		break;
1026 	case 2:
1027 		inst1 = topo_node_instance(topo_node_parent(refnode)) + offset;
1028 		inst2 = topo_node_instance(refnode) + offset;
1029 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
1030 		(void) snprintf(buf, BUFSZ, fmtstr, inst1, inst2);
1031 		break;
1032 	default:
1033 		topo_mod_dprintf(mod, "Invalid 'nparams' argval (%d)\n",
1034 		    nparams);
1035 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1036 	}
1037 
1038 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1039 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1040 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) != 0 ||
1041 	    nvlist_add_string(nvl, TOPO_PROP_VAL_VAL, buf) != 0) {
1042 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1043 		nvlist_free(nvl);
1044 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1045 	}
1046 	*out = nvl;
1047 
1048 	return (0);
1049 }
1050 
1051 /* ARGSUSED */
1052 static int
1053 dimm_ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1054     nvlist_t *in, nvlist_t **out)
1055 {
1056 	char *fmtstr, buf[BUFSZ];
1057 	tnode_t *chip, *dimm;
1058 	int ret;
1059 	uint32_t offset;
1060 	nvlist_t *args, *nvl;
1061 
1062 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
1063 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
1064 		    strerror(ret));
1065 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1066 	}
1067 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
1068 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
1069 		    strerror(ret));
1070 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1071 	}
1072 
1073 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
1074 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
1075 		/* topo errno already set */
1076 		return (-1);
1077 	}
1078 
1079 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1080 		dimm = topo_node_parent(node);
1081 	else
1082 		dimm = node;
1083 
1084 	chip = topo_node_parent(topo_node_parent(dimm));
1085 
1086 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
1087 	(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
1088 	    (topo_node_instance(dimm) + offset));
1089 
1090 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1091 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1092 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) != 0 ||
1093 	    nvlist_add_string(nvl, TOPO_PROP_VAL_VAL, buf) != 0) {
1094 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1095 		nvlist_free(nvl);
1096 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1097 	}
1098 	*out = nvl;
1099 
1100 	return (0);
1101 }
1102 
1103 /*ARGSUSED*/
1104 static int
1105 fac_prov_ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
1106     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
1107 {
1108 	topo_pgroup_info_t pgi;
1109 	int err;
1110 
1111 	if (topo_node_flags(rnode) == TOPO_NODE_DEFAULT) {
1112 		pgi.tpi_name = TOPO_PGROUP_IPMI;
1113 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1114 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1115 		pgi.tpi_version = 1;
1116 		if (topo_pgroup_create(rnode, &pgi, &err) != 0) {
1117 			if (err != ETOPO_PROP_DEFD) {
1118 				topo_mod_dprintf(mod,
1119 				    "pgroups create failure: %s\n",
1120 				    topo_strerror(err));
1121 				return (-1);
1122 			}
1123 		}
1124 		if (topo_method_register(mod, rnode, ipmi_node_methods) != 0) {
1125 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
1126 			    "topo_method_register() failed: %s",
1127 			    topo_mod_errmsg(mod));
1128 			return (-1);
1129 		}
1130 	} else {
1131 		if (topo_method_register(mod, rnode, ipmi_fac_methods) != 0) {
1132 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
1133 			    "topo_method_register() failed: %s",
1134 			    topo_mod_errmsg(mod));
1135 			return (-1);
1136 		}
1137 	}
1138 	return (0);
1139 }
1140