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