[flashrom] [PATCH 1/2] layout: Add -i <region>[:<file>] support.

Stefan Tauner stefan.tauner at student.tuwien.ac.at
Thu Sep 19 01:29:37 CEST 2013


Add an optional sub-parameter to the -i parameter to allow building the
image to be written from multiple files. This will also allow regions to
be read from flash and written to separate image files in a later patch.
Existing function read_buf_from_file() is refined and reused to read the file.

Document the whole layout handling including the new features a bit better
and refine wording regarding files, images, layouts and regions as discussed.

Also, finally introduce helpers.c: a module containing utility functions to be
shared which do not fit elsewhere. The first function to be added is
unquote_string() which is used throughout this patch.

Abort for non-write operations if a layout file is given.

based on chromiumos'
d0ea9ed71e7f86bb8e8db2ca7c32a96de25343d8
Signed-off-by: David Hendricks <dhendrix at chromium.org>
Signed-off-by: Stefan Tauner <stefan.tauner at student.tuwien.ac.at>
---
 Makefile        |   2 +-
 cli_classic.c   |  15 +++++--
 dummyflasher.c  |   2 +-
 flash.h         |   6 ++-
 flashrom.8.tmpl | 130 ++++++++++++++++++++++++++++++++++----------------------
 flashrom.c      |  20 +++++----
 helpers.c       |  74 ++++++++++++++++++++++++++++++++
 layout.c        |  70 ++++++++++++++++++++++--------
 8 files changed, 236 insertions(+), 83 deletions(-)
 create mode 100644 helpers.c

diff --git a/Makefile b/Makefile
index 346d46a..69e0b9c 100644
--- a/Makefile
+++ b/Makefile
@@ -338,7 +338,7 @@ CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
 ###############################################################################
 # Library code.
 
-LIB_OBJS = layout.o flashrom.o udelay.o programmer.o
+LIB_OBJS = layout.o flashrom.o udelay.o programmer.o helpers.o
 
 ###############################################################################
 # Frontend related stuff.
diff --git a/cli_classic.c b/cli_classic.c
index 70bccb5..ed6d6aa 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -40,8 +40,8 @@ static void cli_classic_usage(const char *name)
 #if CONFIG_PRINT_WIKI == 1
 	       "-z|"
 #endif
-	       "-p <programmername>[:<parameters>] [-c <chipname>]\n"
-	       "[-E|(-r|-w|-v) <file>] [-l <layoutfile> [-i <imagename>]...] [-n] [-f]]\n"
+	       "-p <programmername>[:<parameter>[,<parameter>]...] [-c <chipname>]\n"
+	       "[-E|(-r|-w|-v) <imagefile>] [-l <layoutfile> [-i <region>[:<regionfile>]...] [-n] [-f]]\n"
 	       "[-V[V[V]]] [-o <logfile>]\n\n", name);
 
 	printf(" -h | --help                        print this help text\n"
@@ -54,8 +54,9 @@ static void cli_classic_usage(const char *name)
 	       " -c | --chip <chipname>             probe only for specified flash chip\n"
 	       " -f | --force                       force specific operations (see man page)\n"
 	       " -n | --noverify                    don't auto-verify\n"
-	       " -l | --layout <layoutfile>         read ROM layout from <layoutfile>\n"
-	       " -i | --image <name>                only flash image <name> from flash layout\n"
+	       " -l | --layout <layoutfile>         read layout from <layoutfile>\n"
+	       " -i | --include <region>[<:file>]   only flash image <region> from layout \n"
+	       "                                    (optionally with data from <file>)\n"
 	       " -o | --output <logfile>            log output to <logfile>\n"
 	       " -L | --list-supported              print supported devices\n"
 #if CONFIG_PRINT_WIKI == 1
@@ -370,6 +371,12 @@ int main(int argc, char *argv[])
 		ret = 1;
 		goto out;
 	}
+	if (layoutfile != NULL && !write_it) {
+		msg_gerr("Layout files are currently supported for write operations only.\n");
+		ret = 1;
+		goto out;
+	}
+
 	if (process_include_args()) {
 		ret = 1;
 		goto out;
diff --git a/dummyflasher.c b/dummyflasher.c
index 9c0d868..90d2261 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -389,7 +389,7 @@ int dummy_init(void)
 			msg_pdbg("matches.\n");
 			msg_pdbg("Reading %s\n", emu_persistent_image);
 			read_buf_from_file(flashchip_contents, emu_chip_size,
-					   emu_persistent_image);
+					   emu_persistent_image, NULL);
 		} else {
 			msg_pdbg("doesn't match.\n");
 		}
