]> git.ipfire.org Git - thirdparty/util-linux.git/blame - sys-utils/lscpu-dmi.c
Merge branch 'mbsencode' of https://github.com/yontalcar/util-linux
[thirdparty/util-linux.git] / sys-utils / lscpu-dmi.c
CommitLineData
fb2627ce
OO
1/*
2 * lscpu-dmi - Module to parse SMBIOS information
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 *
18 * Code originally taken from the dmidecode utility and slightly rewritten
19 * to suite the needs of lscpu
20 */
21#include <errno.h>
22#include <stdlib.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <string.h>
28#include <stdio.h>
29
30#include "c.h"
31#include "pathnames.h"
32#include "all-io.h"
33#include "lscpu.h"
34
73be2127
KZ
35#define _PATH_SYS_DMI "/sys/firmware/dmi/tables/DMI"
36
fb2627ce
OO
37#define WORD(x) (uint16_t)(*(const uint16_t *)(x))
38#define DWORD(x) (uint32_t)(*(const uint32_t *)(x))
39
40struct dmi_header
41{
42 uint8_t type;
43 uint8_t length;
44 uint16_t handle;
45 uint8_t *data;
46};
47
48static int checksum(const uint8_t *buf, size_t len)
49{
50 uint8_t sum = 0;
51 size_t a;
52
53 for (a = 0; a < len; a++)
54 sum += buf[a];
55 return (sum == 0);
56}
57
58static void *get_mem_chunk(size_t base, size_t len, const char *devmem)
59{
dbbf8395 60 void *p = NULL;
fb2627ce
OO
61 int fd;
62
dbbf8395 63 if ((fd = open(devmem, O_RDONLY)) < 0)
fb2627ce 64 return NULL;
dbbf8395
KZ
65
66 if (!(p = malloc(len)))
67 goto nothing;
68 if (lseek(fd, base, SEEK_SET) == -1)
69 goto nothing;
70 if (read_all(fd, p, len) == -1)
71 goto nothing;
fb2627ce
OO
72
73 close(fd);
74 return p;
dbbf8395
KZ
75
76nothing:
77 free(p);
78 close(fd);
79 return NULL;
fb2627ce
OO
80}
81
82static void to_dmi_header(struct dmi_header *h, uint8_t *data)
83{
84 h->type = data[0];
85 h->length = data[1];
86 h->handle = WORD(data + 2);
87 h->data = data;
88}
89
90static char *dmi_string(const struct dmi_header *dm, uint8_t s)
91{
92 char *bp = (char *)dm->data;
93
94 if (s == 0)
95 return NULL;
96
97 bp += dm->length;
98 while (s > 1 && *bp)
99 {
100 bp += strlen(bp);
101 bp++;
102 s--;
103 }
104
105 if (!*bp)
106 return NULL;
107
108 return bp;
109}
110
111static int hypervisor_from_dmi_table(uint32_t base, uint16_t len,
112 uint16_t num, const char *devmem)
113{
dbbf8395 114 uint8_t *buf;
fb2627ce
OO
115 uint8_t *data;
116 int i = 0;
117 char *vendor = NULL;
118 char *product = NULL;
119 char *manufacturer = NULL;
dbbf8395 120 int rc = HYPER_NONE;
fb2627ce 121
dbbf8395
KZ
122 data = buf = get_mem_chunk(base, len, devmem);
123 if (!buf)
124 goto done;
fb2627ce
OO
125
126 /* 4 is the length of an SMBIOS structure header */
127 while (i < num && data + 4 <= buf + len) {
128 uint8_t *next;
129 struct dmi_header h;
130
131 to_dmi_header(&h, data);
132
133 /*
134 * If a short entry is found (less than 4 bytes), not only it
135 * is invalid, but we cannot reliably locate the next entry.
136 * Better stop at this point.
137 */
dbbf8395
KZ
138 if (h.length < 4)
139 goto done;
fb2627ce
OO
140
141 /* look for the next handle */
142 next = data + h.length;
143 while (next - buf + 1 < len && (next[0] != 0 || next[1] != 0))
144 next++;
145 next += 2;
146 switch (h.type) {
147 case 0:
148 vendor = dmi_string(&h, data[0x04]);
149 break;
150 case 1:
151 manufacturer = dmi_string(&h, data[0x04]);
152 product = dmi_string(&h, data[0x05]);
153 break;
154 default:
155 break;
156 }
157
158 data = next;
159 i++;
160 }
bbff0890 161 if (manufacturer && !strcmp(manufacturer, "innotek GmbH"))
dbbf8395 162 rc = HYPER_INNOTEK;
bbff0890
KZ
163 else if (manufacturer && strstr(manufacturer, "HITACHI") &&
164 product && strstr(product, "LPAR"))
dbbf8395 165 rc = HYPER_HITACHI;
73f2bec5 166 else if (vendor && !strcmp(vendor, "Parallels"))
dbbf8395
KZ
167 rc = HYPER_PARALLELS;
168done:
fb2627ce 169 free(buf);
dbbf8395 170 return rc;
fb2627ce
OO
171}
172
60cb2c37 173#if defined(__x86_64__) || defined(__i386__)
fb2627ce
OO
174static int hypervisor_decode_legacy(uint8_t *buf, const char *devmem)
175{
176 if (!checksum(buf, 0x0F))
c972852b 177 return -1;
fb2627ce
OO
178
179 return hypervisor_from_dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06),
180 WORD(buf + 0x0C),
181 devmem);
182}
60cb2c37 183#endif
fb2627ce
OO
184
185static int hypervisor_decode_smbios(uint8_t *buf, const char *devmem)
186{
187 if (!checksum(buf, buf[0x05])
188 || memcmp(buf + 0x10, "_DMI_", 5) != 0
189 || !checksum(buf + 0x10, 0x0F))
190 return -1;
191
192 return hypervisor_from_dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16),
193 WORD(buf + 0x1C),
194 devmem);
195}
196
92a6392c
AB
197static int hypervisor_decode_sysfw(void)
198{
73be2127 199 static char const sys_fw_dmi_tables[] = _PATH_SYS_DMI;
92a6392c
AB
200 struct stat st;
201
202 if (stat(sys_fw_dmi_tables, &st))
203 return -1;
204
205 return hypervisor_from_dmi_table(0, st.st_size, st.st_size / 4,
206 sys_fw_dmi_tables);
207}
208
fb2627ce
OO
209/*
210 * Probe for EFI interface
211 */
212#define EFI_NOT_FOUND (-1)
213#define EFI_NO_SMBIOS (-2)
214static int address_from_efi(size_t *address)
215{
216 FILE *tab;
217 char linebuf[64];
218 int ret;
219
220 *address = 0; /* Prevent compiler warning */
221
222 /*
223 * Linux up to 2.6.6: /proc/efi/systab
224 * Linux 2.6.7 and up: /sys/firmware/efi/systab
225 */
226 if (!(tab = fopen("/sys/firmware/efi/systab", "r")) &&
227 !(tab = fopen("/proc/efi/systab", "r")))
228 return EFI_NOT_FOUND; /* No EFI interface */
229
230 ret = EFI_NO_SMBIOS;
231 while ((fgets(linebuf, sizeof(linebuf) - 1, tab)) != NULL) {
232 char *addrp = strchr(linebuf, '=');
6d707c1d
KZ
233 if (!addrp)
234 continue;
fb2627ce
OO
235 *(addrp++) = '\0';
236 if (strcmp(linebuf, "SMBIOS") == 0) {
237 *address = strtoul(addrp, NULL, 0);
238 ret = 0;
239 break;
240 }
241 }
242
243 fclose(tab);
244 return ret;
245}
246
247int read_hypervisor_dmi(void)
248{
dbbf8395 249 int rc = HYPER_NONE;
fb2627ce
OO
250 uint8_t *buf = NULL;
251 size_t fp = 0;
252
253 if (sizeof(uint8_t) != 1
254 || sizeof(uint16_t) != 2
255 || sizeof(uint32_t) != 4
256 || '\0' != 0)
c972852b 257 goto done;
fb2627ce 258
c972852b
KZ
259 /* -1 : no DMI in /sys,
260 * 0 : DMI exist, nothing detected (HYPER_NONE)
261 * >0 : hypervisor detected
262 */
92a6392c 263 rc = hypervisor_decode_sysfw();
c972852b
KZ
264 if (rc >= HYPER_NONE)
265 goto done;
92a6392c 266
fb2627ce
OO
267 /* First try EFI (ia64, Intel-based Mac) */
268 switch (address_from_efi(&fp)) {
269 case EFI_NOT_FOUND:
270 goto memory_scan;
271 case EFI_NO_SMBIOS:
dbbf8395 272 goto done;
fb2627ce
OO
273 }
274
275 buf = get_mem_chunk(fp, 0x20, _PATH_DEV_MEM);
276 if (!buf)
dbbf8395 277 goto done;
fb2627ce 278
dbbf8395 279 rc = hypervisor_decode_smbios(buf, _PATH_DEV_MEM);
c972852b 280 if (rc >= HYPER_NONE)
fb2627ce 281 goto done;
c972852b 282
dbbf8395 283 free(buf);
4e6604e5 284 buf = NULL;
fb2627ce 285memory_scan:
6f7234f6 286#if defined(__x86_64__) || defined(__i386__)
fb2627ce
OO
287 /* Fallback to memory scan (x86, x86_64) */
288 buf = get_mem_chunk(0xF0000, 0x10000, _PATH_DEV_MEM);
289 if (!buf)
dbbf8395 290 goto done;
fb2627ce
OO
291
292 for (fp = 0; fp <= 0xFFF0; fp += 16) {
293 if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
dbbf8395 294 rc = hypervisor_decode_smbios(buf + fp, _PATH_DEV_MEM);
c972852b 295 if (rc < 0)
fb2627ce
OO
296 fp += 16;
297
298 } else if (memcmp(buf + fp, "_DMI_", 5) == 0)
dbbf8395
KZ
299 rc = hypervisor_decode_legacy(buf + fp, _PATH_DEV_MEM);
300
c972852b 301 if (rc >= HYPER_NONE)
dbbf8395 302 break;
fb2627ce 303 }
6f7234f6 304#endif
fb2627ce
OO
305done:
306 free(buf);
c972852b 307 return rc < 0 ? HYPER_NONE : rc;
fb2627ce 308}