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