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