Patchworkβ internal dmi decoder

login
register
about
Submitter Sean Nelson
Date 2010-08-08 23:05:55
Message ID <4C5F3853.8020100@gmail.com>
Download mbox | patch
Permalink /patch/1729/
State Superseded
Headers show

Comments

Sean Nelson - 2010-08-08 23:05:55
Here is a new version of the internal dmi decoding. To use the new 
decoder, the user needs to use "-p internal:dmi=new", to cross-check 
with the external dmidecode use "-p internal:dmi=check". Hopefully if 
this is comitted, it will make it easier for people to test the new 
internal decoder.

Signed-off-by: Sean Nelson <audiohacked@gmail.com>
Carl-Daniel Hailfinger - 2010-08-09 00:25:47
Thanks for updating the patch.
Just a few short comments, this is not a full review.

On 09.08.2010 01:05, Sean Nelson wrote:
>  Here is a new version of the internal dmi decoding. To use the new
> decoder, the user needs to use "-p internal:dmi=new", to cross-check
> with the external dmidecode use "-p internal:dmi=check". Hopefully if
> this is comitted, it will make it easier for people to test the new
> internal decoder.
>
> Signed-off-by: Sean Nelson <audiohacked@gmail.com>
>
> diff --git a/dmi.c b/dmi.c
> index f18907f..b2a5daa 100644
> --- a/dmi.c
> +++ b/dmi.c
> @@ -1,76 +1,227 @@
>  /*
>   * This file is part of the flashrom project.
>   *
>   * Copyright (C) 2009,2010 Michael Karcher
> + * Copyright (C) 2010 Sean Nelson
>   *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License as published by
>   * the Free Software Foundation; either version 2 of the License, or
>   * (at your option) any later version.
>   *
>   * This program is distributed in the hope that it will be useful,
>   * but WITHOUT ANY WARRANTY; without even the implied warranty of
>   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>   * GNU General Public License for more details.
>   *
>   * You should have received a copy of the GNU General Public License
>   * along with this program; if not, write to the Free Software
>   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
>   */
>  
>  #include <string.h>
>  #include <stdio.h>
>  #include <stdlib.h>
>  
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/mman.h>
>   

Will this work on DOS and Windows?


> +
>  #include "flash.h"
>  #include "programmer.h"
>  
>  int has_dmi_support = 0;
>  
>  #if STANDALONE
>   

Hm. STANDALONE should activate the builtin decoder. We might elect to
have a separate CONFIG_DMI which would replace !STANDALONE in this file.


