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