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, 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
memnode_to_smbiosid(topo_mod_t * mod,uint16_t chip_smbid,const char * name,uint64_t nodeid,void * data)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
chip_get_smbstruct(topo_mod_t * mod,const smbios_struct_t * sp)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
chip_smbios_iterate(topo_mod_t * mod,smbios_rec_f * func_iter)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
init_chip_smbios(topo_mod_t * mod)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
chip_status_smbios_get(topo_mod_t * mod,id_t smb_id)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
chip_fru_smbios_get(topo_mod_t * mod,id_t smb_id)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
chip_strlen(const char * str)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 *
chip_cleanup_smbios_str(topo_mod_t * mod,const char * begin,int str_type)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 *
chip_label_smbios_get(topo_mod_t * mod,tnode_t * pnode,id_t smb_id,char * ksmbios_label)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 *
chip_serial_smbios_get(topo_mod_t * mod,id_t smb_id)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 *
chip_part_smbios_get(topo_mod_t * mod,id_t smb_id)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 *
chip_rev_smbios_get(topo_mod_t * mod,id_t smb_id)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