>  
>  /* Stub to indicate missing DMI functionality.
>   * has_dmi_support is 0 by default, so nothing to do here.
>   * Because dmidecode is not available on all systems, the goal is to implement
>   * the DMI subset we need directly in this file.
>   */
>  void dmi_init(void)
>  {
>  }
>  
>  int dmi_match(const char *pattern)
>  {
>  	return 0;
>  }
>  
>  #else /* STANDALONE */
>  
> -static const char *dmidecode_names[] = {
> -	"system-manufacturer",
> -	"system-product-name",
> -	"system-version",
> -	"baseboard-manufacturer",
> -	"baseboard-product-name",
> -	"baseboard-version",
> +/* the actual code, based on the dmidecode parsing logic */
> +static const struct string_keyword {
>   

Kill string_keyword.


> +	const char *keyword;
> +	unsigned char type;
> +	unsigned char offset;
> +} flashrom_dmi_strings[] = {
> +	{ "system-manufacturer", 1, 0x04 },
> +	{ "system-product-name", 1, 0x05 },
> +	{ "system-version", 1, 0x06 },
> +	{ "baseboard-manufacturer", 2, 0x04 },
> +	{ "baseboard-product-name", 2, 0x05 },
> +	{ "baseboard-version", 2, 0x06 },
> +	{ "chassis-type", 3, 0x05 },
>  };
>  
>  #define DMI_COMMAND_LEN_MAX 260
>  static const char *dmidecode_command = "dmidecode";
> +static char *external_dmi[ARRAY_SIZE(flashrom_dmi_strings)];
> +#define DMI_MAX_ANSWER_LEN 4096
>  
> -static char *dmistrings[ARRAY_SIZE(dmidecode_names)];
> +static char *dmistrings[ARRAY_SIZE(flashrom_dmi_strings)];
>  
> -/* Strings longer than 4096 in DMI are just insane. */
>   

Why did the comment disappear?


> -#define DMI_MAX_ANSWER_LEN 4096
> +#define WORD(x) (unsigned short)(*(const unsigned short *)(x))
> +#define DWORD(x) (unsigned int)(*(const unsigned int *)(x))
>   

Ugh. Those #defines are really ugly. Can't you use mmio_read[bwl] or
something similar?


> +
> +static int checksum(const unsigned char *buf, size_t len)
>   

dmi_checksum?


> +{
> +	unsigned char sum = 0;
> +	size_t a;
> +
> +	for (a = 0; a < len; a++)
> +		sum += buf[a];
> +	return (sum == 0);
> +}
> +
> +static char *dmi_string(char *bp, unsigned char length, unsigned char s)
>   

Better names for bp and s would be appreciated unless this is a straight
copy from the original dmidecode code (in which case we'd have to add
the author to the copyright header).


> +{
> +	size_t i, len;
> +
> +	if (s == 0)
> +		return "Not Specified";
> +
> +	bp += length;
> +	while (s > 1 && *bp) {
> +		bp += strlen(bp);
> +		bp++;
>   

We happily walk off the cliff here (mapped area) if the DMI strings
contain garbage.


> +		s--;
> +	}
> +
> +	if (!*bp)
> +		return "<BAD INDEX>";
> +
> +	len = strlen(bp);
> +	for (i = 0; i < len; i++)
> +		if (bp[i] < 32 || bp[i] == 127)
> +			bp[i] = '.';
>   

We write to bp?


> +
> +	return bp;
> +}
> +
> +static int dmi_chassis_type(unsigned char code)
> +{
> +	switch(code) {
> +		case 0x08: /* Portable */
> +		case 0x09: /* Laptop */
> +		case 0x0A: /* Notebook */
> +		case 0x0E: /* Sub Notebook */
> +			return 1;
> +		default:
> +			return 0;
> +	}
> +}
> +
> +static void dmi_table(unsigned int base, unsigned short len, unsigned short num)
> +{
> +	unsigned char *data;
> +	unsigned char *dmi_table_mem;
> +	int key;
> +	int i=0, j=0;
>   

spaces


> +
> +	dmi_table_mem = physmap_try_ro("DMI Tables", base, len);
>   

If physmap_try_ro fails this will explode spectacularly.


> +	data = dmi_table_mem;
> +
> +	/* 4 is the length of an SMBIOS structure header */
> +	while (i < num && data+4 <= dmi_table_mem + len) {
> +		unsigned char *next;
> +		/*
> +		 * If a short entry is found (less than 4 bytes), not only it
> +		 * is invalid, but we cannot reliably locate the next entry.
> +		 * Better stop at this point, and let the user know his/her
> +		 * table is broken.
> +		 */
> +		if (data[1] < 4) {
> +			msg_perr("Invalid entry length (%u). DMI table is "
> +				"broken! Stop.\n\n", (unsigned int)data[1]);
> +			break;
> +		}
> +
> +		/* Stop decoding after chassis segment */
> +		if (data[0] == 4)
> +			break;
> +
> +		/* look for the next handle */
> +		next = data + data[1];
> +		while (next - dmi_table_mem + 1 < len && (next[0] != 0 || next[1] != 0))
> +			next++;
> +		next += 2;
> +
> +		for (j = 0; j < ARRAY_SIZE(flashrom_dmi_strings); j++)
> +		{
> +			unsigned char offset = flashrom_dmi_strings[j].offset;
> +			unsigned char type = flashrom_dmi_strings[j].type;
> +
> +			if (offset >= data[1])
> +				return;
> +
> +			key = (type << 8) | offset;
> +
> +			switch (key)
> +			{
> +			case 0x305: /* detect if laptop */
> +				if (dmi_chassis_type(data[offset])) {
> +					msg_pdbg("Laptop detected via DMI\n");
>   

Can you print the chassis type in hex as well, even if it is not a
laptop? This will be helpful if a vendor screws up the DMI tables.


> +					is_laptop = 1;
> +				}
> +				break;
> +			default:
> +				if (type == data[0]) {
> +					dmistrings[j] = dmi_string((char*)data,	data[1], data[offset]);
> +					msg_pinfo("DMI string %s: \"%s\"\n",
> +						flashrom_dmi_strings[j].keyword, dmistrings[j]);
> +				}
> +			}
> +		}
> +		data = next;
> +		i++;
> +	}
> +
> +	physunmap(dmi_table, len);
> +}
> +
> +static int smbios_decode(unsigned char *buf)
> +{
> +	if (!checksum(buf, buf[0x05]) 
> +		|| (memcmp(buf + 0x10, "_DMI_", 5) != 0) 
> +		|| !checksum(buf + 0x10, 0x0F))
> +			return 0;
> +
> +	dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C));
> +
> +	return 1;
> +}
> +
> +static int legacy_decode(unsigned char *buf)
> +{
> +	if (!checksum(buf, 0x0F))
> +		return 0;
> +
> +	dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C));
> +
> +	return 1;
> +}
>  
>  static char *get_dmi_string(const char *string_name)
>  {
>  	FILE *dmidecode_pipe;
>  	char *result;
>  	char answerbuf[DMI_MAX_ANSWER_LEN];
>  	char commandline[DMI_COMMAND_LEN_MAX + 40];
>  
>  	snprintf(commandline, sizeof(commandline),
>  		 "%s -s %s", dmidecode_command, string_name);
>  	dmidecode_pipe = popen(commandline, "r");
>  	if (!dmidecode_pipe) {
>  		msg_perr("DMI pipe open error\n");
> @@ -109,45 +260,111 @@ static char *get_dmi_string(const char *string_name)
>  	    answerbuf[strlen(answerbuf) - 1] == '\n')
>  		answerbuf[strlen(answerbuf) - 1] = 0;
>  	msg_pdbg("DMI string %s: \"%s\"\n", string_name, answerbuf);
>  
>  	result = strdup(answerbuf);
>  	if (!result)
>  		puts("WARNING: Out of memory - DMI support fails");
>  
>  	return result;
>  }
>  
>  void dmi_init(void)
>  {
> -	int i;
> -	char *chassis_type;
> +	int found = 0;
> +	size_t fp;
> +	unsigned char *dmi_mem = NULL;
> +	char *arg = NULL;
> +	int use_new_dmi = 0;
> +	int i = 0;
> +
> +	arg = extract_programmer_param("dmi");
> +	if (arg && !strcmp(arg,"new")) {
> +		use_new_dmi = 1;
> +	} else if (arg && !strcmp(arg, "check")) {
> +		use_new_dmi = 2;
> +	} else if (arg && !strlen(arg)) {
> +		msg_perr("Missing argument for dmi. Falling back to external dmi\n");
> +	} else if (arg) {
> +		msg_perr("Unknown argument for dmi: %s. Falling back to external dmi\n", arg);
>   

You could also return here. If the user doesn't know what he wants, we
shouldn't continue.


> +	}
> +	free(arg);
>  
>  	has_dmi_support = 1;
> -	for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++) {
> -		dmistrings[i] = get_dmi_string(dmidecode_names[i]);
> -		if (!dmistrings[i]) {
> -			has_dmi_support = 0;
> -			return;
> +
> +	if (use_new_dmi > 0) {
> +		dmi_mem = physmap_try_ro("DMI", 0xF0000, 0x10000);
> +
> +		if (!dmi_mem)
> +			goto func_exit;
> +
> +		for (fp = 0; fp <= 0xFFF0; fp += 16) {
> +			if (memcmp(dmi_mem + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
> +				if (smbios_decode(dmi_mem+fp)) {
> +					found++;
> +					fp += 16;
> +				}
> +			}
> +			else if (memcmp(dmi_mem + fp, "_DMI_", 5) == 0)
> +				if (legacy_decode(dmi_mem + fp))
> +					found++;
>  		}
>  	}
>  
> -	chassis_type = get_dmi_string("chassis-type");
> -	if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
> -			     !strcmp(chassis_type, "Portable"))) {
> -		msg_pdbg("Laptop detected via DMI\n");
> -		is_laptop = 1;
> +	if (use_new_dmi == 2) {
> +		for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings); i++)
> +		{
> +			external_dmi[i] = get_dmi_string(flashrom_dmi_strings[i].keyword);
> +			if (!external_dmi[i])
> +				goto func_exit;
> +		}
> +
> +		for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings)-1; i++)
>   

