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 }