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