dmi: check both the AC and ID flags at the same time
[syslinux.git] / core / dmi.c
blob9a108341500e1e9b7643777f4c446ccf944e30af
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2011 Intel Corporation; author: H. Peter Anvin
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * ----------------------------------------------------------------------- */
29 * Search DMI information for specific data or strings
32 #include <string.h>
33 #include <stdio.h>
34 #include <sys/bitops.h>
35 #include <x86/cpu.h>
36 #include <syslinux/sysappend.h>
37 #include "core.h"
39 struct dmi_table {
40 uint8_t type;
41 uint8_t length;
42 uint16_t handle;
45 struct dmi_header {
46 char signature[5];
47 uint8_t csum;
48 uint16_t tbllen;
49 uint32_t tbladdr;
50 uint16_t nstruc;
51 uint8_t revision;
52 uint8_t reserved;
55 struct smbios_header {
56 char signature[4];
57 uint8_t csum;
58 uint8_t len;
59 uint8_t major;
60 uint8_t minor;
61 uint16_t maxsize;
62 uint8_t revision;
63 uint8_t fmt[5];
65 struct dmi_header dmi;
68 static const struct dmi_header *dmi;
70 static uint8_t checksum(const void *buf, size_t len)
72 const uint8_t *p = buf;
73 uint8_t csum = 0;
75 while (len--)
76 csum += *p++;
78 return csum;
81 static bool is_old_dmi(size_t dptr)
83 const struct dmi_header *dmi = (void *)dptr;
85 return !memcmp(dmi->signature, "_DMI_", 5) &&
86 !checksum(dmi, 0x0f);
87 return false;
90 static bool is_smbios(size_t dptr)
92 const struct smbios_header *smb = (void *)dptr;
94 return !memcmp(smb->signature, "_SM_", 4) &&
95 !checksum(smb, smb->len) &&
96 is_old_dmi(dptr+16);
100 * Find the root structure
102 static void dmi_find_header(void)
104 size_t dptr;
106 /* Search for _SM_ or _DMI_ structure */
107 for (dptr = 0xf0000 ; dptr < 0x100000 ; dptr += 16) {
108 if (is_smbios(dptr)) {
109 dmi = (const struct dmi_header *)(dptr + 16);
110 break;
111 } else if (is_old_dmi(dptr)) {
112 dmi = (const struct dmi_header *)dptr;
113 break;
119 * Return a specific data element in a specific table, and verify
120 * that it is within the bounds of the table.
122 static const void *dmi_find_data(uint8_t type, uint8_t base, uint8_t length)
124 const struct dmi_table *table;
125 size_t offset, end;
126 unsigned int tblcount;
128 if (!dmi)
129 return NULL;
131 if (base < 2)
132 return NULL;
134 end = base+length;
136 offset = 0;
137 tblcount = dmi->nstruc;
139 while (offset+6 <= dmi->tbllen && tblcount--) {
140 table = (const struct dmi_table *)(dmi->tbladdr + offset);
142 if (table->type == 127) /* End of table */
143 break;
145 if (table->length < sizeof *table)
146 break; /* Invalid length */
148 offset += table->length;
150 if (table->type == type && end <= table->length)
151 return (const char *)table + base;
153 /* Search for a double NUL terminating the string table */
154 while (offset+2 <= dmi->tbllen &&
155 *(const uint16_t *)(dmi->tbladdr + offset) != 0)
156 offset++;
158 offset += 2;
161 return NULL;
165 * Return a specific string in a specific table.
167 static const char *dmi_find_string(uint8_t type, uint8_t base)
169 const struct dmi_table *table;
170 size_t offset;
171 unsigned int tblcount;
173 if (!dmi)
174 return NULL;
176 if (base < 2)
177 return NULL;
179 offset = 0;
180 tblcount = dmi->nstruc;
182 while (offset+6 <= dmi->tbllen && tblcount--) {
183 table = (const struct dmi_table *)(dmi->tbladdr + offset);
185 if (table->type == 127) /* End of table */
186 break;
188 if (table->length < sizeof *table)
189 break; /* Invalid length */
191 offset += table->length;
193 if (table->type == type && base < table->length) {
194 uint8_t index = ((const uint8_t *)table)[base];
195 const char *p = (const char *)table + table->length;
196 const char *str;
197 char c;
199 if (!index)
200 return NULL; /* String not present */
202 while (--index) {
203 if (!*p)
204 return NULL;
206 do {
207 if (offset++ >= dmi->tbllen)
208 return NULL;
209 c = *p++;
210 } while (c);
213 /* Make sure the string is null-terminated */
214 str = p;
215 do {
216 if (offset++ >= dmi->tbllen)
217 return NULL;
218 c = *p++;
219 } while (c);
220 return str;
223 /* Search for a double NUL terminating the string table */
224 while (offset+2 <= dmi->tbllen &&
225 *(const uint16_t *)(dmi->tbladdr + offset) != 0)
226 offset++;
228 offset += 2;
231 return NULL;
234 struct sysappend_dmi_strings {
235 const char *prefix;
236 enum syslinux_sysappend sa;
237 uint8_t index;
238 uint8_t offset;
241 static const struct sysappend_dmi_strings dmi_strings[] = {
242 { "SYSVENDOR=", SYSAPPEND_SYSVENDOR, 1, 0x04 },
243 { "SYSPRODUCT=", SYSAPPEND_SYSPRODUCT, 1, 0x05 },
244 { "SYSVERSION=", SYSAPPEND_SYSVERSION, 1, 0x06 },
245 { "SYSSERIAL=", SYSAPPEND_SYSSERIAL, 1, 0x07 },
246 { "SYSSKU=", SYSAPPEND_SYSSKU, 1, 0x19 },
247 { "SYSFAMILY=", SYSAPPEND_SYSFAMILY, 1, 0x1a },
248 { "MBVENDOR=", SYSAPPEND_MBVENDOR, 2, 0x04 },
249 { "MBPRODUCT=", SYSAPPEND_MBPRODUCT, 2, 0x05 },
250 { "MBVERSION=", SYSAPPEND_MBVERSION, 2, 0x06 },
251 { "MBSERIAL=", SYSAPPEND_MBSERIAL, 2, 0x07 },
252 { "MBASSET=", SYSAPPEND_MBASSET, 2, 0x08 },
253 { "BIOSVENDOR=", SYSAPPEND_BIOSVENDOR, 0, 0x04 },
254 { "BIOSVERSION=", SYSAPPEND_BIOSVERSION, 0, 0x05 },
255 { NULL, 0, 0, 0 }
259 * Install the string in the string table, if nonempty, after
260 * removing leading and trailing whitespace.
262 static bool is_ctl_or_whitespace(char c)
264 return (c <= ' ' || c == '\x7f');
267 static const char *dmi_install_string(const char *pfx, const char *str)
269 const char *p, *ep;
270 size_t pfxlen;
271 char *nstr, *q;
273 if (!str)
274 return NULL;
276 while (*str && is_ctl_or_whitespace(*str))
277 str++;
279 if (!*str)
280 return NULL;
282 ep = p = str;
283 while (*p) {
284 if (!is_ctl_or_whitespace(*p))
285 ep = p+1;
286 p++;
289 pfxlen = strlen(pfx);
290 q = nstr = malloc(pfxlen + (ep-str) + 1);
291 if (!nstr)
292 return NULL;
293 memcpy(q, pfx, pfxlen);
294 q += pfxlen;
295 memcpy(q, str, ep-str);
296 q += (ep-str);
297 *q = '\0';
299 return nstr;
302 static void sysappend_set_sysff(const uint8_t *type)
304 static char sysff_str[] = "SYSFF=000";
306 if (!type || !*type)
307 return;
309 sprintf(sysff_str+6, "%u", *type & 0x7f);
310 sysappend_strings[SYSAPPEND_SYSFF] = sysff_str;
313 struct cpuflag {
314 uint8_t bit;
315 char flag;
318 static void sysappend_set_cpu(void)
320 unsigned long have_eflags;
321 static char cpu_str[6+6] = "CPU=";
322 char *p = cpu_str + 4;
323 static const struct cpuflag cpuflags[] = {
324 { 0*32+ 6, 'P' }, /* PAE */
325 { 1*32+ 5, 'V' }, /* VMX */
326 { 1*32+ 6, 'T' }, /* SMX (TXT) */
327 { 2*32+20, 'X' }, /* XD/NX */
328 { 2*32+29, 'L' }, /* Long mode (x86-64) */
329 { 3*32+ 2, 'S' }, /* SVM */
330 { 0, 0 }
332 const struct cpuflag *cf;
334 /* Not technically from DMI, but it fit here... */
335 have_eflags = cpu_has_eflags(EFLAGS_ID|EFLAGS_AC);
337 if (!(have_eflags & EFLAGS_ID)) {
338 /* No CPUID */
339 *p++ = (have_eflags & EFLAGS_AC) ? '4' : '3';
340 } else {
341 uint32_t flags[4], eax, ebx, family;
342 uint32_t ext_level;
344 cpuid(1, &eax, &ebx, &flags[1], &flags[0]);
345 family = (eax & 0x0ff00f00) >> 8;
346 *p++ = family >= 6 ? '6' : family + '0';
348 ext_level = cpuid_eax(0x80000000);
349 if (ext_level >= 0x80000001 && ext_level <= 0x8000ffff) {
350 cpuid(0x80000001, &eax, &ebx, &flags[3], &flags[2]);
351 } else {
352 flags[2] = flags[3] = 0;
355 for (cf = cpuflags; cf->flag; cf++) {
356 if (test_bit(cf->bit, flags))
357 *p++ = cf->flag;
361 *p = '\0';
363 sysappend_strings[SYSAPPEND_CPU] = cpu_str;
366 void dmi_init(void)
368 const struct sysappend_dmi_strings *ds;
370 sysappend_set_cpu();
372 dmi_find_header();
373 if (!dmi)
374 return;
376 sysappend_set_uuid(dmi_find_data(1, 0x08, 16));
377 sysappend_set_sysff(dmi_find_data(3, 0x05, 1));
379 for (ds = dmi_strings; ds->prefix; ds++) {
380 if (!sysappend_strings[ds->sa]) {
381 const char *str = dmi_find_string(ds->index, ds->offset);
382 sysappend_strings[ds->sa] = dmi_install_string(ds->prefix, str);