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.
based on chromiumos' d0ea9ed71e7f86bb8e8db2ca7c32a96de25343d8 Signed-off-by: David Hendricks dhendrix@chromium.org
Signed-off-by: Stefan Tauner stefan.tauner@student.tuwien.ac.at ---
TODO: - man page - definition or at least an explanation of precedence of command line parameters --- flash.h | 10 +++++- flashrom.c | 117 ++++++++++++++++++++++++++++++++++++++++++++--------------- layout.c | 84 ++++++++++++++++++++++++++++++++++++------- 3 files changed, 167 insertions(+), 44 deletions(-)
diff --git a/flash.h b/flash.h index e51b6d4..c4d747b 100644 --- a/flash.h +++ b/flash.h @@ -39,6 +39,13 @@ #define TIMEOUT_ERROR -101
typedef unsigned long chipaddr; +typedef struct { + unsigned int start; + unsigned int end; + unsigned int included; + char name[256]; + char file[256]; /* file == "" means not specified. */ +} romlayout_t;
int register_shutdown(int (*function) (void *data), void *data); void *programmer_map_flash_region(const char *descr, unsigned long phys_addr, @@ -292,7 +299,8 @@ int print(int type, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int register_include_arg(char *name); int process_include_args(void); int read_romlayout(char *name); -int handle_romentries(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents); +romlayout_t *get_next_included_romentry(unsigned int start); +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.c b/flashrom.c index f1a6165..e33152f 100644 --- a/flashrom.c +++ b/flashrom.c @@ -1069,7 +1069,7 @@ int read_buf_from_file(unsigned char *buf, unsigned long size, return 0; }
-int write_buf_to_file(unsigned char *buf, unsigned long size, +int write_dump_to_file(unsigned char *buf, unsigned long size, const char *filename) { unsigned long numbytes; @@ -1094,26 +1094,81 @@ int write_buf_to_file(unsigned char *buf, unsigned long size, return 0; }
+int write_buf_to_file(unsigned char *buf, unsigned long size, + const char *filename) +{ + romlayout_t *l; + int ret = 0; + + ret = write_dump_to_file(buf, size, filename); + if (ret) + return ret; + + l = get_next_included_romentry(0); + + while (l != NULL) { + const char* name = (l->file[0] == '\0') ? l->name : l->file; + unsigned int len = l->end - l->start + 1; + msg_gdbg2("Writing "%s" to "%s" 0x%08x - 0x%08x (%uB)... ", + l->name, name, l->start, l->end, len); + if(write_dump_to_file(buf + l->start, len, name)) { + msg_gdbg2("failed. "); + return 1; + } + msg_gdbg2("done. "); + l = get_next_included_romentry(l->end + 1); + }; + + return 0; +} + +int read_flash_to_buf(struct flashctx *flash, uint8_t *buf) +{ + romlayout_t *l; + + if (!flash->read) { + msg_cerr("No read function available for this flash chip.\n"); + return 1; + } + + l = get_next_included_romentry(0); + /* No included rom entries. Assume complete readout wanted. */ + if (l == NULL) + return flash->read(flash, buf, 0, flash->total_size * 1024); + + do { + unsigned int len = l->end - l->start + 1; + msg_gdbg2("Reading "%s" 0x%08x - 0x%08x (%uB)... ", l->name, + l->start, l->end, len); + if(flash->read(flash, buf + l->start, l->start, len)) { + msg_gdbg2("failed. "); + return 1; + } + msg_gdbg2("done. "); + l = get_next_included_romentry(l->end + 1); + } while (l != NULL); + + return 0; +} + int read_flash_to_file(struct flashctx *flash, const char *filename) { unsigned long size = flash->total_size * 1024; - unsigned char *buf = calloc(size, sizeof(char)); int ret = 0; + uint8_t *buf;
msg_cinfo("Reading flash... "); - if (!buf) { + + buf = calloc(size, sizeof(uint8_t)); + if (buf == NULL) { msg_gerr("Memory allocation failed!\n"); - msg_cinfo("FAILED.\n"); - return 1; - } - if (!flash->read) { - msg_cerr("No read function available for this flash chip.\n"); ret = 1; goto out_free; } - if (flash->read(flash, buf, 0, size)) { + + ret = read_flash_to_buf(flash, buf); + if (ret != 0) { msg_cerr("Read operation failed!\n"); - ret = 1; goto out_free; }
@@ -1679,6 +1734,7 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, uint8_t *newcontents; int ret = 0; unsigned long size = flash->total_size * 1024; + int read_all_first = 1; /* FIXME: Make this configurable. */
if (chip_safety_check(flash, force, read_it, write_it, erase_it, verify_it)) { msg_cerr("Aborting.\n"); @@ -1745,28 +1801,27 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it,
/* Read the whole chip to be able to check whether regions need to be * erased and to give better diagnostics in case write fails. - * The alternative would be to read only the regions which are to be + * The alternative is to read only the regions which are to be * preserved, but in that case we might perform unneeded erase which * takes time as well. */ - msg_cinfo("Reading old flash chip contents... "); - if (flash->read(flash, oldcontents, 0, size)) { - ret = 1; - msg_cinfo("FAILED.\n"); - goto out; + if (read_all_first) { + msg_cinfo("Reading old flash chip contents... "); + if (flash->read(flash, oldcontents, 0, size)) { + ret = 1; + msg_cinfo("FAILED.\n"); + goto out; + } } 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); - - // //////////////////////////////////////////////////////////// + /* Build a new image from the given layout. */ + build_new_image(flash, read_all_first, oldcontents, newcontents);
- if (write_it) { - if (erase_and_write_flash(flash, oldcontents, newcontents)) { - msg_cerr("Uh oh. Erase/write failed. Checking if " - "anything changed.\n"); + if (write_it && erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed."); + if (read_all_first) { + msg_cerr("Checking if anything changed... "); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1775,11 +1830,13 @@ int doit(struct flashctx *flash, int force, const char *filename, int read_it, ret = 1; goto out; } - } - emergency_help_message(); - ret = 1; - goto out; - } + } else + msg_cerr("failed.\n"); + } else + msg_cerr("\n"); + emergency_help_message(); + ret = 1; + goto out; }
if (verify_it) { diff --git a/layout.c b/layout.c index 3928699..e16d03b 100644 --- a/layout.c +++ b/layout.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> #include <ctype.h> #include <limits.h> #include "flash.h" @@ -34,13 +35,6 @@ static int romimages = 0;
#define MAX_ROMLAYOUT 32
-typedef struct { - unsigned int start; - unsigned int end; - unsigned int included; - char name[256]; -} romlayout_t; - /* include_args lists arguments specified at the command line with -i. They * must be processed at some point so that desired regions are marked as * "included" in the rom_entries list. @@ -183,6 +177,7 @@ int read_romlayout(char *name) rom_entries[romimages].start = strtol(tstr1, (char **)NULL, 16); rom_entries[romimages].end = strtol(tstr2, (char **)NULL, 16); rom_entries[romimages].included = 0; + strcpy(rom_entries[romimages].file, ""); romimages++; }
@@ -220,14 +215,24 @@ int register_include_arg(char *name) static int find_romentry(char *name) { int i; + char *file = NULL;
if (!romimages) return -1;
- msg_gspew("Looking for region "%s"... ", name); + /* -i <image>[:<file>] */ + if (strtok(name, ":")) { + file = strtok(NULL, ""); + } + msg_gspew("Looking for region "%s" (file="%s")... ", + name, file ? file : "<not specified>"); + for (i = 0; i < romimages; i++) { if (!strcmp(rom_entries[i].name, name)) { rom_entries[i].included = 1; + snprintf(rom_entries[i].file, + sizeof(rom_entries[i].file), + "%s", file ? file : ""); msg_gspew("found.\n"); return i; } @@ -299,7 +304,55 @@ romlayout_t *get_next_included_romentry(unsigned int start) return best_entry; }
-int handle_romentries(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents) +/* If a file name is specified for this region, read the file contents and + * overwrite @newcontents in the range specified by @entry. + */ +static int read_content_from_file(romlayout_t *entry, uint8_t *newcontents) +{ + char *file; + FILE *fp; + int len; + + file = entry->file; + len = entry->end - entry->start + 1; + if (file[0] != '\0') { + int numbytes; + if ((fp = fopen(file, "rb")) == NULL) { + msg_gerr("Could not open file '%s': %s!\n", file, + strerror(errno)); + return 1; + } + numbytes = fread(newcontents + entry->start, 1, len, fp); + fclose(fp); + if (numbytes != len) { + msg_gerr("Could not read %d bytes from file '%s'!\n", + len, file); + return 1; + } + } + return 0; +} + +static void copy_old_content(struct flashctx *flash, int oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents, unsigned int start, unsigned int size) +{ + if (!oldcontents_valid) { + /* oldcontents is a zero-filled buffer. By reading into + * oldcontents, we avoid a rewrite of identical regions even if + * an initial full chip read didn't happen. */ + msg_gdbg2("Read a chunk starting from 0x%06x (len=0x%06x).\n", + start, size); + flash->read(flash, oldcontents + start, start, size); + } + memcpy(newcontents + start, oldcontents + start, size); +} + +/** + * Modify @newcontents so that it contains the data that should be on the chip + * eventually. In the case the user wants to update only parts of it, copy + * the chunks to be preserved from @oldcontents to @newcontents. If @oldcontents + * is not valid, we need to fetch the current data from the chip first. + */ +int build_new_image(struct flashctx *flash, int oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents) { unsigned int start = 0; romlayout_t *entry; @@ -318,14 +371,19 @@ int handle_romentries(struct flashctx *flash, uint8_t *oldcontents, uint8_t *new entry = get_next_included_romentry(start); /* No more romentries for remaining region? */ if (entry == NULL) { - memcpy(newcontents + start, oldcontents + start, - size - start); + copy_old_content(flash, oldcontents_valid, oldcontents, + newcontents, start, size - start); break; } /* For non-included region, copy from old content. */ if (entry->start > start) - memcpy(newcontents + start, oldcontents + start, - entry->start - start); + copy_old_content(flash, oldcontents_valid, oldcontents, + newcontents, start, + entry->start - start); + /* For included region, copy from file if specified. */ + if (read_content_from_file(entry, newcontents) < 0) + return 1; + /* Skip to location after current romentry. */ start = entry->end + 1; /* Catch overflow. */