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