===================================================================
@@ -61,7 +61,9 @@
#if CONFIG_PRINT_WIKI == 1
" -z | --list-supported-wiki print supported devices in wiki syntax\n"
#endif
- " -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
+ " -p | --programmer <name>[:<param>] specify the programmer device. One of\n"
+ " -s | --show-progress show operation progress\n"
+ " -A | --no-read-all don't read the whole memory\n");
list_programmers_linebreak(4, 80, 0);
printf(".\n\nYou can specify one of -h, -R, -L, "
#if CONFIG_PRINT_WIKI == 1
@@ -105,25 +107,28 @@
int dont_verify_it = 0, list_supported = 0, operation_specified = 0;
enum programmer prog = PROGRAMMER_INVALID;
int ret = 0;
+ int read_all_first = 1, included_args = 0;
- static const char optstring[] = "r:Rw:v:nVEfc:l:i:p:Lzho:";
+ static const char optstring[] = "Ac:Efhi:l:Lno:p:r:Rsv:Vw:z";
static const struct option long_options[] = {
- {"read", 1, NULL, 'r'},
- {"write", 1, NULL, 'w'},
+ {"chip", 1, NULL, 'c'},
{"erase", 0, NULL, 'E'},
- {"verify", 1, NULL, 'v'},
- {"noverify", 0, NULL, 'n'},
- {"chip", 1, NULL, 'c'},
- {"verbose", 0, NULL, 'V'},
{"force", 0, NULL, 'f'},
+ {"help", 0, NULL, 'h'},
+ {"image", 1, NULL, 'i'},
{"layout", 1, NULL, 'l'},
- {"image", 1, NULL, 'i'},
{"list-supported", 0, NULL, 'L'},
{"list-supported-wiki", 0, NULL, 'z'},
+ {"no-read-all", 0, NULL, 'A'},
+ {"noverify", 0, NULL, 'n'},
+ {"output", 1, NULL, 'o'},
{"programmer", 1, NULL, 'p'},
- {"help", 0, NULL, 'h'},
+ {"read", 1, NULL, 'r'},
+ {"show-progress", 0, NULL, 's'},
+ {"verbose", 0, NULL, 'V'},
+ {"verify", 1, NULL, 'v'},
{"version", 0, NULL, 'R'},
- {"output", 1, NULL, 'o'},
+ {"write", 1, NULL, 'w'},
{NULL, 0, NULL, 0},
};
@@ -206,6 +211,12 @@
case 'f':
force = 1;
break;
+ case 's':
+ show_progress = 1;
+ break;
+ case 'A':
+ read_all_first = 0;
+ break;
case 'l':
if (layoutfile) {
fprintf(stderr, "Error: --layout specified "
@@ -220,6 +231,7 @@
free(tempstr);
cli_classic_abort_usage();
}
+ included_args = 1;
break;
case 'L':
if (++operation_specified > 1) {
@@ -326,6 +338,11 @@
cli_classic_abort_usage();
}
+ if (!read_all_first && !included_args) {
+ fprintf(stderr, "Error: You must indicate at least one image when using --no-read-all.\n");
+ cli_classic_abort_usage();
+ }
+
if ((read_it | write_it | verify_it) && check_filename(filename, "image")) {
cli_classic_abort_usage();
}
@@ -542,7 +559,7 @@
* Give the chip time to settle.
*/
programmer_delay(100000);
- ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
+ ret |= doit(fill_flash, force, filename, read_it, write_it, erase_it, verify_it, read_all_first);
unmap_flash(fill_flash);
out_shutdown:
===================================================================
@@ -27,6 +27,8 @@
int verbose_screen = MSG_INFO;
int verbose_logfile = MSG_DEBUG2;
+/* Show progress during operations */
+int show_progress = 0;
#ifndef STANDALONE
static FILE *logfile = NULL;
===================================================================
@@ -222,6 +222,7 @@
uintptr_t physical_registers;
chipaddr virtual_registers;
struct registered_master *mst;
+ void (*progress) (struct flashctx *flash, double done);
};
/* Timing used in probe routines. ZERO is -2 to differentiate between an unset
@@ -281,9 +282,11 @@
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 doit(struct flashctx *flash, int force, const char *filename, int read_it, int write_it, int erase_it,
+ int verify_it, int read_all_first);
int read_buf_from_file(unsigned char *buf, unsigned long size, const char *filename);
int write_buf_to_file(const unsigned char *buf, unsigned long size, const char *filename);
+int find_erase_function(struct flashctx *flash);
/* Something happened that shouldn't happen, but we can go on. */
#define ERROR_NONFATAL 0x100
@@ -305,6 +308,7 @@
/* cli_output.c */
extern int verbose_screen;
extern int verbose_logfile;
+extern int show_progress;
#ifndef STANDALONE
int open_logfile(const char * const filename);
int close_logfile(void);
@@ -349,7 +353,8 @@
int process_include_args(void);
int read_romlayout(const char *name);
int normalize_romentries(const struct flashctx *flash);
-int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents);
+int read_cur_content(struct flashctx *flash, uint8_t *oldcontents, const uint8_t *newcontents);
+void modify_new_image_from_old(struct flashctx *flash, const uint8_t *oldcontents, uint8_t *newcontents);
void layout_cleanup(void);
/* spi.c */
===================================================================
@@ -454,6 +454,41 @@
return 0;
}
+static const char *progress_message = NULL;
+
+static
+void do_show_progress(struct flashctx *flash, double done)
+{
+ if (show_progress && progress_message)
+ msg_cinfo("\r%s... %d %%", progress_message, (int)(done+0.5));
+}
+
+static
+void do_show_progress_done(void)
+{
+ msg_cinfo("\r%s... done. \n", progress_message);
+}
+
+static
+void do_show_progress_failed(void)
+{
+ msg_cinfo("\r%s... FAILED.\n", progress_message);
+}
+
+static
+void do_show_progress_verified(void)
+{
+ msg_cinfo("\r%s... VERIFIED.\n", progress_message);
+}
+
+static
+void set_progress_message(struct flashctx *flash, const char *msg)
+{
+ progress_message = msg;
+ msg_cinfo("%s... ", msg);
+ flash->progress = do_show_progress;
+}
+
int programmer_init(enum programmer prog, const char *param)
{
int ret;
@@ -677,9 +712,11 @@
for (i = 0; i < len; i++) {
if (wantbuf[i] != havebuf[i]) {
/* Only print the first failure. */
- if (!failcount++)
- msg_cerr("FAILED at 0x%08x! Expected=0x%02x, Found=0x%02x,",
+ if (!failcount++) {
+ do_show_progress_failed();
+ msg_cerr("FAILED at 0x%08x! Expected=0x%02x, Found=0x%02x, ",
start + i, wantbuf[i], havebuf[i]);
+ }
}
}
if (failcount) {
@@ -1369,7 +1406,7 @@
unsigned char *buf = calloc(size, sizeof(char));
int ret = 0;
- msg_cinfo("Reading flash... ");
+ set_progress_message(flash, "Reading flash");
if (!buf) {
msg_gerr("Memory allocation failed!\n");
msg_cinfo("FAILED.\n");
@@ -1389,7 +1426,10 @@
ret = write_buf_to_file(buf, size, filename);
out_free:
free(buf);
- msg_cinfo("%s.\n", ret ? "FAILED" : "done");
+ if (ret)
+ do_show_progress_failed();
+ else
+ do_show_progress_done();
return ret;
}
@@ -1526,8 +1566,13 @@
int i, j;
unsigned int start = 0;
unsigned int len;
+ unsigned int total_blocks = 0;
+ unsigned int cur_block = 0;
struct block_eraser eraser = flash->chip->block_erasers[erasefunction];
+ /* determine the total number of iterations */
+ for (i = 0; i < NUM_ERASEREGIONS; i++)
+ total_blocks += eraser.eraseblocks[i].count;
for (i = 0; i < NUM_ERASEREGIONS; i++) {
/* count==0 for all automatically initialized array
* members so the loop below won't be executed for them.
@@ -1537,16 +1582,17 @@
/* Print this for every block except the first one. */
if (i || j)
msg_cdbg(", ");
- msg_cdbg("0x%06x-0x%06x", start,
- start + len - 1);
- if (do_something(flash, start, len, param1, param2,
- eraser.block_erase)) {
+ msg_cdbg("0x%06x-0x%06x", start, start + len - 1);
+ cur_block++;
+ do_show_progress(flash, (double)cur_block/total_blocks*100.0);
+ if (do_something(flash, start, len, param1, param2, eraser.block_erase)) {
+ do_show_progress_failed();
return 1;
}
start += len;
}
}
- msg_cdbg("\n");
+ do_show_progress_done();
return 0;
}
@@ -1575,6 +1621,27 @@
return 0;
}
+/* Look for the first valid erase function. Returns -1 if none.
+ */
+int find_erase_function(struct flashctx *flash)
+{
+ int k;
+
+ msg_cdbg("\nLooking for an erase function.\n");
+ for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
+ if (k != 0)
+ msg_cdbg("Looking for another erase function.\n");
+ msg_cdbg("Trying erase function %i... ", k);
+ if (!check_block_eraser(flash, k, 1)) {
+ msg_cdbg("OK\n");
+ return k;
+ }
+ msg_cdbg("INVALID\n");
+ }
+ msg_cdbg("No usable erase functions left.\n");
+ return -1;
+}
+
int erase_and_write_flash(struct flashctx *flash, uint8_t *oldcontents, uint8_t *newcontents)
{
int k, ret = 1;
@@ -1582,7 +1649,10 @@
unsigned long size = flash->chip->total_size * 1024;
unsigned int usable_erasefunctions = count_usable_erasers(flash);
- msg_cinfo("Erasing and writing flash chip... ");
+ set_progress_message(flash, "Erasing and writing flash chip");
+ /* We will read the memory while erasing & writing, so we will show the progress
+ * and we don't want messages from the partial reads. */
+ flash->progress = NULL;
curcontents = malloc(size);
if (!curcontents) {
msg_gerr("Out of memory!\n");
@@ -1593,9 +1663,9 @@
for (k = 0; k < NUM_ERASEFUNCTIONS; k++) {
if (k != 0)
- msg_cinfo("Looking for another erase function.\n");
+ msg_cinfo("\nLooking for another erase function.\n");
if (!usable_erasefunctions) {
- msg_cinfo("No usable erase functions left.\n");
+ msg_cinfo("\nNo usable erase functions left.\n");
break;
}
msg_cdbg("Trying erase function %i... ", k);
@@ -1616,8 +1686,9 @@
/* Reading the whole chip may take a while, inform the user even
* in non-verbose mode.
*/
- msg_cinfo("Reading current flash chip contents... ");
+ set_progress_message(flash, "Reading current flash chip contents");
if (flash->chip->read(flash, curcontents, 0, size)) {
+ do_show_progress_failed();
/* Now we are truly screwed. Read failed as well. */
msg_cerr("Can't read anymore! Aborting.\n");
/* We have no idea about the flash chip contents, so
@@ -1625,7 +1696,7 @@
*/
break;
}
- msg_cinfo("done. ");
+ do_show_progress_done();
}
/* Free the scratchpad. */
free(curcontents);
@@ -1634,7 +1705,7 @@
msg_cerr("FAILED!\n");
} else {
if (all_skipped)
- msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
+ msg_cinfo("Warning: Chip content is identical to the requested image.\n");
msg_cinfo("Erase/write done.\n");
}
return ret;
@@ -1977,13 +2048,12 @@
* Besides that, the function itself is a textbook example of abysmal code flow.
*/
int doit(struct flashctx *flash, int force, const char *filename, int read_it,
- int write_it, int erase_it, int verify_it)
+ int write_it, int erase_it, int verify_it, int read_all_first)
{
uint8_t *oldcontents;
uint8_t *newcontents;
int ret = 0;
unsigned long size = flash->chip->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");
@@ -2065,23 +2135,26 @@
* preserved, but in that case we might perform unneeded erase which
* takes time as well.
*/
+ set_progress_message(flash, "Reading old flash chip contents");
if (read_all_first) {
- msg_cinfo("Reading old flash chip contents... ");
if (flash->chip->read(flash, oldcontents, 0, size)) {
ret = 1;
- msg_cinfo("FAILED.\n");
+ do_show_progress_failed();
goto out;
}
+ /* We read all, we just need to filter out the user indicated images. */
+ modify_new_image_from_old(flash, oldcontents, newcontents);
+ } else {
+ /* Build a new image taking the given layout into account. */
+ if (read_cur_content(flash, oldcontents, newcontents)) {
+ do_show_progress_failed();
+ msg_gerr("Could not prepare the data to be written, aborting.\n");
+ ret = 1;
+ goto out;
+ }
}
- msg_cinfo("done.\n");
+ do_show_progress_done();
- /* Build a new image taking the given layout into account. */
- if (build_new_image(flash, read_all_first, oldcontents, newcontents)) {
- msg_gerr("Could not prepare the data to be written, aborting.\n");
- ret = 1;
- goto out;
- }
-
// ////////////////////////////////////////////////////////////
if (write_it && erase_and_write_flash(flash, oldcontents, newcontents)) {
@@ -2088,9 +2161,9 @@
msg_cerr("Uh oh. Erase/write failed. ");
if (read_all_first) {
msg_cerr("Checking if anything has changed.\n");
- msg_cinfo("Reading current flash chip contents... ");
+ set_progress_message(flash, "Reading old flash chip contents");
if (!flash->chip->read(flash, newcontents, 0, size)) {
- msg_cinfo("done.\n");
+ do_show_progress_done();
if (!memcmp(oldcontents, newcontents, size)) {
nonfatal_help_message();
ret = 1;
@@ -2097,8 +2170,10 @@
goto out;
}
msg_cerr("Apparently at least some data has changed.\n");
- } else
+ } else {
+ do_show_progress_failed();
msg_cerr("Can't even read anymore!\n");
+ }
emergency_help_message();
ret = 1;
goto out;
@@ -2111,12 +2186,20 @@
/* Verify only if we either did not try to write (verify operation) or actually changed something. */
if (verify_it && (!write_it || !all_skipped)) {
- msg_cinfo("Verifying flash... ");
+ set_progress_message(flash, "Verifying flash");
if (write_it) {
/* Work around chips which need some time to calm down. */
programmer_delay(1000*1000);
- ret = verify_range(flash, newcontents, 0, size);
+ if (read_all_first) {
+ /* Verify the whole memory. */
+ ret = verify_range(flash, newcontents, 0, size);
+ } else {
+ /* Read only the regions specified by the user. */
+ ret = read_cur_content(flash, oldcontents, newcontents);
+ if (!ret) /* Compare with what we wanted. */
+ ret = compare_range(newcontents, oldcontents, 0, size);
+ }
/* If we tried to write, and verification now fails, we
* might have an emergency situation.
*/
@@ -2126,7 +2209,7 @@
ret = compare_range(newcontents, oldcontents, 0, size);
}
if (!ret)
- msg_cinfo("VERIFIED.\n");
+ do_show_progress_verified();
}
out:
@@ -2134,3 +2217,4 @@
free(newcontents);
return ret;
}
+
===================================================================
@@ -102,6 +102,13 @@
static uint8_t cs_bits = 0x08;
static uint8_t pindir = 0x0b;
static struct ftdi_context ftdic_context;
+/* SPI configuration memory for iCE40 FPGAs */
+static char ice40_mode = 0;
+/* iCE40 specific: verify that the FPGA asserted CDONE */
+static char check_cdone = 0;
+/* iCE40 memory commands */
+#define RELEASE_FROM_POWER_DOWN 0xAB
+#define DEEP_POWER_DOWN 0xB9
static const char *get_ft2232_devicename(int ft2232_vid, int ft2232_type)
{
@@ -168,6 +175,58 @@
.write_aai = default_spi_write_aai,
};
+/* Put the data bits in the shutdown state. */
+static int ice40_deinit(void *ftdic)
+{
+ unsigned char buf[4];
+ struct ftdi_context *f = (struct ftdi_context *)ftdic;
+
+ /* Put the memory to sleep, saves power */
+ msg_pdbg("Memory power down\n");
+ buf[0] = DEEP_POWER_DOWN;
+ if (ft2232_spi_send_command(NULL,1,0,buf,NULL))
+ return -1;
+
+ /* Disconnect the cable to avoid contention */
+ msg_pdbg("Set data bits, cable disable\n");
+ buf[0] = SET_BITS_LOW;
+ buf[1] = 0;
+ buf[2] = 0; /* All inputs => HiZ */
+ if (send_buf(f, buf, 3))
+ return -1;
+
+ /* Check if the CDONE line is asserted */
+ if (check_cdone) {
+ unsigned int count = 100;
+ unsigned char buf_read_cmd[2] = { GET_BITS_LOW, SEND_IMMEDIATE };
+ unsigned char buf_read_data;
+ msg_cinfo("Waiting for CDONE... ");
+ do {
+ msg_pdbg2("Reading I/O pins: ");
+ if (send_buf(f, buf_read_cmd, 2)) {
+ msg_perr("Error reading FTDI I/O status (send)\n");
+ return -1;
+ }
+ if (get_buf(f, &buf_read_data, 1)) {
+ msg_perr("Error reading FTDI I/O status (receive)\n");
+ return -1;
+ }
+ msg_pdbg2("0x%02X\n", buf_read_data);
+ if (buf_read_data & 0x40) /* IOL2 */
+ break;
+ usleep(10*1000);
+ } while (--count);
+ if (count)
+ msg_cinfo("done.\n");
+ else {
+ msg_cinfo("FAILED.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
/* Returns 0 upon success, a negative number upon errors. */
int ft2232_spi_init(void)
{
@@ -273,6 +332,32 @@
} else if (!strcasecmp(arg, "google-servo-v2-legacy")) {
ft2232_vid = GOOGLE_VID;
ft2232_type = GOOGLE_SERVO_V2_PID0;
+ } else if (!strcasecmp(arg, "ice40")) {
+ /* HW-USBN-2B or generic FTDI cable */
+ char *arg2;
+ ft2232_type = FTDI_FT2232H_PID;
+ channel_count = 2;
+ /* The IOL3 pin is used as system reset (needed to disable the FPGA)
+ * IOL0 pin is used instead of CS, called SS
+ * IOL2 pin is used to check CDONE
+ * value: 0x10 #RST=low, SS=high, DI=low, DO=low, SK=low
+ * dir: 0x93 #RST=output, SS=output, DI=input, DO=output, SK=output */
+ cs_bits = 0x10;
+ pindir = 0x93;
+ ice40_mode = 1;
+ if (register_shutdown(ice40_deinit,ftdic)) {
+ msg_perr("Error: Unable to register shutdown sequence.\n");
+ return -9;
+ }
+ arg2 = extract_programmer_param("cdone");
+ if (arg2) {
+ unsigned int temp = 0;
+ char *endptr;
+ temp = strtoul(arg2, &endptr, 0);
+ if (temp)
+ check_cdone = 1;
+ free(arg2);
+ }
} else {
msg_perr("Error: Invalid device type specified.\n");
free(arg);
@@ -420,6 +505,14 @@
register_spi_master(&spi_master_ft2232);
+ /* iCE40 mode: memory wake-up */
+ if (ice40_mode) {
+ msg_pdbg("Memory wake-up\n");
+ buf[0] = RELEASE_FROM_POWER_DOWN;
+ if (ft2232_spi_send_command(NULL,1,0,buf,NULL))
+ msg_perr("Unable to send wake-up command.\n");
+ }
+
return 0;
ftdi_err:
===================================================================
@@ -257,28 +257,12 @@
return ret;
}
-static int 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 the current data into oldcontents here, we
- * avoid a rewrite of identical regions even if an initial full chip read didn't happen. */
- msg_gdbg2("Read a chunk starting at 0x%06x (len=0x%06x).\n", start, size);
- int ret = flash->chip->read(flash, oldcontents + start, start, size);
- if (ret != 0) {
- msg_gerr("Failed to read chunk 0x%06x-0x%06x.\n", start, start + size - 1);
- return 1;
- }
- }
- memcpy(newcontents + start, oldcontents + start, size);
- return 0;
-}
-
/**
* 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.
+ * wants to update only parts of it, copy the chunks to be preserved from @oldcontents to @newcontents.
+ * This function is used only when @oldcontents contains the full memory information.
*/
-int build_new_image(struct flashctx *flash, bool oldcontents_valid, uint8_t *oldcontents, uint8_t *newcontents)
+void modify_new_image_from_old(struct flashctx *flash, const uint8_t *oldcontents, uint8_t *newcontents)
{
unsigned int start = 0;
romentry_t *entry;
@@ -288,7 +272,7 @@
* that the user wants to write the complete new image.
*/
if (num_include_args == 0)
- return 0;
+ return;
/* Non-included romentries are ignored.
* The union of all included romentries is used from the new image.
@@ -296,20 +280,116 @@
while (start < size) {
entry = get_next_included_romentry(start);
/* No more romentries for remaining region? */
- if (!entry) {
- copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
- size - start);
+ if (!entry) { /* Copy the old content to skip it */
+ memcpy(newcontents + start, oldcontents + start, size);
break;
}
/* For non-included region, copy from old content. */
if (entry->start > start)
- copy_old_content(flash, oldcontents_valid, oldcontents, newcontents, start,
- entry->start - start);
- /* Skip to location after current romentry. */
+ memcpy(newcontents + start, oldcontents + start, entry->start - start);
+ /* Skip to location after current romentry. The user wants to preserve it. */
start = entry->end + 1;
/* Catch overflow. */
if (!start)
break;
}
- return 0;
+ return;
}
+
+/**
+ * Look for the starting address of the erase region that contains @addr.
+ * The size of this region is returned in @sz
+ */
+int find_page_start(unsigned int addr, struct eraseblock *blocks, unsigned int *sz)
+{
+ unsigned int page_start, i, size, count, group_size;
+ /* Look for the beggining of the erase region containing start address */
+ page_start = 0;
+ for (i = 0; i < NUM_ERASEREGIONS; i++) {
+ size = blocks[i].size;
+ count = blocks[i].count;
+ group_size = size * count;
+ if (page_start + group_size > addr) {
+ /* The address is inside one of these erase regions */
+ *sz = size;
+ return page_start + ((addr - page_start) / size) * size;
+ } else {/* Not here */
+ page_start += group_size;
+ }
+ }
+ msg_gerr("ERROR: address out of memory!?\n");
+ exit(1);
+ return -1;
+}
+
+/**
+ * Read all the erase regions containing the @start-@end interval.
+ * Returns the last address fetched in @real_end_p
+ */
+int read_pages(struct flashctx *flash, uint8_t *oldcontents, unsigned int start, unsigned int end,
+ unsigned int *real_end_p)
+{
+ int k;
+ unsigned int real_start, real_end, size;
+
+ k = find_erase_function(flash);
+ if (k == -1) {
+ msg_gerr("ERROR: No erase function available.\n");
+ return -1;
+ }
+ real_start = find_page_start(start, flash->chip->block_erasers[k].eraseblocks, &size);
+ msg_gdbg("Start of range 0x%X rounded to page boundary: 0x%X\n", start, real_start);
+ real_end = find_page_start(end, flash->chip->block_erasers[k].eraseblocks, &size);
+ real_end += size - 1;
+ msg_gdbg("End of range 0x%X rounded to page boundary: 0x%X\n", end, real_end);
+ *real_end_p = real_end;
+ return flash->chip->read(flash, oldcontents + real_start, real_start, real_end - real_start + 1);
+}
+
+/**
+ * Fill the @oldcontents buffer with data from the memory. The regions not included by the user are filled
+ * with data from @newcontents instead of the actual memory content. It forces to skip them.
+ * This function is used only when @oldcontents is empty.
+ * Is also mandatory that at least one region was specified by the user.
+ */
+int read_cur_content(struct flashctx *flash, uint8_t *oldcontents, const uint8_t *newcontents)
+{
+ unsigned int start = 0;
+ romentry_t *entry;
+ unsigned int size = flash->chip->total_size * 1024;
+ unsigned int region_end;
+ int ret = 0;
+
+ /* We will read from memory only the regions indicated by the user.
+ * The rest will contain the same as the newcontents */
+ memcpy(oldcontents, newcontents, size);
+
+ /* Catch a missuse */
+ if (num_include_args == 0) {
+ msg_perr("Internal error! build_old_image() invoked, but no regions specified.\n");
+ return 1;
+ }
+
+ /* Non-included romentries are ignored.
+ * The union of all included romentries is fetched from memory.
+ */
+ while (start < size) {
+ entry = get_next_included_romentry(start);
+ /* No more romentries for remaining region? */
+ if (!entry)
+ break;
+ /* For included regions, read memory content content. */
+ msg_gdbg("Read a chunk 0x%06x-0x%06x.\n", entry->start, entry->end);
+ ret = read_pages(flash, oldcontents, entry->start, entry->end, ®ion_end);
+ if (ret != 0) {
+ msg_gerr("Failed to read chunk 0x%06x-0x%06x.\n", entry->start, entry->end);
+ break;
+ }
+ start = region_end + 1;
+ /* Catch overflow. */
+ if (!start)
+ break;
+ }
+ return ret;
+}
+
===================================================================
@@ -948,6 +948,7 @@
int rc = 0;
unsigned int i, j, starthere, lenhere, toread;
unsigned int page_size = flash->chip->page_size;
+ unsigned int first_page, last_page, total_pages;
/* Warning: This loop has a very unusual condition and body.
* The loop needs to go through each page with at least one affected
@@ -958,7 +959,10 @@
* (start + len - 1) / page_size. Since we want to include that last
* page as well, the loop condition uses <=.
*/
- for (i = start / page_size; i <= (start + len - 1) / page_size; i++) {
+ first_page = start / page_size;
+ last_page = (start + len - 1) / page_size;
+ total_pages = last_page - first_page + 1;
+ for (i = first_page; i <= last_page; i++) {
/* Byte position of the first byte in the range in this page. */
/* starthere is an offset to the base address of the chip. */
starthere = max(start, i * page_size);
@@ -970,6 +974,8 @@
if (rc)
break;
}
+ if (flash->progress)
+ flash->progress(flash,(double)(i-first_page+1)/total_pages*100.0);
if (rc)
break;
}