spaces


> +		{
> +			if (strcmp(dmistrings[i], external_dmi[i])) 
> +				msg_perr("dmidecode vs internal-dmi differs: %s\n", flashrom_dmi_strings[i].keyword);
> +			else
> +				msg_pspew("Matching of dmidecode and internal-dmi succeeded!\n");
>   

Hm. Do we cross-check the laptop info?


> +		}
>  	}
> -	free(chassis_type);
> +	
> +	if (use_new_dmi == 0) {
> +		char *chassis_type;
> +		has_dmi_support = 1;
> +		for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++) {
> +			dmistrings[i] = get_dmi_string(flashrom_dmi_strings[i].keyword);
> +			if (!dmistrings[i]) {
> +				has_dmi_support = 0;
> +				return;
> +			}
> +		}
> +
> +		chassis_type = get_dmi_string("chassis-type");
> +		if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
> +				     !strcmp(chassis_type, "Portable"))) {
> +			msg_pdbg("Laptop detected via DMI\n");
> +			is_laptop = 1;
> +		}
> +		free(chassis_type);
> +	}
> +
> +func_exit:
> +	if (!found)
> +	{
> +		msg_pinfo("No DMI table found.\n");
> +		has_dmi_support = 0;
> +	}
> +
> +	physunmap(dmi_mem, 0x10000);
>  }
>  
>  /**
>   * Does an substring/prefix/postfix/whole-string match.
>   *
>   * The pattern is matched as-is. The only metacharacters supported are '^'
>   * at the beginning and '$' at the end. So you can look for "^prefix",
>   * "suffix$", "substring" or "^complete string$".
>   *
>   * @param value The string to check.
>   * @param pattern The pattern.
>   * @return Nonzero if pattern matches.
>   */
> @@ -185,21 +402,21 @@ static int dmi_compare(const char *value, const char *pattern)
>  	if (anchored)
>  		return strncmp(value, pattern, patternlen) == 0;
>  	else
>  		return strstr(value, pattern) != NULL;
>  }
>  
>  int dmi_match(const char *pattern)
>  {
>  	int i;
>  
>  	if (!has_dmi_support)
>  		return 0;
>  
> -	for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++)
> +	for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++)
>  		if (dmi_compare(dmistrings[i], pattern))
>  			return 1;
>  
>  	return 0;
>  }
>  
>  #endif /* STANDALONE */
>   
>

Regards,
Carl-Daniel
Idwer Vollering - 2010-08-09 01:00:40
2010/8/9 Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>

> Thanks for updating the patch.
> Just a few short comments, this is not a full review.
>
> On 09.08.2010 01:05, Sean Nelson wrote:
> >  Here is a new version of the internal dmi decoding. To use the new
> > decoder, the user needs to use "-p internal:dmi=new", to cross-check
> > with the external dmidecode use "-p internal:dmi=check". Hopefully if
> > this is comitted, it will make it easier for people to test the new
> > internal decoder.
> >
> > Signed-off-by: Sean Nelson <audiohacked@gmail.com>
> >
> > diff --git a/dmi.c b/dmi.c
> > index f18907f..b2a5daa 100644
> > --- a/dmi.c
> > +++ b/dmi.c
> > @@ -1,76 +1,227 @@
> >  /*
> >   * This file is part of the flashrom project.
> >   *
> >   * Copyright (C) 2009,2010 Michael Karcher
> > + * Copyright (C) 2010 Sean Nelson
> >   *
> >   * This program is free software; you can redistribute it and/or modify
> >   * it under the terms of the GNU General Public License as published by
> >   * the Free Software Foundation; either version 2 of the License, or
> >   * (at your option) any later version.
> >   *
> >   * This program is distributed in the hope that it will be useful,
> >   * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >   * GNU General Public License for more details.
> >   *
> >   * You should have received a copy of the GNU General Public License
> >   * along with this program; if not, write to the Free Software
> >   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
> USA
> >   */
> >
> >  #include <string.h>
> >  #include <stdio.h>
> >  #include <stdlib.h>
> >
> > +#include <unistd.h>
> > +#include <fcntl.h>
> > +#include <sys/mman.h>
> >
>
> Will this work on DOS and Windows?
>

DOS:

flashrom v0.9.2-r1136 on FreeDOS 7 (i786), built with libpci 3.1.5, GCC
4.3.2, little endian
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OS timer resolution is 170000 usecs, 1873M loops
per second, 10 myus = 0 us, 100 myus = 0 us, 1000 myus = 0 us, 10000 myus =
0 us, 680000 myus = 600000 us, OK.
Initializing internal programmer
No coreboot table found.
DMI string system-manufacturer: ""
DMI string system-product-name: ""
DMI string system-version: ""
DMI string baseboard-manufacturer: ""
DMI string baseboard-product-name: ""
DMI string baseboard-version: ""
DMI string chassis-type: ""
DMI string chassis-type: ""
No DMI table found.

