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 *
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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 *
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 *
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