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) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <unistd.h> 27 #include <ctype.h> 28 #include <strings.h> 29 #include <sys/types.h> 30 #include <sys/devfm.h> 31 #include <libnvpair.h> 32 #include <sys/smbios.h> 33 #include <fm/topo_mod.h> 34 #include <sys/fm/protocol.h> 35 #include <sys/smbios_impl.h> 36 37 #include "chip.h" 38 39 #define CPU_SLOTS 64 40 #define DIMM_SLOTS 512 41 #define MC_INSTANCES 128 42 43 #define MAXNAMELEN 256 44 #define LABEL 1 45 46 #define SKIP_CS 9999 47 48 49 typedef struct cpu_smbios { 50 id_t cpu_id; 51 uint8_t status; 52 uint8_t fru; 53 }csmb_t; 54 55 typedef struct dimm_smbios { 56 id_t dimm_id; 57 id_t extdimm_id; 58 const char *bankloc; 59 }dsmb_t; 60 61 typedef struct mct_smbios { 62 id_t extmct_id; 63 id_t mct_id; 64 id_t p_id; 65 }msmb_t; 66 67 csmb_t cpusmb[CPU_SLOTS]; 68 dsmb_t dimmsmb[DIMM_SLOTS]; 69 msmb_t mctsmb[MC_INSTANCES]; 70 71 static int ncpu_ids = 0; 72 static int bb_count = 0; 73 static int ndimm_ids, nmct_ids = 0; 74 75 static int fill_chip_smbios = 0; 76 typedef int smbios_rec_f(topo_mod_t *, const smbios_struct_t *); 77 78 static smbios_struct_t * smb_export(const smb_struct_t * stp,smbios_struct_t * sp)79 smb_export(const smb_struct_t *stp, smbios_struct_t *sp) 80 { 81 const smb_header_t *hdr; 82 83 if (stp == NULL) 84 return (NULL); 85 86 hdr = stp->smbst_hdr; 87 sp->smbstr_id = hdr->smbh_hdl; 88 sp->smbstr_type = hdr->smbh_type; 89 sp->smbstr_data = hdr; 90 sp->smbstr_size = (size_t)(stp->smbst_end - (uchar_t *)hdr); 91 92 return (sp); 93 } 94 95 static int extdimmslot_to_dimmslot(topo_mod_t * mod,id_t chip_smbid,int channum,int csnum)96 extdimmslot_to_dimmslot(topo_mod_t *mod, id_t chip_smbid, int channum, 97 int csnum) 98 { 99 smbios_memdevice_ext_t emd; 100 smbios_memdevice_t md; 101 int i, j; 102 int match = 0; 103 smbios_hdl_t *shp; 104 105 shp = topo_mod_smbios(mod); 106 if (shp == NULL) 107 return (-1); 108 109 if (chip_smbid == IGNORE_ID && bb_count <= 1 && nmct_ids <= 1) { 110 for (i = 0; i < ndimm_ids; i++) { 111 if (smbios_info_extmemdevice(shp, dimmsmb[i].extdimm_id, 112 &emd) != 0) { 113 continue; 114 } 115 116 if (emd.smbmdeve_drch == channum) { 117 uint_t ncs; 118 uint8_t *cs; 119 120 if (csnum == SKIP_CS) { 121 return (emd.smbmdeve_md); 122 } 123 124 if (smbios_info_extmemdevice_cs(shp, 125 dimmsmb[i].extdimm_id, &ncs, &cs) != 0) { 126 continue; 127 } 128 129 for (uint_t k = 0; k < ncs; k++) { 130 if (cs[k] != csnum) { 131 continue; 132 } 133 134 smbios_info_extmemdevice_cs_free(shp, 135 ncs, cs); 136 return (emd.smbmdeve_md); 137 } 138 139 smbios_info_extmemdevice_cs_free(shp, ncs, cs); 140 } 141 } 142 } 143 144 for (j = 0; j < nmct_ids; j++) { 145 if (mctsmb[j].p_id == chip_smbid) { 146 for (i = 0; i < ndimm_ids; i++) { 147 if (smbios_info_extmemdevice(shp, 148 dimmsmb[i].extdimm_id, &emd) != 0) { 149 continue; 150 } 151 152 (void) smbios_info_memdevice(shp, 153 emd.smbmdeve_md, &md); 154 if (md.smbmd_array == mctsmb[j].mct_id && 155 emd.smbmdeve_drch == channum) { 156 match = 1; 157 break; 158 } 159 } 160 if (match) { 161 uint_t ncs; 162 uint8_t *cs; 163 164 if (csnum == SKIP_CS) { 165 return (emd.smbmdeve_md); 166 } 167 168 if (smbios_info_extmemdevice_cs(shp, 169 dimmsmb[i].extdimm_id, &ncs, &cs) != 0) { 170 continue; 171 } 172 173 for (uint_t k = 0; k < ncs; k++) { 174 if (cs[k] != csnum) { 175 continue; 176 } 177 178 smbios_info_extmemdevice_cs_free(shp, 179 ncs, cs); 180 return (emd.smbmdeve_md); 181 } 182 smbios_info_extmemdevice_cs_free(shp, ncs, cs); 183 } 184 } 185 } 186 187 return (-1); 188 } 189 190 id_t memnode_to_smbiosid(topo_mod_t * mod,uint16_t chip_smbid,const char * name,uint64_t nodeid,void * data)191 memnode_to_smbiosid(topo_mod_t *mod, uint16_t chip_smbid, const char *name, 192 uint64_t nodeid, void *data) 193 { 194 195 if (strcmp(name, CS_NODE_NAME) == 0) { 196 int channum, csnum; 197 id_t dimmslot = -1; 198 199 if (data == NULL) 200 return (-1); 201 channum = *(int *)data; 202 csnum = nodeid; 203 /* 204 * Set the DIMM Slot label to the Chip Select Node 205 * Set the "data" to carry the DIMM instance 206 */ 207 dimmslot = extdimmslot_to_dimmslot(mod, chip_smbid, channum, 208 csnum); 209 if (dimmslot != -1 && dimmsmb[0].dimm_id != 0) 210 *((id_t *)data) = dimmslot % (dimmsmb[0].dimm_id); 211 else 212 *((id_t *)data) = -1; 213 214 return (dimmslot); 215 216 } else if (strcmp(name, DIMM_NODE_NAME) == 0) { 217 static int dimmnum = 0; 218 219 /* 220 * On certain Intel Chips, topology does not have 221 * chip-select nodes, it has the below layout 222 * chip/memory-controller/dram-channel/dimm 223 * so we check if channel instance is passed 224 * and get the SMBIOS ID based on the channel 225 */ 226 if (data != NULL) { 227 int channum; 228 id_t dimmslot = -1; 229 230 channum = *(int *)data; 231 dimmslot = extdimmslot_to_dimmslot(mod, chip_smbid, 232 channum, SKIP_CS); 233 234 return (dimmslot); 235 } 236 dimmnum = nodeid; 237 return (dimmsmb[dimmnum].dimm_id); 238 } 239 240 return (-1); 241 } 242 243 244 int chip_get_smbstruct(topo_mod_t * mod,const smbios_struct_t * sp)245 chip_get_smbstruct(topo_mod_t *mod, const smbios_struct_t *sp) 246 { 247 smbios_processor_t p; 248 smbios_memdevice_t md; 249 smbios_processor_ext_t extp; 250 smbios_memarray_ext_t extma; 251 smbios_memdevice_ext_t extmd; 252 int ext_match = 0; 253 smbios_hdl_t *shp; 254 255 shp = topo_mod_smbios(mod); 256 if (shp == NULL) 257 return (-1); 258 259 switch (sp->smbstr_type) { 260 case SMB_TYPE_BASEBOARD: 261 bb_count++; 262 break; 263 case SMB_TYPE_MEMARRAY: 264 mctsmb[nmct_ids].mct_id = sp->smbstr_id; 265 nmct_ids++; 266 break; 267 case SUN_OEM_EXT_MEMARRAY: 268 if (shp != NULL) { 269 if (smbios_info_extmemarray(shp, 270 sp->smbstr_id, &extma) != 0) { 271 topo_mod_dprintf(mod, "chip_get_smbstruct : " 272 "smbios_info_extmemarray()" 273 "failed\n"); 274 return (-1); 275 } 276 } else 277 return (-1); 278 for (int i = 0; i < nmct_ids; i++) { 279 if (extma.smbmae_ma == mctsmb[i].mct_id) { 280 mctsmb[i].extmct_id = sp->smbstr_id; 281 mctsmb[i].p_id = extma.smbmae_comp; 282 ext_match = 1; 283 break; 284 } 285 } 286 if (!ext_match) { 287 topo_mod_dprintf(mod, "chip_get_smbstruct : " 288 "EXT_MEMARRAY-MEMARRAY records are mismatched\n"); 289 ext_match = 0; 290 return (-1); 291 } 292 break; 293 case SMB_TYPE_MEMDEVICE: 294 dimmsmb[ndimm_ids].dimm_id = sp->smbstr_id; 295 if (shp != NULL) { 296 if (smbios_info_memdevice(shp, 297 sp->smbstr_id, &md) != 0) 298 return (-1); 299 } else 300 return (-1); 301 dimmsmb[ndimm_ids].bankloc = md.smbmd_bloc; 302 ndimm_ids++; 303 break; 304 /* 305 * Every SMB_TYPE_MEMDEVICE SHOULD have a 306 * corresponding SUN_OEM_EXT_MEMDEVICE 307 */ 308 case SUN_OEM_EXT_MEMDEVICE: 309 if (smbios_info_extmemdevice(shp, 310 sp->smbstr_id, &extmd) != 0) { 311 topo_mod_dprintf(mod, "chip_get_smbstruct : " 312 "smbios_info_extmemdevice()" 313 "failed\n"); 314 return (-1); 315 } 316 for (int i = 0; i < ndimm_ids; i++) { 317 if (extmd.smbmdeve_md == dimmsmb[i].dimm_id) { 318 dimmsmb[i].extdimm_id = sp->smbstr_id; 319 ext_match = 1; 320 break; 321 } 322 } 323 if (!ext_match) { 324 topo_mod_dprintf(mod, "chip_get_smbstruct : " 325 "EXT_MEMDEVICE-MEMDEVICE records are mismatched\n"); 326 ext_match = 0; 327 return (-1); 328 } 329 break; 330 case SMB_TYPE_PROCESSOR: 331 cpusmb[ncpu_ids].cpu_id = sp->smbstr_id; 332 if (shp != NULL) { 333 if (smbios_info_processor(shp, 334 sp->smbstr_id, &p) != 0) { 335 topo_mod_dprintf(mod, "chip_get_smbstruct : " 336 "smbios_info_processor()" 337 "failed\n"); 338 return (-1); 339 } 340 } 341 cpusmb[ncpu_ids].status = p.smbp_status; 342 ncpu_ids++; 343 break; 344 /* 345 * Every SMB_TYPE_PROCESSOR SHOULD have a 346 * corresponding SUN_OEM_EXT_PROCESSOR 347 */ 348 case SUN_OEM_EXT_PROCESSOR: 349 if (smbios_info_extprocessor(shp, 350 sp->smbstr_id, &extp) != 0) { 351 topo_mod_dprintf(mod, "chip_get_smbstruct : " 352 "smbios_info_extprocessor()" 353 "failed\n"); 354 return (-1); 355 } 356 for (int i = 0; i < ncpu_ids; i++) { 357 if (extp.smbpe_processor == cpusmb[i].cpu_id) { 358 cpusmb[i].fru = extp.smbpe_fru; 359 ext_match = 1; 360 break; 361 } 362 } 363 if (!ext_match) { 364 topo_mod_dprintf(mod, "chip_get_smbstruct : " 365 "EXT_PROCESSOR-PROCESSOR records are mismatched\n"); 366 ext_match = 0; 367 return (-1); 368 } 369 break; 370 } 371 return (0); 372 } 373 374 static int chip_smbios_iterate(topo_mod_t * mod,smbios_rec_f * func_iter)375 chip_smbios_iterate(topo_mod_t *mod, smbios_rec_f *func_iter) 376 { 377 const smb_struct_t *sp; 378 smbios_struct_t s; 379 int i, rv = 0; 380 smbios_hdl_t *shp; 381 382 shp = topo_mod_smbios(mod); 383 if (shp == NULL) 384 return (rv); 385 386 sp = shp->sh_structs; 387 for (i = 0; i < shp->sh_nstructs; i++, sp++) { 388 if (sp->smbst_hdr->smbh_type != SMB_TYPE_INACTIVE && 389 (rv = func_iter(mod, smb_export(sp, &s))) != 0) 390 break; 391 } 392 return (rv); 393 } 394 395 int init_chip_smbios(topo_mod_t * mod)396 init_chip_smbios(topo_mod_t *mod) 397 { 398 if (!fill_chip_smbios) { 399 if (chip_smbios_iterate(mod, chip_get_smbstruct) == -1) 400 return (-1); 401 fill_chip_smbios = 1; 402 } 403 404 return (0); 405 } 406 407 int chip_status_smbios_get(topo_mod_t * mod,id_t smb_id)408 chip_status_smbios_get(topo_mod_t *mod, id_t smb_id) 409 { 410 /* 411 * Type-4 Socket Status bit definitions per SMBIOS Version 2.6 412 * 413 * STATUS 414 * CPU Socket Populated 415 * CPU Socket Unpopulated 416 * Populated : Enabled 417 * Populated : Disabled by BIOS (Setup) 418 * Populated : Disabled by BIOS (Error) 419 * Populated : Idle 420 */ 421 uint8_t enabled = 0x01; 422 uint8_t populated = 0x40; 423 424 for (int i = 0; i < ncpu_ids; i++) { 425 if (smb_id == cpusmb[i].cpu_id) { 426 if (cpusmb[i].status == (enabled | populated)) 427 return (1); 428 } 429 } 430 431 topo_mod_dprintf(mod, "chip_status_smbios_get() failed" 432 " considering that Type 4 ID : %ld is disabled", smb_id); 433 return (0); 434 } 435 436 int chip_fru_smbios_get(topo_mod_t * mod,id_t smb_id)437 chip_fru_smbios_get(topo_mod_t *mod, id_t smb_id) 438 { 439 /* 440 * smbios_processor_ext_t->smbpe_fru : if set to 1 441 * processor is a FRU 442 */ 443 uint8_t fru = 1; 444 445 for (int i = 0; i < ncpu_ids; i++) { 446 if (smb_id == cpusmb[i].cpu_id) { 447 if (cpusmb[i].fru == fru) 448 return (1); 449 else 450 return (0); 451 } 452 } 453 454 topo_mod_dprintf(mod, "chip_fru_smbios_get() failed" 455 " considering that Type 4 ID : %ld is not a FRU", smb_id); 456 return (0); 457 } 458 459 /* 460 * This could be defined as topo_mod_strlen() 461 */ 462 size_t chip_strlen(const char * str)463 chip_strlen(const char *str) 464 { 465 int len = 0; 466 467 if (str != NULL) 468 len = strlen(str); 469 470 return (len); 471 } 472 473 /* 474 * We clean Serials, Revisions, Part No. strings, to 475 * avoid getting lost when fmd synthesizes these 476 * strings. :, =, /, ' ' characters are replaced 477 * with character '-' any non-printable characters 478 * as seen with !isprint() is also replaced with '-' 479 * Labels are checked only for non-printable characters. 480 */ 481 static const char * chip_cleanup_smbios_str(topo_mod_t * mod,const char * begin,int str_type)482 chip_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type) 483 { 484 char buf[MAXNAMELEN]; 485 const char *end, *cp; 486 char *pp; 487 char c; 488 int i; 489 490 end = begin + strlen(begin); 491 492 while (begin < end && isspace(*begin)) 493 begin++; 494 while (begin < end && isspace(*(end - 1))) 495 end--; 496 497 if (begin >= end) 498 return (NULL); 499 500 cp = begin; 501 for (i = 0; i < MAXNAMELEN - 1; i++) { 502 if (cp >= end) 503 break; 504 c = *cp; 505 if (str_type == LABEL) { 506 if (!isprint(c)) 507 buf[i] = '-'; 508 else 509 buf[i] = c; 510 } else { 511 if (c == ':' || c == '=' || c == '/' || 512 isspace(c) || !isprint(c)) 513 buf[i] = '-'; 514 else 515 buf[i] = c; 516 } 517 cp++; 518 } 519 buf[i] = 0; 520 521 pp = topo_mod_strdup(mod, buf); 522 523 if (str_type == LABEL) 524 topo_mod_strfree(mod, (char *)begin); 525 526 return (pp); 527 } 528 529 const char * chip_label_smbios_get(topo_mod_t * mod,tnode_t * pnode,id_t smb_id,char * ksmbios_label)530 chip_label_smbios_get(topo_mod_t *mod, tnode_t *pnode, id_t smb_id, 531 char *ksmbios_label) 532 { 533 smbios_info_t c; 534 char *label = NULL; 535 char *buf = NULL; 536 const char *lsmbios_label = NULL; 537 int bufsz = 0; 538 char *delim = NULL, *blank = " "; 539 const char *dimm_bank = NULL; 540 const char *clean_label = NULL; 541 int err; 542 smbios_hdl_t *shp; 543 544 shp = topo_mod_smbios(mod); 545 if (shp != NULL) { 546 /* 547 * Get Parent FRU's label 548 */ 549 if (topo_prop_get_string(pnode, TOPO_PGROUP_PROTOCOL, 550 TOPO_PROP_LABEL, &label, &err) == -1) 551 topo_mod_dprintf(mod, "Failed to get" 552 " Label of Parent Node error : %d\n", err); 553 554 if (label != NULL) 555 label = (char *)chip_cleanup_smbios_str(mod, 556 label, LABEL); 557 558 /* 559 * On Intel the driver gets the label from ksmbios 560 * so we check if we already have it, if not we 561 * get it from libsmbios 562 */ 563 if (ksmbios_label == NULL && smb_id != -1) { 564 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 565 for (int i = 0; i < ndimm_ids; i++) { 566 if (smb_id == dimmsmb[i].dimm_id) { 567 dimm_bank = dimmsmb[i].bankloc; 568 break; 569 } 570 } 571 if (dimm_bank != NULL) { 572 bufsz += chip_strlen(blank) + 573 chip_strlen(dimm_bank); 574 } 575 lsmbios_label = c.smbi_location; 576 } 577 } else 578 lsmbios_label = ksmbios_label; 579 580 if (label != NULL && lsmbios_label != NULL) 581 delim = "/"; 582 583 bufsz += chip_strlen(label) + chip_strlen(delim) + 584 chip_strlen(lsmbios_label) + 1; 585 586 buf = topo_mod_alloc(mod, bufsz); 587 588 if (buf != NULL) { 589 if (label != NULL) { 590 (void) strlcpy(buf, label, bufsz); 591 if (lsmbios_label != NULL) { 592 (void) strlcat(buf, delim, bufsz); 593 /* 594 * If we are working on a DIMM 595 * and we are deriving from libsmbios 596 * smbi_location has the Device Locator. 597 * add the Device Locator 598 * add Bank Locator latter 599 */ 600 (void) strlcat(buf, lsmbios_label, 601 bufsz); 602 } 603 } else if (lsmbios_label != NULL) 604 (void) strlcpy(buf, lsmbios_label, 605 bufsz); 606 607 if (dimm_bank != NULL) { 608 (void) strlcat(buf, blank, bufsz); 609 (void) strlcat(buf, dimm_bank, bufsz); 610 } 611 } 612 613 clean_label = chip_cleanup_smbios_str(mod, buf, LABEL); 614 topo_mod_strfree(mod, label); 615 616 return (clean_label); 617 } 618 619 topo_mod_dprintf(mod, "Failed to get Label\n"); 620 return (NULL); 621 } 622 623 624 const char * chip_serial_smbios_get(topo_mod_t * mod,id_t smb_id)625 chip_serial_smbios_get(topo_mod_t *mod, id_t smb_id) 626 { 627 smbios_info_t c; 628 const char *clean_serial = NULL; 629 smbios_hdl_t *shp; 630 631 shp = topo_mod_smbios(mod); 632 if (shp != NULL && smb_id != -1) 633 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 634 clean_serial = chip_cleanup_smbios_str(mod, 635 c.smbi_serial, 0); 636 return (clean_serial); 637 } 638 639 topo_mod_dprintf(mod, "Failed to get Serial \n"); 640 return (NULL); 641 } 642 643 644 const char * chip_part_smbios_get(topo_mod_t * mod,id_t smb_id)645 chip_part_smbios_get(topo_mod_t *mod, id_t smb_id) 646 { 647 smbios_info_t c; 648 const char *clean_part = NULL; 649 smbios_hdl_t *shp; 650 651 shp = topo_mod_smbios(mod); 652 if (shp != NULL && smb_id != -1) 653 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 654 clean_part = chip_cleanup_smbios_str(mod, 655 c.smbi_part, 0); 656 return (clean_part); 657 } 658 659 topo_mod_dprintf(mod, "Failed to get Part\n"); 660 return (NULL); 661 } 662 663 const char * chip_rev_smbios_get(topo_mod_t * mod,id_t smb_id)664 chip_rev_smbios_get(topo_mod_t *mod, id_t smb_id) 665 { 666 smbios_info_t c; 667 const char *clean_rev = NULL; 668 smbios_hdl_t *shp; 669 670 shp = topo_mod_smbios(mod); 671 if (shp != NULL && smb_id != -1) 672 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 673 clean_rev = chip_cleanup_smbios_str(mod, 674 c.smbi_version, 0); 675 return (clean_rev); 676 } 677 678 topo_mod_dprintf(mod, "Failed to get Revision\n"); 679 return (NULL); 680 } 681