diff --git a/flash.h b/flash.h
index 7b88477..b4946a0 100644
--- a/flash.h
+++ b/flash.h
@@ -253,9 +253,12 @@ void print_banner(void);
 void list_programmers_linebreak(int startcol, int cols, int paren);
 int selfcheck(void);
 int doit(struct flashctx *flash, int force, const char *filename, int read_it, int write_it, int erase_it, int verify_it);
-int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
+int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename, const char *size_msg);
 int write_buf_to_file(unsigned char *buf, unsigned long size, const char *filename);
 
+/* helpers.c */
+int unquote_string(char **startp, char **endp, const char *delimiters);
+
 enum test_state {
 	OK = 0,
 	NT = 1,	/* Not tested */
@@ -321,6 +324,7 @@ int process_include_args(void);
 int read_romlayout(char *name);
 int handle_romentries(const struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents);
 void layout_cleanup(void);
+int build_new_image(struct flashctx *flash, int oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents);
 
 /* spi.c */
 struct spi_command {
diff --git a/flashrom.8.tmpl b/flashrom.8.tmpl
index b507a97..0ccdcf1 100644
--- a/flashrom.8.tmpl
+++ b/flashrom.8.tmpl
@@ -3,10 +3,10 @@
 flashrom \- detect, read, write, verify and erase flash chips
 .SH SYNOPSIS
 .B flashrom \fR[\fB\-h\fR|\fB\-R\fR|\fB\-L\fR|\fB\-z\fR|\
-\fB\-p\fR <programmername>[:<parameters>]
-               [\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\fB\-v\fR <file>] \
+\fB\-p\fR <programmername>[:<parameter>[,<parameter>]...]]
+               [\fB\-E\fR|\fB\-r\fR <imagefile>|\fB\-w\fR <imagefile>|\fB\-v\fR <imagefile>] \
 [\fB\-c\fR <chipname>]
-               [\fB\-l\fR <file> [\fB\-i\fR <image>]] [\fB\-n\fR] [\fB\-f\fR]]
+               [\fB\-l\fR <layoutfile> [\fB\-i\fR <regionname>[:<regionfile>]]...] [\fB\-n\fR] [\fB\-f\fR]]
          [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>]
 .SH DESCRIPTION
 .B flashrom
