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