Home | History | Annotate | Download | only in core
      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 
     39 struct dmi_table {
     40     uint8_t type;
     41     uint8_t length;
     42     uint16_t handle;
     43 };
     44 
     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;
     53 };
     54 
     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];
     64 
     65     struct dmi_header dmi;
     66 };
     67 
     68 static const struct dmi_header *dmi;
     69 
     70 static 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 
     81 static 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 
     90 static 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  */
    102 static 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  */
    122 static 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  */
    167 static 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 
    234 struct sysappend_dmi_strings {
    235     const char *prefix;
    236     enum syslinux_sysappend sa;
    237     uint8_t index;
    238     uint8_t offset;
    239 };
    240 
    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 }
    256 };
    257 
    258 /*
    259  * Install the string in the string table, if nonempty, after
    260  * removing leading and trailing whitespace.
    261  */
    262 static bool is_ctl_or_whitespace(char c)
    263 {
    264     return (c <= ' ' || c == '\x7f');
    265 }
    266 
    267 static 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 
    302 static 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 
    313 struct cpuflag {
    314     uint8_t bit;
    315     char flag;
    316 };
    317 
    318 static 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 
    364 void 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 }
    384