@@ -38,14 +38,14 @@ before you try to write a new image. All operations involving any chip access (p
 .B -p/--programmer
 option to be used (please see below).
 .TP
-.B "\-r, \-\-read <file>"
+.B "\-r, \-\-read <imagefile>"
 Read flash ROM contents and save them into the given
-.BR <file> .
+.BR <imagefile> .
 If the file already exists, it will be overwritten.
 .TP
-.B "\-w, \-\-write <file>"
+.B "\-w, \-\-write <imagefile>"
 Write
-.B <file>
+.B <imagefile>
 into flash ROM. This will first automatically
 .B erase
 the chip, then write to it.
@@ -65,14 +65,14 @@ recommended, you should only use it if you know what you are doing and if you
 feel that the time for verification takes too long.
 .sp
 Typical usage is:
-.B "flashrom \-p prog \-n \-w <file>"
+.B "flashrom \-p prog \-n \-w <imagefile>"
 .sp
 This option is only useful in combination with
 .BR \-\-write .
 .TP
-.B "\-v, \-\-verify <file>"
+.B "\-v, \-\-verify <imagefile>"
 Verify the flash ROM contents against the given
-.BR <file> .
+.BR <imagefile> .
 .TP
 .B "\-E, \-\-erase"
 Erase the flash ROM chip.
@@ -102,46 +102,6 @@ size for the flash bus.
 .sp
 * Force write even if write is known bad.
 .TP
-.B "\-l, \-\-layout <file>"
-Read ROM layout from
-.BR <file> .
-.sp
-flashrom supports ROM layouts. This allows you to flash certain parts of
-the flash chip only. A ROM layout file contains multiple lines with the
-following syntax:
-.sp
-.B "  startaddr:endaddr imagename"
-.sp
-.BR "startaddr " "and " "endaddr "
-are hexadecimal addresses within the ROM file and do not refer to any
-physical address. Please note that using a 0x prefix for those hexadecimal
-numbers is not necessary, but you can't specify decimal/octal numbers.
-.BR "imagename " "is an arbitrary name for the region/image from"
-.BR " startaddr " "to " "endaddr " "(both addresses included)."
-.sp
-Example:
-.sp
-  00000000:00008fff gfxrom
-  00009000:0003ffff normal
-  00040000:0007ffff fallback
-.sp
-If you only want to update the image named
-.BR "normal " "in a ROM based on the layout above, run"
-.sp
-.B "  flashrom \-p prog \-\-layout rom.layout \-\-image normal \-w some.rom"
-.sp
-To update only the images named
-.BR "normal " "and " "fallback" ", run:"
-.sp
-.B "  flashrom \-p prog \-l rom.layout \-i normal -i fallback \-w some.rom"
-.sp
-Overlapping sections are not supported.
-.TP
-.B "\-i, \-\-image <imagename>"
-Only flash region/image
-.B <imagename>
-from flash layout.
-.TP
 .B "\-L, \-\-list\-supported"
 List the flash chips, chipsets, mainboards, and external programmers
 (including PCI, USB, parallel port, and serial port based devices)
@@ -226,6 +186,76 @@ Some programmers have optional or mandatory parameters which are described
 in detail in the
 .B PROGRAMMER SPECIFIC INFO
 section. Support for some programmers can be disabled at compile time.
+.TP
+flashrom supports reading/writing/erasing/verifying flash chips in whole (default) or in part. To access only \
+parts of a chip one has to use layout files and respective arguments described below.
+.TP
+.B "\-l, \-\-layout <layoutfile>"
+Read layout entries from
+.BR <layoutfile> .
+.sp
+A layout file can contain comments which are
+started by a pound sign (#) and causes all following characters on the same line to be ignored with one
+exception: Every layout file
+.B must
+start with a comment in the following form to define the version of the file. Example for the current version 2:
+.sp
+.B "  # flashrom layout v2"
+.sp
+Every other line may contain either a command to include another layout file, a layout entry or white space
+(and an optional comment at the end).
+.sp
+To include another file you can use the
+.sp
+.B "  source <path>"
+.sp
+syntax where
+.B path
+specifies the absolute or relative path to the file to be included. Relative paths are interpreted to be
+relative to the file containing the include command. If the path contains spaces it has to be written in double
+quotes, or else only the part before the first space will be used.
+.sp
+Each layout entry describes an address region of the flash chip and gives it a name (hereinafter referred to as
+a region). One entry per line is allowed with the following syntax:
+.sp
+.B "  startaddr:endaddr regionname"
+.sp
+.BR "startaddr " "and " "endaddr "
+are addresses within the ROM image representing the flash ROM contents. They are interpreted in the 'usual' form
+i.e.\ a leading 0 means octal, leading 0x or 0X means hexadecimal, everything else is just decimal.
+.BR "regionname " "is the name for the region from " "startaddr " "to " "endaddr " "(both addresses included)."
+If the name contains spaces it has to be written in double quotes, or else only the part before the first space
+will be used.
+.sp
+Example content of file rom.layout:
+.sp
+  # flashrom layout v2
+  source /home/flashrom/include.layout
+  0x00000000:0x00008fff "gfx rom"
+  0x00009000:0x0003ffff normal
+  0x00040000:0x0007ffff fallback
+.sp
+.TP
+.B "\-i, \-\-include <name>[:<regionfile>]"
+Work on the flash region
+.B name
+instead of the full address space if a layout file is given and parsed correctly.
+Multiple such include parameters can be used to work on the union of different regions.
+.sp
+The optional
+.B regionfile
+parameter specifies the name of a file that is used to map the contents of that very region only.
+The optional file parameter causes the contents of this region to be replaced by the contents of the file
+specified here.
+.sp
+If you only want to update the regions named
+.BR "normal " "and " "gfx rom " "in a ROM based on the layout mentioned above, run"
+.sp
+.B "  flashrom \-p prog \-l rom.layout \-i normal -i ""gfx rom"" \-w some.rom"
+.sp
+Overlapping regions are resolved in an implementation-dependent manner (or may even yield an error) - do
+.BR "not " "rely on it."
+.sp
 .B "flashrom \-h"
 lists all supported programmers.
 .TP
diff --git a/flashrom.c b/flashrom.c
index 9169620..b98507c 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -1165,8 +1165,7 @@ notfound:
 	return chip - flashchips;
 }
 
-int read_buf_from_file(unsigned char *buf, unsigned long size,
-		       const char *filename)
+int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename, const char *size_msg)
 {
 #ifdef __LIBPAYLOAD__
 	msg_gerr("Error: No file I/O support in libpayload\n");
@@ -1186,8 +1185,10 @@ int read_buf_from_file(unsigned char *buf, unsigned long size,
 		return 1;
 	}
 	if (image_stat.st_size != size) {
-		msg_gerr("Error: Image size (%jd B) doesn't match the flash chip's size (%lu B)!\n",
-			 (intmax_t)image_stat.st_size, size);
+		if (size_msg == NULL)
+			size_msg = "the expected size";
+		msg_gerr("Error: Image size (%jd B) doesn't match %s (%lu B)!\n",
+			 (intmax_t)image_stat.st_size, size_msg, size);
 		fclose(image);
 		return 1;
 	}
@@ -1962,7 +1963,7 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it,
 	}
 
 	if (write_it || verify_it) {
-		if (read_buf_from_file(newcontents, size, filename)) {
+		if (read_buf_from_file(newcontents, size, filename, "the flash chip's size")) {
 			ret = 1;
 			goto out;
 		}
@@ -1995,9 +1996,12 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it,
 	}
 	msg_cinfo("done.\n");
 
-	// This should be moved into each flash part's code to do it 
-	// cleanly. This does the job.
-	handle_romentries(flash, oldcontents, newcontents);
+	if (handle_romentries(flash, oldcontents, newcontents)) {
+		msg_gerr("Could not prepare the data to be written due to problems with the layout,\n"
+			 "aborting.\n");
+		ret = 1;
+		goto out;
+	}
 
 	// ////////////////////////////////////////////////////////////
 
diff --git a/helpers.c b/helpers.c
new file mode 100644
index 0000000..ebb6f29
--- /dev/null
+++ b/helpers.c
@@ -0,0 +1,74 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2013 Stefan Tauner
+ *
+ * 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 "flash.h"
+
+/** Parse a \em possibly quoted string.
+ *
+ * The function expects \a startp to point to the string to be parsed without a prefix. If the
+ * string starts with a quotation mark it looks for the second one and removes both in-place, if not then it
+ * looks for the first character contained in \a delimiters and ends the wanted string there by replacing the
+ * character with '\0'. If \a delimiters is NULL an empty set is assumed, hence the whole start string equals
+ * the wanted string.
+ *
+ * After returning \a startp will point to a string that is either the first quoted part of the
+ * original string with the quotation marks removed, or the first word of that string before any delimiter.
+ * If \a endp is not NULL it will be set to point to the first character after the parsed string, or to the
+ * '\0' at the end of the string pointed to by \a startp if there are no more subsequent characters.
+ *
+ * @param start	Points to the input string.
+ * @param end	Is set to the first char following the input string.
+ * @return	-1 if a quotation mark is not matched,
+ *		0 on success,
+ *		1 if the parsed string is empty.
+ */
+int unquote_string(char **startp, char **endp, const char *delimiters)
+{
+	msg_gspew("unquoting \'%s\'\n", *startp);
+	char *end;
+	size_t len;
+	if (**startp == '"') {
+		(*startp)++;
+		len = strcspn(*startp, "\"");
+		if (*(*startp + len) != '"')
+			return -1;
+		
+	} else {
+		if (delimiters == NULL)
+			len = strlen(*startp);
+		else
+			len = strcspn(*startp, delimiters);
+	}
+	if (len == 0) {
+		*startp = '\0';
+		return 1;
+	}
+
+	end = *startp + len;
+	if (*end != '\0') {
+		*end = '\0';
+		end++;
+	}
+	if (endp != NULL)
+		*endp = end;
+	msg_gspew("%s: start=\'%s\', end=\'%s\'\n", __func__, *startp, end);
+	return 0;
+}
diff --git a/layout.c b/layout.c
index 86351b8..4d4f35c 100644
--- a/layout.c
+++ b/layout.c
@@ -33,6 +33,7 @@ typedef struct {
 	unsigned int end;
 	unsigned int included;
 	char name[256];
+	char *file;
 } romentry_t;
 
 /* rom_entries store the entries specified in a layout file and associated run-time data */
@@ -54,7 +55,7 @@ int read_romlayout(char *name)
 	romlayout = fopen(name, "r");
 
 	if (!romlayout) {
-		msg_gerr("ERROR: Could not open ROM layout (%s).\n",
+		msg_gerr("ERROR: Could not open layout file (%s).\n",
 			name);
 		return -1;
 	}
@@ -63,8 +64,7 @@ int read_romlayout(char *name)
 		char *tstr1, *tstr2;
 
 		if (num_rom_entries >= MAX_ROMLAYOUT) {
-			msg_gerr("Maximum number of ROM images (%i) in layout "
-				 "file reached.\n", MAX_ROMLAYOUT);
+			msg_gerr("Maximum number of entries (%i) in layout file reached.\n", MAX_ROMLAYOUT);
 			return 1;
 		}
 		if (2 != fscanf(romlayout, "%s %s\n", tempstr, rom_entries[num_rom_entries].name))
@@ -85,6 +85,7 @@ int read_romlayout(char *name)
 		rom_entries[num_rom_entries].start = strtol(tstr1, (char **)NULL, 16);
 		rom_entries[num_rom_entries].end = strtol(tstr2, (char **)NULL, 16);
 		rom_entries[num_rom_entries].included = 0;
+		rom_entries[num_rom_entries].file = NULL;
 		num_rom_entries++;
 	}
 
@@ -138,14 +139,9 @@ int register_include_arg(char *name)
 static int find_romentry(char *name)
 {
 	int i;
-
-	if (num_rom_entries == 0)
-		return -1;
-
 	msg_gspew("Looking for region \"%s\"... ", name);
 	for (i = 0; i < num_rom_entries; i++) {
-		if (!strcmp(rom_entries[i].name, name)) {
-			rom_entries[i].included = 1;
+		if (strcmp(rom_entries[i].name, name) == 0) {
 			msg_gspew("found.\n");
 			return i;
 		}
@@ -167,25 +163,53 @@ int process_include_args(void)
 
 	/* User has specified an area, but no layout file is loaded. */
 	if (num_rom_entries == 0) {
-		msg_gerr("Region requested (with -i \"%s\"), "
-			 "but no layout data is available.\n",
+		msg_gerr("Region requested (with -i/--include \"%s\"),\n"
+			 "but no layout data is available. To include one use the -l/--layout syntax).\n",
 			 include_args[0]);
 		return 1;
 	}
 
 	for (i = 0; i < num_include_args; i++) {
-		if (find_romentry(include_args[i]) < 0) {
-			msg_gerr("Invalid region specified: \"%s\".\n",
-				 include_args[i]);
+		char *file;
+		char *name = include_args[i];
+		int ret = unquote_string(&name, &file, ":"); /* -i <region>[:<file>] */
+		if (ret != 0) {
+			msg_gerr("Invalid include argument specified: \"%s\".\n", name);
 			return 1;
 		}
+		int idx = find_romentry(name);
+		if (idx < 0) {
+			msg_gerr("Invalid region name specified: \"%s\".\n", name);
+			return 1;
+		}
+		rom_entries[idx].included = 1;
 		found++;
+
+		if (file[0] == ':') {
+			file++;
+			/* The remaining characters are interpreted as possible quoted filename.  */
+			ret = unquote_string(&file, NULL, NULL);
+			if (ret != 0) {
+				msg_gerr("Invalid region file name specified: \"%s\".\n", file);
+				return 1;
+			}
+#ifdef __LIBPAYLOAD__
+			msg_gerr("Error: No file I/O support in libpayload\n");
+			return 1;
+#else
+			file = strdup(file);
+			if (file == NULL) {
+				msg_gerr("Out of memory!\n");
+				return 1;
+			}
+			rom_entries[idx].file = file;
+#endif
+		}
 	}
 
-	msg_ginfo("Using region%s: \"%s\"", num_include_args > 1 ? "s" : "",
-		  include_args[0]);
-	for (i = 1; i < num_include_args; i++)
-		msg_ginfo(", \"%s\"", include_args[i]);
+	msg_ginfo("Using region%s: \"%s\"", num_rom_entries > 1 ? "s" : "", rom_entries[0].name);
+	for (i = 1; i < num_rom_entries; i++)
+		msg_ginfo(", \"%s\"", rom_entries[i].name);
 	msg_ginfo(".\n");
 	return 0;
 }
@@ -200,6 +224,8 @@ void layout_cleanup(void)
 	num_include_args = 0;
 
 	for (i = 0; i < num_rom_entries; i++) {
+		free(rom_entries[i].file);
+		rom_entries[i].file = NULL;
 		rom_entries[i].included = 0;
 	}
 	num_rom_entries = 0;
@@ -259,6 +285,14 @@ int handle_romentries(const struct flashctx *flash, uint8_t *oldcontents, uint8_
 		if (entry->start > start)
 			memcpy(newcontents + start, oldcontents + start,
 			       entry->start - start);
+		/* If a file name is specified for this region, read the file contents and
+		 * overwrite @newcontents in the range specified by @entry. */
+		if (entry->file != NULL) {
+			if (read_buf_from_file(newcontents + entry->start, entry->end - entry->start + 1,
+			    entry->file, "the region's size") != 0)
+				return 1;
+		}
+
 		/* Skip to location after current romentry. */
 		start = entry->end + 1;
 		/* Catch overflow. */
-- 
Kind regards, Stefan Tauner





More information about the flashrom mailing list