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