Linux (Arch Linux):

flashrom v0.9.2-r1136 on Linux 2.6.34-ARCH (i686), built with libpci 3.1.7,
GCC 4.5.0 20100610 (prerelease), little
endi                                      an
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OS timer resolution is 1 usecs, 2002M loops per
second, 10 myus = 10 us, 100 myus = 100 us,
10                                      00 myus = 1136 us, 10000 myus =
10275 us, 4 myus = 5 us, OK.
Initializing internal programmer
No coreboot table found.
sh: dmidecode: command not found
dmidecode execution unsucessfull - continuing without DMI info

FreeBSD:

flashrom v0.9.2-r1136 on FreeBSD 8.1-RELEASE (i386), built with libpci
3.1.7, GCC 4.2.1 20070719  [FreeBSD], little endian
flashrom is free software, get the source code at http://www.flashrom.org

Calibrating delay loop... OS timer resolution is 6 usecs, 1973M loops per
second, 10 myus = 11 us, 100 myus = 112 us, 1000 myus = 1005 us, 10000 myus
= 9742 us, 24 myus = 48 us, OK.
Initializing internal programmer
No coreboot table found.
DMI string system-manufacturer: "        "
DMI string system-product-name: "P4i65GV"
DMI string system-version: "1.00"
DMI string baseboard-manufacturer: "        "
DMI string baseboard-product-name: "P4i65GV"
DMI string baseboard-version: "1.0"
DMI string chassis-type: ""
DMI string chassis-type: ""
No DMI table found.

