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