Home | History | Annotate | Download | only in ipmi
      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 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #include <assert.h>
     28 #include <fm/libtopo.h>
     29 #include <fm/topo_mod.h>
     30 #include <sys/fm/protocol.h>
     31 #include <string.h>
     32 
     33 #define	TOPO_PGROUP_IPMI 		"ipmi"
     34 #define	TOPO_PROP_IPMI_ENTITY_REF	"entity_ref"
     35 #define	TOPO_PROP_IPMI_ENTITY_PRESENT	"entity_present"
     36 
     37 typedef struct ipmi_enum_data {
     38 	topo_mod_t	*ed_mod;
     39 	tnode_t		*ed_pnode;
     40 	const char	*ed_name;
     41 	char		*ed_label;
     42 	uint8_t		ed_entity;
     43 	topo_instance_t	ed_instance;
     44 	boolean_t	ed_hasfru;
     45 } ipmi_enum_data_t;
     46 
     47 static int ipmi_present(topo_mod_t *, tnode_t *, topo_version_t, nvlist_t *,
     48     nvlist_t **);
     49 static int ipmi_enum(topo_mod_t *, tnode_t *, const char *,
     50     topo_instance_t, topo_instance_t, void *, void *);
     51 static int ipmi_post_process(topo_mod_t *, tnode_t *);
     52 
     53 extern int ipmi_fru_label(topo_mod_t *mod, tnode_t *node,
     54     topo_version_t vers, nvlist_t *in, nvlist_t **out);
     55 
     56 extern int ipmi_fru_fmri(topo_mod_t *mod, tnode_t *node,
     57     topo_version_t vers, nvlist_t *in, nvlist_t **out);
     58 
     59 static const topo_method_t ipmi_methods[] = {
     60 	{ TOPO_METH_PRESENT, TOPO_METH_PRESENT_DESC,
     61 	    TOPO_METH_PRESENT_VERSION0, TOPO_STABILITY_INTERNAL, ipmi_present },
     62 	{ "ipmi_fru_label", "Property method", 0,
     63 	    TOPO_STABILITY_INTERNAL, ipmi_fru_label},
     64 	{ "ipmi_fru_fmri", "Property method", 0,
     65 	    TOPO_STABILITY_INTERNAL, ipmi_fru_fmri},
     66 	{ TOPO_METH_SENSOR_FAILURE, TOPO_METH_SENSOR_FAILURE_DESC,
     67 	    TOPO_METH_SENSOR_FAILURE_VERSION, TOPO_STABILITY_INTERNAL,
     68 	    topo_method_sensor_failure },
     69 	{ NULL }
     70 };
     71 
     72 const topo_modops_t ipmi_ops = { ipmi_enum, NULL };
     73 
     74 const topo_modinfo_t ipmi_info =
     75 	{ "ipmi", FM_FMRI_SCHEME_HC, TOPO_VERSION, &ipmi_ops };
     76 
     77 /*
     78  * Determine if the entity is present.
     79  */
     80 /*ARGSUSED*/
     81 static int
     82 ipmi_present(topo_mod_t *mod, tnode_t *tn, topo_version_t version,
     83     nvlist_t *in, nvlist_t **out)
     84 {
     85 	ipmi_handle_t *ihp;
     86 	ipmi_entity_t *ep;
     87 	boolean_t present;
     88 	nvlist_t *nvl;
     89 	int err;
     90 	char *name;
     91 	ipmi_sdr_t *sdrp;
     92 
     93 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
     94 		return (topo_mod_seterrno(mod, ETOPO_METHOD_UNKNOWN));
     95 
     96 	ep = topo_node_getspecific(tn);
     97 	if (ep == NULL) {
     98 		if (topo_prop_get_string(tn, TOPO_PGROUP_IPMI,
     99 		    TOPO_PROP_IPMI_ENTITY_PRESENT, &name, &err) == 0) {
    100 			/*
    101 			 * Some broken IPMI implementations don't export correct
    102 			 * entities, so referring to an entity isn't sufficient.
    103 			 * For these platforms, we allow the XML to specify a
    104 			 * single SDR record that represents the current present
    105 			 * state.
    106 			 */
    107 			if ((sdrp = ipmi_sdr_lookup(ihp, name)) == NULL ||
    108 			    ipmi_entity_present_sdr(ihp, sdrp, &present) != 0) {
    109 				topo_mod_dprintf(mod,
    110 				    "Failed to get present state of %s (%s)\n",
    111 				    name, ipmi_errmsg(ihp));
    112 				topo_mod_strfree(mod, name);
    113 				topo_mod_ipmi_rele(mod);
    114 				return (-1);
    115 			}
    116 
    117 			topo_mod_dprintf(mod,
    118 			    "ipmi_entity_present_sdr(%s) = %d\n", name,
    119 			    present);
    120 			topo_mod_strfree(mod, name);
    121 		} else {
    122 			if (topo_prop_get_string(tn, TOPO_PGROUP_IPMI,
    123 			    TOPO_PROP_IPMI_ENTITY_REF, &name, &err) != 0) {
    124 				/*
    125 				 * Not all nodes have an entity_ref attribute.
    126 				 * For these cases, return ENOTSUP so that we
    127 				 * fall back to the default hc presence
    128 				 * detection.
    129 				 */
    130 				topo_mod_ipmi_rele(mod);
    131 				return (topo_mod_seterrno(mod,
    132 				    ETOPO_METHOD_NOTSUP));
    133 			}
    134 
    135 			if ((ep = ipmi_entity_lookup_sdr(ihp, name)) == NULL) {
    136 				topo_mod_strfree(mod, name);
    137 				topo_mod_dprintf(mod,
    138 				    "Failed to lookup ipmi entity "
    139 				    "%s (%s)\n", name, ipmi_errmsg(ihp));
    140 				topo_mod_ipmi_rele(mod);
    141 				return (-1);
    142 			}
    143 
    144 			topo_mod_strfree(mod, name);
    145 			topo_node_setspecific(tn, ep);
    146 		}
    147 	}
    148 
    149 	if (ep != NULL) {
    150 		if (ipmi_entity_present(ihp, ep, &present) != 0) {
    151 			topo_mod_dprintf(mod,
    152 			    "ipmi_entity_present() failed: %s",
    153 			    ipmi_errmsg(ihp));
    154 			topo_mod_ipmi_rele(mod);
    155 			return (-1);
    156 		}
    157 
    158 		topo_mod_dprintf(mod,
    159 		    "ipmi_entity_present() = %d\n", present);
    160 	}
    161 
    162 	topo_mod_ipmi_rele(mod);
    163 
    164 	if (topo_mod_nvalloc(mod, &nvl, NV_UNIQUE_NAME) != 0)
    165 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    166 
    167 	if (nvlist_add_uint32(nvl, TOPO_METH_PRESENT_RET, present) != 0) {
    168 		nvlist_free(nvl);
    169 		return (topo_mod_seterrno(mod, EMOD_FMRI_NVL));
    170 	}
    171 
    172 	*out = nvl;
    173 
    174 	return (0);
    175 }
    176 
    177 /*
    178  * This determines if the entity has a FRU locator record set, in which case we
    179  * treat this as a FRU, even if it's part of an association.
    180  */
    181 /*ARGSUSED*/
    182 static int
    183 ipmi_check_sdr(ipmi_handle_t *ihp, ipmi_entity_t *ep, const char *name,
    184     ipmi_sdr_t *sdrp, void *data)
    185 {
    186 	ipmi_enum_data_t *edp = data;
    187 
    188 	if (sdrp->is_type == IPMI_SDR_TYPE_FRU_LOCATOR)
    189 		edp->ed_hasfru = B_TRUE;
    190 
    191 	return (0);
    192 }
    193 
    194 /*
    195  * Main entity enumerator.  If we find a matching entity type, then instantiate
    196  * a topo node.
    197  */
    198 static int
    199 ipmi_check_entity(ipmi_handle_t *ihp, ipmi_entity_t *ep, void *data)
    200 {
    201 	ipmi_enum_data_t *edp = data;
    202 	ipmi_enum_data_t cdata;
    203 	tnode_t *pnode = edp->ed_pnode;
    204 	topo_mod_t *mod = edp->ed_mod;
    205 	nvlist_t *auth, *fmri;
    206 	tnode_t *tn;
    207 	topo_pgroup_info_t pgi;
    208 	int err;
    209 	const char *labelname;
    210 	char label[64];
    211 	size_t len;
    212 
    213 	if (ep->ie_type != edp->ed_entity)
    214 		return (0);
    215 
    216 	/*
    217 	 * The purpose of power and cooling domains is to group psus and fans
    218 	 * together.  Unfortunately, some broken IPMI implementations declare
    219 	 * domains that don't contain other elements.  Since the end goal is to
    220 	 * only enumerate psus and fans, we'll just ignore such elements.
    221 	 */
    222 	if ((ep->ie_type == IPMI_ET_POWER_DOMAIN ||
    223 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN) &&
    224 	    ep->ie_children == 0)
    225 		return (0);
    226 
    227 	if ((auth = topo_mod_auth(mod, pnode)) == NULL) {
    228 		topo_mod_dprintf(mod, "topo_mod_auth() failed: %s",
    229 		    topo_mod_errmsg(mod));
    230 		return (1);
    231 	}
    232 
    233 	if ((fmri = topo_mod_hcfmri(mod, pnode, FM_HC_SCHEME_VERSION,
    234 	    edp->ed_name, edp->ed_instance, NULL, auth, NULL, NULL,
    235 	    NULL)) == NULL) {
    236 		nvlist_free(auth);
    237 		topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s",
    238 		    topo_mod_errmsg(mod));
    239 		return (1);
    240 	}
    241 
    242 	nvlist_free(auth);
    243 
    244 	if ((tn = topo_node_bind(mod, pnode, edp->ed_name,
    245 	    edp->ed_instance, fmri)) == NULL) {
    246 		nvlist_free(fmri);
    247 		topo_mod_dprintf(mod, "topo_node_bind() failed: %s",
    248 		    topo_mod_errmsg(mod));
    249 		return (1);
    250 	}
    251 
    252 	/*
    253 	 * We inherit our label from our parent, appending our label in the
    254 	 * process.  This results in defaults labels of the form "FM 1 FAN 0"
    255 	 * by default when given a hierarchy.
    256 	 */
    257 	if (edp->ed_label != NULL)
    258 		(void) snprintf(label, sizeof (label), "%s ", edp->ed_label);
    259 	else
    260 		label[0] = '\0';
    261 
    262 	switch (edp->ed_entity) {
    263 	case IPMI_ET_POWER_DOMAIN:
    264 		labelname = "PM";
    265 		break;
    266 
    267 	case IPMI_ET_PSU:
    268 		labelname = "PSU";
    269 		break;
    270 
    271 	case IPMI_ET_COOLING_DOMAIN:
    272 		labelname = "FM";
    273 		break;
    274 
    275 	case IPMI_ET_FAN:
    276 		labelname = "FAN";
    277 		break;
    278 	}
    279 
    280 	len = strlen(label);
    281 	(void) snprintf(label + len, sizeof (label) - len, "%s %d",
    282 	    labelname, edp->ed_instance);
    283 
    284 	nvlist_free(fmri);
    285 	edp->ed_instance++;
    286 
    287 	if (topo_node_label_set(tn, label, &err) != 0) {
    288 		topo_mod_dprintf(mod, "failed to set label: %s\n",
    289 		    topo_strerror(err));
    290 		return (1);
    291 	}
    292 
    293 	/*
    294 	 * Store IPMI entity details as properties on the node
    295 	 */
    296 	pgi.tpi_name = TOPO_PGROUP_IPMI;
    297 	pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
    298 	pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
    299 	pgi.tpi_version = TOPO_VERSION;
    300 	if (topo_pgroup_create(tn, &pgi, &err) != 0) {
    301 		if (err != ETOPO_PROP_DEFD) {
    302 			topo_mod_dprintf(mod, "failed to create propgroup "
    303 			    "%s: %s\n", TOPO_PGROUP_IPMI, topo_strerror(err));
    304 			return (1);
    305 		}
    306 	}
    307 
    308 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
    309 		topo_mod_dprintf(mod, "topo_method_register() failed: %s",
    310 		    topo_mod_errmsg(mod));
    311 		return (1);
    312 	}
    313 
    314 	/*
    315 	 * If we are a child of a non-chassis node, and there isn't an explicit
    316 	 * FRU locator record, then propagate the parent's FRU.  Otherwise, set
    317 	 * the FRU to be the same as the resource.
    318 	 */
    319 	edp->ed_hasfru = B_FALSE;
    320 	(void) ipmi_entity_iter_sdr(ihp, ep, ipmi_check_sdr, edp);
    321 
    322 	if (strcmp(topo_node_name(pnode), CHASSIS) == 0 ||
    323 	    edp->ed_hasfru) {
    324 		if (topo_node_resource(tn, &fmri, &err) != 0) {
    325 			topo_mod_dprintf(mod, "topo_node_resource() failed: %s",
    326 			    topo_strerror(err));
    327 			(void) topo_mod_seterrno(mod, err);
    328 			return (1);
    329 		}
    330 	} else {
    331 		if (topo_node_fru(pnode, &fmri, NULL, &err) != 0) {
    332 			topo_mod_dprintf(mod, "topo_node_fru() failed: %s",
    333 			    topo_strerror(err));
    334 			(void) topo_mod_seterrno(mod, err);
    335 			return (1);
    336 		}
    337 	}
    338 
    339 	if (topo_node_fru_set(tn, fmri, 0, &err) != 0) {
    340 		nvlist_free(fmri);
    341 		topo_mod_dprintf(mod, "topo_node_fru_set() failed: %s",
    342 		    topo_strerror(err));
    343 		(void) topo_mod_seterrno(mod, err);
    344 		return (1);
    345 	}
    346 
    347 	topo_node_setspecific(tn, ep);
    348 
    349 	nvlist_free(fmri);
    350 
    351 	/*
    352 	 * Iterate over children, once for recursive domains and once for
    353 	 * psu/fans.
    354 	 */
    355 	if (ep->ie_children != 0 &&
    356 	    (ep->ie_type == IPMI_ET_POWER_DOMAIN ||
    357 	    ep->ie_type == IPMI_ET_COOLING_DOMAIN)) {
    358 		cdata.ed_mod = edp->ed_mod;
    359 		cdata.ed_pnode = tn;
    360 		cdata.ed_instance = 0;
    361 		cdata.ed_name = edp->ed_name;
    362 		cdata.ed_entity = edp->ed_entity;
    363 		cdata.ed_label = label;
    364 
    365 		if (ipmi_entity_iter_children(ihp, ep,
    366 		    ipmi_check_entity, &cdata) != 0)
    367 			return (1);
    368 
    369 		switch (cdata.ed_entity) {
    370 		case IPMI_ET_POWER_DOMAIN:
    371 			cdata.ed_entity = IPMI_ET_PSU;
    372 			cdata.ed_name = PSU;
    373 			break;
    374 
    375 		case IPMI_ET_COOLING_DOMAIN:
    376 			cdata.ed_entity = IPMI_ET_FAN;
    377 			cdata.ed_name = FAN;
    378 			break;
    379 		}
    380 
    381 		if (ipmi_entity_iter_children(ihp, ep,
    382 		    ipmi_check_entity, &cdata) != 0)
    383 			return (1);
    384 	}
    385 
    386 	return (0);
    387 }
    388 
    389 /*
    390  * libtopo enumeration point.  This simply iterates over entities looking for
    391  * the appropriate type.
    392  */
    393 /*ARGSUSED*/
    394 static int
    395 ipmi_enum(topo_mod_t *mod, tnode_t *rnode, const char *name,
    396     topo_instance_t min, topo_instance_t max, void *arg, void *unused)
    397 {
    398 	ipmi_handle_t *ihp;
    399 	ipmi_enum_data_t data;
    400 	int ret;
    401 
    402 	/*
    403 	 * If the node being passed in ISN'T the chassis node, then we're being
    404 	 * asked to post-process a statically defined node.
    405 	 */
    406 	if (strcmp(topo_node_name(rnode), CHASSIS) != 0) {
    407 		if (ipmi_post_process(mod, rnode) != 0) {
    408 			topo_mod_dprintf(mod, "post processing of node %s=%d "
    409 			    "failed!", topo_node_name(rnode),
    410 			    topo_node_instance(rnode));
    411 			return (-1);
    412 		}
    413 		return (0);
    414 	}
    415 
    416 	if (strcmp(name, POWERMODULE) == 0) {
    417 		data.ed_entity = IPMI_ET_POWER_DOMAIN;
    418 	} else if (strcmp(name, PSU) == 0) {
    419 		data.ed_entity = IPMI_ET_PSU;
    420 	} else if (strcmp(name, FANMODULE) == 0) {
    421 		data.ed_entity = IPMI_ET_COOLING_DOMAIN;
    422 	} else if (strcmp(name, FAN) == 0) {
    423 		data.ed_entity = IPMI_ET_FAN;
    424 	} else {
    425 		topo_mod_dprintf(mod, "unknown enumeration type '%s'",
    426 		    name);
    427 		return (-1);
    428 	}
    429 
    430 	if ((ihp = topo_mod_ipmi_hold(mod)) == NULL)
    431 		return (0);
    432 
    433 	data.ed_mod = mod;
    434 	data.ed_pnode = rnode;
    435 	data.ed_name = name;
    436 	data.ed_instance = 0;
    437 	data.ed_label = NULL;
    438 
    439 	if ((ret = ipmi_entity_iter(ihp, ipmi_check_entity, &data)) != 0) {
    440 		/*
    441 		 * We don't return failure if IPMI enumeration fails.  This may
    442 		 * be due to the SP being unavailable or an otherwise transient
    443 		 * event.
    444 		 */
    445 		if (ret < 0) {
    446 			topo_mod_dprintf(mod,
    447 			    "failed to enumerate entities: %s",
    448 			    ipmi_errmsg(ihp));
    449 		} else {
    450 			topo_mod_ipmi_rele(mod);
    451 			return (-1);
    452 		}
    453 	}
    454 
    455 	topo_mod_ipmi_rele(mod);
    456 	return (0);
    457 }
    458 
    459 static int
    460 ipmi_post_process(topo_mod_t *mod, tnode_t *tn)
    461 {
    462 	if (topo_method_register(mod, tn, ipmi_methods) != 0) {
    463 		topo_mod_dprintf(mod, "ipmi_post_process() failed: %s",
    464 		    topo_mod_errmsg(mod));
    465 		return (1);
    466 	}
    467 	return (0);
    468 }
    469 
    470 /*ARGSUSED*/
    471 int
    472 _topo_init(topo_mod_t *mod, topo_version_t version)
    473 {
    474 	if (getenv("TOPOIPMIDEBUG") != NULL)
    475 		topo_mod_setdebug(mod);
    476 
    477 	if (topo_mod_register(mod, &ipmi_info, TOPO_VERSION) != 0) {
    478 		topo_mod_dprintf(mod, "%s registration failed: %s\n",
    479 		    DISK, topo_mod_errmsg(mod));
    480 		return (-1); /* mod errno already set */
    481 	}
    482 
    483 	topo_mod_dprintf(mod, "IPMI enumerator initialized\n");
    484 	return (0);
    485 }
    486 
    487 void
    488 _topo_fini(topo_mod_t *mod)
    489 {
    490 	topo_mod_unregister(mod);
    491 }
    492