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
49typedef struct cpu_smbios {
50	id_t cpu_id;
51	uint8_t status;
52	uint8_t fru;
53}csmb_t;
54
55typedef struct dimm_smbios {
56	id_t dimm_id;
57	id_t extdimm_id;
58	const char *bankloc;
59}dsmb_t;
60
61typedef struct mct_smbios {
62	id_t extmct_id;
63	id_t mct_id;
64	id_t p_id;
65}msmb_t;
66
67csmb_t cpusmb[CPU_SLOTS];
68dsmb_t dimmsmb[DIMM_SLOTS];
69msmb_t mctsmb[MC_INSTANCES];
70
71static int ncpu_ids = 0;
72static int bb_count = 0;
73static int ndimm_ids, nmct_ids = 0;
74
75static int fill_chip_smbios = 0;
76typedef int smbios_rec_f(topo_mod_t *, const smbios_struct_t *);
77
78static smbios_struct_t *
79smb_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
95static int
96extdimmslot_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
149id_t
150memnode_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
203int
204chip_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
333static int
334chip_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
354int
355init_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
366int
367chip_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
395int
396chip_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 */
421size_t
422chip_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 */
440static const char *
441chip_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
488const char *
489chip_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
583const char *
584chip_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
603const char *
604chip_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
622const char *
623chip_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