Index: cli_classic.c =================================================================== --- cli_classic.c (revisión: 1955) +++ cli_classic.c (copia de trabajo) @@ -61,7 +61,9 @@ #if CONFIG_PRINT_WIKI == 1 " -z | --list-supported-wiki print supported devices in wiki syntax\n" #endif - " -p | --programmer [:] specify the programmer device. One of\n"); + " -p | --programmer [:] 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: Index: cli_output.c =================================================================== --- cli_output.c (revisión: 1955) +++ cli_output.c (copia de trabajo) @@ -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; Index: flash.h =================================================================== --- flash.h (revisión: 1955) +++ flash.h (copia de trabajo) @@ -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 */ Index: flashrom.c =================================================================== --- flashrom.c (revisión: 1955) +++ flashrom.c (copia de trabajo) @@ -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; } + Index: ft2232_spi.c =================================================================== --- ft2232_spi.c (revisión: 1955) +++ ft2232_spi.c (copia de trabajo) @@ -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: Index: layout.c =================================================================== --- layout.c (revisión: 1955) +++ layout.c (copia de trabajo) @@ -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; +} + Index: spi25.c =================================================================== --- spi25.c (revisión: 1955) +++ spi25.c (copia de trabajo) @@ -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; }