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 2009 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 206 /* 207 * We expect that the first SUN_OEM_EXT_XXX 208 * that comes after a SMB_TYPE_XXX correspond 209 * to each other in the SMBIOS ordering of the 210 * records. And SUN_OEM_EXT_XXX always comes 211 * after its SMB_TYPE_XXX record. 212 */ 213 switch (sp->smbstr_type) { 214 case SMB_TYPE_BASEBOARD: 215 bb_count++; 216 break; 217 case SMB_TYPE_MEMARRAY: 218 mctsmb[nmct_ids].mct_id = sp->smbstr_id; 219 break; 220 case SUN_OEM_EXT_MEMARRAY: 221 mctsmb[nmct_ids].extmct_id = sp->smbstr_id; 222 if (shp != NULL) { 223 if (smbios_info_extmemarray(shp, 224 sp->smbstr_id, &extma) != 0) { 225 topo_mod_dprintf(mod, "chip_get_smbstruct : " 226 "smbios_info_extmemarray()" 227 "failed\n"); 228 return (-1); 229 } 230 } else 231 return (-1); 232 if (extma.smbmae_ma == mctsmb[nmct_ids].mct_id) 233 mctsmb[nmct_ids].p_id = extma.smbmae_comp; 234 else 235 return (-1); 236 237 nmct_ids++; 238 break; 239 case SMB_TYPE_MEMDEVICE: 240 dimmsmb[ndimm_ids].dimm_id = sp->smbstr_id; 241 if (shp != NULL) { 242 if (smbios_info_memdevice(shp, 243 sp->smbstr_id, &md) != 0) 244 return (-1); 245 } else 246 return (-1); 247 dimmsmb[ndimm_ids].bankloc = md.smbmd_bloc; 248 break; 249 /* 250 * Every SMB_TYPE_MEMDEVICE SHOULD have a 251 * corresponding SUN_OEM_EXT_MEMDEVICE 252 */ 253 case SUN_OEM_EXT_MEMDEVICE: 254 dimmsmb[ndimm_ids++].extdimm_id = sp->smbstr_id; 255 break; 256 case SMB_TYPE_PROCESSOR: 257 cpusmb[ncpu_ids].cpu_id = sp->smbstr_id; 258 if (shp != NULL) { 259 if (smbios_info_processor(shp, 260 sp->smbstr_id, &p) != 0) { 261 topo_mod_dprintf(mod, "chip_get_smbstruct : " 262 "smbios_info_processor()" 263 "failed\n"); 264 return (-1); 265 } 266 } 267 cpusmb[ncpu_ids].status = p.smbp_status; 268 break; 269 /* 270 * Every SMB_TYPE_PROCESSOR SHOULD have a 271 * corresponding SUN_OEM_EXT_PROCESSOR 272 */ 273 case SUN_OEM_EXT_PROCESSOR: 274 if (smbios_info_extprocessor(shp, 275 sp->smbstr_id, &extp) != 0) { 276 topo_mod_dprintf(mod, "chip_get_smbstruct : " 277 "smbios_info_extprocessor()" 278 "failed\n"); 279 return (-1); 280 } 281 cpusmb[ncpu_ids].fru = extp.smbpe_fru; 282 ncpu_ids++; 283 break; 284 } 285 return (0); 286 } 287 288 static int 289 chip_smbios_iterate(topo_mod_t *mod, smbios_rec_f *func_iter) 290 { 291 const smb_struct_t *sp = shp->sh_structs; 292 smbios_struct_t s; 293 int i, rv = 0; 294 295 for (i = 0; i < shp->sh_nstructs; i++, sp++) { 296 if (sp->smbst_hdr->smbh_type != SMB_TYPE_INACTIVE && 297 (rv = func_iter(mod, smb_export(sp, &s))) != 0) 298 break; 299 } 300 return (rv); 301 } 302 303 int 304 init_chip_smbios(topo_mod_t *mod) 305 { 306 if (shp == NULL) { 307 if ((shp = topo_mod_smbios(mod)) == NULL) { 308 whinge(mod, NULL, "init_chip_smbios: smbios " 309 "handle get failed\n"); 310 return (-1); 311 } 312 } 313 314 if (!fill_chip_smbios) { 315 if (chip_smbios_iterate(mod, chip_get_smbstruct) == -1) 316 return (-1); 317 fill_chip_smbios = 1; 318 } 319 320 return (0); 321 } 322 323 int 324 chip_status_smbios_get(topo_mod_t *mod, id_t smb_id) 325 { 326 /* 327 * Type-4 Socket Status bit definitions per SMBIOS Version 2.6 328 * 329 * STATUS 330 * CPU Socket Populated 331 * CPU Socket Unpopulated 332 * Populated : Enabled 333 * Populated : Disabled by BIOS (Setup) 334 * Populated : Disabled by BIOS (Error) 335 * Populated : Idle 336 */ 337 uint8_t enabled = 0x01; 338 uint8_t populated = 0x40; 339 340 for (int i = 0; i < ncpu_ids; i++) { 341 if (smb_id == cpusmb[i].cpu_id) { 342 if (cpusmb[i].status == (enabled | populated)) 343 return (1); 344 else 345 return (0); 346 } 347 } 348 349 topo_mod_dprintf(mod, "topo_status_smbios_get() failed" 350 " considering that Type 4 ID : %d is disabled", smb_id); 351 return (0); 352 } 353 354 int 355 chip_fru_smbios_get(topo_mod_t *mod, id_t smb_id) 356 { 357 /* 358 * smbios_processor_ext_t->smbpe_fru : if set to 1 359 * processor is a FRU 360 */ 361 uint8_t fru = 1; 362 363 for (int i = 0; i < ncpu_ids; i++) { 364 if (smb_id == cpusmb[i].cpu_id) { 365 if (cpusmb[i].fru == fru) 366 return (1); 367 else 368 return (0); 369 } 370 } 371 372 topo_mod_dprintf(mod, "topo_fru_smbios_get() failed" 373 " considering that Type 4 ID : %d is not a FRU", smb_id); 374 return (0); 375 } 376 377 /* 378 * This could be defined as topo_mod_strlen() 379 */ 380 size_t 381 chip_strlen(const char *str) 382 { 383 int len = 0; 384 385 if (str != NULL) 386 len = strlen(str); 387 388 return (len); 389 } 390 391 /* 392 * We clean Serials, Revisions, Part No. strings, to 393 * avoid getting lost when fmd synthesizes these 394 * strings. :, =, /, ' ' characters are replaced 395 * with character '-' any non-printable characters 396 * as seen with !isprint() is also replaced with '-' 397 * Labels are checked only for non-printable characters. 398 */ 399 static const char * 400 chip_cleanup_smbios_str(topo_mod_t *mod, const char *begin, int str_type) 401 { 402 char buf[MAXNAMELEN]; 403 const char *end, *cp; 404 char *pp; 405 char c; 406 int i; 407 408 end = begin + strlen(begin); 409 410 while (begin < end && isspace(*begin)) 411 begin++; 412 while (begin < end && isspace(*(end - 1))) 413 end--; 414 415 if (begin >= end) 416 return (NULL); 417 418 cp = begin; 419 for (i = 0; i < MAXNAMELEN - 1; i++) { 420 if (cp >= end) 421 break; 422 c = *cp; 423 if (str_type == LABEL) { 424 if (!isprint(c)) 425 buf[i] = '-'; 426 else 427 buf[i] = c; 428 } else { 429 if (c == ':' || c == '=' || c == '/' || 430 isspace(c) || !isprint(c)) 431 buf[i] = '-'; 432 else 433 buf[i] = c; 434 } 435 cp++; 436 } 437 buf[i] = 0; 438 439 pp = topo_mod_strdup(mod, buf); 440 441 if (str_type == LABEL) 442 topo_mod_strfree(mod, (char *)begin); 443 444 return (pp); 445 } 446 447 const char * 448 chip_label_smbios_get(topo_mod_t *mod, tnode_t *pnode, id_t smb_id, 449 char *ksmbios_label) 450 { 451 smbios_info_t c; 452 char *label = NULL; 453 char *buf = NULL; 454 const char *lsmbios_label = NULL; 455 int bufsz = 0; 456 char *delim = NULL, *blank = " "; 457 const char *dimm_bank = NULL; 458 const char *clean_label = NULL; 459 int err; 460 461 if (shp != NULL) { 462 /* 463 * Get Parent FRU's label 464 */ 465 if (topo_prop_get_string(pnode, TOPO_PGROUP_PROTOCOL, 466 TOPO_PROP_LABEL, &label, &err) == -1) 467 topo_mod_dprintf(mod, "Failed to get" 468 " Label of Parent Node error : %d\n", err); 469 470 if (label != NULL) 471 label = (char *)chip_cleanup_smbios_str(mod, 472 label, LABEL); 473 474 /* 475 * On Intel the driver gets the label from ksmbios 476 * so we check if we already have it, if not we 477 * get it from libsmbios 478 */ 479 if (ksmbios_label == NULL && smb_id != -1) { 480 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 481 for (int i = 0; i < ndimm_ids; i++) { 482 if (smb_id == dimmsmb[i].dimm_id) { 483 dimm_bank = dimmsmb[i].bankloc; 484 break; 485 } 486 } 487 if (dimm_bank != NULL) { 488 bufsz += chip_strlen(blank) + 489 chip_strlen(dimm_bank); 490 } 491 lsmbios_label = c.smbi_location; 492 } 493 } else 494 lsmbios_label = ksmbios_label; 495 496 if (label != NULL && lsmbios_label != NULL) 497 delim = "/"; 498 499 bufsz += chip_strlen(label) + chip_strlen(delim) + 500 chip_strlen(lsmbios_label) + 1; 501 502 buf = topo_mod_alloc(mod, bufsz); 503 504 if (buf != NULL) { 505 if (label != NULL) { 506 (void) strlcpy(buf, label, bufsz); 507 if (lsmbios_label != NULL) { 508 (void) strlcat(buf, delim, bufsz); 509 /* 510 * If we are working on a DIMM 511 * and we are deriving from libsmbios 512 * smbi_location has the Device Locator. 513 * add the Device Locator 514 * add Bank Locator latter 515 */ 516 (void) strlcat(buf, lsmbios_label, 517 bufsz); 518 } 519 } else if (lsmbios_label != NULL) 520 (void) strlcpy(buf, lsmbios_label, 521 bufsz); 522 523 if (dimm_bank != NULL) { 524 (void) strlcat(buf, blank, bufsz); 525 (void) strlcat(buf, dimm_bank, bufsz); 526 } 527 } 528 529 clean_label = chip_cleanup_smbios_str(mod, buf, LABEL); 530 topo_mod_strfree(mod, label); 531 532 return (clean_label); 533 } 534 535 topo_mod_dprintf(mod, "Failed to get Label\n"); 536 return (NULL); 537 } 538 539 540 const char * 541 chip_serial_smbios_get(topo_mod_t *mod, id_t smb_id) 542 { 543 smbios_info_t c; 544 const char *clean_serial = NULL; 545 546 if (shp != NULL && smb_id != -1) 547 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 548 clean_serial = chip_cleanup_smbios_str(mod, 549 c.smbi_serial, 0); 550 return (clean_serial); 551 } 552 553 topo_mod_dprintf(mod, "Failed to get Serial \n"); 554 return (NULL); 555 } 556 557 558 const char * 559 chip_part_smbios_get(topo_mod_t *mod, id_t smb_id) 560 { 561 smbios_info_t c; 562 const char *clean_part = NULL; 563 564 if (shp != NULL && smb_id != -1) 565 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 566 clean_part = chip_cleanup_smbios_str(mod, 567 c.smbi_part, 0); 568 return (clean_part); 569 } 570 571 topo_mod_dprintf(mod, "Failed to get Part\n"); 572 return (NULL); 573 } 574 575 const char * 576 chip_rev_smbios_get(topo_mod_t *mod, id_t smb_id) 577 { 578 smbios_info_t c; 579 const char *clean_rev = NULL; 580 581 if (shp != NULL && smb_id != -1) 582 if (smbios_info_common(shp, smb_id, &c) != SMB_ERR) { 583 clean_rev = chip_cleanup_smbios_str(mod, 584 c.smbi_version, 0); 585 return (clean_rev); 586 } 587 588 topo_mod_dprintf(mod, "Failed to get Revision\n"); 589 return (NULL); 590 } 591