xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/fac_prov_ipmi/fac_prov_ipmi.c (revision 0b1b4412cfd6c4ac5467dbe6f4088dcec4f55fe8)
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 #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_FAST;
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_FAST)
537 		ledmode = TOPO_LED_STATE_ON;
538 	else
539 		ledmode = TOPO_LED_STATE_OFF;
540 
541 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
542 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
543 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
544 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
545 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
546 		nvlist_free(nvl);
547 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
548 	}
549 	*out = nvl;
550 
551 	return (0);
552 }
553 
554 
555 /*
556  * This is a method for the "mode" property that is specific for the drive bay
557  * LED's on thumper platforms.  On thumper, the drive bay LED's are manipulated
558  * by asserting the right state bits in the hdd#.state compact SDR.
559  */
560 static int
561 thumper_indicator_mode(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
562     nvlist_t *in, nvlist_t **out)
563 {
564 	char *entity_ref;
565 	ipmi_sdr_compact_sensor_t *cs = NULL;
566 	ipmi_handle_t *hdl;
567 	int err, ret;
568 	uint32_t mask, type, ledmode;
569 	nvlist_t *pargs, *nvl;
570 
571 	if (vers > TOPO_METH_THUMPER_MODE_VERSION)
572 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
573 
574 	/*
575 	 * Figure out which sensor state mask to use based on the indicator
576 	 * node's type prop val
577 	 */
578 	if (topo_prop_get_uint32(node, TOPO_PGROUP_FACILITY, TOPO_FACILITY_TYPE,
579 	    &type, &err) != 0) {
580 		topo_mod_dprintf(mod, "Failed to lookup %s property "
581 		    "(%s)", TOPO_FACILITY_TYPE, topo_strerror(err));
582 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
583 	}
584 	switch (type) {
585 	case (TOPO_LED_TYPE_SERVICE):
586 		mask = THUMPER_SERVICE_LED_MASK;
587 		break;
588 	case (TOPO_LED_TYPE_PRESENT):
589 		mask = THUMPER_PRESENT_LED_MASK;
590 		break;
591 	case (TOPO_LED_TYPE_OK2RM):
592 		mask = THUMPER_OK2RM_LED_MASK;
593 		break;
594 	default:
595 		topo_mod_dprintf(mod, "Invalid LED type: 0x%x\n", type);
596 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
597 	}
598 
599 	/*
600 	 * Get an IPMI handle and then lookup the compact sensor data record
601 	 * referenced by the entity_ref prop val
602 	 */
603 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
604 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
605 		return (-1);
606 	}
607 
608 	if (topo_prop_get_string(node, TOPO_PGROUP_FACILITY, "entity_ref",
609 	    &entity_ref, &err) != 0) {
610 		topo_mod_dprintf(mod, "Failed to lookup entity_ref property "
611 		    "(%s)", topo_strerror(err));
612 		topo_mod_ipmi_rele(mod);
613 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
614 	}
615 
616 	if ((cs = ipmi_sdr_lookup_compact_sensor(hdl, entity_ref))
617 	    == NULL) {
618 		topo_mod_dprintf(mod, "Failed to lookup SDR for %s (%s)\n",
619 		    entity_ref, ipmi_errmsg(hdl));
620 		topo_mod_strfree(mod, entity_ref);
621 		topo_mod_ipmi_rele(mod);
622 		return (-1);
623 	}
624 
625 	/*
626 	 * Now lookup the propmethod argument list and figure out whether we're
627 	 * doing a get or a set operation, and then do it.
628 	 */
629 	if ((nvlist_lookup_nvlist(in, TOPO_PROP_PARGS, &pargs) == 0) &&
630 	    nvlist_exists(pargs, TOPO_PROP_VAL_VAL)) {
631 		/*
632 		 * Set the LED mode
633 		 */
634 		ipmi_set_sensor_reading_t sr_out = { 0 };
635 
636 		if ((ret = nvlist_lookup_uint32(pargs, TOPO_PROP_VAL_VAL,
637 		    &ledmode)) != 0) {
638 			topo_mod_dprintf(mod, "Failed to lookup %s nvpair "
639 			    "(%s)\n", TOPO_PROP_VAL_VAL, strerror(ret));
640 			topo_mod_strfree(mod, entity_ref);
641 			topo_mod_ipmi_rele(mod);
642 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
643 		}
644 
645 		if (ledmode == TOPO_LED_STATE_OFF) {
646 			sr_out.iss_deassert_state = mask;
647 			sr_out.iss_deassrt_op = IPMI_SENSOR_OP_SET;
648 		} else if (ledmode == TOPO_LED_STATE_ON) {
649 			sr_out.iss_assert_state = mask;
650 			sr_out.iss_assert_op = IPMI_SENSOR_OP_SET;
651 		} else {
652 			topo_mod_dprintf(mod, "Invalid LED mode: %d 0x%x\n",
653 			    ledmode);
654 			topo_mod_strfree(mod, entity_ref);
655 			topo_mod_ipmi_rele(mod);
656 			return (-1);
657 		}
658 		sr_out.iss_id = cs->is_cs_number;
659 		topo_mod_dprintf(mod, "Setting LED mode (mask = 0x%x)\n", mask);
660 		if (ipmi_set_sensor_reading(hdl, &sr_out) != 0) {
661 			topo_mod_dprintf(mod, "Failed to set sensor reading "
662 			    "for sensor %s (%s)\n", entity_ref,
663 			    ipmi_errmsg(hdl));
664 			topo_mod_strfree(mod, entity_ref);
665 			topo_mod_ipmi_rele(mod);
666 			return (-1);
667 		}
668 	} else {
669 		/*
670 		 * Get the LED mode
671 		 */
672 		ipmi_sensor_reading_t *sr_in;
673 
674 		topo_mod_dprintf(mod, "Getting LED mode\n");
675 		if ((sr_in = ipmi_get_sensor_reading(hdl, cs->is_cs_number))
676 		    == NULL) {
677 			topo_mod_dprintf(mod, "Failed to get sensor reading "
678 			    "for sensor %s (sensor num: %d) (error: %s)\n",
679 			    entity_ref, cs->is_cs_number, ipmi_errmsg(hdl));
680 			topo_mod_strfree(mod, entity_ref);
681 			topo_mod_ipmi_rele(mod);
682 			return (-1);
683 		}
684 		if (sr_in->isr_state & (uint16_t)mask)
685 			ledmode = TOPO_LED_STATE_ON;
686 		else
687 			ledmode = TOPO_LED_STATE_OFF;
688 	}
689 	topo_mod_strfree(mod, entity_ref);
690 	topo_mod_ipmi_rele(mod);
691 
692 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
693 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, TOPO_LED_MODE) != 0 ||
694 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_UINT32) != 0 ||
695 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL, ledmode) != 0) {
696 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
697 		nvlist_free(nvl);
698 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
699 	}
700 	*out = nvl;
701 	return (0);
702 }
703 
704 static int
705 make_sensor_node(topo_mod_t *mod, tnode_t *pnode, struct sensor_data *sd)
706 {
707 	int err, ret;
708 	tnode_t *fnode;
709 	char *ftype = "sensor";
710 	topo_pgroup_info_t pgi;
711 	nvlist_t *arg_nvl = NULL;
712 
713 	if ((fnode = topo_node_facbind(mod, pnode, sd->sd_entity_ref,
714 	    ftype)) == NULL) {
715 		topo_mod_dprintf(mod, "Failed to bind facility node: %s\n",
716 		    sd->sd_entity_ref);
717 		/* topo errno set */
718 		return (-1);
719 	}
720 
721 	pgi.tpi_name = TOPO_PGROUP_FACILITY;
722 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
723 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
724 	pgi.tpi_version = 1;
725 	if (topo_pgroup_create(fnode, &pgi, &err) != 0) {
726 		if (err != ETOPO_PROP_DEFD) {
727 			topo_mod_dprintf(mod,  "pgroups create failure: %s\n",
728 			    topo_strerror(err));
729 			topo_node_unbind(fnode);
730 			return (-1);
731 		}
732 	}
733 	if (topo_method_register(mod, fnode, ipmi_fac_methods) < 0) {
734 		topo_mod_dprintf(mod, "make_fac_node: "
735 		    "failed to register facility methods");
736 		topo_node_unbind(fnode);
737 		return (-1);
738 	}
739 	/*
740 	 * For both threshold and discrete sensors we set up a propmethod for
741 	 * getting the sensor state and properties to hold the entity ref,
742 	 * sensor class and sensor type.
743 	 *
744 	 * Additionally, for analog sensors we set up a property method for
745 	 * getting the converted sensor reading and property for the base
746 	 * unit type
747 	 */
748 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, "entity_ref",
749 	    TOPO_PROP_IMMUTABLE, sd->sd_entity_ref, &err) != 0) {
750 		topo_mod_dprintf(mod, "Failed to set entity_ref property on "
751 		    "node: %s=%d (%s)\n", topo_node_name(fnode),
752 		    topo_node_instance(fnode), topo_strerror(err));
753 		return (-1);
754 	}
755 	if (topo_prop_set_string(fnode, TOPO_PGROUP_FACILITY, TOPO_SENSOR_CLASS,
756 	    TOPO_PROP_IMMUTABLE, sd->sd_class, &err) != 0) {
757 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
758 		    "%s=%d (%s)\n", TOPO_SENSOR_CLASS, topo_node_name(fnode),
759 		    topo_node_instance(fnode), topo_strerror(err));
760 		return (-1);
761 	}
762 	if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
763 	    TOPO_FACILITY_TYPE, TOPO_PROP_IMMUTABLE, sd->sd_stype, &err) != 0) {
764 		topo_mod_dprintf(mod, "Failed to set %s property on node: "
765 		    "%s=%d (%s)\n", TOPO_FACILITY_TYPE, topo_node_name(fnode),
766 		    topo_node_instance(fnode), topo_strerror(err));
767 		return (-1);
768 	}
769 	if (topo_mod_nvalloc(mod, &arg_nvl, NV_UNIQUE_NAME) < 0) {
770 		topo_node_unbind(fnode);
771 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
772 	}
773 
774 	if ((ret = nvlist_add_string(arg_nvl, "ipmi_entity", sd->sd_entity_ref))
775 	    != 0) {
776 		topo_mod_dprintf(mod, "Failed build arg nvlist (%s)\n",
777 		    strerror(ret));
778 		nvlist_free(arg_nvl);
779 		return (-1);
780 	}
781 
782 	if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
783 	    TOPO_SENSOR_STATE, TOPO_TYPE_UINT32, "ipmi_sensor_state", arg_nvl,
784 	    &err) != 0) {
785 		topo_mod_dprintf(mod, "Failed to register %s propmeth on fac "
786 		    "node %s (%s)\n", TOPO_SENSOR_STATE, topo_node_name(fnode),
787 		    topo_strerror(err));
788 		nvlist_free(arg_nvl);
789 		return (-1);
790 	}
791 
792 	if (strcmp(sd->sd_class, TOPO_SENSOR_CLASS_THRESHOLD) == 0) {
793 		if (topo_prop_method_register(fnode, TOPO_PGROUP_FACILITY,
794 		    TOPO_SENSOR_READING, TOPO_TYPE_DOUBLE,
795 		    "ipmi_sensor_reading", arg_nvl, &err) != 0) {
796 			topo_mod_dprintf(mod, "Failed to register %s propmeth "
797 			    "on fac node %s (%s)\n", TOPO_SENSOR_READING,
798 			    topo_node_name(fnode), topo_strerror(err));
799 			nvlist_free(arg_nvl);
800 			return (-1);
801 		}
802 		if (topo_prop_set_uint32(fnode, TOPO_PGROUP_FACILITY,
803 		    TOPO_SENSOR_UNITS, TOPO_PROP_IMMUTABLE, sd->sd_units, &err)
804 		    != 0) {
805 			topo_mod_dprintf(mod, "Failed to set units property on "
806 			    "node: %s (%s)\n", topo_node_name(fnode),
807 			    topo_strerror(err));
808 			nvlist_free(arg_nvl);
809 			return (-1);
810 		}
811 	}
812 	nvlist_free(arg_nvl);
813 	return (0);
814 }
815 
816 /* ARGSUSED */
817 static int
818 sdr_callback(ipmi_handle_t *hdl, const char *id, ipmi_sdr_t *sdr, void *data)
819 {
820 	uint8_t sensor_entity, sensor_inst;
821 	int sensor_idlen;
822 	ipmi_sdr_full_sensor_t *f_sensor = NULL;
823 	ipmi_sdr_compact_sensor_t *c_sensor = NULL;
824 	struct sensor_data sd;
825 	struct entity_info *ei = (struct entity_info *)data;
826 
827 	switch (sdr->is_type) {
828 		case IPMI_SDR_TYPE_FULL_SENSOR:
829 			f_sensor =
830 			    (ipmi_sdr_full_sensor_t *)sdr->is_record;
831 			sensor_entity = f_sensor->is_fs_entity_id;
832 			sensor_inst = f_sensor->is_fs_entity_instance;
833 			sensor_idlen = f_sensor->is_fs_idlen;
834 			(void) strncpy(sd.sd_entity_ref,
835 			    f_sensor->is_fs_idstring,
836 			    f_sensor->is_fs_idlen);
837 			sd.sd_entity_ref[sensor_idlen] = '\0';
838 			sd.sd_class = TOPO_SENSOR_CLASS_THRESHOLD;
839 			sd.sd_units = f_sensor->is_fs_unit2;
840 			sd.sd_stype = f_sensor->is_fs_type;
841 			sd.sd_rtype = f_sensor->is_fs_reading_type;
842 			break;
843 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
844 			c_sensor =
845 			    (ipmi_sdr_compact_sensor_t *)sdr->is_record;
846 			sensor_entity = c_sensor->is_cs_entity_id;
847 			sensor_inst = c_sensor->is_cs_entity_instance;
848 			sensor_idlen = c_sensor->is_cs_idlen;
849 			(void) strncpy(sd.sd_entity_ref,
850 			    c_sensor->is_cs_idstring,
851 			    sensor_idlen);
852 			sd.sd_entity_ref[sensor_idlen] = '\0';
853 			sd.sd_class = TOPO_SENSOR_CLASS_DISCRETE;
854 			sd.sd_units = c_sensor->is_cs_unit2;
855 			sd.sd_stype = c_sensor->is_cs_type;
856 			sd.sd_rtype = c_sensor->is_cs_reading_type;
857 			break;
858 		default:
859 			return (0);
860 	}
861 	/*
862 	 * We offset the threshold and generic sensor reading types by 0x100
863 	 */
864 	if (sd.sd_rtype >= 0x1 && sd.sd_rtype <= 0xc)
865 		sd.sd_stype = sd.sd_rtype + 0x100;
866 
867 	if ((sensor_entity == ei->ei_id) && (sensor_inst == ei->ei_inst))
868 		if (make_sensor_node(ei->ei_mod, ei->ei_node, &sd) != 0) {
869 			topo_mod_dprintf(ei->ei_mod, "Failed to create sensor "
870 			    "node for %s\n", sd.sd_entity_ref);
871 			if (topo_mod_errno(ei->ei_mod) != EMOD_NODE_DUP)
872 				return (-1);
873 		}
874 	return (0);
875 }
876 
877 /* ARGSUSED */
878 static int
879 ipmi_sensor_enum(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
880     nvlist_t *in, nvlist_t **out)
881 {
882 	char *entity_ref;
883 	int err;
884 	struct entity_info ei;
885 	ipmi_sdr_t *ref_sdr;
886 	ipmi_handle_t *hdl;
887 	ipmi_sdr_full_sensor_t *fsensor;
888 	ipmi_sdr_compact_sensor_t *csensor;
889 	ipmi_sdr_fru_locator_t *floc;
890 	ipmi_sdr_generic_locator_t *gloc;
891 
892 	if ((hdl = topo_mod_ipmi_hold(mod)) == NULL) {
893 		topo_mod_dprintf(mod, "Failed to get IPMI handle\n");
894 		return (-1);
895 	}
896 
897 	/*
898 	 * Use the entity ref to lookup the SDR, which will have the entity ID
899 	 * and instance.
900 	 */
901 	if (topo_prop_get_string(node, TOPO_PGROUP_IPMI,
902 	    "entity_ref", &entity_ref, &err) != 0) {
903 		topo_mod_dprintf(mod, "Failed to lookup entity_ref "
904 		    "property (%s)\n", topo_strerror(err));
905 		topo_mod_ipmi_rele(mod);
906 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
907 	}
908 
909 	topo_mod_dprintf(mod, "Looking up SDR for %s ...\n",
910 	    entity_ref);
911 	if ((ref_sdr = ipmi_sdr_lookup(hdl, entity_ref)) == NULL) {
912 		topo_mod_dprintf(mod, "Failed to lookup SDR (%s)\n",
913 		    ipmi_errmsg(hdl));
914 		topo_mod_strfree(mod, entity_ref);
915 		topo_mod_ipmi_rele(mod);
916 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
917 	}
918 	topo_mod_strfree(mod, entity_ref);
919 
920 	switch (ref_sdr->is_type) {
921 		case IPMI_SDR_TYPE_FULL_SENSOR:
922 			fsensor = (ipmi_sdr_full_sensor_t *)ref_sdr->is_record;
923 			ei.ei_id = fsensor->is_fs_entity_id;
924 			ei.ei_inst = fsensor->is_fs_entity_instance;
925 			break;
926 		case IPMI_SDR_TYPE_COMPACT_SENSOR:
927 			csensor
928 			    = (ipmi_sdr_compact_sensor_t *)ref_sdr->is_record;
929 			ei.ei_id = csensor->is_cs_entity_id;
930 			ei.ei_inst = csensor->is_cs_entity_instance;
931 			break;
932 		case IPMI_SDR_TYPE_FRU_LOCATOR:
933 			floc = (ipmi_sdr_fru_locator_t *)ref_sdr->is_record;
934 			ei.ei_id = floc->is_fl_entity;
935 			ei.ei_inst = floc->is_fl_instance;
936 			break;
937 		case IPMI_SDR_TYPE_GENERIC_LOCATOR:
938 			gloc = (ipmi_sdr_generic_locator_t *)ref_sdr->is_record;
939 			ei.ei_id = gloc->is_gl_entity;
940 			ei.ei_inst = gloc->is_gl_instance;
941 			break;
942 		default:
943 			topo_mod_dprintf(mod, "Failed to determine entity id "
944 			    "and instance\n", ipmi_errmsg(hdl));
945 			topo_mod_ipmi_rele(mod);
946 			return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
947 	}
948 	topo_mod_dprintf(mod, "Entity ID = 0x%x, Entity Instance = 0x%x\n",
949 	    ei.ei_id, ei.ei_inst);
950 
951 	ei.ei_node = node;
952 	ei.ei_mod = mod;
953 
954 	/*
955 	 * Now iterate through all of the full and compact sensor data records
956 	 * and create a sensor facility node for each record that matches our
957 	 * entity ID and instance
958 	 */
959 	if (ipmi_sdr_iter(hdl, sdr_callback, &ei) != 0) {
960 		topo_mod_dprintf(mod, "ipmi_sdr_iter() failed\n");
961 		topo_mod_ipmi_rele(mod);
962 		return (-1);
963 	}
964 
965 	topo_mod_ipmi_rele(mod);
966 
967 	return (0);
968 }
969 
970 static int
971 ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
972     nvlist_t *in, nvlist_t **out)
973 {
974 	char *fmtstr, buf[BUFSZ];
975 	tnode_t *refnode;
976 	int ret, inst1, inst2;
977 	uint32_t offset, nparams;
978 	nvlist_t *args, *nvl;
979 
980 	if (vers > TOPO_METH_IPMI_ENTITY_VERSION)
981 		return (topo_mod_seterrno(mod, ETOPO_METHOD_VERNEW));
982 
983 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
984 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
985 		    strerror(ret));
986 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
987 	}
988 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
989 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
990 		    strerror(ret));
991 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
992 	}
993 	if ((ret = nvlist_lookup_uint32(args, "nparams", &nparams)) != 0) {
994 		topo_mod_dprintf(mod, "Failed to lookup 'nparams' arg (%s)\n",
995 		    strerror(ret));
996 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
997 	}
998 
999 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
1000 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
1001 		/* topo errno already set */
1002 		return (-1);
1003 	}
1004 
1005 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1006 		refnode = topo_node_parent(node);
1007 	else
1008 		refnode = node;
1009 
1010 	switch (nparams) {
1011 	case 1:
1012 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
1013 		(void) snprintf(buf, BUFSZ, fmtstr,
1014 		    (topo_node_instance(refnode) + offset));
1015 		break;
1016 	case 2:
1017 		inst1 = topo_node_instance(topo_node_parent(refnode)) + offset;
1018 		inst2 = topo_node_instance(refnode) + offset;
1019 		/* LINTED: E_SEC_PRINTF_VAR_FMT */
1020 		(void) snprintf(buf, BUFSZ, fmtstr, inst1, inst2);
1021 		break;
1022 	default:
1023 		topo_mod_dprintf(mod, "Invalid 'nparams' argval (%d)\n",
1024 		    nparams);
1025 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1026 	}
1027 
1028 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1029 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1030 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) != 0 ||
1031 	    nvlist_add_string(nvl, TOPO_PROP_VAL_VAL, buf) != 0) {
1032 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1033 		nvlist_free(nvl);
1034 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1035 	}
1036 	*out = nvl;
1037 
1038 	return (0);
1039 }
1040 
1041 /* ARGSUSED */
1042 static int
1043 dimm_ipmi_entity(topo_mod_t *mod, tnode_t *node, topo_version_t vers,
1044     nvlist_t *in, nvlist_t **out)
1045 {
1046 	char *fmtstr, buf[BUFSZ];
1047 	tnode_t *chip, *dimm;
1048 	int ret;
1049 	uint32_t offset;
1050 	nvlist_t *args, *nvl;
1051 
1052 	if ((ret = nvlist_lookup_nvlist(in, TOPO_PROP_ARGS, &args)) != 0) {
1053 		topo_mod_dprintf(mod, "Failed to lookup 'args' list (%s)\n",
1054 		    strerror(ret));
1055 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1056 	}
1057 	if ((ret = nvlist_lookup_uint32(args, "offset", &offset)) != 0) {
1058 		topo_mod_dprintf(mod, "Failed to lookup 'offset' arg (%s)\n",
1059 		    strerror(ret));
1060 		return (topo_mod_seterrno(mod, EMOD_NVL_INVAL));
1061 	}
1062 
1063 	if ((fmtstr = get_fmtstr(mod, in)) == NULL) {
1064 		topo_mod_dprintf(mod, "Failed to retrieve 'format' arg\n");
1065 		/* topo errno already set */
1066 		return (-1);
1067 	}
1068 
1069 	if (topo_node_flags(node) & TOPO_NODE_FACILITY)
1070 		dimm = topo_node_parent(node);
1071 	else
1072 		dimm = node;
1073 
1074 	chip = topo_node_parent(topo_node_parent(dimm));
1075 
1076 	/* LINTED: E_SEC_PRINTF_VAR_FMT */
1077 	(void) snprintf(buf, BUFSZ, fmtstr, topo_node_instance(chip),
1078 	    (topo_node_instance(dimm) + offset));
1079 
1080 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0 ||
1081 	    nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, "entity_ref") != 0 ||
1082 	    nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, TOPO_TYPE_STRING) != 0 ||
1083 	    nvlist_add_string(nvl, TOPO_PROP_VAL_VAL, buf) != 0) {
1084 		topo_mod_dprintf(mod, "Failed to allocate 'out' nvlist\n");
1085 		nvlist_free(nvl);
1086 		return (topo_mod_seterrno(mod, EMOD_NOMEM));
1087 	}
1088 	*out = nvl;
1089 
1090 	return (0);
1091 }
1092 
1093 /*ARGSUSED*/
1094 static int
1095 fac_prov_ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
1096     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
1097 {
1098 	topo_pgroup_info_t pgi;
1099 	int err;
1100 
1101 	if (topo_node_flags(rnode) == TOPO_NODE_DEFAULT) {
1102 		pgi.tpi_name = TOPO_PGROUP_IPMI;
1103 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1104 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1105 		pgi.tpi_version = 1;
1106 		if (topo_pgroup_create(rnode, &pgi, &err) != 0) {
1107 			if (err != ETOPO_PROP_DEFD) {
1108 				topo_mod_dprintf(mod,
1109 				    "pgroups create failure: %s\n",
1110 				    topo_strerror(err));
1111 				return (-1);
1112 			}
1113 		}
1114 		if (topo_method_register(mod, rnode, ipmi_node_methods) != 0) {
1115 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
1116 			    "topo_method_register() failed: %s",
1117 			    topo_mod_errmsg(mod));
1118 			return (-1);
1119 		}
1120 	} else {
1121 		if (topo_method_register(mod, rnode, ipmi_fac_methods) != 0) {
1122 			topo_mod_dprintf(mod, "fac_prov_ipmi_enum: "
1123 			    "topo_method_register() failed: %s",
1124 			    topo_mod_errmsg(mod));
1125 			return (-1);
1126 		}
1127 	}
1128 	return (0);
1129 }
1130