source: bootcd/isolinux/syslinux-6.03/core/dmi.c @ 26ffad7

Last change on this file since 26ffad7 was e16e8f2, checked in by Edwin Eefting <edwin@datux.nl>, 3 years ago

bootstuff

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/* ----------------------------------------------------------------------- *
2 *
3 *   Copyright 2011 Intel Corporation; author: H. Peter Anvin
4 *
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:
13 *
14 *   The above copyright notice and this permission notice shall
15 *   be included in all copies or substantial portions of the Software.
16 *
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.
25 *
26 * ----------------------------------------------------------------------- */
27
28/*
29 * Search DMI information for specific data or strings
30 */
31
32#include <string.h>
33#include <stdio.h>
34#include <sys/bitops.h>
35#include <sys/cpu.h>
36#include <syslinux/sysappend.h>
37#include "core.h"
38
39struct dmi_table {
40    uint8_t type;
41    uint8_t length;
42    uint16_t handle;
43};
44
45struct 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;
53};
54
55struct 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];
64
65    struct dmi_header dmi;
66};
67
68static const struct dmi_header *dmi;
69
70static uint8_t checksum(const void *buf, size_t len)
71{
72    const uint8_t *p = buf;
73    uint8_t csum = 0;
74
75    while (len--)
76        csum += *p++;
77
78    return csum;
79}
80
81static bool is_old_dmi(size_t dptr)
82{
83    const struct dmi_header *dmi = (void *)dptr;
84
85    return !memcmp(dmi->signature, "_DMI_", 5) &&
86        !checksum(dmi, 0x0f);
87    return false;
88}
89
90static bool is_smbios(size_t dptr)
91{
92    const struct smbios_header *smb = (void *)dptr;
93
94    return !memcmp(smb->signature, "_SM_", 4) &&
95        !checksum(smb, smb->len) &&
96        is_old_dmi(dptr+16);
97}
98
99/*
100 * Find the root structure
101 */
102static void dmi_find_header(void)
103{
104    size_t dptr;
105
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;
114        }
115    }
116}
117
118/*
119 * Return a specific data element in a specific table, and verify
120 * that it is within the bounds of the table.
121 */
122static const void *dmi_find_data(uint8_t type, uint8_t base, uint8_t length)
123{
124    const struct dmi_table *table;
125    size_t offset, end;
126    unsigned int tblcount;
127
128    if (!dmi)
129        return NULL;
130
131    if (base < 2)
132        return NULL;
133
134    end = base+length;
135
136    offset = 0;
137    tblcount = dmi->nstruc;
138
139    while (offset+6 <= dmi->tbllen && tblcount--) {
140        table = (const struct dmi_table *)(dmi->tbladdr + offset);
141
142        if (table->type == 127) /* End of table */
143            break;
144       
145        if (table->length < sizeof *table)
146            break;              /* Invalid length */
147
148        offset += table->length;
149
150        if (table->type == type && end <= table->length)
151            return (const char *)table + base;
152
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++;
157
158        offset += 2;
159    }
160
161    return NULL;
162}
163
164/*
165 * Return a specific string in a specific table.
166 */
167static const char *dmi_find_string(uint8_t type, uint8_t base)
168{
169    const struct dmi_table *table;
170    size_t offset;
171    unsigned int tblcount;
172
173    if (!dmi)
174        return NULL;
175
176    if (base < 2)
177        return NULL;
178
179    offset = 0;
180    tblcount = dmi->nstruc;
181
182    while (offset+6 <= dmi->tbllen && tblcount--) {
183        table = (const struct dmi_table *)(dmi->tbladdr + offset);
184
185        if (table->type == 127) /* End of table */
186            break;
187       
188        if (table->length < sizeof *table)
189            break;              /* Invalid length */
190
191        offset += table->length;
192
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;
198
199            if (!index)
200                return NULL;    /* String not present */
201
202            while (--index) {
203                if (!*p)
204                    return NULL;
205
206                do {
207                    if (offset++ >= dmi->tbllen)
208                        return NULL;
209                    c = *p++;
210                } while (c);
211            }
212
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;
221        }
222
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++;
227
228        offset += 2;
229    }
230
231    return NULL;
232}
233
234struct sysappend_dmi_strings {
235    const char *prefix;
236    enum syslinux_sysappend sa;
237    uint8_t index;
238    uint8_t offset;
239};
240
241static 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 }
256};
257
258/*
259 * Install the string in the string table, if nonempty, after
260 * removing leading and trailing whitespace.
261 */
262static bool is_ctl_or_whitespace(char c)
263{
264    return (c <= ' ' || c == '\x7f');
265}
266
267static const char *dmi_install_string(const char *pfx, const char *str)
268{
269    const char *p, *ep;
270    size_t pfxlen;
271    char *nstr, *q;
272
273    if (!str)
274        return NULL;
275
276    while (*str && is_ctl_or_whitespace(*str))
277        str++;
278
279    if (!*str)
280        return NULL;
281
282    ep = p = str;
283    while (*p) {
284        if (!is_ctl_or_whitespace(*p))
285            ep = p+1;
286        p++;
287    }
288
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';
298
299    return nstr;
300}
301
302static void sysappend_set_sysff(const uint8_t *type)
303{
304    static char sysff_str[] = "SYSFF=000";
305
306    if (!type || !*type)
307        return;
308   
309    sprintf(sysff_str+6, "%u", *type & 0x7f);
310    sysappend_strings[SYSAPPEND_SYSFF] = sysff_str;
311}
312
313struct cpuflag {
314    uint8_t bit;
315    char flag;
316};
317
318static void sysappend_set_cpu(void)
319{
320    static char cpu_str[6+6] = "CPU=";
321    char *p = cpu_str + 4;
322    static const struct cpuflag cpuflags[] = {
323        { 0*32+ 6, 'P' }, /* PAE */
324        { 1*32+ 5, 'V' }, /* VMX */
325        { 1*32+ 6, 'T' }, /* SMX (TXT) */
326        { 2*32+20, 'X' }, /* XD/NX */
327        { 2*32+29, 'L' }, /* Long mode (x86-64) */
328        { 3*32+ 2, 'S' }, /* SVM */
329        { 0, 0 }
330    };
331    const struct cpuflag *cf;
332
333    /* Not technically from DMI, but it fit here... */
334
335    if (!cpu_has_eflag(EFLAGS_ID)) {
336        /* No CPUID */
337        *p++ = cpu_has_eflag(EFLAGS_AC) ? '4' : '3';
338    } else {
339        uint32_t flags[4], eax, ebx, family;
340        uint32_t ext_level;
341
342        cpuid(1, &eax, &ebx, &flags[1], &flags[0]);
343        family = (eax & 0x0ff00f00) >> 8;
344        *p++ = family >= 6 ? '6' : family + '0';
345       
346        ext_level = cpuid_eax(0x80000000);
347        if (ext_level >= 0x80000001 && ext_level <= 0x8000ffff) {
348            cpuid(0x80000001, &eax, &ebx, &flags[3], &flags[2]);
349        } else {
350            flags[2] = flags[3] = 0;
351        }
352
353        for (cf = cpuflags; cf->flag; cf++) {
354            if (test_bit(cf->bit, flags))
355                *p++ = cf->flag;
356        }
357    }
358
359    *p = '\0';
360
361    sysappend_strings[SYSAPPEND_CPU] = cpu_str;
362}
363
364void dmi_init(void)
365{
366    const struct sysappend_dmi_strings *ds;
367
368    sysappend_set_cpu();
369
370    dmi_find_header();
371    if (!dmi)
372        return;
373
374    sysappend_set_uuid(dmi_find_data(1, 0x08, 16));
375    sysappend_set_sysff(dmi_find_data(3, 0x05, 1));
376
377    for (ds = dmi_strings; ds->prefix; ds++) {
378        if (!sysappend_strings[ds->sa]) {
379            const char *str = dmi_find_string(ds->index, ds->offset);
380            sysappend_strings[ds->sa] = dmi_install_string(ds->prefix, str);
381        }
382    }
383}
Note: See TracBrowser for help on using the repository browser.