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(a)chromium.org>
Signed-off-by: Stefan Tauner <stefan.tauner(a)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. */
--
1.7.1