>
>
> > +
> >  #include "flash.h"
> >  #include "programmer.h"
> >
> >  int has_dmi_support = 0;
> >
> >  #if STANDALONE
> >
>
> Hm. STANDALONE should activate the builtin decoder. We might elect to
> have a separate CONFIG_DMI which would replace !STANDALONE in this file.
>
>
> >
> >  /* Stub to indicate missing DMI functionality.
> >   * has_dmi_support is 0 by default, so nothing to do here.
> >   * Because dmidecode is not available on all systems, the goal is to
> implement
> >   * the DMI subset we need directly in this file.
> >   */
> >  void dmi_init(void)
> >  {
> >  }
> >
> >  int dmi_match(const char *pattern)
> >  {
> >       return 0;
> >  }
> >
> >  #else /* STANDALONE */
> >
> > -static const char *dmidecode_names[] = {
> > -     "system-manufacturer",
> > -     "system-product-name",
> > -     "system-version",
> > -     "baseboard-manufacturer",
> > -     "baseboard-product-name",
> > -     "baseboard-version",
> > +/* the actual code, based on the dmidecode parsing logic */
> > +static const struct string_keyword {
> >
>
> Kill string_keyword.
>
>
> > +     const char *keyword;
> > +     unsigned char type;
> > +     unsigned char offset;
> > +} flashrom_dmi_strings[] = {
> > +     { "system-manufacturer", 1, 0x04 },
> > +     { "system-product-name", 1, 0x05 },
> > +     { "system-version", 1, 0x06 },
> > +     { "baseboard-manufacturer", 2, 0x04 },
> > +     { "baseboard-product-name", 2, 0x05 },
> > +     { "baseboard-version", 2, 0x06 },
> > +     { "chassis-type", 3, 0x05 },
> >  };
> >
> >  #define DMI_COMMAND_LEN_MAX 260
> >  static const char *dmidecode_command = "dmidecode";
> > +static char *external_dmi[ARRAY_SIZE(flashrom_dmi_strings)];
> > +#define DMI_MAX_ANSWER_LEN 4096
> >
> > -static char *dmistrings[ARRAY_SIZE(dmidecode_names)];
> > +static char *dmistrings[ARRAY_SIZE(flashrom_dmi_strings)];
> >
> > -/* Strings longer than 4096 in DMI are just insane. */
> >
>
> Why did the comment disappear?
>
>
> > -#define DMI_MAX_ANSWER_LEN 4096
> > +#define WORD(x) (unsigned short)(*(const unsigned short *)(x))
> > +#define DWORD(x) (unsigned int)(*(const unsigned int *)(x))
> >
>
> Ugh. Those #defines are really ugly. Can't you use mmio_read[bwl] or
> something similar?
>
>
> > +
> > +static int checksum(const unsigned char *buf, size_t len)
> >
>
> dmi_checksum?
>
>
> > +{
> > +     unsigned char sum = 0;
> > +     size_t a;
> > +
> > +     for (a = 0; a < len; a++)
> > +             sum += buf[a];
> > +     return (sum == 0);
> > +}
> > +
> > +static char *dmi_string(char *bp, unsigned char length, unsigned char s)
> >
>
> Better names for bp and s would be appreciated unless this is a straight
> copy from the original dmidecode code (in which case we'd have to add
> the author to the copyright header).
>
>
> > +{
> > +     size_t i, len;
> > +
> > +     if (s == 0)
> > +             return "Not Specified";
> > +
> > +     bp += length;
> > +     while (s > 1 && *bp) {
> > +             bp += strlen(bp);
> > +             bp++;
> >
>
> We happily walk off the cliff here (mapped area) if the DMI strings
> contain garbage.
>
>
> > +             s--;
> > +     }
> > +
> > +     if (!*bp)
> > +             return "<BAD INDEX>";
> > +
> > +     len = strlen(bp);
> > +     for (i = 0; i < len; i++)
> > +             if (bp[i] < 32 || bp[i] == 127)
> > +                     bp[i] = '.';
> >
>
> We write to bp?
>
>
> > +
> > +     return bp;
> > +}
> > +
> > +static int dmi_chassis_type(unsigned char code)
> > +{
> > +     switch(code) {
> > +             case 0x08: /* Portable */
> > +             case 0x09: /* Laptop */
> > +             case 0x0A: /* Notebook */
> > +             case 0x0E: /* Sub Notebook */
> > +                     return 1;
> > +             default:
> > +                     return 0;
> > +     }
> > +}
> > +
> > +static void dmi_table(unsigned int base, unsigned short len, unsigned
> short num)
> > +{
> > +     unsigned char *data;
> > +     unsigned char *dmi_table_mem;
> > +     int key;
> > +     int i=0, j=0;
> >
>
> spaces
>
>
> > +
> > +     dmi_table_mem = physmap_try_ro("DMI Tables", base, len);
> >
>
> If physmap_try_ro fails this will explode spectacularly.
>
>
> > +     data = dmi_table_mem;
> > +
> > +     /* 4 is the length of an SMBIOS structure header */
> > +     while (i < num && data+4 <= dmi_table_mem + len) {
> > +             unsigned char *next;
> > +             /*
> > +              * If a short entry is found (less than 4 bytes), not only
> it
> > +              * is invalid, but we cannot reliably locate the next
> entry.
> > +              * Better stop at this point, and let the user know his/her
> > +              * table is broken.
> > +              */
> > +             if (data[1] < 4) {
> > +                     msg_perr("Invalid entry length (%u). DMI table is "
> > +                             "broken! Stop.\n\n", (unsigned
> int)data[1]);
> > +                     break;
> > +             }
> > +
> > +             /* Stop decoding after chassis segment */
> > +             if (data[0] == 4)
> > +                     break;
> > +
> > +             /* look for the next handle */
> > +             next = data + data[1];
> > +             while (next - dmi_table_mem + 1 < len && (next[0] != 0 ||
> next[1] != 0))
> > +                     next++;
> > +             next += 2;
> > +
> > +             for (j = 0; j < ARRAY_SIZE(flashrom_dmi_strings); j++)
> > +             {
> > +                     unsigned char offset =
> flashrom_dmi_strings[j].offset;
> > +                     unsigned char type = flashrom_dmi_strings[j].type;
> > +
> > +                     if (offset >= data[1])
> > +                             return;
> > +
> > +                     key = (type << 8) | offset;
> > +
> > +                     switch (key)
> > +                     {
> > +                     case 0x305: /* detect if laptop */
> > +                             if (dmi_chassis_type(data[offset])) {
> > +                                     msg_pdbg("Laptop detected via
> DMI\n");
> >
>
> Can you print the chassis type in hex as well, even if it is not a
> laptop? This will be helpful if a vendor screws up the DMI tables.
>
>
> > +                                     is_laptop = 1;
> > +                             }
> > +                             break;
> > +                     default:
> > +                             if (type == data[0]) {
> > +                                     dmistrings[j] =
> dmi_string((char*)data, data[1], data[offset]);
> > +                                     msg_pinfo("DMI string %s:
> \"%s\"\n",
> > +
> flashrom_dmi_strings[j].keyword, dmistrings[j]);
> > +                             }
> > +                     }
> > +             }
> > +             data = next;
> > +             i++;
> > +     }
> > +
> > +     physunmap(dmi_table, len);
> > +}
> > +
> > +static int smbios_decode(unsigned char *buf)
> > +{
> > +     if (!checksum(buf, buf[0x05])
> > +             || (memcmp(buf + 0x10, "_DMI_", 5) != 0)
> > +             || !checksum(buf + 0x10, 0x0F))
> > +                     return 0;
> > +
> > +     dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C));
> > +
> > +     return 1;
> > +}
> > +
> > +static int legacy_decode(unsigned char *buf)
> > +{
> > +     if (!checksum(buf, 0x0F))
> > +             return 0;
> > +
> > +     dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C));
> > +
> > +     return 1;
> > +}
> >
> >  static char *get_dmi_string(const char *string_name)
> >  {
> >       FILE *dmidecode_pipe;
> >       char *result;
> >       char answerbuf[DMI_MAX_ANSWER_LEN];
> >       char commandline[DMI_COMMAND_LEN_MAX + 40];
> >
> >       snprintf(commandline, sizeof(commandline),
> >                "%s -s %s", dmidecode_command, string_name);
> >       dmidecode_pipe = popen(commandline, "r");
> >       if (!dmidecode_pipe) {
> >               msg_perr("DMI pipe open error\n");
> > @@ -109,45 +260,111 @@ static char *get_dmi_string(const char
> *string_name)
> >           answerbuf[strlen(answerbuf) - 1] == '\n')
> >               answerbuf[strlen(answerbuf) - 1] = 0;
> >       msg_pdbg("DMI string %s: \"%s\"\n", string_name, answerbuf);
> >
> >       result = strdup(answerbuf);
> >       if (!result)
> >               puts("WARNING: Out of memory - DMI support fails");
> >
> >       return result;
> >  }
> >
> >  void dmi_init(void)
> >  {
> > -     int i;
> > -     char *chassis_type;
> > +     int found = 0;
> > +     size_t fp;
> > +     unsigned char *dmi_mem = NULL;
> > +     char *arg = NULL;
> > +     int use_new_dmi = 0;
> > +     int i = 0;
> > +
> > +     arg = extract_programmer_param("dmi");
> > +     if (arg && !strcmp(arg,"new")) {
> > +             use_new_dmi = 1;
> > +     } else if (arg && !strcmp(arg, "check")) {
> > +             use_new_dmi = 2;
> > +     } else if (arg && !strlen(arg)) {
> > +             msg_perr("Missing argument for dmi. Falling back to
> external dmi\n");
> > +     } else if (arg) {
> > +             msg_perr("Unknown argument for dmi: %s. Falling back to
> external dmi\n", arg);
> >
>
> You could also return here. If the user doesn't know what he wants, we
> shouldn't continue.
>
>
> > +     }
> > +     free(arg);
> >
> >       has_dmi_support = 1;
> > -     for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++) {
> > -             dmistrings[i] = get_dmi_string(dmidecode_names[i]);
> > -             if (!dmistrings[i]) {
> > -                     has_dmi_support = 0;
> > -                     return;
> > +
> > +     if (use_new_dmi > 0) {
> > +             dmi_mem = physmap_try_ro("DMI", 0xF0000, 0x10000);
> > +
> > +             if (!dmi_mem)
> > +                     goto func_exit;
> > +
> > +             for (fp = 0; fp <= 0xFFF0; fp += 16) {
> > +                     if (memcmp(dmi_mem + fp, "_SM_", 4) == 0 && fp <=
> 0xFFE0) {
> > +                             if (smbios_decode(dmi_mem+fp)) {
> > +                                     found++;
> > +                                     fp += 16;
> > +                             }
> > +                     }
> > +                     else if (memcmp(dmi_mem + fp, "_DMI_", 5) == 0)
> > +                             if (legacy_decode(dmi_mem + fp))
> > +                                     found++;
> >               }
> >       }
> >
> > -     chassis_type = get_dmi_string("chassis-type");
> > -     if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
> > -                          !strcmp(chassis_type, "Portable"))) {
> > -             msg_pdbg("Laptop detected via DMI\n");
> > -             is_laptop = 1;
> > +     if (use_new_dmi == 2) {
> > +             for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings); i++)
> > +             {
> > +                     external_dmi[i] =
> get_dmi_string(flashrom_dmi_strings[i].keyword);
> > +                     if (!external_dmi[i])
> > +                             goto func_exit;
> > +             }
> > +
> > +             for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings)-1; i++)
> >
>
> spaces
>
>
> > +             {
> > +                     if (strcmp(dmistrings[i], external_dmi[i]))
> > +                             msg_perr("dmidecode vs internal-dmi
> differs: %s\n", flashrom_dmi_strings[i].keyword);
> > +                     else
> > +                             msg_pspew("Matching of dmidecode and
> internal-dmi succeeded!\n");
> >
>
> Hm. Do we cross-check the laptop info?
>
>
> > +             }
> >       }
> > -     free(chassis_type);
> > +
> > +     if (use_new_dmi == 0) {
> > +             char *chassis_type;
> > +             has_dmi_support = 1;
> > +             for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++) {
> > +                     dmistrings[i] =
> get_dmi_string(flashrom_dmi_strings[i].keyword);
> > +                     if (!dmistrings[i]) {
> > +                             has_dmi_support = 0;
> > +                             return;
> > +                     }
> > +             }
> > +
> > +             chassis_type = get_dmi_string("chassis-type");
> > +             if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
> > +                                  !strcmp(chassis_type, "Portable"))) {
> > +                     msg_pdbg("Laptop detected via DMI\n");
> > +                     is_laptop = 1;
> > +             }
> > +             free(chassis_type);
> > +     }
> > +
> > +func_exit:
> > +     if (!found)
> > +     {
> > +             msg_pinfo("No DMI table found.\n");
> > +             has_dmi_support = 0;
> > +     }
> > +
> > +     physunmap(dmi_mem, 0x10000);
> >  }
> >
> >  /**
> >   * Does an substring/prefix/postfix/whole-string match.
> >   *
> >   * The pattern is matched as-is. The only metacharacters supported are
> '^'
> >   * at the beginning and '$' at the end. So you can look for "^prefix",
> >   * "suffix$", "substring" or "^complete string$".
> >   *
> >   * @param value The string to check.
> >   * @param pattern The pattern.
> >   * @return Nonzero if pattern matches.
> >   */
> > @@ -185,21 +402,21 @@ static int dmi_compare(const char *value, const
> char *pattern)
> >       if (anchored)
> >               return strncmp(value, pattern, patternlen) == 0;
> >       else
> >               return strstr(value, pattern) != NULL;
> >  }
> >
> >  int dmi_match(const char *pattern)
> >  {
> >       int i;
> >
> >       if (!has_dmi_support)
> >               return 0;
> >
> > -     for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++)
> > +     for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++)
> >               if (dmi_compare(dmistrings[i], pattern))
> >                       return 1;
> >
> >       return 0;
> >  }
> >
> >  #endif /* STANDALONE */
> >
> >
>
> Regards,
> Carl-Daniel
>
> --
> http://www.hailfinger.org/
>
>
> _______________________________________________
> flashrom mailing list
> flashrom@flashrom.org
> http://www.flashrom.org/mailman/listinfo/flashrom
>
Sean Nelson - 2010-09-20 00:28:53
Updated patch 2010-09-19. Uses its own copy of the dmi_chassis_types 
list. Each dmi_init prints if its either internal DMI or external 
Dmidecode. Selectable using -D'STANDALONE=1'

