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