xref: /illumos-gate/usr/src/lib/fm/topo/modules/i86pc/chip/chip_smbios.c (revision 679a141ebeb538153d1438154e7f8b036213b063)
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