Signed-off-by: Sean Nelson <audiohacked@gmail.com>
Carl-Daniel Hailfinger - 2010-09-26 21:45:42
On 20.09.2010 02:28, Sean Nelson wrote:
> Updated patch 2010-09-19. Uses its own copy of the dmi_chassis_types
> list. Each dmi_init prints if its either internal DMI or external
> Dmidecode. Selectable using -D'STANDALONE=1'
>
> Signed-off-by: Sean Nelson <audiohacked@gmail.com>

Looks like you forgot to attach the patch.

Regards,
Carl-Daniel

Patch

diff --git a/dmi.c b/dmi.c
index f18907f..b2a5daa 100644
--- a/dmi.c
+++ b/dmi.c
@@ -1,76 +1,227 @@ 
 /*
  * This file is part of the flashrom project.
  *
  * Copyright (C) 2009,2010 Michael Karcher
+ * Copyright (C) 2010 Sean Nelson
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  */
 
 #include <string.h>
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
 #include "flash.h"
 #include "programmer.h"
 
 int has_dmi_support = 0;
 
 #if STANDALONE
 
 /* Stub to indicate missing DMI functionality.
  * has_dmi_support is 0 by default, so nothing to do here.
  * Because dmidecode is not available on all systems, the goal is to implement
  * the DMI subset we need directly in this file.
  */
 void dmi_init(void)
 {
 }
 
 int dmi_match(const char *pattern)
 {
 	return 0;
 }
 
 #else /* STANDALONE */
 
