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