12eeaed14Srobj /*
22eeaed14Srobj  * CDDL HEADER START
32eeaed14Srobj  *
42eeaed14Srobj  * The contents of this file are subject to the terms of the
52eeaed14Srobj  * Common Development and Distribution License (the "License").
62eeaed14Srobj  * You may not use this file except in compliance with the License.
72eeaed14Srobj  *
82eeaed14Srobj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92eeaed14Srobj  * or http://www.opensolaris.org/os/licensing.
102eeaed14Srobj  * See the License for the specific language governing permissions
112eeaed14Srobj  * and limitations under the License.
122eeaed14Srobj  *
132eeaed14Srobj  * When distributing Covered Code, include this CDDL HEADER in each
142eeaed14Srobj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152eeaed14Srobj  * If applicable, add the following below this CDDL HEADER, with the
162eeaed14Srobj  * fields enclosed by brackets "[]" replaced with your own identifying
172eeaed14Srobj  * information: Portions Copyright [yyyy] [name of copyright owner]
182eeaed14Srobj  *
192eeaed14Srobj  * CDDL HEADER END
202eeaed14Srobj  */
212eeaed14Srobj 
222eeaed14Srobj /*
2372c9c967SRobert Johnston  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
248f022dd6SRob Johnston  * Copyright (c) 2017, Joyent, Inc.
25985366beSMatthias Scheler  * Copyright 2019 by Western Digital Corporation
262eeaed14Srobj  */
272eeaed14Srobj 
282eeaed14Srobj #include <assert.h>
292eeaed14Srobj #include <fm/libtopo.h>
302eeaed14Srobj #include <fm/topo_mod.h>
312eeaed14Srobj #include <sys/fm/protocol.h>
322eeaed14Srobj #include <string.h>
332eeaed14Srobj 
342eeaed14Srobj #define	TOPO_PGROUP_IPMI		"ipmi"
352eeaed14Srobj #define	TOPO_PROP_IPMI_ENTITY_REF	"entity_ref"
362eeaed14Srobj #define	TOPO_PROP_IPMI_ENTITY_PRESENT	"entity_present"
378f022dd6SRob Johnston #define	FAC_PROV_IPMI			"fac_prov_ipmi"
382eeaed14Srobj 
392eeaed14Srobj typedef struct ipmi_enum_data {
402eeaed14Srobj 	topo_mod_t		*ed_mod;
412eeaed14Srobj 	tnode_t			*ed_pnode;
422eeaed14Srobj 	const char		*ed_name;
432eeaed14Srobj 	char			*ed_label;
442eeaed14Srobj 	uint8_t			ed_entity;
452eeaed14Srobj 	topo_instance_t		ed_instance;
468f022dd6SRob Johnston 	ipmi_sdr_fru_locator_t	*ed_frusdr;
472eeaed14Srobj } ipmi_enum_data_t;
482eeaed14Srobj 
492eeaed14Srobj static int ipmi_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
502eeaed14Srobj     nvlist_t **);
51985366beSMatthias Scheler static int ipmi_unusable(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
52985366beSMatthias Scheler     nvlist_t **);
532eeaed14Srobj static int ipmi_enum(topo_mod_t *, tnode_t *, const char *,
542eeaed14Srobj     topo_instance_t, topo_instance_t, void *, void *);
552eeaed14Srobj static int ipmi_post_process(topo_mod_t *, tnode_t *);
562eeaed14Srobj 
572eeaed14Srobj extern int ipmi_fru_label(topo_mod_t *mod, tnode_t *node,
582eeaed14Srobj     topo_version_t vers, nvlist_t *in, nvlist_t **out);
592eeaed14Srobj 
602eeaed14Srobj extern int ipmi_fru_fmri(topo_mod_t *mod, tnode_t *node,
612eeaed14Srobj     topo_version_t vers, nvlist_t *in, nvlist_t **out);
622eeaed14Srobj 
632eeaed14Srobj static const topo_method_t ipmi_methods[] = {
642eeaed14Srobj 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
652eeaed14Srobj 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ipmi_present },
66985366beSMatthias Scheler 	{ TOPO_METH_UNUSABLE, TOPO_METH_UNUSABLE_DESC,
67985366beSMatthias Scheler 	    TOPO_METH_UNUSABLE_VERSION, TOPO_STABILITY_INTERNAL,
68985366beSMatthias Scheler 	    ipmi_unusable },
692eeaed14Srobj 	{ "ipmi_fru_label", "Property method", 0,
702eeaed14Srobj 	    TOPO_STABILITY_INTERNAL, ipmi_fru_label},
712eeaed14Srobj 	{ "ipmi_fru_fmri", "Property method", 0,
722eeaed14Srobj 	    TOPO_STABILITY_INTERNAL, ipmi_fru_fmri},
73e5dcf7beSRobert Johnston 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
74e5dcf7beSRobert Johnston 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
75e5dcf7beSRobert Johnston 	    topo_method_sensor_failure },
762eeaed14Srobj 	{ NULL }
772eeaed14Srobj };
782eeaed14Srobj 
792eeaed14Srobj const topo_modops_t ipmi_ops = { ipmi_enum, NULL };
802eeaed14Srobj 
812eeaed14Srobj const topo_modinfo_t ipmi_info =
822eeaed14Srobj 	{ "ipmi", FM_FMRI_SCHEME_HC, TOPO_VERSION, &ipmi_ops };
832eeaed14Srobj 
84985366beSMatthias Scheler /* Common code used by topo methods below to find an IPMI entity */
852eeaed14Srobj static int
ipmi_find_entity(topo_mod_t * mod,tnode_t * tn,ipmi_handle_t ** ihpp,ipmi_entity_t ** epp,char ** namep,ipmi_sdr_t ** sdrpp)86985366beSMatthias Scheler ipmi_find_entity(topo_mod_t *mod, tnode_t *tn, ipmi_handle_t **ihpp,
87985366beSMatthias Scheler     ipmi_entity_t **epp, char **namep, ipmi_sdr_t **sdrpp)
882eeaed14Srobj {
892eeaed14Srobj 	ipmi_handle_t *ihp;
902eeaed14Srobj 	ipmi_entity_t *ep;
91985366beSMatthias Scheler 	int err;
92985366beSMatthias Scheler 	char *name = NULL, **names;
93985366beSMatthias Scheler 	ipmi_sdr_t *sdrp = NULL;
94985366beSMatthias Scheler 	uint_t nelems, i;
95985366beSMatthias Scheler 
96985366beSMatthias Scheler 	*ihpp = NULL;
97985366beSMatthias Scheler 	*epp = NULL;
98985366beSMatthias Scheler 	*namep = NULL;
99985366beSMatthias Scheler 	*sdrpp = NULL;
1002eeaed14Srobj 
1010b1b4412SEric Schrock 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
1022eeaed14Srobj 		return (topo_mod_seterrno(mod, ETOPO_METHOD_UNKNOWN));
1032eeaed14Srobj 
1042eeaed14Srobj 	ep = topo_node_getspecific(tn);
105985366beSMatthias Scheler 	if (ep != NULL) {
106985366beSMatthias Scheler 		*ihpp = ihp;
107985366beSMatthias Scheler 		*epp = ep;
108985366beSMatthias Scheler 		return (0);
109985366beSMatthias Scheler 	}
110985366beSMatthias Scheler 
1112eeaed14Srobj 	if (topo_prop_get_string(tn, TOPO_PGROUP_IPMI,
1122eeaed14Srobj 	    TOPO_PROP_IPMI_ENTITY_PRESENT, &name, &err) == 0) {
1132eeaed14Srobj 		/*
1142eeaed14Srobj 		 * Some broken IPMI implementations don't export correct
1152eeaed14Srobj 		 * entities, so referring to an entity isn't sufficient.
1162eeaed14Srobj 		 * For these platforms, we allow the XML to specify a
1172eeaed14Srobj 		 * single SDR record that represents the current present
1182eeaed14Srobj 		 * state.
1192eeaed14Srobj 		 */
120985366beSMatthias Scheler 		sdrp = ipmi_sdr_lookup(ihp, name);
1212eeaed14Srobj 	} else {
12272c9c967SRobert Johnston 		if (topo_prop_get_string_array(tn, TOPO_PGROUP_IPMI,
123985366beSMatthias Scheler 		    TOPO_PROP_IPMI_ENTITY_REF, &names, &nelems, &err) != 0) {
1247793aa8bSEric Schrock 			/*
1257793aa8bSEric Schrock 			 * Not all nodes have an entity_ref attribute.
1267793aa8bSEric Schrock 			 * For these cases, return ENOTSUP so that we
1277793aa8bSEric Schrock 			 * fall back to the default hc presence
1287793aa8bSEric Schrock 			 * detection.
1297793aa8bSEric Schrock 			 */
1300b1b4412SEric Schrock 			topo_mod_ipmi_rele(mod);
131985366beSMatthias Scheler 			return (topo_mod_seterrno(mod, ETOPO_METHOD_NOTSUP));
1322eeaed14Srobj 		}
1332eeaed14Srobj 
134985366beSMatthias Scheler 		for (i = 0; i < nelems; i++) {
13572c9c967SRobert Johnston 			if ((ep = ipmi_entity_lookup_sdr(ihp, names[i]))
136985366beSMatthias Scheler 			    != NULL) {
137985366beSMatthias Scheler 				name = names[i];
138985366beSMatthias Scheler 				names[i] = NULL;
13972c9c967SRobert Johnston 				break;
140985366beSMatthias Scheler 			}
141985366beSMatthias Scheler 		}
14272c9c967SRobert Johnston 
14372c9c967SRobert Johnston 		for (i = 0; i < nelems; i++)
14472c9c967SRobert Johnston 			topo_mod_strfree(mod, names[i]);
14572c9c967SRobert Johnston 		topo_mod_free(mod, names, (nelems * sizeof (char *)));
14672c9c967SRobert Johnston 
14772c9c967SRobert Johnston 		if (ep == NULL) {
1482eeaed14Srobj 			topo_mod_dprintf(mod,
149*6597d6fcSRobert Mustacchi 			    "Failed to get present state of %s=%" PRIu64 "\n",
15072c9c967SRobert Johnston 			    topo_node_name(tn), topo_node_instance(tn));
1510b1b4412SEric Schrock 			topo_mod_ipmi_rele(mod);
1522eeaed14Srobj 			return (-1);
1532eeaed14Srobj 		}
1542eeaed14Srobj 		topo_node_setspecific(tn, ep);
1552eeaed14Srobj 	}
156985366beSMatthias Scheler 
157985366beSMatthias Scheler 	*ihpp = ihp;
158985366beSMatthias Scheler 	*namep = name;
159985366beSMatthias Scheler 	*sdrpp = sdrp;
160985366beSMatthias Scheler 	return (0);
1612eeaed14Srobj }
1622eeaed14Srobj 
163985366beSMatthias Scheler /*
164985366beSMatthias Scheler  * Determine if the entity is present.
165985366beSMatthias Scheler  */
166985366beSMatthias Scheler /*ARGSUSED*/
167985366beSMatthias Scheler static int
ipmi_present(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)168985366beSMatthias Scheler ipmi_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
169985366beSMatthias Scheler     nvlist_t *in, nvlist_t **out)
170985366beSMatthias Scheler {
171985366beSMatthias Scheler 	ipmi_handle_t *ihp;
172985366beSMatthias Scheler 	ipmi_entity_t *ep;
173985366beSMatthias Scheler 	char *name;
174985366beSMatthias Scheler 	ipmi_sdr_t *sdrp;
175985366beSMatthias Scheler 	int err;
176985366beSMatthias Scheler 	boolean_t present = B_FALSE;
177985366beSMatthias Scheler 	nvlist_t *nvl;
178985366beSMatthias Scheler 
179985366beSMatthias Scheler 	err = ipmi_find_entity(mod, tn, &ihp, &ep, &name, &sdrp);
180985366beSMatthias Scheler 	if (err != 0)
181985366beSMatthias Scheler 		return (err);
182985366beSMatthias Scheler 
1832eeaed14Srobj 	if (ep != NULL) {
1842eeaed14Srobj 		if (ipmi_entity_present(ihp, ep, &present) != 0) {
1852eeaed14Srobj 			topo_mod_dprintf(mod,
1862eeaed14Srobj 			    "ipmi_entity_present() failed: %s",
1872eeaed14Srobj 			    ipmi_errmsg(ihp));
188985366beSMatthias Scheler 			topo_mod_strfree(mod, name);
1890b1b4412SEric Schrock 			topo_mod_ipmi_rele(mod);
1909af3851aSeschrock 			return (-1);
1912eeaed14Srobj 		}
192525b85dbSEric Schrock 
193525b85dbSEric Schrock 		topo_mod_dprintf(mod,
19472c9c967SRobert Johnston 		    "ipmi_entity_present(%d, %d) = %d\n", ep->ie_type,
19572c9c967SRobert Johnston 		    ep->ie_instance, present);
196985366beSMatthias Scheler 	} else if (sdrp != NULL) {
197985366beSMatthias Scheler 		if (ipmi_entity_present_sdr(ihp, sdrp, &present) != 0) {
198985366beSMatthias Scheler 			topo_mod_dprintf(mod,
199985366beSMatthias Scheler 			    "Failed to get present state of %s (%s)\n",
200985366beSMatthias Scheler 			    name, ipmi_errmsg(ihp));
201985366beSMatthias Scheler 			topo_mod_strfree(mod, name);
202985366beSMatthias Scheler 			topo_mod_ipmi_rele(mod);
203985366beSMatthias Scheler 			return (-1);
2042eeaed14Srobj 		}
2052eeaed14Srobj 
206985366beSMatthias Scheler 		topo_mod_dprintf(mod, "ipmi_entity_present_sdr(%s) = %d\n",
207985366beSMatthias Scheler 		    name, present);
208985366beSMatthias Scheler 	}
209985366beSMatthias Scheler 
210985366beSMatthias Scheler 	topo_mod_strfree(mod, name);
2110b1b4412SEric Schrock 	topo_mod_ipmi_rele(mod);
2120b1b4412SEric Schrock 
2132eeaed14Srobj 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
2142eeaed14Srobj 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2152eeaed14Srobj 
2162eeaed14Srobj 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, present) != 0) {
2172eeaed14Srobj 		nvlist_free(nvl);
2182eeaed14Srobj 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
2192eeaed14Srobj 	}
2202eeaed14Srobj 
2212eeaed14Srobj 	*out = nvl;
2222eeaed14Srobj 
2232eeaed14Srobj 	return (0);
2242eeaed14Srobj }
2252eeaed14Srobj 
2262eeaed14Srobj /*
227985366beSMatthias Scheler  * Check whether an IPMI entity is a sensor that is unavailable
228985366beSMatthias Scheler  */
229985366beSMatthias Scheler static int
ipmi_check_sensor(ipmi_handle_t * ihp,ipmi_entity_t * ep,const char * name,ipmi_sdr_t * sdrp,void * data)230985366beSMatthias Scheler ipmi_check_sensor(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name,
231985366beSMatthias Scheler     ipmi_sdr_t *sdrp, void *data)
232985366beSMatthias Scheler {
233985366beSMatthias Scheler 	ipmi_sdr_full_sensor_t *fsp;
234985366beSMatthias Scheler 	ipmi_sdr_compact_sensor_t *csp;
235985366beSMatthias Scheler 	uint8_t sensor_number;
236985366beSMatthias Scheler 	ipmi_sensor_reading_t *reading;
237985366beSMatthias Scheler 
238985366beSMatthias Scheler 	switch (sdrp->is_type) {
239985366beSMatthias Scheler 	case IPMI_SDR_TYPE_FULL_SENSOR:
240985366beSMatthias Scheler 		fsp = (ipmi_sdr_full_sensor_t *)sdrp->is_record;
241985366beSMatthias Scheler 		sensor_number = fsp->is_fs_number;
242985366beSMatthias Scheler 		break;
243985366beSMatthias Scheler 
244985366beSMatthias Scheler 	case IPMI_SDR_TYPE_COMPACT_SENSOR:
245985366beSMatthias Scheler 		csp = (ipmi_sdr_compact_sensor_t *)sdrp->is_record;
246985366beSMatthias Scheler 		sensor_number = csp->is_cs_number;
247985366beSMatthias Scheler 		break;
248985366beSMatthias Scheler 
249985366beSMatthias Scheler 	default:
250985366beSMatthias Scheler 		return (0);
251985366beSMatthias Scheler 	}
252985366beSMatthias Scheler 
253985366beSMatthias Scheler 	reading = ipmi_get_sensor_reading(ihp, sensor_number);
254985366beSMatthias Scheler 	if (reading != NULL && reading->isr_state_unavailable)
255985366beSMatthias Scheler 		return (1);
256985366beSMatthias Scheler 
257985366beSMatthias Scheler 	return (0);
258985366beSMatthias Scheler }
259985366beSMatthias Scheler 
260985366beSMatthias Scheler /*
261985366beSMatthias Scheler  * Determine if the entity is unusable
262985366beSMatthias Scheler  */
263985366beSMatthias Scheler /*ARGSUSED*/
264985366beSMatthias Scheler static int
ipmi_unusable(topo_mod_t * mod,tnode_t * tn,topo_version_t version,nvlist_t * in,nvlist_t ** out)265985366beSMatthias Scheler ipmi_unusable(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
266985366beSMatthias Scheler     nvlist_t *in, nvlist_t **out)
267985366beSMatthias Scheler {
268985366beSMatthias Scheler 	ipmi_handle_t *ihp;
269985366beSMatthias Scheler 	ipmi_entity_t *ep;
270985366beSMatthias Scheler 	char *name;
271985366beSMatthias Scheler 	ipmi_sdr_t *sdrp;
272985366beSMatthias Scheler 	int err;
273985366beSMatthias Scheler 	boolean_t unusable = B_FALSE;
274985366beSMatthias Scheler 	nvlist_t *nvl;
275985366beSMatthias Scheler 
276985366beSMatthias Scheler 	err = ipmi_find_entity(mod, tn, &ihp, &ep, &name, &sdrp);
277985366beSMatthias Scheler 	if (err != 0)
278985366beSMatthias Scheler 		return (err);
279985366beSMatthias Scheler 
280985366beSMatthias Scheler 	/*
281985366beSMatthias Scheler 	 * Check whether the IPMI presented us with an entity for a
282985366beSMatthias Scheler 	 * sensor that is unavailable.
283985366beSMatthias Scheler 	 */
284985366beSMatthias Scheler 	if (ep != NULL) {
285985366beSMatthias Scheler 		unusable = (ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sensor,
286985366beSMatthias Scheler 		    NULL) != 0);
287985366beSMatthias Scheler 	} else if (sdrp != NULL) {
288985366beSMatthias Scheler 		unusable = (ipmi_check_sensor(ihp, NULL, NULL, sdrp,
289985366beSMatthias Scheler 		    NULL) != 0);
290985366beSMatthias Scheler 	}
291985366beSMatthias Scheler 
292985366beSMatthias Scheler 	topo_mod_strfree(mod, name);
293985366beSMatthias Scheler 	topo_mod_ipmi_rele(mod);
294985366beSMatthias Scheler 
295985366beSMatthias Scheler 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
296985366beSMatthias Scheler 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
297985366beSMatthias Scheler 
298985366beSMatthias Scheler 	if (nvlist_add_uint32(nvl, TOPO_METH_UNUSABLE_RET, unusable) != 0) {
299985366beSMatthias Scheler 		nvlist_free(nvl);
300985366beSMatthias Scheler 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
301985366beSMatthias Scheler 	}
302985366beSMatthias Scheler 
303985366beSMatthias Scheler 	*out = nvl;
304985366beSMatthias Scheler 
305985366beSMatthias Scheler 	return (0);
306985366beSMatthias Scheler }
307985366beSMatthias Scheler 
308985366beSMatthias Scheler /*
3092eeaed14Srobj  * This determines if the entity has a FRU locator record set, in which case we
3102eeaed14Srobj  * treat this as a FRU, even if it's part of an association.
3112eeaed14Srobj  */
3122eeaed14Srobj /*ARGSUSED*/
3132eeaed14Srobj static int
ipmi_check_sdr(ipmi_handle_t * ihp,ipmi_entity_t * ep,const char * name,ipmi_sdr_t * sdrp,void * data)3142eeaed14Srobj ipmi_check_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name,
3152eeaed14Srobj     ipmi_sdr_t *sdrp, void *data)
3162eeaed14Srobj {
3172eeaed14Srobj 	ipmi_enum_data_t *edp = data;
3182eeaed14Srobj 
3192eeaed14Srobj 	if (sdrp->is_type == IPMI_SDR_TYPE_FRU_LOCATOR)
3208f022dd6SRob Johnston 		edp->ed_frusdr = (ipmi_sdr_fru_locator_t *)sdrp->is_record;
3212eeaed14Srobj 
3222eeaed14Srobj 	return (0);
3232eeaed14Srobj }
3242eeaed14Srobj 
3252eeaed14Srobj /*
3262eeaed14Srobj  * Main entity enumerator.  If we find a matching entity type, then instantiate
3272eeaed14Srobj  * a topo node.
3282eeaed14Srobj  */
3292eeaed14Srobj static int
ipmi_check_entity(ipmi_handle_t * ihp,ipmi_entity_t * ep,void * data)3302eeaed14Srobj ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data)
3312eeaed14Srobj {
3322eeaed14Srobj 	ipmi_enum_data_t *edp = data;
3332eeaed14Srobj 	ipmi_enum_data_t cdata;
3342eeaed14Srobj 	tnode_t *pnode = edp->ed_pnode;
3352eeaed14Srobj 	topo_mod_t *mod = edp->ed_mod;
3368f022dd6SRob Johnston 	topo_mod_t *fmod = topo_mod_getspecific(mod);
3372eeaed14Srobj 	nvlist_t *auth, *fmri;
3382eeaed14Srobj 	tnode_t *tn;
3392eeaed14Srobj 	topo_pgroup_info_t pgi;
3408f022dd6SRob Johnston 	char *frudata = NULL, *part = NULL, *rev = NULL, *serial = NULL;
3418f022dd6SRob Johnston 	ipmi_fru_prod_info_t fruprod = {0};
3428f022dd6SRob Johnston 	ipmi_fru_brd_info_t frubrd = {0};
3432eeaed14Srobj 	int err;
3442eeaed14Srobj 	const char *labelname;
3452eeaed14Srobj 	char label[64];
3462eeaed14Srobj 	size_t len;
3472eeaed14Srobj 
3488f022dd6SRob Johnston 	/*
3498f022dd6SRob Johnston 	 * Some questionable IPMI implementations group psu and fan entities
3508f022dd6SRob Johnston 	 * under things like motherboard or chassis entities.  So even if this
3518f022dd6SRob Johnston 	 * entity type isn't typically associated with fans and psus, if it has
3528f022dd6SRob Johnston 	 * children, then regardless of the type we need to decend down and
3538f022dd6SRob Johnston 	 * iterate over them.
3548f022dd6SRob Johnston 	 */
3558f022dd6SRob Johnston 	if (ep->ie_type != edp->ed_entity) {
3568f022dd6SRob Johnston 		if (ep->ie_children != 0 &&
3578f022dd6SRob Johnston 		    ipmi_entity_iter_children(ihp, ep, ipmi_check_entity,
3588f022dd6SRob Johnston 		    data) != 0)
3598f022dd6SRob Johnston 			return (1);
3602eeaed14Srobj 		return (0);
3618f022dd6SRob Johnston 	}
3622eeaed14Srobj 
3632eeaed14Srobj 	/*
3642eeaed14Srobj 	 * The purpose of power and cooling domains is to group psus and fans
3652eeaed14Srobj 	 * together.  Unfortunately, some broken IPMI implementations declare
3662eeaed14Srobj 	 * domains that don't contain other elements.  Since the end goal is to
3672eeaed14Srobj 	 * only enumerate psus and fans, we'll just ignore such elements.
3682eeaed14Srobj 	 */
3692eeaed14Srobj 	if ((ep->ie_type == IPMI_ET_POWER_DOMAIN ||
3702eeaed14Srobj 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN) &&
3712eeaed14Srobj 	    ep->ie_children == 0)
3722eeaed14Srobj 		return (0);
3732eeaed14Srobj 
3742eeaed14Srobj 	if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
3752eeaed14Srobj 		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
3762eeaed14Srobj 		    topo_mod_errmsg(mod));
3772eeaed14Srobj 		return (1);
3782eeaed14Srobj 	}
3792eeaed14Srobj 
3808f022dd6SRob Johnston 	/*
3818f022dd6SRob Johnston 	 * Determine if there's a FRU record associated with this entity.  If
3828f022dd6SRob Johnston 	 * so, then read in the FRU identity info so that it can be included
3838f022dd6SRob Johnston 	 * in the authority portion of the FMRI.
3848f022dd6SRob Johnston 	 *
3858f022dd6SRob Johnston 	 * topo_mod_hcfmri() will safely except NULL values for the part,
3868f022dd6SRob Johnston 	 * rev and serial params, so we opt to simply drive on in the face of
3878f022dd6SRob Johnston 	 * any strdup failures.
3888f022dd6SRob Johnston 	 */
3898f022dd6SRob Johnston 	edp->ed_frusdr = NULL;
3908f022dd6SRob Johnston 	(void) ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sdr, edp);
3918f022dd6SRob Johnston 	if (edp->ed_frusdr != NULL &&
3928f022dd6SRob Johnston 	    ipmi_fru_read(ihp, edp->ed_frusdr, &frudata) != -1) {
3938f022dd6SRob Johnston 		if (ipmi_fru_parse_product(ihp, frudata, &fruprod) == 0) {
3948f022dd6SRob Johnston 			part = strdup(fruprod.ifpi_part_number);
3958f022dd6SRob Johnston 			rev = strdup(fruprod.ifpi_product_version);
3968f022dd6SRob Johnston 			serial = strdup(fruprod.ifpi_product_serial);
3978f022dd6SRob Johnston 		} else if (ipmi_fru_parse_board(ihp, frudata, &frubrd) == 0) {
3988f022dd6SRob Johnston 			part = strdup(frubrd.ifbi_part_number);
3998f022dd6SRob Johnston 			serial = strdup(frubrd.ifbi_product_serial);
4008f022dd6SRob Johnston 		}
4018f022dd6SRob Johnston 	}
4028f022dd6SRob Johnston 	free(frudata);
4038f022dd6SRob Johnston 
4042eeaed14Srobj 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
4058f022dd6SRob Johnston 	    edp->ed_name, edp->ed_instance, NULL, auth, part, rev,
4068f022dd6SRob Johnston 	    serial)) == NULL) {
4072eeaed14Srobj 		nvlist_free(auth);
4088f022dd6SRob Johnston 		free(part);
4098f022dd6SRob Johnston 		free(rev);
4108f022dd6SRob Johnston 		free(serial);
4112eeaed14Srobj 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
4122eeaed14Srobj 		    topo_mod_errmsg(mod));
4132eeaed14Srobj 		return (1);
4142eeaed14Srobj 	}
4152eeaed14Srobj 	nvlist_free(auth);
4168f022dd6SRob Johnston 	free(part);
4178f022dd6SRob Johnston 	free(rev);
4188f022dd6SRob Johnston 	free(serial);
4192eeaed14Srobj 
4202eeaed14Srobj 	if ((tn = topo_node_bind(mod, pnode, edp->ed_name,
4212eeaed14Srobj 	    edp->ed_instance, fmri)) == NULL) {
4222eeaed14Srobj 		nvlist_free(fmri);
4232eeaed14Srobj 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
4242eeaed14Srobj 		    topo_mod_errmsg(mod));
4252eeaed14Srobj 		return (1);
4262eeaed14Srobj 	}
4272eeaed14Srobj 
4282eeaed14Srobj 	/*
4292eeaed14Srobj 	 * We inherit our label from our parent, appending our label in the
4302eeaed14Srobj 	 * process.  This results in defaults labels of the form "FM 1 FAN 0"
4312eeaed14Srobj 	 * by default when given a hierarchy.
4322eeaed14Srobj 	 */
4332eeaed14Srobj 	if (edp->ed_label != NULL)
4342eeaed14Srobj 		(void) snprintf(label, sizeof (label), "%s ", edp->ed_label);
4352eeaed14Srobj 	else
4362eeaed14Srobj 		label[0] = '\0';
4372eeaed14Srobj 
4382eeaed14Srobj 	switch (edp->ed_entity) {
4392eeaed14Srobj 	case IPMI_ET_POWER_DOMAIN:
4402eeaed14Srobj 		labelname = "PM";
4412eeaed14Srobj 		break;
4422eeaed14Srobj 
4432eeaed14Srobj 	case IPMI_ET_PSU:
4442eeaed14Srobj 		labelname = "PSU";
4452eeaed14Srobj 		break;
4462eeaed14Srobj 
4472eeaed14Srobj 	case IPMI_ET_COOLING_DOMAIN:
4482eeaed14Srobj 		labelname = "FM";
4492eeaed14Srobj 		break;
4502eeaed14Srobj 
4512eeaed14Srobj 	case IPMI_ET_FAN:
4522eeaed14Srobj 		labelname = "FAN";
4532eeaed14Srobj 		break;
45458d4b16fSRobert Mustacchi 
45558d4b16fSRobert Mustacchi 	default:
45658d4b16fSRobert Mustacchi 		topo_mod_dprintf(mod, "unknown entity type, %u: cannot set "
45758d4b16fSRobert Mustacchi 		    "label name", edp->ed_entity);
45858d4b16fSRobert Mustacchi 		nvlist_free(fmri);
45958d4b16fSRobert Mustacchi 		return (1);
4602eeaed14Srobj 	}
4612eeaed14Srobj 
4622eeaed14Srobj 	len = strlen(label);
4632eeaed14Srobj 	(void) snprintf(label + len, sizeof (label) - len, "%s %d",
4642eeaed14Srobj 	    labelname, edp->ed_instance);
4652eeaed14Srobj 
4662eeaed14Srobj 	nvlist_free(fmri);
4672eeaed14Srobj 	edp->ed_instance++;
4682eeaed14Srobj 
4692eeaed14Srobj 	if (topo_node_label_set(tn, label, &err) != 0) {
4702eeaed14Srobj 		topo_mod_dprintf(mod, "failed to set label: %s\n",
4712eeaed14Srobj 		    topo_strerror(err));
4722eeaed14Srobj 		return (1);
4732eeaed14Srobj 	}
4742eeaed14Srobj 
4752eeaed14Srobj 	/*
4762eeaed14Srobj 	 * Store IPMI entity details as properties on the node
4772eeaed14Srobj 	 */
4782eeaed14Srobj 	pgi.tpi_name = TOPO_PGROUP_IPMI;
4792eeaed14Srobj 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
4802eeaed14Srobj 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
4812eeaed14Srobj 	pgi.tpi_version = TOPO_VERSION;
4822eeaed14Srobj 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
4832eeaed14Srobj 		if (err != ETOPO_PROP_DEFD) {
4842eeaed14Srobj 			topo_mod_dprintf(mod, "failed to create propgroup "
4852eeaed14Srobj 			    "%s: %s\n", TOPO_PGROUP_IPMI, topo_strerror(err));
4862eeaed14Srobj 			return (1);
4872eeaed14Srobj 		}
4882eeaed14Srobj 	}
4892eeaed14Srobj 
4908f022dd6SRob Johnston 	/*
4918f022dd6SRob Johnston 	 * Add properties to contain the IPMI entity id and instance.  This
4928f022dd6SRob Johnston 	 * will be used by the fac_prov_ipmi module to discover and enumerate
4938f022dd6SRob Johnston 	 * facility nodes for any associated sensors.
4948f022dd6SRob Johnston 	 */
4958f022dd6SRob Johnston 	if (topo_prop_set_uint32(tn, TOPO_PGROUP_IPMI, TOPO_PROP_IPMI_ENTITY_ID,
4968f022dd6SRob Johnston 	    TOPO_PROP_IMMUTABLE, ep->ie_type, &err) != 0 ||
4978f022dd6SRob Johnston 	    topo_prop_set_uint32(tn, TOPO_PGROUP_IPMI,
4988f022dd6SRob Johnston 	    TOPO_PROP_IPMI_ENTITY_INST, TOPO_PROP_IMMUTABLE, ep->ie_instance,
4998f022dd6SRob Johnston 	    &err) != 0) {
5008f022dd6SRob Johnston 		topo_mod_dprintf(mod, "failed to add ipmi properties (%s)",
5018f022dd6SRob Johnston 		    topo_strerror(err));
5028f022dd6SRob Johnston 		return (1);
5038f022dd6SRob Johnston 	}
5042eeaed14Srobj 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
5052eeaed14Srobj 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
5062eeaed14Srobj 		    topo_mod_errmsg(mod));
5072eeaed14Srobj 		return (1);
5082eeaed14Srobj 	}
5092eeaed14Srobj 
5102eeaed14Srobj 	/*
5118f022dd6SRob Johnston 	 * Invoke the tmo_enum callback from the fac_prov_ipmi module on this
5128f022dd6SRob Johnston 	 * node.  This will have the effect of registering a method on this node
5138f022dd6SRob Johnston 	 * for enumerating sensors.
5148f022dd6SRob Johnston 	 */
5158f022dd6SRob Johnston 	if (fmod == NULL && (fmod = topo_mod_load(mod, FAC_PROV_IPMI,
5168f022dd6SRob Johnston 	    TOPO_VERSION)) == NULL) {
5178f022dd6SRob Johnston 		topo_mod_dprintf(mod, "failed to load %s: %s",
5188f022dd6SRob Johnston 		    FAC_PROV_IPMI, topo_mod_errmsg(mod));
5198f022dd6SRob Johnston 		return (-1);
5208f022dd6SRob Johnston 	}
5218f022dd6SRob Johnston 	topo_mod_setspecific(mod, fmod);
5228f022dd6SRob Johnston 
5238f022dd6SRob Johnston 	if (topo_mod_enumerate(fmod, tn, FAC_PROV_IPMI, FAC_PROV_IPMI, 0, 0,
5248f022dd6SRob Johnston 	    NULL) != 0) {
5258f022dd6SRob Johnston 		topo_mod_dprintf(mod, "facility provider enum failed (%s)",
5268f022dd6SRob Johnston 		    topo_mod_errmsg(mod));
5278f022dd6SRob Johnston 		return (1);
5288f022dd6SRob Johnston 	}
5298f022dd6SRob Johnston 
5308f022dd6SRob Johnston 	/*
5312eeaed14Srobj 	 * If we are a child of a non-chassis node, and there isn't an explicit
5322eeaed14Srobj 	 * FRU locator record, then propagate the parent's FRU.  Otherwise, set
5332eeaed14Srobj 	 * the FRU to be the same as the resource.
5342eeaed14Srobj 	 */
5352eeaed14Srobj 	if (strcmp(topo_node_name(pnode), CHASSIS) == 0 ||
5368f022dd6SRob Johnston 	    edp->ed_frusdr != NULL) {
5372eeaed14Srobj 		if (topo_node_resource(tn, &fmri, &err) != 0) {
5382eeaed14Srobj 			topo_mod_dprintf(mod, "topo_node_resource() failed: %s",
5392eeaed14Srobj 			    topo_strerror(err));
5402eeaed14Srobj 			(void) topo_mod_seterrno(mod, err);
5412eeaed14Srobj 			return (1);
5422eeaed14Srobj 		}
5432eeaed14Srobj 	} else {
5442eeaed14Srobj 		if (topo_node_fru(pnode, &fmri, NULL, &err) != 0) {
5452eeaed14Srobj 			topo_mod_dprintf(mod, "topo_node_fru() failed: %s",
5462eeaed14Srobj 			    topo_strerror(err));
5472eeaed14Srobj 			(void) topo_mod_seterrno(mod, err);
5482eeaed14Srobj 			return (1);
5492eeaed14Srobj 		}
5502eeaed14Srobj 	}
5512eeaed14Srobj 
5522eeaed14Srobj 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
5532eeaed14Srobj 		nvlist_free(fmri);
5542eeaed14Srobj 		topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s",
5552eeaed14Srobj 		    topo_strerror(err));
5562eeaed14Srobj 		(void) topo_mod_seterrno(mod, err);
5572eeaed14Srobj 		return (1);
5582eeaed14Srobj 	}
5592eeaed14Srobj 
5602eeaed14Srobj 	topo_node_setspecific(tn, ep);
5612eeaed14Srobj 
5622eeaed14Srobj 	nvlist_free(fmri);
5632eeaed14Srobj 
5642eeaed14Srobj 	/*
5652eeaed14Srobj 	 * Iterate over children, once for recursive domains and once for
5662eeaed14Srobj 	 * psu/fans.
5672eeaed14Srobj 	 */
5682eeaed14Srobj 	if (ep->ie_children != 0 &&
5692eeaed14Srobj 	    (ep->ie_type == IPMI_ET_POWER_DOMAIN ||
5702eeaed14Srobj 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN)) {
5712eeaed14Srobj 		cdata.ed_mod = edp->ed_mod;
5722eeaed14Srobj 		cdata.ed_pnode = tn;
5732eeaed14Srobj 		cdata.ed_instance = 0;
5742eeaed14Srobj 		cdata.ed_name = edp->ed_name;
5752eeaed14Srobj 		cdata.ed_entity = edp->ed_entity;
5762eeaed14Srobj 		cdata.ed_label = label;
5772eeaed14Srobj 
5782eeaed14Srobj 		if (ipmi_entity_iter_children(ihp, ep,
5792eeaed14Srobj 		    ipmi_check_entity, &cdata) != 0)
5802eeaed14Srobj 			return (1);
5812eeaed14Srobj 
5822eeaed14Srobj 		switch (cdata.ed_entity) {
5832eeaed14Srobj 		case IPMI_ET_POWER_DOMAIN:
5842eeaed14Srobj 			cdata.ed_entity = IPMI_ET_PSU;
5852eeaed14Srobj 			cdata.ed_name = PSU;
5862eeaed14Srobj 			break;
5872eeaed14Srobj 
5882eeaed14Srobj 		case IPMI_ET_COOLING_DOMAIN:
5892eeaed14Srobj 			cdata.ed_entity = IPMI_ET_FAN;
5902eeaed14Srobj 			cdata.ed_name = FAN;
5912eeaed14Srobj 			break;
5922eeaed14Srobj 		}
5932eeaed14Srobj 
5942eeaed14Srobj 		if (ipmi_entity_iter_children(ihp, ep,
5952eeaed14Srobj 		    ipmi_check_entity, &cdata) != 0)
5962eeaed14Srobj 			return (1);
5972eeaed14Srobj 	}
5982eeaed14Srobj 
5992eeaed14Srobj 	return (0);
6002eeaed14Srobj }
6012eeaed14Srobj 
6022eeaed14Srobj /*
6032eeaed14Srobj  * libtopo enumeration point.  This simply iterates over entities looking for
6042eeaed14Srobj  * the appropriate type.
6052eeaed14Srobj  */
6062eeaed14Srobj /*ARGSUSED*/
6072eeaed14Srobj static int
ipmi_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * unused)6082eeaed14Srobj ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
6092eeaed14Srobj     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
6102eeaed14Srobj {
6112eeaed14Srobj 	ipmi_handle_t *ihp;
6122eeaed14Srobj 	ipmi_enum_data_t data;
6132eeaed14Srobj 	int ret;
6142eeaed14Srobj 
6152eeaed14Srobj 	/*
6162eeaed14Srobj 	 * If the node being passed in ISN'T the chassis node, then we're being
6172eeaed14Srobj 	 * asked to post-process a statically defined node.
6182eeaed14Srobj 	 */
6192eeaed14Srobj 	if (strcmp(topo_node_name(rnode), CHASSIS) != 0) {
6202eeaed14Srobj 		if (ipmi_post_process(mod, rnode) != 0) {
621*6597d6fcSRobert Mustacchi 			topo_mod_dprintf(mod, "post processing of node %s=%"
622*6597d6fcSRobert Mustacchi 			    PRIu64 " failed!", topo_node_name(rnode),
6232eeaed14Srobj 			    topo_node_instance(rnode));
6242eeaed14Srobj 			return (-1);
6252eeaed14Srobj 		}
6262eeaed14Srobj 		return (0);
6272eeaed14Srobj 	}
6282eeaed14Srobj 
6292eeaed14Srobj 	if (strcmp(name, POWERMODULE) == 0) {
6302eeaed14Srobj 		data.ed_entity = IPMI_ET_POWER_DOMAIN;
6312eeaed14Srobj 	} else if (strcmp(name, PSU) == 0) {
6322eeaed14Srobj 		data.ed_entity = IPMI_ET_PSU;
6332eeaed14Srobj 	} else if (strcmp(name, FANMODULE) == 0) {
6342eeaed14Srobj 		data.ed_entity = IPMI_ET_COOLING_DOMAIN;
6352eeaed14Srobj 	} else if (strcmp(name, FAN) == 0) {
6362eeaed14Srobj 		data.ed_entity = IPMI_ET_FAN;
6372eeaed14Srobj 	} else {
6382eeaed14Srobj 		topo_mod_dprintf(mod, "unknown enumeration type '%s'",
6392eeaed14Srobj 		    name);
6402eeaed14Srobj 		return (-1);
6412eeaed14Srobj 	}
6422eeaed14Srobj 
6430b1b4412SEric Schrock 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
6440b1b4412SEric Schrock 		return (0);
6450b1b4412SEric Schrock 
6462eeaed14Srobj 	data.ed_mod = mod;
6472eeaed14Srobj 	data.ed_pnode = rnode;
6482eeaed14Srobj 	data.ed_name = name;
6492eeaed14Srobj 	data.ed_instance = 0;
6502eeaed14Srobj 	data.ed_label = NULL;
6512eeaed14Srobj 
6522eeaed14Srobj 	if ((ret = ipmi_entity_iter(ihp, ipmi_check_entity, &data)) != 0) {
6532eeaed14Srobj 		/*
6542eeaed14Srobj 		 * We don't return failure if IPMI enumeration fails.  This may
6552eeaed14Srobj 		 * be due to the SP being unavailable or an otherwise transient
6562eeaed14Srobj 		 * event.
6572eeaed14Srobj 		 */
6580b1b4412SEric Schrock 		if (ret < 0) {
6592eeaed14Srobj 			topo_mod_dprintf(mod,
6602eeaed14Srobj 			    "failed to enumerate entities: %s",
6612eeaed14Srobj 			    ipmi_errmsg(ihp));
6620b1b4412SEric Schrock 		} else {
6630b1b4412SEric Schrock 			topo_mod_ipmi_rele(mod);
6642eeaed14Srobj 			return (-1);
6652eeaed14Srobj 		}
6660b1b4412SEric Schrock 	}
6672eeaed14Srobj 
6680b1b4412SEric Schrock 	topo_mod_ipmi_rele(mod);
6692eeaed14Srobj 	return (0);
6702eeaed14Srobj }
6712eeaed14Srobj 
6722eeaed14Srobj static int
ipmi_post_process(topo_mod_t * mod,tnode_t * tn)6732eeaed14Srobj ipmi_post_process(topo_mod_t *mod, tnode_t *tn)
6742eeaed14Srobj {
6752eeaed14Srobj 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
6762eeaed14Srobj 		topo_mod_dprintf(mod, "ipmi_post_process() failed: %s",
6772eeaed14Srobj 		    topo_mod_errmsg(mod));
6782eeaed14Srobj 		return (1);
6792eeaed14Srobj 	}
6802eeaed14Srobj 	return (0);
6812eeaed14Srobj }
6822eeaed14Srobj 
6832eeaed14Srobj /*ARGSUSED*/
6842eeaed14Srobj int
_topo_init(topo_mod_t * mod,topo_version_t version)6852eeaed14Srobj _topo_init(topo_mod_t *mod, topo_version_t version)
6862eeaed14Srobj {
6872eeaed14Srobj 	if (getenv("TOPOIPMIDEBUG") != NULL)
6882eeaed14Srobj 		topo_mod_setdebug(mod);
6892eeaed14Srobj 
6902eeaed14Srobj 	if (topo_mod_register(mod, &ipmi_info, TOPO_VERSION) != 0) {
6918f022dd6SRob Johnston 		topo_mod_dprintf(mod, "module registration failed: %s\n",
6928f022dd6SRob Johnston 		    topo_mod_errmsg(mod));
6932eeaed14Srobj 		return (-1); /* mod errno already set */
6942eeaed14Srobj 	}
6952eeaed14Srobj 
6962eeaed14Srobj 	topo_mod_dprintf(mod, "IPMI enumerator initialized\n");
6972eeaed14Srobj 	return (0);
6982eeaed14Srobj }
6992eeaed14Srobj 
7002eeaed14Srobj void
_topo_fini(topo_mod_t * mod)7012eeaed14Srobj _topo_fini(topo_mod_t *mod)
7022eeaed14Srobj {
7038f022dd6SRob Johnston 	/*
7048f022dd6SRob Johnston 	 * This is the logical, and probably only safe spot where we could
7058f022dd6SRob Johnston 	 * unload fac_prov_ipmi.  But unfortunately, calling topo_mod_unload()
7068f022dd6SRob Johnston 	 * in the context of a module's _topo_fini entry point would result
7078f022dd6SRob Johnston 	 * in recursively grabbing the modhash lock and we'd deadlock.
7088f022dd6SRob Johnston 	 *
7098f022dd6SRob Johnston 	 * Unfortunately, libtopo doesn't currently have a mechanism for
7108f022dd6SRob Johnston 	 * expressing and handling intermodule dependencies, so we're left
7118f022dd6SRob Johnston 	 * with this situation where once a module loads another module,
7128f022dd6SRob Johnston 	 * it's going to be with us until we teardown the process.
7138f022dd6SRob Johnston 	 */
7142eeaed14Srobj 	topo_mod_unregister(mod);
7152eeaed14Srobj }
716