-static const char *dmidecode_names[] = {
-	"system-manufacturer",
-	"system-product-name",
-	"system-version",
-	"baseboard-manufacturer",
-	"baseboard-product-name",
-	"baseboard-version",
+/* the actual code, based on the dmidecode parsing logic */
+static const struct string_keyword {
+	const char *keyword;
+	unsigned char type;
+	unsigned char offset;
+} flashrom_dmi_strings[] = {
+	{ "system-manufacturer", 1, 0x04 },
+	{ "system-product-name", 1, 0x05 },
+	{ "system-version", 1, 0x06 },
+	{ "baseboard-manufacturer", 2, 0x04 },
+	{ "baseboard-product-name", 2, 0x05 },
+	{ "baseboard-version", 2, 0x06 },
+	{ "chassis-type", 3, 0x05 },
 };
 
 #define DMI_COMMAND_LEN_MAX 260
 static const char *dmidecode_command = "dmidecode";
+static char *external_dmi[ARRAY_SIZE(flashrom_dmi_strings)];
+#define DMI_MAX_ANSWER_LEN 4096
 
-static char *dmistrings[ARRAY_SIZE(dmidecode_names)];
+static char *dmistrings[ARRAY_SIZE(flashrom_dmi_strings)];
 
-/* Strings longer than 4096 in DMI are just insane. */
-#define DMI_MAX_ANSWER_LEN 4096
+#define WORD(x) (unsigned short)(*(const unsigned short *)(x))
+#define DWORD(x) (unsigned int)(*(const unsigned int *)(x))
+
+static int checksum(const unsigned char *buf, size_t len)
+{
+	unsigned char sum = 0;
+	size_t a;
+
+	for (a = 0; a < len; a++)
+		sum += buf[a];
+	return (sum == 0);
+}
+
+static char *dmi_string(char *bp, unsigned char length, unsigned char s)
+{
+	size_t i, len;
+
+	if (s == 0)
+		return "Not Specified";
+
+	bp += length;
+	while (s > 1 && *bp) {
+		bp += strlen(bp);
+		bp++;
+		s--;
+	}
+
+	if (!*bp)
+		return "<BAD INDEX>";
+
+	len = strlen(bp);
+	for (i = 0; i < len; i++)
+		if (bp[i] < 32 || bp[i] == 127)
+			bp[i] = '.';
+
+	return bp;
+}
+
+static int dmi_chassis_type(unsigned char code)
+{
+	switch(code) {
+		case 0x08: /* Portable */
+		case 0x09: /* Laptop */
+		case 0x0A: /* Notebook */
+		case 0x0E: /* Sub Notebook */
+			return 1;
+		default:
+			return 0;
+	}
+}
+
+static void dmi_table(unsigned int base, unsigned short len, unsigned short num)
+{
+	unsigned char *data;
+	unsigned char *dmi_table_mem;
+	int key;
+	int i=0, j=0;
+
+	dmi_table_mem = physmap_try_ro("DMI Tables", base, len);
+	data = dmi_table_mem;
+
+	/* 4 is the length of an SMBIOS structure header */
+	while (i < num && data+4 <= dmi_table_mem + len) {
+		unsigned char *next;
+		/*
+		 * If a short entry is found (less than 4 bytes), not only it
+		 * is invalid, but we cannot reliably locate the next entry.
+		 * Better stop at this point, and let the user know his/her
+		 * table is broken.
+		 */
+		if (data[1] < 4) {
+			msg_perr("Invalid entry length (%u). DMI table is "
+				"broken! Stop.\n\n", (unsigned int)data[1]);
+			break;
+		}
+
+		/* Stop decoding after chassis segment */
+		if (data[0] == 4)
+			break;
+
+		/* look for the next handle */
+		next = data + data[1];
+		while (next - dmi_table_mem + 1 < len && (next[0] != 0 || next[1] != 0))
+			next++;
+		next += 2;
+
+		for (j = 0; j < ARRAY_SIZE(flashrom_dmi_strings); j++)
+		{
+			unsigned char offset = flashrom_dmi_strings[j].offset;
+			unsigned char type = flashrom_dmi_strings[j].type;
+
+			if (offset >= data[1])
+				return;
+
+			key = (type << 8) | offset;
+
+			switch (key)
+			{
+			case 0x305: /* detect if laptop */
+				if (dmi_chassis_type(data[offset])) {
+					msg_pdbg("Laptop detected via DMI\n");
+					is_laptop = 1;
+				}
+				break;
+			default:
+				if (type == data[0]) {
+					dmistrings[j] = dmi_string((char*)data,	data[1], data[offset]);
+					msg_pinfo("DMI string %s: \"%s\"\n",
+						flashrom_dmi_strings[j].keyword, dmistrings[j]);
+				}
+			}
+		}
+		data = next;
+		i++;
+	}
+
+	physunmap(dmi_table, len);
+}
+
+static int smbios_decode(unsigned char *buf)
+{
+	if (!checksum(buf, buf[0x05]) 
+		|| (memcmp(buf + 0x10, "_DMI_", 5) != 0) 
+		|| !checksum(buf + 0x10, 0x0F))
+			return 0;
+
+	dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C));
+
+	return 1;
+}
+
+static int legacy_decode(unsigned char *buf)
+{
+	if (!checksum(buf, 0x0F))
+		return 0;
+
+	dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C));
+
+	return 1;
+}
 
 static char *get_dmi_string(const char *string_name)
 {
 	FILE *dmidecode_pipe;
 	char *result;
 	char answerbuf[DMI_MAX_ANSWER_LEN];
 	char commandline[DMI_COMMAND_LEN_MAX + 40];
 
 	snprintf(commandline, sizeof(commandline),
 		 "%s -s %s", dmidecode_command, string_name);
 	dmidecode_pipe = popen(commandline, "r");
 	if (!dmidecode_pipe) {
 		msg_perr("DMI pipe open error\n");
@@ -109,45 +260,111 @@  static char *get_dmi_string(const char *string_name)
 	    answerbuf[strlen(answerbuf) - 1] == '\n')
 		answerbuf[strlen(answerbuf) - 1] = 0;
 	msg_pdbg("DMI string %s: \"%s\"\n", string_name, answerbuf);
 
 	result = strdup(answerbuf);
 	if (!result)
 		puts("WARNING: Out of memory - DMI support fails");
 
 	return result;
 }
 
 void dmi_init(void)
 {
-	int i;
-	char *chassis_type;
+	int found = 0;
+	size_t fp;
+	unsigned char *dmi_mem = NULL;
+	char *arg = NULL;
+	int use_new_dmi = 0;
+	int i = 0;
+
+	arg = extract_programmer_param("dmi");
+	if (arg && !strcmp(arg,"new")) {
+		use_new_dmi = 1;
+	} else if (arg && !strcmp(arg, "check")) {
+		use_new_dmi = 2;
+	} else if (arg && !strlen(arg)) {
+		msg_perr("Missing argument for dmi. Falling back to external dmi\n");
+	} else if (arg) {
+		msg_perr("Unknown argument for dmi: %s. Falling back to external dmi\n", arg);
+	}
+	free(arg);
 
 	has_dmi_support = 1;
-	for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++) {
-		dmistrings[i] = get_dmi_string(dmidecode_names[i]);
-		if (!dmistrings[i]) {
-			has_dmi_support = 0;
-			return;
+
+	if (use_new_dmi > 0) {
+		dmi_mem = physmap_try_ro("DMI", 0xF0000, 0x10000);
+
+		if (!dmi_mem)
+			goto func_exit;
+
+		for (fp = 0; fp <= 0xFFF0; fp += 16) {
+			if (memcmp(dmi_mem + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) {
+				if (smbios_decode(dmi_mem+fp)) {
+					found++;
+					fp += 16;
+				}
+			}
+			else if (memcmp(dmi_mem + fp, "_DMI_", 5) == 0)
+				if (legacy_decode(dmi_mem + fp))
+					found++;
 		}
 	}
 
-	chassis_type = get_dmi_string("chassis-type");
-	if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
-			     !strcmp(chassis_type, "Portable"))) {
-		msg_pdbg("Laptop detected via DMI\n");
-		is_laptop = 1;
+	if (use_new_dmi == 2) {
+		for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings); i++)
+		{
+			external_dmi[i] = get_dmi_string(flashrom_dmi_strings[i].keyword);
+			if (!external_dmi[i])
+				goto func_exit;
+		}
+
+		for (i = 0;  i < ARRAY_SIZE(flashrom_dmi_strings)-1; i++)
+		{
+			if (strcmp(dmistrings[i], external_dmi[i])) 
+				msg_perr("dmidecode vs internal-dmi differs: %s\n", flashrom_dmi_strings[i].keyword);
+			else
+				msg_pspew("Matching of dmidecode and internal-dmi succeeded!\n");
+		}
 	}
