[flashrom] DMI matching patch

Carl-Daniel Hailfinger c-d.hailfinger.devel.2006 at gmx.net
Mon Jan 4 04:53:18 CET 2010


On 03.01.2010 01:09, Michael Karcher wrote:
> ^Matching via DMI: If a board is not uniquely identifiable by PCI
> device/subsystem IDs, a string can be specified to be looked for
> (case-sensitive, substring or anchored) for now in one of the following
> DMI items in addition to matching the PCI IDs:
>  - System Manufacturer
>  - System Product Name
>  - System Version
>  - Baseboard Manufacturer
>  - Baseboard Product Name
>  - Baseboard Version
>
> Strings are anchored re-like (^ at the beginning, $ at the end), but
> there are no plans to support full regular expressions. Field selection
> has been intentionally omitted as the match via PCI IDs should be strong
> enough that no cross-pollution between the different strings should ever
> occur, as long as the string is meaningfull.
>
> The match is only made if DMI info is available and the string matches.
> If no DMI info is available and the PCI IDs match, a warning is printed
> as the board can not be autodetected.
>
> It's still open to discussion whether we add an DMI override switch to
> specify a string that will definitely match, and whether this switch is
> only used if no DMI is available or whether it overrides or augments DMI
> data.
>   

Hm. Good question. I'd leave that to future discussion, though.


> DMI data is currently read using dmidecode. This tool is available for
> all major platforms except MacOS X. I heard that there also is a MacOS X
> version of dmidecode, but didn't investigate that.
>
> Signed-Off-By: Michael Karcher <flashrom at mkarcher.dialup.fu-berlin.de>
> ---
> Revision 2 of this patch:
>  - adjust coding style (one item per line, FILE *dmidecode_pipe, space
> after if, for, while) [carldani, comparing to other flashrom code]
>  - Implement anchoring of the string [inspired by carldani]
>  - use snprintf [agaran]
>  - dmidecode path in separate constant [inspired by carldani]
>   

Thanks. I stripped everything I agree with, the review below only has
the code snippets where I have comments.


> Index: dmi.c
> ===================================================================
> --- dmi.c	(Revision 0)
> +++ dmi.c	(Revision 0)
> @@ -0,0 +1,142 @@
> +/*
> + * This file is part of the flashrom project.
> + *
> + * Copyright (C) 2009 Michael Karcher
> + *
> + * 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 "flash.h"
> +
> +enum dmi_strings {
> +        DMI_SYS_VENDOR,
> +        DMI_SYS_PRODUCT,
> +        DMI_SYS_VERSION,
> +        DMI_BB_VENDOR,
> +        DMI_BB_PRODUCT,
> +        DMI_BB_VERSION,
> +        DMI_ID_INVALID /* This must always be the last entry */
> +};
>   

Please use tabs instead of spaces for indentation (all enum values above).


> +
> +const char * const dmidecode_names[DMI_ID_INVALID] = {
> +        "system-manufacturer",
> +        "system-product-name",
>   

Please use tabs instead of spaces for indentation (the two strings above).


> +	"system-version",
> +	"baseboard-manufacturer",
> +	"baseboard-product-name",
> +	"baseboard-version"
> +};
> +
> +#define DMI_COMMAND_LEN_MAX 260
> +const char * dmidecode_command = "dmidecode";
>   

Remove space after *.


> +
> +int has_dmi_support = 0;
> +char dmistrings[DMI_ID_INVALID][80];
>   

I'm not so happy about the static allocation here. Can't we use malloc()
for the read buffer and strdup() for filling in dmistrings?


> +
> +void dmi_init(void)
> +{
> +	FILE *dmidecode_pipe;
> +	int i;
> +	for (i = 0; i < DMI_ID_INVALID; i++)
> +	{
> +		char commandline[DMI_COMMAND_LEN_MAX+40];
> +		snprintf(commandline, sizeof(commandline),
> +		         "%s -s %s", dmidecode_command, dmidecode_names[i]);
> +		dmidecode_pipe = popen(commandline, "r");
>   

The popen() man page suggests fflush(NULL) before popen(). Not sure.
I'm not seeing any error checking on popen() here.


> +		fgets(dmistrings[i], sizeof(dmistrings[i]), dmidecode_pipe);
>   

Hm. Check the result of fgets()?


> +		if (ferror(dmidecode_pipe))
> +		{
> +			printf_debug("DMI pipe read error");
> +			pclose(dmidecode_pipe);
> +			return;
> +		}
> +		/* Toss all output above 80 characters away to prevent
> +		   deadlock on pclose. */
> +		while (!feof(dmidecode_pipe))
> +			getc(dmidecode_pipe);
> +		if (pclose(dmidecode_pipe) != 0)
> +		{
> +			printf_debug("DMI pipe close error");
> +			return;
> +		}
> +
> +		/* chomp trailing newline */
> +		if (dmistrings[i][0] != 0 &&
> +		    dmistrings[i][strlen(dmistrings[i]) - 1] == '\n')
> +		   	dmistrings[i][strlen(dmistrings[i]) - 1] = 0;
>   

Please use tabs instead of spaces for indentation (line above).


> +		printf_debug("DMI string %d: %s\n", i, dmistrings[i]);
> +	}
> +	has_dmi_support = 1;
> +}
> +
> +/**
> + * 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.
> + */
> +static int dmi_compare(const char * value, const char * pattern)
>   

