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