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