Please remove the space after *.


> +{
> +        int anchored = 0;
> +        int patternlen;
> +        /* The empty string is part of all strings */
> +        if (pattern[0] == 0)
> +                return 1;
> +
> +        if (pattern[0] == '^') {
> +                anchored = 1;
> +                pattern++;
> +        }
> +
> +        patternlen = strlen(pattern);
> +        if (pattern[patternlen - 1] == '$') {
> +                int valuelen = strlen(value);
> +                patternlen--;
> +                if(patternlen > valuelen)
> +                        return 0;
> +
> +                /* full string match: require same length */
> +                if(anchored && valuelen != patternlen)
>   

Additional () please. Precedence is nice, but readability is even better.


> +                        return 0;
> +
> +                /* start character to make ends match */
> +                value += valuelen - patternlen;
> +                anchored = 1;
> +        }
> +
> +        if (anchored)
> +                return strncmp(value, pattern, patternlen) == 0;
> +        else
> +                return strstr(value, pattern) != NULL;
> +}
>   

Please use tabs instead of spaces for indentation (whole function above).


> +
> +int dmi_match(const char * identifier)
> +{
> +	int i;
> +	if (!has_dmi_support)
> +		return 0;
> +	for (i = 0;i < DMI_ID_INVALID; i++)
> +		if (dmi_compare(dmistrings[i], identifier))
>   

I believe the "match any of the strings" policy is fundamentally wrong.
If you and Luc think specifying multiple strings for the match is
overkill/overdesign, please do at least allow specifying the DMI string
this should be compared with.
Since this is a policy decision, we could have the inner loop here
inside #ifdef FIXME_POLICY_DECISION or whatever (resulting in a
non-match for all inputs) to get the infrstructure merged, then finish
the policy decision in a separate patch.


> +			return 1;
> +	return 0;
> +}
> Index: board_enable.c
> ===================================================================
> --- board_enable.c	(Revision 824)
> +++ board_enable.c	(Arbeitskopie)
> @@ -1162,6 +1162,11 @@
>   * provide an as complete set of pci ids as possible; autodetection is the
>   * preferred behaviour and we would like to make sure that matches are unique.
>   *
> + * If PCI IDs are not sufficient for board matching, the match can be further
> + * constrained by a string that has to be present in the DMI database for
> + * the baseboard or the system entry. The match type is case-sensitive
> + * substring match.
>   

Maybe add some text which tells people to always include DMI IDs if they
create new board matches. Not sure. It would certainly tighten board
matching, and we don't know how bad the subsystem ID overlap between
different boards is.


> + *
>   * The coreboot ids are used two fold. When running with a coreboot firmware,
>   * the ids uniquely matches the coreboot board identification string. When a
>   * legacy bios is installed and when autodetection is not possible, these ids
> @@ -1314,6 +1319,18 @@
>  			}
>  		}
>  
> +		if (board->dmi_identifier) {
> +		        if (!has_dmi_support) {
> +		                fprintf(stderr, "WARNING: Can't autodetect %s %s,"
> +		                       " DMI info unavailable.\n",
> +		                       board->vendor_name, board->board_name);
> +		                continue;
> +                        } else {
> +                                if (!dmi_match(board->dmi_identifier))
> +                                        continue;
> +                        }
> +                }
> +
>   

Some of the indentation in the block above uses spaces instead of tabs.
Spaces are only OK if they are less than 8 and if they are preceded by
tabs. The most common use case for spaces is indenting function
arguments or expressions.

One more iteration, and I think you can commit.

Regards,
Carl-Daniel

-- 
Developer quote of the year:
"We are juggling too many chainsaws and flaming arrows and tigers."





More information about the flashrom mailing list