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