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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Subroutines used by the i86pc Generic Topology Enumerator 29 */ 30 31 #include <sys/types.h> 32 #include <strings.h> 33 #include <deflt.h> 34 #include <fcntl.h> 35 #include <unistd.h> 36 #include <fm/topo_mod.h> 37 #include <fm/topo_hc.h> 38 #include <sys/devfm.h> 39 #include <sys/pci.h> 40 #include <sys/systeminfo.h> 41 #include <sys/fm/protocol.h> 42 #include <sys/utsname.h> 43 #include <sys/smbios.h> 44 #include <sys/smbios_impl.h> 45 #include <x86pi_impl.h> 46 47 48 static const topo_pgroup_info_t sys_pgroup = { 49 TOPO_PGROUP_SYSTEM, 50 TOPO_STABILITY_PRIVATE, 51 TOPO_STABILITY_PRIVATE, 52 1 53 }; 54 55 static const topo_pgroup_info_t auth_pgroup = { 56 FM_FMRI_AUTHORITY, 57 TOPO_STABILITY_PRIVATE, 58 TOPO_STABILITY_PRIVATE, 59 1 60 }; 61 62 63 /* 64 * Free hcfmri strings. 65 */ 66 void 67 x86pi_hcfmri_info_fini(topo_mod_t *mod, x86pi_hcfmri_t *hc) 68 { 69 if (hc->hc_name != NULL) 70 topo_mod_strfree(mod, (char *)hc->hc_name); 71 if (hc->manufacturer != NULL) 72 topo_mod_strfree(mod, (char *)hc->manufacturer); 73 if (hc->product != NULL) 74 topo_mod_strfree(mod, (char *)hc->product); 75 if (hc->version != NULL) 76 topo_mod_strfree(mod, (char *)hc->version); 77 if (hc->serial_number != NULL) 78 topo_mod_strfree(mod, (char *)hc->serial_number); 79 if (hc->asset_tag != NULL) 80 topo_mod_strfree(mod, (char *)hc->asset_tag); 81 if (hc->location != NULL) 82 topo_mod_strfree(mod, (char *)hc->location); 83 if (hc->part_number != NULL) 84 topo_mod_strfree(mod, (char *)hc->part_number); 85 } 86 87 88 /* 89 * Get the server hostname (the ID as far as the topo authority is 90 * concerned) from sysinfo and return a copy to the caller. 91 * 92 * The string must be freed with topo_mod_strfree() 93 */ 94 char * 95 x86pi_get_serverid(topo_mod_t *mod) 96 { 97 int result; 98 char hostname[MAXNAMELEN]; 99 100 topo_mod_dprintf(mod, "x86pi_get_serverid\n"); 101 102 result = sysinfo(SI_HOSTNAME, hostname, sizeof (hostname)); 103 /* Everything is freed up and it's time to return the platform-id */ 104 if (result == -1) { 105 return (NULL); 106 } 107 topo_mod_dprintf(mod, "x86pi_get_serverid: hostname = %s\n", hostname); 108 109 return (topo_mod_strdup(mod, hostname)); 110 } 111 112 113 /* 114 * Get copy of SMBIOS. 115 */ 116 smbios_hdl_t * 117 x86pi_smb_open(topo_mod_t *mod) 118 { 119 smbios_hdl_t *smb_hdl; 120 char *f = "x86pi_smb_open"; 121 122 topo_mod_dprintf(mod, "%s\n", f); 123 124 smb_hdl = topo_mod_smbios(mod); 125 if (smb_hdl == NULL) { 126 topo_mod_dprintf(mod, "%s: failed to load SMBIOS\n", f); 127 return (NULL); 128 } 129 130 return (smb_hdl); 131 } 132 133 134 /* 135 * Go through the smbios structures looking for a type. Fill in 136 * the structure count as well as the id(s) of the struct types. 137 */ 138 void 139 x86pi_smb_strcnt(smbios_hdl_t *shp, smbs_cnt_t *stype) 140 { 141 const smb_struct_t *sp = shp->sh_structs; 142 int nstructs = shp->sh_nstructs; 143 int i, cnt; 144 145 for (i = 0, cnt = 0; i < nstructs; i++, sp++) { 146 if (sp->smbst_hdr->smbh_type == stype->type) { 147 stype->ids[cnt].node = NULL; 148 stype->ids[cnt].id = sp->smbst_hdr->smbh_hdl; 149 cnt++; 150 } 151 } 152 153 stype->count = cnt; 154 } 155 156 157 /* 158 * Calculate the authority information for a node. Inherit the data if 159 * possible, but always create an appropriate property group. 160 */ 161 int 162 x86pi_set_auth(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent, 163 tnode_t *t_node) 164 { 165 int result; 166 int err; 167 int is_chassis = 0; 168 int chassis_instance = 0; 169 nvlist_t *auth; 170 char *val = NULL; 171 char *prod = NULL; 172 char *psn = NULL; 173 char *csn = NULL; 174 char *server = NULL; 175 char *f = "x86pi_set_auth"; 176 177 if (mod == NULL || t_parent == NULL || t_node == NULL) { 178 return (-1); 179 } 180 181 result = topo_pgroup_create(t_node, &auth_pgroup, &err); 182 if (result != 0 && err != ETOPO_PROP_DEFD) { 183 /* 184 * We failed to create the property group and it was not 185 * already defined. Set the err code and return failure. 186 */ 187 (void) topo_mod_seterrno(mod, err); 188 return (-1); 189 } 190 191 /* Get the authority information already available from the parent */ 192 auth = topo_mod_auth(mod, t_parent); 193 194 /* Determnine if this is a chassis node and set it's instance */ 195 if ((strlen(hcfmri->hc_name) == strlen(CHASSIS)) && 196 strncmp(hcfmri->hc_name, CHASSIS, strlen(CHASSIS)) == 0) { 197 is_chassis = 1; 198 chassis_instance = hcfmri->instance; 199 } 200 201 /* 202 * Set the authority data, inheriting it if possible, but creating it 203 * if necessary. 204 */ 205 206 /* product-id */ 207 result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY, 208 FM_FMRI_AUTH_PRODUCT, &err); 209 if (result != 0 && err != ETOPO_PROP_DEFD) { 210 result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT, 211 &prod); 212 if (result != 0 || prod == NULL) { 213 /* 214 * No product information in the parent node or auth 215 * list. Use the product information in the hcfrmi 216 * struct. 217 */ 218 prod = (char *)hcfmri->product; 219 if (prod == NULL) { 220 topo_mod_dprintf(mod, "%s: product name not " 221 "found for %s node\n", f, hcfmri->hc_name); 222 } 223 } 224 225 /* 226 * We continue even if the product information is not available 227 * to enumerate as much as possible. 228 */ 229 if (prod != NULL) { 230 result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY, 231 FM_FMRI_AUTH_PRODUCT, TOPO_PROP_IMMUTABLE, prod, 232 &err); 233 if (result != 0) { 234 /* Preserve the error and continue */ 235 (void) topo_mod_seterrno(mod, err); 236 topo_mod_dprintf(mod, "%s: failed to set " 237 "property %s (%d) : %s\n", f, 238 FM_FMRI_AUTH_PRODUCT, err, 239 topo_strerror(err)); 240 } 241 } 242 } 243 244 /* product-sn */ 245 result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY, 246 FM_FMRI_AUTH_PRODUCT_SN, &err); 247 if (result != 0 && err != ETOPO_PROP_DEFD) { 248 result = nvlist_lookup_string(auth, FM_FMRI_AUTH_PRODUCT_SN, 249 &psn); 250 if (result != 0 || psn == NULL) { 251 /* 252 * No product-sn information in the parent node or auth 253 * list. 254 */ 255 topo_mod_dprintf(mod, "%s: psn not found\n", f); 256 } else { 257 /* 258 * We continue even if the product-sn information is 259 * not available to enumerate as much as possible. 260 */ 261 result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY, 262 FM_FMRI_AUTH_PRODUCT_SN, TOPO_PROP_IMMUTABLE, psn, 263 &err); 264 if (result != 0) { 265 /* Preserve the error and continue */ 266 (void) topo_mod_seterrno(mod, err); 267 topo_mod_dprintf(mod, "%s: failed to " 268 "set property %s (%d) : %s\n", f, 269 FM_FMRI_AUTH_PRODUCT_SN, err, 270 topo_strerror(err)); 271 } 272 } 273 } 274 275 /* chassis-id */ 276 if (is_chassis == 0 || (is_chassis == 1 && chassis_instance == 0)) { 277 /* either not a chassis node, or chassis #0 */ 278 result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY, 279 FM_FMRI_AUTH_CHASSIS, &err); 280 } else { 281 /* chassis 'n' in a >1 chassis system */ 282 result = err = -1; 283 } 284 if (result != 0 && err != ETOPO_PROP_DEFD) { 285 if (is_chassis == 0) { 286 result = nvlist_lookup_string(auth, 287 FM_FMRI_AUTH_CHASSIS, &csn); 288 if (result != 0 || csn == NULL) { 289 /* 290 * No chassis information in the parent 291 * node or auth list. 292 */ 293 topo_mod_dprintf(mod, 294 "%s: csn name not found\n", f); 295 } 296 } else { 297 /* 298 * So as not to blindly set the chassis-id to 299 * chassis #0's serial number. 300 */ 301 csn = val = topo_mod_strdup(mod, hcfmri->serial_number); 302 } 303 304 /* 305 * We continue even if the chassis information is not available 306 * to enumerate as much as possible. 307 */ 308 if (csn != NULL) { 309 if (is_chassis == 1) 310 result = topo_prop_set_string(t_node, 311 FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS, 312 TOPO_PROP_MUTABLE, csn, &err); 313 else 314 result = topo_prop_set_string(t_node, 315 FM_FMRI_AUTHORITY, FM_FMRI_AUTH_CHASSIS, 316 TOPO_PROP_IMMUTABLE, csn, &err); 317 318 if (result != 0) { 319 /* Preserve the error and continue */ 320 (void) topo_mod_seterrno(mod, err); 321 topo_mod_dprintf(mod, "%s: failed to " 322 "set property %s (%d) : %s\n", f, 323 FM_FMRI_AUTH_CHASSIS, err, 324 topo_strerror(err)); 325 } 326 } 327 328 if (val != NULL) { 329 topo_mod_strfree(mod, val); 330 val = NULL; 331 } 332 } 333 334 /* server-id */ 335 result = topo_prop_inherit(t_node, FM_FMRI_AUTHORITY, 336 FM_FMRI_AUTH_SERVER, &err); 337 if (result != 0 && err != ETOPO_PROP_DEFD) { 338 result = nvlist_lookup_string(auth, FM_FMRI_AUTH_SERVER, 339 &server); 340 if (result != 0 || server == NULL) { 341 /* 342 * No server information in the parent node or auth 343 * list. Find the server information in hostname. 344 */ 345 server = val = x86pi_get_serverid(mod); 346 if (server == NULL) { 347 topo_mod_dprintf(mod, "%s: server " 348 "name not found for %s node\n", f, 349 hcfmri->hc_name); 350 } 351 } 352 353 /* 354 * We continue even if the server information is not available 355 * to enumerate as much as possible. 356 */ 357 if (server != NULL) { 358 result = topo_prop_set_string(t_node, FM_FMRI_AUTHORITY, 359 FM_FMRI_AUTH_SERVER, TOPO_PROP_IMMUTABLE, server, 360 &err); 361 if (result != 0) { 362 /* Preserve the error and continue */ 363 (void) topo_mod_seterrno(mod, err); 364 topo_mod_dprintf(mod, "%s: failed to " 365 "set property %s (%d) : %s\n", f, 366 FM_FMRI_AUTH_SERVER, err, 367 topo_strerror(err)); 368 } 369 } 370 371 if (val != NULL) 372 topo_mod_strfree(mod, val); 373 } 374 375 nvlist_free(auth); 376 377 return (0); 378 } 379 380 381 /* 382 * Calculate a generic FRU for the given node. If the node is not a FRU, 383 * then inherit the FRU data from the nodes parent. 384 */ 385 int 386 x86pi_set_frufmri(topo_mod_t *mod, x86pi_hcfmri_t *hcfmri, tnode_t *t_parent, 387 tnode_t *t_node, int flag) 388 { 389 int result; 390 int err; 391 392 nvlist_t *auth = NULL; 393 nvlist_t *frufmri = NULL; 394 395 if (t_node == NULL || mod == NULL) { 396 return (-1); 397 } 398 399 /* 400 * Determine if this node is a FRU 401 */ 402 if (!(flag & X86PI_ENUM_FRU)) { 403 /* This node is not a FRU. Inherit from parent and return */ 404 (void) topo_node_fru_set(t_node, NULL, 0, &result); 405 return (0); 406 } 407 408 /* 409 * This node is a FRU. Create an FMRI. 410 */ 411 auth = topo_mod_auth(mod, t_parent); 412 frufmri = topo_mod_hcfmri(mod, t_parent, FM_HC_SCHEME_VERSION, 413 hcfmri->hc_name, hcfmri->instance, NULL, auth, 414 hcfmri->part_number, hcfmri->version, hcfmri->serial_number); 415 if (frufmri == NULL) { 416 topo_mod_dprintf(mod, "failed to create FRU: %s\n", 417 topo_strerror(topo_mod_errno(mod))); 418 } 419 nvlist_free(auth); 420 421 /* Set the FRU, whether NULL or not */ 422 result = topo_node_fru_set(t_node, frufmri, 0, &err); 423 if (result != 0) { 424 (void) topo_mod_seterrno(mod, err); 425 } 426 nvlist_free(frufmri); 427 428 return (result); 429 } 430 431 432 /* 433 * Set the label for a topo node. 434 */ 435 int 436 x86pi_set_label(topo_mod_t *mod, const char *label, const char *name, 437 tnode_t *t_node) 438 { 439 int result; 440 int err; 441 442 if (mod == NULL) { 443 return (-1); 444 } 445 446 /* 447 * Set the label for this topology node. 448 * Note that a NULL label will inherit the label from topology 449 * node's parent. 450 */ 451 result = topo_node_label_set(t_node, (char *)label, &err); 452 if (result != 0) { 453 (void) topo_mod_seterrno(mod, err); 454 topo_mod_dprintf(mod, "x86pi_set_label: failed with label %s " 455 "on %s node: %s\n", (label == NULL ? "NULL" : label), 456 name, topo_strerror(err)); 457 } 458 459 return (result); 460 } 461 462 463 /* 464 * Calculate the system information for a node. Inherit the data if 465 * possible, but always create an appropriate property group. 466 */ 467 int 468 x86pi_set_system(topo_mod_t *mod, tnode_t *t_node) 469 { 470 int result; 471 int err; 472 struct utsname uts; 473 char isa[MAXNAMELEN]; 474 475 if (mod == NULL || t_node == NULL) { 476 return (-1); 477 } 478 479 result = topo_pgroup_create(t_node, &sys_pgroup, &err); 480 if (result != 0 && err != ETOPO_PROP_DEFD) { 481 /* 482 * We failed to create the property group and it was not 483 * already defined. Set the err code and return failure. 484 */ 485 (void) topo_mod_seterrno(mod, err); 486 return (-1); 487 } 488 489 result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA, 490 &err); 491 if (result != 0 && err != ETOPO_PROP_DEFD) { 492 isa[0] = '\0'; 493 result = sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)); 494 if (result == -1) { 495 /* Preserve the error and continue */ 496 topo_mod_dprintf(mod, "x86pi_set_system: failed to " 497 "read SI_ARCHITECTURE: %d\n", errno); 498 } 499 if (strnlen(isa, MAXNAMELEN) > 0) { 500 result = topo_prop_set_string(t_node, 501 TOPO_PGROUP_SYSTEM, TOPO_PROP_ISA, 502 TOPO_PROP_IMMUTABLE, isa, &err); 503 if (result != 0) { 504 /* Preserve the error and continue */ 505 (void) topo_mod_seterrno(mod, err); 506 topo_mod_dprintf(mod, 507 "x86pi_set_auth: failed to " 508 "set property %s (%d) : %s\n", 509 TOPO_PROP_ISA, err, topo_strerror(err)); 510 } 511 } 512 } 513 514 result = topo_prop_inherit(t_node, TOPO_PGROUP_SYSTEM, 515 TOPO_PROP_MACHINE, &err); 516 if (result != 0 && err != ETOPO_PROP_DEFD) { 517 result = uname(&uts); 518 if (result == -1) { 519 /* Preserve the error and continue */ 520 (void) topo_mod_seterrno(mod, errno); 521 topo_mod_dprintf(mod, "x86pi_set_system: failed to " 522 "read uname: %d\n", errno); 523 } 524 if (strnlen(uts.machine, sizeof (uts.machine)) > 0) { 525 result = topo_prop_set_string(t_node, 526 TOPO_PGROUP_SYSTEM, TOPO_PROP_MACHINE, 527 TOPO_PROP_IMMUTABLE, uts.machine, &err); 528 if (result != 0) { 529 /* Preserve the error and continue */ 530 (void) topo_mod_seterrno(mod, err); 531 topo_mod_dprintf(mod, 532 "x86pi_set_auth: failed to " 533 "set property %s (%d) : %s\n", 534 TOPO_PROP_MACHINE, err, topo_strerror(err)); 535 } 536 } 537 } 538 539 return (0); 540 } 541 542 /* 543 * All the checks for compatibility are done within the kernel where the 544 * ereport generators are. They'll determine first if there's a problem 545 * and the topo enum will follow suit. The /dev/fm ioclt returns the value 546 * of the x86gentopo_legacy kernel variable which determines if this platform 547 * will provide an x86 generic topo or legacy topo enumeration. 548 */ 549 /* ARGSUSED */ 550 int 551 x86pi_check_comp(topo_mod_t *mod, smbios_hdl_t *shp) 552 { 553 int rv; 554 int fd; 555 int32_t legacy; 556 nvlist_t *nvl = NULL; 557 fm_ioc_data_t fid; 558 char *ibuf = NULL, *obuf = NULL; 559 size_t insz = 0, outsz = 0; 560 char *f = "x86pi_check_comp"; 561 562 /* open /dev/fm */ 563 fd = open("/dev/fm", O_RDONLY); 564 if (fd < 0) { 565 topo_mod_dprintf(mod, "%s: failed to open /dev/fm.\n", f); 566 return (X86PI_NONE); 567 } 568 569 /* set up buffers and ioctl data structure */ 570 outsz = FM_IOC_MAXBUFSZ; 571 obuf = topo_mod_alloc(mod, outsz); 572 if (obuf == NULL) { 573 perror("umem_alloc"); 574 return (X86PI_NONE); 575 } 576 577 fid.fid_version = 1; 578 fid.fid_insz = insz; 579 fid.fid_inbuf = ibuf; 580 fid.fid_outsz = outsz; 581 fid.fid_outbuf = obuf; 582 583 /* send the ioctl to /dev/fm to retrieve legacy variable */ 584 rv = ioctl(fd, FM_IOC_GENTOPO_LEGACY, &fid); 585 if (rv < 0) { 586 topo_mod_dprintf(mod, "%s: ioctl to /dev/fm failed", f); 587 perror("fm_ioctl"); 588 (void) close(fd); 589 return (X86PI_NONE); 590 } 591 (void) close(fd); 592 593 (void) nvlist_unpack(fid.fid_outbuf, fid.fid_outsz, &nvl, 0); 594 (void) nvlist_lookup_int32(nvl, FM_GENTOPO_LEGACY, &legacy); 595 596 nvlist_free(nvl); 597 topo_mod_free(mod, obuf, outsz); 598 599 if (legacy == 1) { 600 /* legacy kernel variable set; will do the same */ 601 return (X86PI_NONE); 602 } 603 604 /* legacy kernel variable not set; generic topo enum */ 605 return (X86PI_FULL); 606 } 607 608 const char * 609 x86pi_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type) 610 { 611 char buf[MAXNAMELEN]; 612 const char *end, *cp; 613 char *pp; 614 char c; 615 int i; 616 617 end = begin + strlen(begin); 618 619 while (begin < end && isspace(*begin)) 620 begin++; 621 while (begin < end && isspace(*(end - 1))) 622 end--; 623 624 if (begin >= end) 625 return (NULL); 626 627 cp = begin; 628 for (i = 0; i < MAXNAMELEN - 1; i++) { 629 if (cp >= end) 630 break; 631 c = *cp; 632 if (str_type == LABEL) { 633 if (!isprint(c)) 634 buf[i] = '-'; 635 else 636 buf[i] = c; 637 } else { 638 if (c == ':' || c == '=' || c == '/' || 639 isspace(c) || !isprint(c)) 640 buf[i] = '-'; 641 else 642 buf[i] = c; 643 } 644 cp++; 645 } 646 buf[i] = 0; 647 648 pp = topo_mod_strdup(mod, buf); 649 650 if (str_type == LABEL) 651 topo_mod_strfree(mod, (char *)begin); 652 653 return (pp); 654 } 655 656 /* 657 * Return Bus/Dev/Func from "reg" devinfo property. 658 */ 659 uint16_t 660 x86pi_bdf(topo_mod_t *mod, di_node_t node) 661 { 662 int *val; 663 664 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "reg", &val) < 0) { 665 topo_mod_dprintf(mod, "couldn't get \"reg\" prop: %s.\n", 666 strerror(errno)); 667 return ((uint16_t)-1); 668 } 669 670 return (uint16_t)((*val & PCI_REG_BDFR_M) >> PCI_REG_FUNC_SHIFT); 671 } 672 673 /* 674 * Return PHY from "sata-phy" devinfo proporty. 675 */ 676 int 677 x86pi_phy(topo_mod_t *mod, di_node_t node) 678 { 679 int *phy; 680 681 if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "sata-phy", &phy) < 0) { 682 topo_mod_dprintf(mod, "couldn't get \"sata-phy\" prop: %s.\n", 683 strerror(errno)); 684 return (-1); 685 } 686 687 return (*phy); 688 } 689