-	free(chassis_type);
+	
+	if (use_new_dmi == 0) {
+		char *chassis_type;
+		has_dmi_support = 1;
+		for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++) {
+			dmistrings[i] = get_dmi_string(flashrom_dmi_strings[i].keyword);
+			if (!dmistrings[i]) {
+				has_dmi_support = 0;
+				return;
+			}
+		}
+
+		chassis_type = get_dmi_string("chassis-type");
+		if (chassis_type && (!strcmp(chassis_type, "Notebook") ||
+				     !strcmp(chassis_type, "Portable"))) {
+			msg_pdbg("Laptop detected via DMI\n");
+			is_laptop = 1;
+		}
+		free(chassis_type);
+	}
+
+func_exit:
+	if (!found)
+	{
+		msg_pinfo("No DMI table found.\n");
+		has_dmi_support = 0;
+	}
+
+	physunmap(dmi_mem, 0x10000);
 }
 
 /**
  * Does an substring/prefix/postfix/whole-string match.
  *
  * The pattern is matched as-is. The only metacharacters supported are '^'
  * at the beginning and '$' at the end. So you can look for "^prefix",
  * "suffix$", "substring" or "^complete string$".
  *
  * @param value The string to check.
  * @param pattern The pattern.
  * @return Nonzero if pattern matches.
  */
@@ -185,21 +402,21 @@  static int dmi_compare(const char *value, const char *pattern)
 	if (anchored)
 		return strncmp(value, pattern, patternlen) == 0;
 	else
 		return strstr(value, pattern) != NULL;
 }
 
 int dmi_match(const char *pattern)
 {
 	int i;
 
 	if (!has_dmi_support)
 		return 0;
 
-	for (i = 0; i < ARRAY_SIZE(dmidecode_names); i++)
+	for (i = 0; i < ARRAY_SIZE(flashrom_dmi_strings); i++)
 		if (dmi_compare(dmistrings[i], pattern))
 			return 1;
 
 	return 0;
 }
 
 #endif /* STANDALONE */