(root)/
util-linux-2.39/
sys-utils/
lscpu-dmi.c
       1  // SPDX-License-Identifier: GPL-2.0
       2  /*
       3   * Copyright (C) 2020 FUJITSU LIMITED.  All rights reserved.
       4   */
       5  
       6  #include "lscpu.h"
       7  
       8  void to_dmi_header(struct lscpu_dmi_header *h, uint8_t *data)
       9  {
      10  	h->type = data[0];
      11  	h->length = data[1];
      12  	memcpy(&h->handle, data + 2, sizeof(h->handle));
      13  	h->data = data;
      14  }
      15  
      16  char *dmi_string(const struct lscpu_dmi_header *dm, uint8_t s)
      17  {
      18  	char *bp = (char *)dm->data;
      19  
      20  	if (!s || !bp)
      21  		return NULL;
      22  
      23  	bp += dm->length;
      24  	while (s > 1 && *bp) {
      25  		bp += strlen(bp);
      26  		bp++;
      27  		s--;
      28  	}
      29  
      30  	return !*bp ? NULL : bp;
      31  }
      32  
      33  int parse_dmi_table(uint16_t len, uint16_t num,
      34  				uint8_t *data,
      35  				struct dmi_info *di)
      36  {
      37  	uint8_t *buf = data;
      38  	int rc = -1;
      39  	int i = 0;
      40  
      41  	 /* 4 is the length of an SMBIOS structure header */
      42  	while (i < num && data + 4 <= buf + len) {
      43  		uint8_t *next;
      44  		struct lscpu_dmi_header h;
      45  
      46  		to_dmi_header(&h, data);
      47  
      48  		/*
      49  		 * If a short entry is found (less than 4 bytes), not only it
      50  		 * is invalid, but we cannot reliably locate the next entry.
      51  		 * Better stop at this point.
      52  		 */
      53  		if (h.length < 4)
      54  			goto done;
      55  
      56  		/* look for the next handle */
      57  		next = data + h.length;
      58  		while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
      59  			next++;
      60  		next += 2;
      61  		switch (h.type) {
      62  		case 0:
      63  			di->vendor = dmi_string(&h, data[0x04]);
      64  			break;
      65  		case 1:
      66  			di->manufacturer = dmi_string(&h, data[0x04]);
      67  			di->product = dmi_string(&h, data[0x05]);
      68  			break;
      69  		case 4:
      70  			/* Get the first processor information */
      71  			if (di->sockets == 0) {
      72  				di->processor_manufacturer = dmi_string(&h, data[0x7]);
      73  				di->processor_version = dmi_string(&h, data[0x10]);
      74  				di->current_speed = *((uint16_t *)(&data[0x16]));
      75  				di->part_num = dmi_string(&h, data[0x22]);
      76  
      77  				if (data[0x6] == 0xfe)
      78  					di->processor_family = *((uint16_t *)(&data[0x28]));
      79  				else
      80  					di->processor_family = data[0x6];
      81  			}
      82  			di->sockets++;
      83  			break;
      84  		default:
      85  			break;
      86  		}
      87  
      88  		data = next;
      89  		i++;
      90  	}
      91  	rc = 0;
      92  done:
      93  	return rc;
      94  }
      95  
      96  int dmi_decode_cputype(struct lscpu_cputype *ct)
      97  {
      98  	static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI;
      99  	struct dmi_info di = { };
     100  	struct stat st;
     101  	uint8_t *data;
     102  	int rc = 0;
     103  	char buf[100] = { };
     104  
     105  	if (stat(sys_fw_dmi_tables, &st))
     106  		return rc;
     107  
     108  	data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables);
     109  	if (!data)
     110  		return rc;
     111  
     112  	rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di);
     113  	if (rc < 0) {
     114  		free(data);
     115  		return rc;
     116  	}
     117  
     118  	if (di.processor_manufacturer)
     119  		ct->bios_vendor = xstrdup(di.processor_manufacturer);
     120  
     121  	snprintf(buf, sizeof(buf), "%s %s CPU @ %d.%dGHz",
     122  			(di.processor_version ?: ""), (di.part_num ?: ""),
     123  			di.current_speed/1000, (di.current_speed % 1000) / 100);
     124  	ct->bios_modelname = xstrdup(buf);
     125  
     126  	/* Get CPU family */
     127  	memset(buf, 0, sizeof(buf));
     128  	snprintf(buf, sizeof(buf), "%d", di.processor_family);
     129  	ct->bios_family = xstrdup(buf);
     130  
     131  	free(data);
     132  	return 0;
     133  }
     134  
     135  size_t get_number_of_physical_sockets_from_dmi(void)
     136  {
     137  	static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI;
     138  	struct dmi_info di;
     139  	struct stat st;
     140  	uint8_t *data;
     141  	int rc = 0;
     142  
     143  	if (stat(sys_fw_dmi_tables, &st))
     144  		return rc;
     145  
     146  	data = get_mem_chunk(0, st.st_size, sys_fw_dmi_tables);
     147  	if (!data)
     148  		return rc;
     149  
     150  	memset(&di, 0, sizeof(struct dmi_info));
     151  	rc = parse_dmi_table(st.st_size, st.st_size/4, data, &di);
     152  
     153  	free(data);
     154  
     155  	if ((rc < 0) || !di.sockets)
     156  		return 0;
     157  	else
     158  		return di.sockets;
     159  }