If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
If you're going to test this, please be advised that I have not tested execution, only compilation. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, j, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (j = 0; j < len / 256; j++) { + limit = min(256, len - j * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + j * 256, want + j * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = j * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = j * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(j * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,58 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, + newcontents, + len, gran)) { + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents + start, 0xff, len); + } + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, start + starthere, lenhere); + if (ret) + break; + starthere += lenhere; + } + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1288,7 +1432,7 @@ for (j = 0; j < eraser.eraseblocks[i].count; j++) { msg_cdbg("0x%06x-0x%06x, ", start, start + len - 1); - if (do_something(flash, start, len)) + if (do_something(flash, start, len, param1, param2, eraser.block_erase)) return 1; start += len; } @@ -1296,11 +1440,11 @@ return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
- msg_cinfo("Erasing flash chip... "); + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,7 +1468,7 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, oldcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) @@ -1637,6 +1781,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1801,13 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); programmer_shutdown(); return 1; } }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1820,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1839,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1701,24 +1854,6 @@ emergency_help_message(); programmer_shutdown(); return 1; - } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; } else { msg_cinfo("COMPLETE.\n"); }
On 10/20/2010 12:06 AM, Carl-Daniel Hailfinger wrote:
Here's the result of a write test with this patch.
================== rsmith@sparky:/home/src/flashrom$ ./flashrom -p dediprog -Vw ~/olpc/ec/ec-1.75/image/ecimage.bin flashrom v0.9.3-r1215 on Linux 2.6.36-020636rc7-generic (i686), built with libpci 3.0.0, GCC 4.4.3, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 2 usecs, 2777M loops per second, 10 myus = 11 us, 100 myus = 101 us, 1000 myus = 996 us, 10000 myus = 10010 us, 8 myus = 9 us, OK. Initializing dediprog programmer Found USB device (0483:dada). Found a SF100 V:5.1.5 Setting SPI voltage to 3.500 V Probing for AMD Am29F010A/B, 128 KB: skipped. Probing for AMD Am29F002(N)BB, 256 KB: skipped. Probing for AMD Am29F002(N)BT, 256 KB: skipped. Probing for AMD Am29F016D, 2048 KB: skipped. Probing for AMD Am29F040B, 512 KB: skipped. Probing for AMD Am29F080B, 1024 KB: skipped. Probing for AMD Am29LV040B, 512 KB: skipped. Probing for AMD Am29LV081B, 1024 KB: skipped. Probing for AMIC A25L05PT, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L05PU, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L10PT, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L10PU, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L20PT, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L20PU, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L40PT, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L40PU, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L80P, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L16PT, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L16PU, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L512, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L010, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L020, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L040, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L080, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L016, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25L032, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A25LQ032, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for AMIC A29002B, 256 KB: skipped. Probing for AMIC A29002T, 256 KB: skipped. Probing for AMIC A29040B, 512 KB: skipped. Probing for AMIC A49LF040A, 512 KB: skipped. Probing for Atmel AT25DF021, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF041A, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF081, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF081A, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF161, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF321, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF321A, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DF641, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25DQ161, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25F512B, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25FS010, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT25FS040, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT26DF041, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT26DF081A, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT26DF161, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT26DF161A, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT26F004, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT29C512, 64 KB: skipped. Probing for Atmel AT29C010A, 128 KB: skipped. Probing for Atmel AT29C020, 256 KB: skipped. Probing for Atmel AT29C040A, 512 KB: skipped. Probing for Atmel AT45CS1282, 16896 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB011D, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB021D, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB041D, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB081D, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB161D, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB321C, 4224 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB321D, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT45DB642D, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel AT49BV512, 64 KB: skipped. Probing for Atmel AT49F020, 256 KB: skipped. Probing for Atmel AT49F002(N), 256 KB: skipped. Probing for Atmel AT49F002(N)T, 256 KB: skipped. Probing for Bright BM29F040, 512 KB: skipped. Probing for EMST F49B002UA, 256 KB: skipped. Probing for EMST F25L008A, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B05, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B05T, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B10, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B10T, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B20, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B20T, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B40, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B40T, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B80T, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B16T, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B32T, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B64, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25B64T, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25D16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F05, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F10, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F20, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F40, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN25F32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon EN29F010, 128 KB: skipped. Probing for Eon EN29F002(A)(N)B, 256 KB: skipped. Probing for Eon EN29F002(A)(N)T, 256 KB: skipped. Probing for Fujitsu MBM29F004BC, 512 KB: skipped. Probing for Fujitsu MBM29F004TC, 512 KB: skipped. Probing for Fujitsu MBM29F400BC, 512 KB: skipped. Probing for Fujitsu MBM29F400TC, 512 KB: skipped. Probing for Hyundai HY29F002T, 256 KB: skipped. Probing for Hyundai HY29F002B, 256 KB: skipped. Probing for Hyundai HY29F040A, 512 KB: skipped. Probing for Intel 28F001BN/BX-B, 128 KB: skipped. Probing for Intel 28F001BN/BX-T, 128 KB: skipped. Probing for Intel 28F002BC/BL/BV/BX-T, 256 KB: skipped. Probing for Intel 28F008S3/S5/SC, 512 KB: skipped. Probing for Intel 28F004B5/BE/BV/BX-B, 512 KB: skipped. Probing for Intel 28F004B5/BE/BV/BX-T, 512 KB: skipped. Probing for Intel 28F400BV/BX/CE/CV-B, 512 KB: skipped. Probing for Intel 28F400BV/BX/CE/CV-T, 512 KB: skipped. Probing for Intel 82802AB, 512 KB: skipped. Probing for Intel 82802AC, 1024 KB: skipped. Probing for Macronix MX25L512, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L1005, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L2005, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L4005, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L8005, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L1605, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L1635D, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L1635E, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L3205, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L3235D, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L6405, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX25L12805, 16384 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix MX29F001B, 128 KB: skipped. Probing for Macronix MX29F001T, 128 KB: skipped. Probing for Macronix MX29F002B, 256 KB: skipped. Probing for Macronix MX29F002T, 256 KB: skipped. Probing for Macronix MX29F040, 512 KB: skipped. Probing for Macronix MX29LV040, 512 KB: skipped. Probing for MoselVitelic V29C51000B, 64 KB: skipped. Probing for MoselVitelic V29C51000T, 64 KB: skipped. Probing for MoselVitelic V29C51400B, 512 KB: skipped. Probing for MoselVitelic V29C51400T, 512 KB: skipped. Probing for MoselVitelic V29LC51000, 64 KB: skipped. Probing for MoselVitelic V29LC51001, 128 KB: skipped. Probing for MoselVitelic V29LC51002, 256 KB: skipped. Probing for Numonyx M25PE10, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Numonyx M25PE20, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Numonyx M25PE40, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Numonyx M25PE80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Numonyx M25PE16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV010, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV016B, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV020, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV040, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV080B, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm25LV512, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC Pm29F002T, 256 KB: skipped. Probing for PMC Pm29F002B, 256 KB: skipped. Probing for PMC Pm39LV010, 128 KB: skipped. Probing for PMC Pm39LV020, 256 KB: skipped. Probing for PMC Pm39LV040, 512 KB: skipped. Probing for PMC Pm49FL002, 256 KB: skipped. Probing for PMC Pm49FL004, 512 KB: skipped. Probing for Sanyo LF25FW203A, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Sharp LHF00L04, 1024 KB: skipped. Probing for Spansion S25FL008A, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Spansion S25FL016A, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST25VF016B, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST25VF032B, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST25VF064C, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST25VF040.REMS, 512 KB: probe_spi_rems: id1 0xef, id2 0x10 Probing for SST SST25VF040B, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST25LF040A.RES, 512 KB: probe_spi_res2: id1 0x10, id2 0x10 Probing for SST SST25VF040B.REMS, 512 KB: probe_spi_rems: id1 0xef, id2 0x10 Probing for SST SST25VF080B, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST SST28SF040A, 512 KB: skipped. Probing for SST SST29EE010, 128 KB: skipped. Probing for SST SST29LE010, 128 KB: skipped. Probing for SST SST29EE020A, 256 KB: skipped. Probing for SST SST29LE020, 256 KB: skipped. Probing for SST SST39SF512, 64 KB: skipped. Probing for SST SST39SF010A, 128 KB: skipped. Probing for SST SST39SF020A, 256 KB: skipped. Probing for SST SST39SF040, 512 KB: skipped. Probing for SST SST39VF512, 64 KB: skipped. Probing for SST SST39VF010, 128 KB: skipped. Probing for SST SST39VF020, 256 KB: skipped. Probing for SST SST39VF040, 512 KB: skipped. Probing for SST SST39VF080, 1024 KB: skipped. Probing for SST SST49LF002A/B, 256 KB: skipped. Probing for SST SST49LF003A/B, 384 KB: skipped. Probing for SST SST49LF004A/B, 512 KB: skipped. Probing for SST SST49LF004C, 512 KB: skipped. Probing for SST SST49LF008A, 1024 KB: skipped. Probing for SST SST49LF008C, 1024 KB: skipped. Probing for SST SST49LF016C, 2048 KB: skipped. Probing for SST SST49LF020, 256 KB: skipped. Probing for SST SST49LF020A, 256 KB: skipped. Probing for SST SST49LF040, 512 KB: skipped. Probing for SST SST49LF040B, 512 KB: skipped. Probing for SST SST49LF080A, 1024 KB: skipped. Probing for SST SST49LF160C, 2048 KB: skipped. Probing for ST M25P05-A, 64 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P05.RES, 64 KB: Ignoring RES in favour of RDID. Probing for ST M25P10-A, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P10.RES, 128 KB: Ignoring RES in favour of RDID. Probing for ST M25P20, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P40, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P40-old, 512 KB: Ignoring RES in favour of RDID. Probing for ST M25P80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P64, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25P128, 16384 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25PX32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M25PX64, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST M29F002B, 256 KB: skipped. Probing for ST M29F002T/NT, 256 KB: skipped. Probing for ST M29F040B, 512 KB: skipped. Probing for ST M29F400BB, 512 KB: skipped. Probing for ST M29F400BT, 512 KB: skipped. Probing for ST M29W010B, 128 KB: skipped. Probing for ST M29W040B, 512 KB: skipped. Probing for ST M29W512B, 64 KB: skipped. Probing for ST M50FLW040A, 512 KB: skipped. Probing for ST M50FLW040B, 512 KB: skipped. Probing for ST M50FLW080A, 1024 KB: skipped. Probing for ST M50FLW080B, 1024 KB: skipped. Probing for ST M50FW002, 256 KB: skipped. Probing for ST M50FW016, 2048 KB: skipped. Probing for ST M50FW040, 512 KB: skipped. Probing for ST M50FW080, 1024 KB: skipped. Probing for ST M50LPW116, 2048 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51001B, 128 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51001T, 128 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51002B, 256 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51002T, 256 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51004B, 512 KB: skipped. Probing for SyncMOS/MoselVitelic {F,S,V}29C51004T, 512 KB: skipped. Probing for SyncMOS/MoselVitelic {S,V}29C31004B, 512 KB: skipped. Probing for SyncMOS/MoselVitelic {S,V}29C31004T, 512 KB: skipped. Probing for TI TMS29F002RB, 256 KB: skipped. Probing for TI TMS29F002RT, 256 KB: skipped. Probing for Winbond W25Q80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25Q16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25Q32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25Q64, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x10, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Chip status register is 00 Found chip "Winbond W25x10" (128 KB, SPI) at physical address 0xfffe0000. Probing for Winbond W25x20, 256 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x40, 512 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x80, 1024 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x16, 2048 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x32, 4096 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W25x64, 8192 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Winbond W29C010(M)/W29C011A/W29EE011/W29EE012, 128 KB: skipped. Probing for Winbond W29C020(C)/W29C022, 256 KB: skipped. Probing for Winbond W29C040/P, 512 KB: skipped. Probing for Winbond W29C010(M)/W29C011A/W29EE011/W29EE012, 128 KB: skipped. Probing for Winbond W39V040A, 512 KB: skipped. Probing for Winbond W39V040(F)B, 512 KB: skipped. Probing for Winbond W39V040(F)C, 512 KB: skipped. Probing for Winbond W39V040FA, 512 KB: skipped. Probing for Winbond W39V080A, 1024 KB: skipped. Probing for Winbond W49F002U/N, 256 KB: skipped. Probing for Winbond W49F020, 256 KB: skipped. Probing for Winbond W49V002A, 256 KB: skipped. Probing for Winbond W49V002FA, 256 KB: skipped. Probing for Winbond W39V080FA, 1024 KB: skipped. Probing for Winbond W39V080FA (dual mode), 512 KB: skipped. Probing for AMIC unknown AMIC SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Atmel unknown Atmel SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Eon unknown Eon SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Macronix unknown Macronix SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for PMC unknown PMC SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for SST unknown SST SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for ST unknown ST SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Sanyo unknown Sanyo SPI chip, 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Generic unknown SPI chip (RDID), 0 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Probing for Generic unknown SPI chip (REMS), 0 KB: probe_spi_rems: id1 0xef, id2 0x10 === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff, 0x001000-0x001fff, 0x002000-0x002fff, 0x003000-0x003fff, 0x004000-0x004fff, 0x005000-0x005fff, 0x006000-0x006fff, 0x007000-0x007fff, 0x008000-0x008fff, 0x009000-0x009fff, 0x00a000-0x00afff, 0x00b000-0x00bfff, 0x00c000-0x00cfff, 0x00d000-0x00dfff, 0x00e000-0x00efff, 0x00f000-0x00ffff, 0x010000-0x010fff, 0x011000-0x011fff, 0x012000-0x012fff, 0x013000-0x013fff, 0x014000-0x014fff, 0x015000-0x015fff, 0x016000-0x016fff, 0x017000-0x017fff, 0x018000-0x018fff, 0x019000-0x019fff, 0x01a000-0x01afff, 0x01b000-0x01bfff, 0x01c000-0x01cfff, 0x01d000-0x01dfff, 0x01e000-0x01efff, 0x01f000-0x01ffff, SUCCESS. COMPLETE. Verifying flash... VERIFY FAILED at 0x00001000! Expected=0xfe, Read=0x85, failed byte count from 0x00000000-0x0001ffff: 0x32c8 Your flash chip is in an unknown state. Get help on IRC at irc.freenode.net (channel #flashrom) or mail flashrom@flashrom.org with FAILED: your board name in the subject line! ------------------------------------------------------------------------------- DO NOT REBOOT OR POWEROFF! Setting SPI voltage to 0.000 V
On 20.10.2010 23:42, Richard A. Smith wrote:
Here's the result of a write test with this patch.
Thanks!
rsmith@sparky:/home/src/flashrom$ ./flashrom -p dediprog -Vw ~/olpc/ec/ec-1.75/image/ecimage.bin flashrom v0.9.3-r1215 on Linux 2.6.36-020636rc7-generic (i686), built with libpci 3.0.0, GCC 4.4.3, little endian flashrom is free software, get the source code at http://www.flashrom.org Calibrating delay loop... OS timer resolution is 2 usecs, 2777M loops per second, 10 myus = 11 us, 100 myus = 101 us, 1000 myus = 996 us, 10000 myus = 10010 us, 8 myus = 9 us, OK. Initializing dediprog programmer Found USB device (0483:dada). Found a SF100 V:5.1.5 Setting SPI voltage to 3.500 V Probing for Winbond W25x10, 128 KB: probe_spi_rdid_generic: id1 0xef, id2 0x3011 Chip status register is 00 Found chip "Winbond W25x10" (128 KB, SPI) at physical address 0xfffe0000. === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE
Right, we need to add that chip to the tested list.
Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff, 0x001000-0x001fff, 0x002000-0x002fff, 0x003000-0x003fff, 0x004000-0x004fff, 0x005000-0x005fff, 0x006000-0x006fff, 0x007000-0x007fff, 0x008000-0x008fff, 0x009000-0x009fff, 0x00a000-0x00afff, 0x00b000-0x00bfff, 0x00c000-0x00cfff, 0x00d000-0x00dfff, 0x00e000-0x00efff, 0x00f000-0x00ffff, 0x010000-0x010fff, 0x011000-0x011fff, 0x012000-0x012fff, 0x013000-0x013fff, 0x014000-0x014fff, 0x015000-0x015fff, 0x016000-0x016fff, 0x017000-0x017fff, 0x018000-0x018fff, 0x019000-0x019fff, 0x01a000-0x01afff, 0x01b000-0x01bfff, 0x01c000-0x01cfff, 0x01d000-0x01dfff, 0x01e000-0x01efff, 0x01f000-0x01ffff, SUCCESS. COMPLETE.
Those messages should only be printed if erase worked everywhere. This also means that we're probably writing garbage.
Verifying flash... VERIFY FAILED at 0x00001000! Expected=0xfe, Read=0x85, failed byte count from 0x00000000-0x0001ffff: 0x32c8
Regards, Carl-Daniel
On 20/10/10 05:06, Carl-Daniel Hailfinger wrote:
If you're going to test this, please be advised that I have not tested execution, only compilation. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
Write seems work, but erase on its own doesn't. Some blocks aren't erased: (good = erased, bad = not erased)
0x000000-0x000fff, 0x001000-0x001fff, good 0x002000-0x002fff, bad 0x003000-0x003fff, 0x004000-0x004fff, 0x005000-0x005fff, good 0x006000-0x006fff, bad 0x007000-0x007fff, good 0x008000-0x008fff, bad 0x009000-0x009fff, good 0x00a000-0x00afff, bad 0x00b000-0x00bfff, 0x00c000-0x00cfff, 0x00d000-0x00dfff, good 0x00e000-0x00efff, bad 0x00f000-0x00ffff, 0x010000-0x010fff, 0x011000-0x011fff, good 0x012000-0x012fff, bad 0x013000-0x013fff, 0x014000-0x014fff, 0x015000-0x015fff, good 0x016000-0x016fff, bad 0x017000-0x017fff, good 0x018000-0x018fff, bad 0x019000-0x019fff, good 0x01a000-0x01afff, bad 0x01b000-0x01bfff, 0x01c000-0x01cfff, 0x01d000-0x01dfff, good 0x01e000-0x01efff, bad 0x01f000-0x01ffff, good 0x020000-0x020fff, bad 0x021000-0x021fff, good 0x022000-0x022fff, bad 0x023000-0x023fff, 0x024000-0x024fff, 0x025000-0x025fff, good 0x026000-0x026fff, bad 0x027000-0x027fff, good 0x028000-0x028fff, bad 0x029000-0x029fff, good 0x02a000-0x02afff, bad 0x02b000-0x02bfff, 0x02c000-0x02cfff, 0x02d000-0x02dfff, good 0x02e000-0x02efff, bad 0x02f000-0x02ffff, 0x030000-0x030fff, 0x031000-0x031fff, good 0x032000-0x032fff, bad 0x033000-0x033fff, 0x034000-0x034fff, 0x035000-0x035fff, good 0x036000-0x036fff, bad 0x037000-0x037fff, good 0x038000-0x038fff, bad 0x039000-0x039fff, good 0x03a000-0x03afff, bad 0x03b000-0x03bfff, 0x03c000-0x03cfff, 0x03d000-0x03dfff, good 0x03e000-0x03efff, bad 0x03f000-0x03ffff, good
On 21.10.2010 02:00, Andrew Morgan wrote:
On 20/10/10 05:06, Carl-Daniel Hailfinger wrote:
If you're going to test this, please be advised that I have not tested execution, only compilation. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
Write seems work, but erase on its own doesn't. Some blocks aren't erased: (good = erased, bad = not erased)
0x000000-0x000fff, 0x001000-0x001fff, good 0x002000-0x002fff, bad 0x003000-0x003fff, 0x004000-0x004fff, 0x005000-0x005fff, good 0x006000-0x006fff, bad 0x007000-0x007fff, good 0x008000-0x008fff, bad
I suspect that there are more bugs in the code than I'd like. Please try the patch below which has more debug output.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, j, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (j = 0; j < len / 256; j++) { + limit = min(256, len - j * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + j * 256, want + j * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = j * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = j * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(j * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,59 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg(" E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents + start, 0xff, len); + } else + msg_cdbg(" e"); + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, start + starthere, lenhere); + if (ret) + break; + starthere += lenhere; + } + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1431,22 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + msg_cdbg("0x%06x-0x%06x", start, start + len - 1); - if (do_something(flash, start, len)) + if (do_something(flash, start, len, param1, param2, eraser.block_erase)) return 1; + msg_cdbg(", "); start += len; } } return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
- msg_cinfo("Erasing flash chip... "); + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,7 +1470,7 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, oldcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) @@ -1637,6 +1783,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1803,13 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); programmer_shutdown(); return 1; } }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1822,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1841,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1701,24 +1856,6 @@ emergency_help_message(); programmer_shutdown(); return 1; - } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; } else { msg_cinfo("COMPLETE.\n"); }
On 21.10.2010 02:08, Carl-Daniel Hailfinger wrote:
I suspect that there are more bugs in the code than I'd like. Please try the patch below which has more debug output.
need_erase used an incorrect offset calculation in the write_gran_256bytes case.
This one may even work. Please make sure to test with an image generated with a random number generator (like /dev/urandom or similar).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, j, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (j = 0; j < len / 256; j++) { + limit = min(256, len - j * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + j * 256, want + j * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = j * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = j * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(j * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,59 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg(" E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents + start, 0xff, len); + } else + msg_cdbg(" e"); + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, start + starthere, lenhere); + if (ret) + break; + starthere += lenhere; + } + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1431,22 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + msg_cdbg("0x%06x-0x%06x", start, start + len - 1); - if (do_something(flash, start, len)) + if (do_something(flash, start, len, param1, param2, eraser.block_erase)) return 1; + msg_cdbg(", "); start += len; } } return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
- msg_cinfo("Erasing flash chip... "); + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,7 +1470,7 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, oldcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) @@ -1637,6 +1783,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1803,13 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); programmer_shutdown(); return 1; } }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1822,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1841,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1701,24 +1856,6 @@ emergency_help_message(); programmer_shutdown(); return 1; - } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; } else { msg_cinfo("COMPLETE.\n"); }
Logs from newest patch attached.
More logs, from latest patch plus removing ' + start' on line 1395.
Looks to me as if it is working correctly!
"...1diff...log" is the result of writing an image which has a difference in only the first block.
On 21.10.2010 02:54, Andrew Morgan wrote:
More logs, from latest patch plus removing ' + start' on line 1395.
Looks to me as if it is working correctly!
"...1diff...log" is the result of writing an image which has a difference in only the first block.
Awesome, thanks for running this big test series each time I sent a new version.
Regards, Carl-Daniel
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, j, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (j = 0; j < len / 256; j++) { + limit = min(256, len - j * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + j * 256, want + j * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = j * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = j * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(j * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,66 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + int skip = 1; + int writecount = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + msg_cdbg(":"); + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg("E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents, 0xff, len); + skip = 0; + } + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + if (!writecount++) + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, start + starthere, lenhere); + if (ret) + return ret; + starthere += lenhere; + skip = 0; + } + if (skip) + msg_cdbg("S"); + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1438,22 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + msg_cdbg("0x%06x-0x%06x", start, start + len - 1); - if (do_something(flash, start, len)) + if (do_something(flash, start, len, param1, param2, eraser.block_erase)) return 1; + msg_cdbg(", "); start += len; } } return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
- msg_cinfo("Erasing flash chip... "); + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,7 +1477,7 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, oldcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) @@ -1637,6 +1790,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1810,13 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); programmer_shutdown(); return 1; } }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1829,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1848,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1701,24 +1863,6 @@ emergency_help_message(); programmer_shutdown(); return 1; - } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; } else { msg_cinfo("COMPLETE.\n"); }
New version. This one should guarantee correct results even if the first erase/write attempt fails.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks for Andrew Morgan for testing countless iterations of this patch.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, j, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (j = 0; j < len / 256; j++) { + limit = min(256, len - j * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + j * 256, want + j * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = j * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = j * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(j * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,66 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + int skip = 1; + int writecount = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + msg_cdbg(":"); + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg("E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents, 0xff, len); + skip = 0; + } + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + if (!writecount++) + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, start + starthere, lenhere); + if (ret) + return ret; + starthere += lenhere; + skip = 0; + } + if (skip) + msg_cdbg("S"); + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1438,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + /* 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)) + if (do_something(flash, start, len, param1, param2, + eraser.block_erase)) { + msg_cdbg("\n"); return 1; + } start += len; } } + msg_cdbg("\n"); return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0; + uint8_t *curcontents; + unsigned long size = flash->total_size * 1024;
- msg_cinfo("Erasing flash chip... "); + curcontents = (uint8_t *) malloc(size); + /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */ + memcpy(curcontents, oldcontents, size); + + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1489,14 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break; } + /* Free the scratchpad. */ + free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this flash chip.\n"); return 1; @@ -1637,6 +1804,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1824,13 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); programmer_shutdown(); return 1; } }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1843,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1862,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1701,24 +1877,6 @@ emergency_help_message(); programmer_shutdown(); return 1; - } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; } else { msg_cinfo("COMPLETE.\n"); }
On 10/20/2010 09:31 PM, Carl-Daniel Hailfinger wrote:
New version. This one should guarantee correct results even if the first erase/write attempt fails.
This new version works for me on the dediprog.
New version. This one should address most review comments.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted). Tests on boards which need a board enable and where you can lock down the whole chip or parts of it would be appreciated as well.
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks for Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,85 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @first_start offset of the first byte which needs to be written (passed in + value is discarded, new value is written when get_next_write + returns) + * @return length of the first contiguous area which needs to be written + * 0 if no write is needed + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, enum write_granularity gran) +{ + int need_write = 0; + int first_len = 0; + int i, limit, stride; + + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + stride = 1; + break; + case write_gran_256bytes: + stride = 256; + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + /* Claim that no write was needed. A write with unknown + * granularity is too dangerous to try. + */ + return 0; + } + for (i = 0; i < len / stride; i++) { + limit = min(stride, len - i * stride); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + i * stride, want + i * stride, limit)) { + if (!need_write) { + /* First location where have and want differ. */ + need_write = 1; + *first_start = i * stride; + } + } else { + if (need_write) { + /* First location where have and want + * do not differ anymore. + */ + first_len = i * stride - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (need_write && ! first_len) + first_len = min(i * stride - *first_start, len); + + return first_len; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1281,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1350,68 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + int skip = 1; + int writecount = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + msg_cdbg(":"); + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg("E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents, 0xff, len); + skip = 0; + } + /* get_next_write() sets starthere to a new value after the call. */ + while ((lenhere = get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + gran))) { + if (!writecount++) + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, + start + starthere, lenhere); + if (ret) + return ret; + starthere += lenhere; + skip = 0; + } + if (skip) + msg_cdbg("S"); + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1423,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + /* 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)) + if (do_something(flash, start, len, param1, param2, + eraser.block_erase)) { + msg_cdbg("\n"); return 1; + } start += len; } } + msg_cdbg("\n"); return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0; + uint8_t *curcontents; + unsigned long size = flash->total_size * 1024;
- msg_cinfo("Erasing flash chip... "); + curcontents = (uint8_t *) malloc(size); + /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */ + memcpy(curcontents, oldcontents, size); + + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1474,14 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break; } + /* Free the scratchpad. */ + free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this flash chip.\n"); return 1; @@ -1338,7 +1490,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else { - msg_cinfo("SUCCESS.\n"); + msg_cinfo("Done.\n"); } return ret; } @@ -1637,6 +1789,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1809,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); - programmer_shutdown(); - return 1; + ret = 1; } + programmer_shutdown(); + return ret; }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1829,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1848,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1864,6 @@ programmer_shutdown(); return 1; } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; - } else { - msg_cinfo("COMPLETE.\n"); - } }
if (verify_it) {
2010/10/21 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
New version. This one should address most review comments.
With -p nic3com:
Erase: $ sudo ./flashrom -p nic3com -V -E flashrom v0.9.3-r1216 on Linux 2.6.35-ARCH (i686), built with libpci 3.1.7, GCC 4.5.1, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1936M loops per second, 10 myus = 10 us, 100 myus = 97 us, 1000 myus = 972 us, 10000 myus = 11516 us, 4 myus = 5 us, OK. Initializing nic3com programmer Found "3COM 3C905C: EtherLink 10/100 PCI (TX)" (10b7:9200, BDF 01:01.0). Requested BAR is IO // snip Found chip "Atmel AT49BV512" (64 KB, Parallel) at physical address 0xffff0000. //snip === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x00ffff:E
Done.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
On Intel hardware (abusing the wide concept of "chipset" :)):
Write (yes, I have a backup file): $ sudo ./flashrom -p nicintel_spi -w nicintel_spi.rom -c M25P10.RES -V flashrom v0.9.3-r1216 on Linux 2.6.35-ARCH (i686), built with libpci 3.1.7, GCC 4.5.1, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1907M loops per second, 10 myus = 10 us, 100 myus = 96 us, 1000 myus = 1014 us, 10000 myus = 10801 us, 4 myus = 4 us, OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF 01:03.0). Requested BAR is MEM, 32bit, not prefetchable Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x007fff:S, 0x008000-0x00ffff:EW, 0x010000-0x017fff:EW, 0x018000-0x01ffff:S
Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6, Read=0x15, failed byte count from 0x00000000-0x0001ffff: 0x872f Your flash chip is in an unknown state. Get help on IRC at irc.freenode.net (channel #flashrom) or mail flashrom@flashrom.org with FAILED: your board name in the subject line! ------------------------------------------------------------------------------- DO NOT REBOOT OR POWEROFF!
Erase: $ sudo ./flashrom -p nicintel_spi -E -c M25P10.RES -V flashrom v0.9.3-r1216 on Linux 2.6.35-ARCH (i686), built with libpci 3.1.7, GCC 4.5.1, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1833M loops per second, 10 myus = 9 us, 100 myus = 92 us, 1000 myus = 917 us, 10000 myus = 11106 us, 4 myus = 4 us, OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF 01:03.0). Requested BAR is MEM, 32bit, not prefetchable Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x007fff:E, 0x008000-0x00ffff:E, 0x010000-0x017fff:E, 0x018000-0x01ffff:E
Done.
Tests on boards which need a board enable and where you can lock down
the whole chip or parts of it would be appreciated as well.
Mainboard that needs a board enable:
Erase: $ sudo ./flashrom -V -E flashrom v0.9.3-r1216 on Linux 2.6.35-ARCH (i686), built with libpci 3.1.7, GCC 4.5.1, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1945M loops per second, 10 myus = 10 us, 100 myus = 98 us, 1000 myus = 973 us, 10000 myus = 13546 us, 4 myus = 4 us, OK. Initializing internal programmer No coreboot table found. sh: dmidecode: command not found dmidecode execution unsucessfull - continuing without DMI info Found chipset "Intel ICH5/ICH5R", enabling flash write... chipset PCI ID is 8086:24d0, BIOS Lock Enable: disabled, BIOS Write Enable: enabled, BIOS_CNTL is 0x1 OK. This chipset supports the following protocols: FWH. Disabling flash write protection for board "ASRock P4i65GV"... Intel ICH LPC Bridge: Raising GPIO23. OK. // snip Found chip "PMC Pm49FL004" (512 KB, LPC,FWH) at physical address 0xfff80000. // snip === This flash part has status UNTESTED for operations: WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff:E, 0x001000-0x001fff:E, 0x002000-0x002fff:E, 0x003000-0x003fff:E, 0x004000-0x004fff:E, 0x005000-0x005fff:E, 0x006000-0x006fff:E, 0x007000-0x007fff:E, 0x008000-0x008fff:E, 0x009000-0x009fff:E, 0x00a000-0x00afff:E, 0x00b000-0x00bfff:E, 0x00c000-0x00cfff:E, 0x00d000-0x00dfff:E, 0x00e000-0x00efff:E, 0x00f000-0x00ffff:E, 0x010000-0x010fff:E, 0x011000-0x011fff:E, 0x012000-0x012fff:E, 0x013000-0x013fff:E, 0x014000-0x014fff:E, 0x015000-0x015fff:E, 0x016000-0x016fff:E, 0x017000-0x017fff:E, 0x018000-0x018fff:E, 0x019000-0x019fff:E, 0x01a000-0x01afff:E, 0x01b000-0x01bfff:E, 0x01c000-0x01cfff:E, 0x01d000-0x01dfff:E, 0x01e000-0x01efff:E, 0x01f000-0x01ffff:E, 0x020000-0x020fff:E, 0x021000-0x021fff:E, 0x022000-0x022fff:E, 0x023000-0x023fff:E, 0x024000-0x024fff:E, 0x025000-0x025fff:E, 0x026000-0x026fff:E, 0x027000-0x027fff:E, 0x028000-0x028fff:E, 0x029000-0x029fff:E, 0x02a000-0x02afff:E, 0x02b000-0x02bfff:E, 0x02c000-0x02cfff:E, 0x02d000-0x02dfff:E, 0x02e000-0x02efff:E, 0x02f000-0x02ffff:E, 0x030000-0x030fff:E, 0x031000-0x031fff:E, 0x032000-0x032fff:E, 0x033000-0x033fff:E, 0x034000-0x034fff:E, 0x035000-0x035fff:E, 0x036000-0x036fff:E, 0x037000-0x037fff:E, 0x038000-0x038fff:E, 0x039000-0x039fff:E, 0x03a000-0x03afff:E, 0x03b000-0x03bfff:E, 0x03c000-0x03cfff:E, 0x03d000-0x03dfff:E, 0x03e000-0x03efff:E, 0x03f000-0x03ffff:E, 0x040000-0x040fff:E, 0x041000-0x041fff:E, 0x042000-0x042fff:E, 0x043000-0x043fff:E, 0x044000-0x044fff:E, 0x045000-0x045fff:E, 0x046000-0x046fff:E, 0x047000-0x047fff:E, 0x048000-0x048fff:E, 0x049000-0x049fff:E, 0x04a000-0x04afff:E, 0x04b000-0x04bfff:E, 0x04c000-0x04cfff:E, 0x04d000-0x04dfff:E, 0x04e000-0x04efff:E, 0x04f000-0x04ffff:E, 0x050000-0x050fff:E, 0x051000-0x051fff:E, 0x052000-0x052fff:E, 0x053000-0x053fff:E, 0x054000-0x054fff:E, 0x055000-0x055fff:E, 0x056000-0x056fff:E, 0x057000-0x057fff:E, 0x058000-0x058fff:E, 0x059000-0x059fff:E, 0x05a000-0x05afff:E, 0x05b000-0x05bfff:E, 0x05c000-0x05cfff:E, 0x05d000-0x05dfff:E, 0x05e000-0x05efff:E, 0x05f000-0x05ffff:E, 0x060000-0x060fff:E, 0x061000-0x061fff:E, 0x062000-0x062fff:E, 0x063000-0x063fff:E, 0x064000-0x064fff:E, 0x065000-0x065fff:E, 0x066000-0x066fff:E, 0x067000-0x067fff:E, 0x068000-0x068fff:E, 0x069000-0x069fff:E, 0x06a000-0x06afff:E, 0x06b000-0x06bfff:E, 0x06c000-0x06cfff:E, 0x06d000-0x06dfff:E, 0x06e000-0x06efff:E, 0x06f000-0x06ffff:E, 0x070000-0x070fff:E, 0x071000-0x071fff:E, 0x072000-0x072fff:E, 0x073000-0x073fff:E, 0x074000-0x074fff:E, 0x075000-0x075fff:E, 0x076000-0x076fff:E, 0x077000-0x077fff:E, 0x078000-0x078fff:E, 0x079000-0x079fff:E, 0x07a000-0x07afff:E, 0x07b000-0x07bfff:E, 0x07c000-0x07cfff:E, 0x07d000-0x07dfff:E, 0x07e000-0x07efff:E, 0x07f000-0x07ffff:E
Done.
Write: $ sudo ./flashrom -V -w ~/asrock_p4i65gv_512KB.rom flashrom v0.9.3-r1216 on Linux 2.6.35-ARCH (i686), built with libpci 3.1.7, GCC 4.5.1, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1886M loops per second, 10 myus = 9 us, 100 myus = 95 us, 1000 myus = 1032 us, 10000 myus = 9180 us, 4 myus = 4 us, OK. Initializing internal programmer No coreboot table found. sh: dmidecode: command not found dmidecode execution unsucessfull - continuing without DMI info Found chipset "Intel ICH5/ICH5R", enabling flash write... chipset PCI ID is 8086:24d0, BIOS Lock Enable: disabled, BIOS Write Enable: enabled, BIOS_CNTL is 0x1 OK. This chipset supports the following protocols: FWH. Disabling flash write protection for board "ASRock P4i65GV"... Intel ICH LPC Bridge: Raising GPIO23. OK. // snip Found chip "PMC Pm49FL004" (512 KB, LPC,FWH) at physical address 0xfff80000. // snip === This flash part has status UNTESTED for operations: WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Flash image seems to be a legacy BIOS. Disabling checks. Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff:EW, 0x001000-0x001fff:EW, 0x002000-0x002fff:EW, 0x003000-0x003fff:EW, 0x004000-0x004fff:EW, 0x005000-0x005fff:EW, 0x006000-0x006fff:EW, 0x007000-0x007fff:EW, 0x008000-0x008fff:EW, 0x009000-0x009fff:EW, 0x00a000-0x00afff:EW, 0x00b000-0x00bfff:EW, 0x00c000-0x00cfff:EW, 0x00d000-0x00dfff:EW, 0x00e000-0x00efff:EW, 0x00f000-0x00ffff:EW, 0x010000-0x010fff:EW, 0x011000-0x011fff:EW, 0x012000-0x012fff:W, 0x013000-0x013fff:W, 0x014000-0x014fff:W, 0x015000-0x015fff:W, 0x016000-0x016fff:W, 0x017000-0x017fff:W, 0x018000-0x018fff:W, 0x019000-0x019fff:W, 0x01a000-0x01afff:W, 0x01b000-0x01bfff:W, 0x01c000-0x01cfff:W, 0x01d000-0x01dfff:W, 0x01e000-0x01efff:W, 0x01f000-0x01ffff:W, 0x020000-0x020fff:W, 0x021000-0x021fff:W, 0x022000-0x022fff:W, 0x023000-0x023fff:W, 0x024000-0x024fff:W, 0x025000-0x025fff:W, 0x026000-0x026fff:W, 0x027000-0x027fff:W, 0x028000-0x028fff:W, 0x029000-0x029fff:W, 0x02a000-0x02afff:W, 0x02b000-0x02bfff:W, 0x02c000-0x02cfff:W, 0x02d000-0x02dfff:W, 0x02e000-0x02efff:W, 0x02f000-0x02ffff:W, 0x030000-0x030fff:W, 0x031000-0x031fff:W, 0x032000-0x032fff:W, 0x033000-0x033fff:W, 0x034000-0x034fff:W, 0x035000-0x035fff:W, 0x036000-0x036fff:W, 0x037000-0x037fff:W, 0x038000-0x038fff:W, 0x039000-0x039fff:W, 0x03a000-0x03afff:W, 0x03b000-0x03bfff:W, 0x03c000-0x03cfff:W, 0x03d000-0x03dfff:W, 0x03e000-0x03efff:W, 0x03f000-0x03ffff:W, 0x040000-0x040fff:W, 0x041000-0x041fff:W, 0x042000-0x042fff:W, 0x043000-0x043fff:W, 0x044000-0x044fff:W, 0x045000-0x045fff:W, 0x046000-0x046fff:W, 0x047000-0x047fff:W, 0x048000-0x048fff:W, 0x049000-0x049fff:W, 0x04a000-0x04afff:W, 0x04b000-0x04bfff:W, 0x04c000-0x04cfff:W, 0x04d000-0x04dfff:W, 0x04e000-0x04efff:W, 0x04f000-0x04ffff:W, 0x050000-0x050fff:W, 0x051000-0x051fff:W, 0x052000-0x052fff:W, 0x053000-0x053fff:W, 0x054000-0x054fff:W, 0x055000-0x055fff:W, 0x056000-0x056fff:W, 0x057000-0x057fff:W, 0x058000-0x058fff:W, 0x059000-0x059fff:W, 0x05a000-0x05afff:W, 0x05b000-0x05bfff:W, 0x05c000-0x05cfff:W, 0x05d000-0x05dfff:W, 0x05e000-0x05efff:W, 0x05f000-0x05ffff:W, 0x060000-0x060fff:W, 0x061000-0x061fff:W, 0x062000-0x062fff:W, 0x063000-0x063fff:W, 0x064000-0x064fff:W, 0x065000-0x065fff:W, 0x066000-0x066fff:W, 0x067000-0x067fff:W, 0x068000-0x068fff:W, 0x069000-0x069fff:W, 0x06a000-0x06afff:W, 0x06b000-0x06bfff:W, 0x06c000-0x06cfff:W, 0x06d000-0x06dfff:W, 0x06e000-0x06efff:W, 0x06f000-0x06ffff:W, 0x070000-0x070fff:W, 0x071000-0x071fff:W, 0x072000-0x072fff:W, 0x073000-0x073fff:W, 0x074000-0x074fff:W, 0x075000-0x075fff:W, 0x076000-0x076fff:W, 0x077000-0x077fff:W, 0x078000-0x078fff:W, 0x079000-0x079fff:W, 0x07a000-0x07afff:W, 0x07b000-0x07bfff:W, 0x07c000-0x07cfff:W, 0x07d000-0x07dfff:W, 0x07e000-0x07efff:W, 0x07f000-0x07ffff:EW
Done. Verifying flash... VERIFIED.
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks for Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c
--- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@
- Check if the buffer @have can be programmed to the content of @want
without
- erasing. This is only possible if all chunks of size @gran are either
kept
- as-is or changed from an all-ones state to any other state.
- The following write granularities (enum @gran) are known:
- 1 bit. Each bit can be cleared individually.
- 1 byte. A byte can be written once. Further writes to an already
written @@ -803,10 +804,12 @@
- this function.
- 256 bytes. If less than 256 bytes are written, the contents of the
- unwritten bytes are undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the verified area
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no erase is needed, 1 otherwise
*/ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++)
if (have[i] != 0xff) {
if (have[j * 256 + i] != 0xff) { result = 1; break; }
@@ -846,10 +849,85 @@ break; } break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__); } return result;
}
+/**
- Check if the buffer @have needs to be programmed to get the content of
@want.
- If yes, return 1 and fill in first_start with the start address of the
- write operation and first_len with the length of the first
to-be-written
- chunk. If not, return 0 and leave first_start and first_len undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the checked area
- @gran write granularity (enum, not count)
- @first_start offset of the first byte which needs to be written
(passed in
value is discarded, new value is written when
get_next_write
returns)
- @return length of the first contiguous area which needs to be
written
0 if no write is needed
- FIXME: This function needs a parameter which tells it about coalescing
- in relation to the max write length of the programmer and the max write
- length of the chip.
- */
+static int get_next_write(uint8_t *have, uint8_t *want, int len,
int *first_start, enum write_granularity gran)
+{
int need_write = 0;
int first_len = 0;
int i, limit, stride;
switch (gran) {
case write_gran_1bit:
case write_gran_1byte:
stride = 1;
break;
case write_gran_256bytes:
stride = 256;
break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__);
/* Claim that no write was needed. A write with unknown
* granularity is too dangerous to try.
*/
return 0;
}
for (i = 0; i < len / stride; i++) {
limit = min(stride, len - i * stride);
/* Are 'have' and 'want' identical? */
if (memcmp(have + i * stride, want + i * stride, limit)) {
if (!need_write) {
/* First location where have and want
differ. */
need_write = 1;
*first_start = i * stride;
}
} else {
if (need_write) {
/* First location where have and want
* do not differ anymore.
*/
first_len = i * stride - *first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (need_write && ! first_len)
first_len = min(i * stride - *first_start, len);
return first_len;
+}
/* This function generates various test patterns useful for testing controller
- and chip communication as well as chip behaviour.
@@ -1203,7 +1281,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and
- walk_eraseregions().
- Even if an error is found, the function will keep going and check the
rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1350,68 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash,
unsigned int start, unsigned int
len,
uint8_t *oldcontents,
uint8_t *newcontents,
int (*erasefn) (struct flashchip
*flash,
unsigned int addr,
unsigned int len))
+{
int starthere = 0;
int lenhere = 0;
int ret = 0;
int skip = 1;
int writecount = 0;
enum write_granularity gran = write_gran_256bytes; /* FIXME */
/* oldcontents and newcontents are opaque to walk_eraseregions, and
* need to be adjusted here to keep the impression of proper
abstraction
*/
oldcontents += start;
newcontents += start;
msg_cdbg(":");
/* FIXME: Assume 256 byte granularity for now to play it safe. */
if (need_erase(oldcontents, newcontents, len, gran)) {
msg_cdbg("E");
ret = erasefn(flash, start, len);
if (ret)
return ret;
/* Erase was successful. Adjust oldcontents. */
memset(oldcontents, 0xff, len);
skip = 0;
}
/* get_next_write() sets starthere to a new value after the call.
*/
while ((lenhere = get_next_write(oldcontents + starthere,
newcontents + starthere,
len - starthere, &starthere,
gran))) {
if (!writecount++)
msg_cdbg("W");
/* Needs the partial write function signature. */
ret = flash->write(flash, newcontents + starthere,
start + starthere, lenhere);
if (ret)
return ret;
starthere += lenhere;
skip = 0;
}
if (skip)
msg_cdbg("S");
return ret;
+}
static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr,
unsigned int len))
unsigned int len,
uint8_t *param1,
uint8_t *param2,
int (*erasefn) (
struct flashchip
*flash,
unsigned int addr,
unsigned int len)),
void *param1, void *param2)
{ int i, j; unsigned int start = 0; @@ -1286,21 +1423,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) {
msg_cdbg("0x%06x-0x%06x, ", start,
/* 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))
if (do_something(flash, start, len, param1, param2,
eraser.block_erase)) {
msg_cdbg("\n"); return 1;
} start += len; } }
msg_cdbg("\n"); return 0;
}
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
uint8_t *curcontents;
unsigned long size = flash->total_size * 1024;
msg_cinfo("Erasing flash chip... ");
curcontents = (uint8_t *) malloc(size);
/* Copy oldcontents to curcontents to avoid clobbering oldcontents.
*/
memcpy(curcontents, oldcontents, size);
msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1474,14 @@ } found = 1; msg_cdbg("trying... ");
ret = walk_eraseregions(flash, k, eraser.block_erase);
ret = walk_eraseregions(flash, k,
&erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break; }
/* Free the scratchpad. */
free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this
flash chip.\n"); return 1; @@ -1338,7 +1490,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else {
msg_cinfo("SUCCESS.\n");
msg_cinfo("Done.\n"); } return ret;
} @@ -1637,6 +1789,19 @@ programmer_shutdown(); return ret; }
oldcontents = (uint8_t *) malloc(size);
/* Assume worst case: All bits are 0. */
memset(oldcontents, 0x00, size);
newcontents = (uint8_t *) malloc(size);
/* Assume best case: All bits should be 1. */
memset(newcontents, 0xff, size);
/* Side effect of the assumptions above: Default write action is
erase
* because newcontents looks like a completely erased chip, and
* oldcontents being completely 0x00 means we have to erase
everything
* before we can write.
*/
if (erase_it) { /* FIXME: Do we really want the scary warning if erase
failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1809,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */
if (erase_flash(flash)) {
if (erase_and_write_flash(flash, oldcontents, newcontents))
{ emergency_help_message();
programmer_shutdown();
return 1;
ret = 1; }
programmer_shutdown();
return ret; }
newcontents = (uint8_t *) calloc(size, sizeof(char));
if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown();
@@ -1665,8 +1829,6 @@ #endif }
oldcontents = (uint8_t *) calloc(size, sizeof(char));
/* 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 @@ -1686,9 +1848,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) {
if (erase_flash(flash)) {
msg_cerr("Uh oh. Erase failed. Checking if anything
"
"changed.\n");
if (erase_and_write_flash(flash, oldcontents, newcontents))
{
msg_cerr("Uh oh. Erase/write failed. Checking if "
"anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size))
{ msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1864,6 @@ programmer_shutdown(); return 1; }
msg_cinfo("Writing flash chip... ");
ret = flash->write(flash, newcontents, 0, size);
if (ret) {
msg_cerr("Uh oh. Write failed. Checking if anything
"
"changed.\n");
if (!flash->read(flash, newcontents, 0, size)) {
if (!memcmp(oldcontents, newcontents,
size)) {
msg_cinfo("Good. It seems nothing
was "
"changed.\n");
nonfatal_help_message();
programmer_shutdown();
return 1;
}
}
emergency_help_message();
programmer_shutdown();
return 1;
} else {
msg_cinfo("COMPLETE.\n");
} } if (verify_it) {
flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom
Given that the version which addresses the review comments apparently is buggy, I hereby propose to merge the earlier version which worked. It is here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,102 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0; + int i, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - *first_start; + break; + case write_gran_256bytes: + for (i = 0; i < len / 256; i++) { + limit = min(256, len - i * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + i * 256, want + i * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + *first_start = i * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i * 256 - *first_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(i * 256 - *first_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,67 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + int skip = 1; + int writecount = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + msg_cdbg(":"); + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg("E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents, 0xff, len); + skip = 0; + } + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + if (!writecount++) + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, + start + starthere, lenhere); + if (ret) + return ret; + starthere += lenhere; + skip = 0; + } + if (skip) + msg_cdbg("S"); + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1439,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + /* 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)) + if (do_something(flash, start, len, param1, param2, + eraser.block_erase)) { + msg_cdbg("\n"); return 1; + } start += len; } } + msg_cdbg("\n"); return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0; + uint8_t *curcontents; + unsigned long size = flash->total_size * 1024;
- msg_cinfo("Erasing flash chip... "); + curcontents = (uint8_t *) malloc(size); + /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */ + memcpy(curcontents, oldcontents, size); + + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1490,19 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break; + /* FIXME: Reread the whole chip here so we know the current + * chip contents? curcontents might be up to date, but this + * code is only reached if something failed, and then we don't + * know exactly what failed, and how. + */ } + /* Free the scratchpad. */ + free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this flash chip.\n"); return 1; @@ -1338,7 +1511,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else { - msg_cinfo("SUCCESS.\n"); + msg_cinfo("Done.\n"); } return ret; } @@ -1637,6 +1810,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1830,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); - programmer_shutdown(); - return 1; + ret = 1; } + programmer_shutdown(); + return ret; }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1850,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1869,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1885,6 @@ programmer_shutdown(); return 1; } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; - } else { - msg_cinfo("COMPLETE.\n"); - } }
if (verify_it) {
2010/10/22 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Given that the version which addresses the review comments apparently is buggy, I hereby propose to merge the earlier version which worked. It is here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
With your latest patch (http://patchwork.coreboot.org/patch/2160/) applied:
$ sudo ./flashrom -p nicintel_spi -V -w nicintel_spi.rom -c M25P10.RES flashrom v0.9.3-r1215 on FreeBSD 8.1-RELEASE (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 4 usecs, 942M loops per second, delay more than 10% too short (got 80% of expected delay), recalculating... 1923M loops per second, 10 myus = 11 us, 100 myus = 138 us, 1000 myus = 981 us, 10000 myus = 10947 us, 16 myus = 33 us, OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF 01:03.0). Requested BAR is MEM, 32bit, not prefetchable Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. === This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x007fff:W, 0x008000-0x00ffff:W, 0x010000-0x017fff:W, 0x018000-0x01ffff:S
Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6, Read=0x15, failed byte count from 0x00000000-0x0001ffff: 0x8d75 Your flash chip is in an unknown state. Get help on IRC at irc.freenode.net (channel #flashrom) or mail flashrom@flashrom.org with FAILED: your board name in the subject line! ------------------------------------------------------------------------------- DO NOT REBOOT OR POWEROFF!
Index: flashrom-partial_write_rolling_erase_write/flashrom.c
--- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@
- Check if the buffer @have can be programmed to the content of @want
without
- erasing. This is only possible if all chunks of size @gran are either
kept
- as-is or changed from an all-ones state to any other state.
- The following write granularities (enum @gran) are known:
- 1 bit. Each bit can be cleared individually.
- 1 byte. A byte can be written once. Further writes to an already
written @@ -803,10 +804,12 @@
- this function.
- 256 bytes. If less than 256 bytes are written, the contents of the
- unwritten bytes are undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the verified area
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no erase is needed, 1 otherwise
*/ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++)
if (have[i] != 0xff) {
if (have[j * 256 + i] != 0xff) { result = 1; break; }
@@ -846,10 +849,102 @@ break; } break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__); } return result;
}
+/**
- Check if the buffer @have needs to be programmed to get the content of
@want.
- If yes, return 1 and fill in first_start with the start address of the
- write operation and first_len with the length of the first
to-be-written
- chunk. If not, return 0 and leave first_start and first_len undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no write is needed, 1 otherwise
- @first_start offset of the first byte which needs to be written
- @first_len length of the first contiguous area which needs to be
written
- FIXME: This function needs a parameter which tells it about coalescing
- in relation to the max write length of the programmer and the max write
- length of the chip.
- */
+static int get_next_write(uint8_t *have, uint8_t *want, int len,
int *first_start, int *first_len,
enum write_granularity gran)
+{
int result = 0;
int i, limit;
/* The passed in variable might be uninitialized. */
*first_len = 0;
switch (gran) {
case write_gran_1bit:
case write_gran_1byte:
for (i = 0; i < len; i++) {
if (have[i] != want[i]) {
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = i;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i - *first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = i - *first_start;
break;
case write_gran_256bytes:
for (i = 0; i < len / 256; i++) {
limit = min(256, len - i * 256);
/* Are 'have' and 'want' identical? */
if (memcmp(have + i * 256, want + i * 256, limit))
{
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = i * 256;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i * 256 -
*first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = min(i * 256 - *first_start, len);
break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__);
}
return result;
+}
/* This function generates various test patterns useful for testing controller
- and chip communication as well as chip behaviour.
@@ -1203,7 +1298,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and
- walk_eraseregions().
- Even if an error is found, the function will keep going and check the
rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1367,67 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash,
unsigned int start, unsigned int
len,
uint8_t *oldcontents,
uint8_t *newcontents,
int (*erasefn) (struct flashchip
*flash,
unsigned int addr,
unsigned int len))
+{
int starthere = 0;
int lenhere = 0;
int ret = 0;
int skip = 1;
int writecount = 0;
enum write_granularity gran = write_gran_256bytes; /* FIXME */
/* oldcontents and newcontents are opaque to walk_eraseregions, and
* need to be adjusted here to keep the impression of proper
abstraction
*/
oldcontents += start;
newcontents += start;
msg_cdbg(":");
/* FIXME: Assume 256 byte granularity for now to play it safe. */
if (need_erase(oldcontents, newcontents, len, gran)) {
msg_cdbg("E");
ret = erasefn(flash, start, len);
if (ret)
return ret;
/* Erase was successful. Adjust oldcontents. */
memset(oldcontents, 0xff, len);
skip = 0;
}
while (get_next_write(oldcontents + starthere,
newcontents + starthere,
len - starthere, &starthere,
&lenhere, gran)) {
if (!writecount++)
msg_cdbg("W");
/* Needs the partial write function signature. */
ret = flash->write(flash, newcontents + starthere,
start + starthere, lenhere);
if (ret)
return ret;
starthere += lenhere;
skip = 0;
}
if (skip)
msg_cdbg("S");
return ret;
+}
static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr,
unsigned int len))
unsigned int len,
uint8_t *param1,
uint8_t *param2,
int (*erasefn) (
struct flashchip
*flash,
unsigned int addr,
unsigned int len)),
void *param1, void *param2)
{ int i, j; unsigned int start = 0; @@ -1286,21 +1439,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) {
msg_cdbg("0x%06x-0x%06x, ", start,
/* 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))
if (do_something(flash, start, len, param1, param2,
eraser.block_erase)) {
msg_cdbg("\n"); return 1;
} start += len; } }
msg_cdbg("\n"); return 0;
}
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
uint8_t *curcontents;
unsigned long size = flash->total_size * 1024;
msg_cinfo("Erasing flash chip... ");
curcontents = (uint8_t *) malloc(size);
/* Copy oldcontents to curcontents to avoid clobbering oldcontents.
*/
memcpy(curcontents, oldcontents, size);
msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1490,19 @@ } found = 1; msg_cdbg("trying... ");
ret = walk_eraseregions(flash, k, eraser.block_erase);
ret = walk_eraseregions(flash, k,
&erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break;
/* FIXME: Reread the whole chip here so we know the current
* chip contents? curcontents might be up to date, but this
* code is only reached if something failed, and then we
don't
* know exactly what failed, and how.
*/ }
/* Free the scratchpad. */
free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this
flash chip.\n"); return 1; @@ -1338,7 +1511,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else {
msg_cinfo("SUCCESS.\n");
msg_cinfo("Done.\n"); } return ret;
} @@ -1637,6 +1810,19 @@ programmer_shutdown(); return ret; }
oldcontents = (uint8_t *) malloc(size);
/* Assume worst case: All bits are 0. */
memset(oldcontents, 0x00, size);
newcontents = (uint8_t *) malloc(size);
/* Assume best case: All bits should be 1. */
memset(newcontents, 0xff, size);
/* Side effect of the assumptions above: Default write action is
erase
* because newcontents looks like a completely erased chip, and
* oldcontents being completely 0x00 means we have to erase
everything
* before we can write.
*/
if (erase_it) { /* FIXME: Do we really want the scary warning if erase
failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1830,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */
if (erase_flash(flash)) {
if (erase_and_write_flash(flash, oldcontents, newcontents))
{ emergency_help_message();
programmer_shutdown();
return 1;
ret = 1; }
programmer_shutdown();
return ret; }
newcontents = (uint8_t *) calloc(size, sizeof(char));
if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown();
@@ -1665,8 +1850,6 @@ #endif }
oldcontents = (uint8_t *) calloc(size, sizeof(char));
/* 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 @@ -1686,9 +1869,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) {
if (erase_flash(flash)) {
msg_cerr("Uh oh. Erase failed. Checking if anything
"
"changed.\n");
if (erase_and_write_flash(flash, oldcontents, newcontents))
{
msg_cerr("Uh oh. Erase/write failed. Checking if "
"anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size))
{ msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1885,6 @@ programmer_shutdown(); return 1; }
msg_cinfo("Writing flash chip... ");
ret = flash->write(flash, newcontents, 0, size);
if (ret) {
msg_cerr("Uh oh. Write failed. Checking if anything
"
"changed.\n");
if (!flash->read(flash, newcontents, 0, size)) {
if (!memcmp(oldcontents, newcontents,
size)) {
msg_cinfo("Good. It seems nothing
was "
"changed.\n");
nonfatal_help_message();
programmer_shutdown();
return 1;
}
}
emergency_help_message();
programmer_shutdown();
return 1;
} else {
msg_cinfo("COMPLETE.\n");
} } if (verify_it) {
flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom
On 26.10.2010 16:27, Idwer Vollering wrote:
2010/10/22 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Given that the version which addresses the review comments apparently is buggy, I hereby propose to merge the earlier version which worked. It is here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
With your latest patch (http://patchwork.coreboot.org/patch/2160/) applied:
$ sudo ./flashrom -p nicintel_spi -V -w nicintel_spi.rom -c M25P10.RES flashrom v0.9.3-r1215 on FreeBSD 8.1-RELEASE (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian Calibrating delay loop... OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF 01:03.0). Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x007fff:W, 0x008000-0x00ffff:W, 0x010000-0x017fff:W, 0x018000-0x01ffff:S Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6, Read=0x15, failed byte count from 0x00000000-0x0001ffff: 0x8d75
Crap. Could you please retest http://patchwork.coreboot.org/patch/2155/ ?
If that one works, I am tempted to commit it, and send all other changes as separate patches on top.
Regards, Carl-Daniel
On 26.10.2010 19:22, Carl-Daniel Hailfinger wrote:
On 26.10.2010 16:27, Idwer Vollering wrote:
2010/10/22 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Given that the version which addresses the review comments apparently is buggy, I hereby propose to merge the earlier version which worked. It is here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
With your latest patch (http://patchwork.coreboot.org/patch/2160/) applied:
$ sudo ./flashrom -p nicintel_spi -V -w nicintel_spi.rom -c M25P10.RES flashrom v0.9.3-r1215 on FreeBSD 8.1-RELEASE (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian Calibrating delay loop... OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF 01:03.0). Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x007fff:W, 0x008000-0x00ffff:W, 0x010000-0x017fff:W, 0x018000-0x01ffff:S Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6, Read=0x15, failed byte count from 0x00000000-0x0001ffff: 0x8d75
Crap. Could you please retest http://patchwork.coreboot.org/patch/2155/ ?
If that one works, I am tempted to commit it, and send all other changes as separate patches on top.
Idwer tested this patch together with patch 2155 from patchwork and it worked. http://paste.flashrom.org/view.php?id=138
Regards, Carl-Daniel
2010/10/29 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
On 26.10.2010 19:22, Carl-Daniel Hailfinger wrote:
On 26.10.2010 16:27, Idwer Vollering wrote:
2010/10/22 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Given that the version which addresses the review comments apparently
is
buggy, I hereby propose to merge the earlier version which worked. It
is
here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work
in
most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193
reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code
is
unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an
image
full of 0xff, flashrom will erase and detect that no write is needed.
If
you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged
areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as
well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net>
With your latest patch (http://patchwork.coreboot.org/patch/2160/)
applied:
$ sudo ./flashrom -p nicintel_spi -V -w nicintel_spi.rom -c M25P10.RES flashrom v0.9.3-r1215 on FreeBSD 8.1-RELEASE (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian Calibrating delay loop... OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF
01:03.0).
Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function
0...
trying... 0x000000-0x007fff:W, 0x008000-0x00ffff:W, 0x010000-0x017fff:W, 0x018000-0x01ffff:S Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6,
Read=0x15,
failed byte count from 0x00000000-0x0001ffff: 0x8d75
Crap. Could you please retest http://patchwork.coreboot.org/patch/2155/?
If that one works, I am tempted to commit it, and send all other changes as separate patches on top.
Idwer tested this patch together with patch 2155
No. That was 2160, like you asked me to test on IRC.
from patchwork and it worked. http://paste.flashrom.org/view.php?id=138
Regards, Carl-Daniel
On 29.10.2010 01:02, Idwer Vollering wrote:
2010/10/29 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
On 26.10.2010 19:22, Carl-Daniel Hailfinger wrote:
On 26.10.2010 16:27, Idwer Vollering wrote:
2010/10/22 Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Given that the version which addresses the review comments apparently
is
buggy, I hereby propose to merge the earlier version which worked. It
is
here again for your reference with some slight cosmetic fixes which should not impact functionality. Error recovery still needs work (see the FIXME comment). It will work
in
most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193
reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code
is
unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an
image
full of 0xff, flashrom will erase and detect that no write is needed.
If
you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged
areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as
well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later).
Signed-off-by: Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net>
With your latest patch (http://patchwork.coreboot.org/patch/2160/)
applied:
$ sudo ./flashrom -p nicintel_spi -V -w nicintel_spi.rom -c M25P10.RES flashrom v0.9.3-r1215 on FreeBSD 8.1-RELEASE (i386), built with libpci 3.1.7, GCC 4.2.1 20070719 [FreeBSD], little endian Calibrating delay loop... OK. Initializing nicintel_spi programmer Found "Intel 82541PI Gigabit Ethernet Controller" (8086:107c, BDF
01:03.0).
Probing for ST M25P10.RES, 128 KB: probe_spi_res1: id 0x10 Chip status register is 00 Found chip "ST M25P10.RES" (128 KB, SPI) at physical address 0xfffe0000. Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function
0...
trying... 0x000000-0x007fff:W, 0x008000-0x00ffff:W, 0x010000-0x017fff:W, 0x018000-0x01ffff:S Done. Verifying flash... VERIFY FAILED at 0x00008000! Expected=0xc6,
Read=0x15,
failed byte count from 0x00000000-0x0001ffff: 0x8d75
Crap. Could you please retest http://patchwork.coreboot.org/patch/2155/?
If that one works, I am tempted to commit it, and send all other changes as separate patches on top.
Idwer tested this patch together with patch 2155
No. That was 2160, like you asked me to test on IRC.
Indeed, you tested patch 2160 (partial write) together with patch 2188 (fix offset calculation).
from patchwork and it worked. http://paste.flashrom.org/view.php?id=138
Sorry for the confusion.
Regards, Carl-Daniel
No. That was 2160, like you asked me to test on IRC.
I mean yes, 2160 in combination with 2188. I'll shut up now, good night :)
Thanks to David Hendricks for telling me that flashrom was writing the same region over and over with his test image. Turns out that get_next_write had = instead of += in one place and that caused an endless loop if an eraseblock was filled with the following pattern: k*256 bytes of non-0xff, m*256 bytes of 0xff, n*256 bytes of non-0xff k,m,n >=1
This bug was not spotted earlier because most other tests were with random images, and the likelihood of an eraseblock in a random image fitting the pattern above is roughly 1 in 2^2048 (well, not exacly, but the order of magnitude is right).
Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later) and for convincing me to review the logic again.
Thanks to Idwer Vollering for testing with Intel SPI NICs.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1217) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@ * Check if the buffer @have can be programmed to the content of @want without * erasing. This is only possible if all chunks of size @gran are either kept * as-is or changed from an all-ones state to any other state. + * * The following write granularities (enum @gran) are known: * - 1 bit. Each bit can be cleared individually. * - 1 byte. A byte can be written once. Further writes to an already written @@ -803,10 +804,12 @@ * this function. * - 256 bytes. If less than 256 bytes are written, the contents of the * unwritten bytes are undefined. + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. * * @have buffer with current content * @want buffer with desired content - * @len length of the verified area + * @len length of the checked area * @gran write granularity (enum, not count) * @return 0 if no erase is needed, 1 otherwise */ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++) - if (have[i] != 0xff) { + if (have[j * 256 + i] != 0xff) { result = 1; break; } @@ -846,10 +849,103 @@ break; } break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); } return result; }
+/** + * Check if the buffer @have needs to be programmed to get the content of @want. + * If yes, return 1 and fill in first_start with the start address of the + * write operation and first_len with the length of the first to-be-written + * chunk. If not, return 0 and leave first_start and first_len undefined. + * + * Warning: This function assumes that @have and @want point to naturally + * aligned regions. + * + * @have buffer with current content + * @want buffer with desired content + * @len length of the checked area + * @gran write granularity (enum, not count) + * @return 0 if no write is needed, 1 otherwise + * @first_start offset of the first byte which needs to be written + * @first_len length of the first contiguous area which needs to be written + * + * FIXME: This function needs a parameter which tells it about coalescing + * in relation to the max write length of the programmer and the max write + * length of the chip. + */ +static int get_next_write(uint8_t *have, uint8_t *want, int len, + int *first_start, int *first_len, + enum write_granularity gran) +{ + int result = 0, rel_start = 0; + int i, limit; + + /* The passed in variable might be uninitialized. */ + *first_len = 0; + switch (gran) { + case write_gran_1bit: + case write_gran_1byte: + for (i = 0; i < len; i++) { + if (have[i] != want[i]) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + rel_start = i; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i - rel_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = i - rel_start; + break; + case write_gran_256bytes: + for (i = 0; i < len / 256; i++) { + limit = min(256, len - i * 256); + /* Are 'have' and 'want' identical? */ + if (memcmp(have + i * 256, want + i * 256, limit)) { + if (!result) { + /* First location where have and want + * differ. + */ + result = 1; + rel_start = i * 256; + } + } else { + if (result) { + /* First location where have and want + * do not differ anymore. + */ + *first_len = i * 256 - rel_start; + break; + } + } + } + /* Did the loop terminate without setting first_len? */ + if (result && ! *first_len) + *first_len = min(i * 256 - rel_start, len); + break; + default: + msg_cerr("%s: Unsupported granularity! Please report a bug at " + "flashrom@flashrom.org\n", __func__); + } + *first_start += rel_start; + return result; +} + /* This function generates various test patterns useful for testing controller * and chip communication as well as chip behaviour. * @@ -1203,7 +1299,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and + * walk_eraseregions(). * Even if an error is found, the function will keep going and check the rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1368,67 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash, + unsigned int start, unsigned int len, + uint8_t *oldcontents, + uint8_t *newcontents, + int (*erasefn) (struct flashchip *flash, + unsigned int addr, + unsigned int len)) +{ + int starthere = 0; + int lenhere = 0; + int ret = 0; + int skip = 1; + int writecount = 0; + enum write_granularity gran = write_gran_256bytes; /* FIXME */ + + /* oldcontents and newcontents are opaque to walk_eraseregions, and + * need to be adjusted here to keep the impression of proper abstraction + */ + oldcontents += start; + newcontents += start; + msg_cdbg(":"); + /* FIXME: Assume 256 byte granularity for now to play it safe. */ + if (need_erase(oldcontents, newcontents, len, gran)) { + msg_cdbg("E"); + ret = erasefn(flash, start, len); + if (ret) + return ret; + /* Erase was successful. Adjust oldcontents. */ + memset(oldcontents, 0xff, len); + skip = 0; + } + while (get_next_write(oldcontents + starthere, + newcontents + starthere, + len - starthere, &starthere, + &lenhere, gran)) { + if (!writecount++) + msg_cdbg("W"); + /* Needs the partial write function signature. */ + ret = flash->write(flash, newcontents + starthere, + start + starthere, lenhere); + if (ret) + return ret; + starthere += lenhere; + skip = 0; + } + if (skip) + msg_cdbg("S"); + return ret; +} + static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr, - unsigned int len)) + unsigned int len, + uint8_t *param1, + uint8_t *param2, + int (*erasefn) ( + struct flashchip *flash, + unsigned int addr, + unsigned int len)), + void *param1, void *param2) { int i, j; unsigned int start = 0; @@ -1286,21 +1440,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) { - msg_cdbg("0x%06x-0x%06x, ", start, + /* 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)) + if (do_something(flash, start, len, param1, param2, + eraser.block_erase)) { + msg_cdbg("\n"); return 1; + } start += len; } } + msg_cdbg("\n"); return 0; }
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0; + uint8_t *curcontents; + unsigned long size = flash->total_size * 1024;
- msg_cinfo("Erasing flash chip... "); + curcontents = (uint8_t *) malloc(size); + /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */ + memcpy(curcontents, oldcontents, size); + + msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1491,19 @@ } found = 1; msg_cdbg("trying... "); - ret = walk_eraseregions(flash, k, eraser.block_erase); + ret = walk_eraseregions(flash, k, &erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break; + /* FIXME: Reread the whole chip here so we know the current + * chip contents? curcontents might be up to date, but this + * code is only reached if something failed, and then we don't + * know exactly what failed, and how. + */ } + /* Free the scratchpad. */ + free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this flash chip.\n"); return 1; @@ -1338,7 +1512,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else { - msg_cinfo("SUCCESS.\n"); + msg_cinfo("Done.\n"); } return ret; } @@ -1637,6 +1811,19 @@ programmer_shutdown(); return ret; } + + oldcontents = (uint8_t *) malloc(size); + /* Assume worst case: All bits are 0. */ + memset(oldcontents, 0x00, size); + newcontents = (uint8_t *) malloc(size); + /* Assume best case: All bits should be 1. */ + memset(newcontents, 0xff, size); + /* Side effect of the assumptions above: Default write action is erase + * because newcontents looks like a completely erased chip, and + * oldcontents being completely 0x00 means we have to erase everything + * before we can write. + */ + if (erase_it) { /* FIXME: Do we really want the scary warning if erase failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1831,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */ - if (erase_flash(flash)) { + if (erase_and_write_flash(flash, oldcontents, newcontents)) { emergency_help_message(); - programmer_shutdown(); - return 1; + ret = 1; } + programmer_shutdown(); + return ret; }
- newcontents = (uint8_t *) calloc(size, sizeof(char)); - if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown(); @@ -1665,8 +1851,6 @@ #endif }
- oldcontents = (uint8_t *) calloc(size, sizeof(char)); - /* 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 @@ -1686,9 +1870,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - if (erase_flash(flash)) { - msg_cerr("Uh oh. Erase failed. Checking if anything " - "changed.\n"); + if (erase_and_write_flash(flash, oldcontents, newcontents)) { + msg_cerr("Uh oh. Erase/write failed. Checking if " + "anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size)) { msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1886,6 @@ programmer_shutdown(); return 1; } - msg_cinfo("Writing flash chip... "); - ret = flash->write(flash, newcontents, 0, size); - if (ret) { - msg_cerr("Uh oh. Write failed. Checking if anything " - "changed.\n"); - if (!flash->read(flash, newcontents, 0, size)) { - if (!memcmp(oldcontents, newcontents, size)) { - msg_cinfo("Good. It seems nothing was " - "changed.\n"); - nonfatal_help_message(); - programmer_shutdown(); - return 1; - } - } - emergency_help_message(); - programmer_shutdown(); - return 1; - } else { - msg_cinfo("COMPLETE.\n"); - } }
if (verify_it) {
I think this patch is important and should go in very soon. The code looks good to me, and I've successfully tested with it several times.
Are there any outstanding issues or patches that should be submitted first?
On Thu, Oct 28, 2010 at 4:13 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
Thanks to David Hendricks for telling me that flashrom was writing the same region over and over with his test image. Turns out that get_next_write had = instead of += in one place and that caused an endless loop if an eraseblock was filled with the following pattern: k*256 bytes of non-0xff, m*256 bytes of 0xff, n*256 bytes of non-0xff k,m,n >=1
This bug was not spotted earlier because most other tests were with random images, and the likelihood of an eraseblock in a random image fitting the pattern above is roughly 1 in 2^2048 (well, not exacly, but the order of magnitude is right).
Error recovery still needs work (see the FIXME comment). It will work in most cases, but if a given erase command for a block only erased parts of the block (partial write lock), the code will make incorrect assumptions.
If anyone feels adventurous, I would love to see logs on Intel/VIA chipsets with SPI (preferably locked down chipsets or with r1193 reverted).
If you're going to review this, make sure you keep a stack of bananas (quickly mobilized carbohydrates for your brain), a bucket of ice (to prevent brain overheating) and a bottle of aspirin handy. If any code is unclear, please tell me and I'll try to add comments to improve readability.
This code has been tested. Testing erase (and checking with a separate readback that erase actually worked) and write (same test with separate readback) would be highly appreciated. Verbose logs are even more appreciated.
I think the code is ready for merge if you trust write/erase to never fail. The error cases still need to be tested. Should we reread the whole chip if write/erase failed to make sure our current view of the chip contents is not stale?
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to Andrew Morgan for testing countless iterations of this patch. Thanks to Richard A. Smith for testing on Dediprog. Thanks to David Hendricks for the review (will be addressed later) and for convincing me to review the logic again.
Thanks to Idwer Vollering for testing with Intel SPI NICs.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net Index: flashrom-partial_write_rolling_erase_write/flashrom.c =================================================================== --- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1217) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@
- Check if the buffer @have can be programmed to the content of @want
without
- erasing. This is only possible if all chunks of size @gran are either
kept
- as-is or changed from an all-ones state to any other state.
- The following write granularities (enum @gran) are known:
- 1 bit. Each bit can be cleared individually.
- 1 byte. A byte can be written once. Further writes to an already
written @@ -803,10 +804,12 @@
- this function.
- 256 bytes. If less than 256 bytes are written, the contents of the
- unwritten bytes are undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the verified area
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no erase is needed, 1 otherwise
*/ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++)
if (have[i] != 0xff) {
if (have[j * 256 + i] != 0xff) { result = 1; break; }
@@ -846,10 +849,103 @@ break; } break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__); } return result;
}
+/**
- Check if the buffer @have needs to be programmed to get the content of
@want.
- If yes, return 1 and fill in first_start with the start address of the
- write operation and first_len with the length of the first
to-be-written
- chunk. If not, return 0 and leave first_start and first_len undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no write is needed, 1 otherwise
- @first_start offset of the first byte which needs to be written
- @first_len length of the first contiguous area which needs to be
written
- FIXME: This function needs a parameter which tells it about coalescing
- in relation to the max write length of the programmer and the max write
- length of the chip.
- */
+static int get_next_write(uint8_t *have, uint8_t *want, int len,
int *first_start, int *first_len,
enum write_granularity gran)
+{
int result = 0, rel_start = 0;
int i, limit;
/* The passed in variable might be uninitialized. */
*first_len = 0;
switch (gran) {
case write_gran_1bit:
case write_gran_1byte:
for (i = 0; i < len; i++) {
if (have[i] != want[i]) {
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
rel_start = i;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i - rel_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = i - rel_start;
break;
case write_gran_256bytes:
for (i = 0; i < len / 256; i++) {
limit = min(256, len - i * 256);
/* Are 'have' and 'want' identical? */
if (memcmp(have + i * 256, want + i * 256, limit))
{
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
rel_start = i * 256;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i * 256 - rel_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = min(i * 256 - rel_start, len);
break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__);
}
*first_start += rel_start;
return result;
+}
/* This function generates various test patterns useful for testing controller
- and chip communication as well as chip behaviour.
@@ -1203,7 +1299,8 @@ return ret; }
-/* This function shares a lot of its structure with erase_flash(). +/* This function shares a lot of its structure with erase_and_write_flash() and
- walk_eraseregions().
- Even if an error is found, the function will keep going and check the
rest. */ static int selfcheck_eraseblocks(struct flashchip *flash) @@ -1271,10 +1368,67 @@ return ret; }
+static int erase_and_write_block_helper(struct flashchip *flash,
unsigned int start, unsigned int
len,
uint8_t *oldcontents,
uint8_t *newcontents,
int (*erasefn) (struct flashchip
*flash,
unsigned int addr,
unsigned int len))
+{
int starthere = 0;
int lenhere = 0;
int ret = 0;
int skip = 1;
int writecount = 0;
enum write_granularity gran = write_gran_256bytes; /* FIXME */
/* oldcontents and newcontents are opaque to walk_eraseregions, and
* need to be adjusted here to keep the impression of proper
abstraction
*/
oldcontents += start;
newcontents += start;
msg_cdbg(":");
/* FIXME: Assume 256 byte granularity for now to play it safe. */
if (need_erase(oldcontents, newcontents, len, gran)) {
msg_cdbg("E");
ret = erasefn(flash, start, len);
if (ret)
return ret;
/* Erase was successful. Adjust oldcontents. */
memset(oldcontents, 0xff, len);
skip = 0;
}
while (get_next_write(oldcontents + starthere,
newcontents + starthere,
len - starthere, &starthere,
&lenhere, gran)) {
if (!writecount++)
msg_cdbg("W");
/* Needs the partial write function signature. */
ret = flash->write(flash, newcontents + starthere,
start + starthere, lenhere);
if (ret)
return ret;
starthere += lenhere;
skip = 0;
}
if (skip)
msg_cdbg("S");
return ret;
+}
static int walk_eraseregions(struct flashchip *flash, int erasefunction, int (*do_something) (struct flashchip *flash, unsigned int addr,
unsigned int len))
unsigned int len,
uint8_t *param1,
uint8_t *param2,
int (*erasefn) (
struct flashchip
*flash,
unsigned int addr,
unsigned int len)),
void *param1, void *param2)
{ int i, j; unsigned int start = 0; @@ -1286,21 +1440,34 @@ */ len = eraser.eraseblocks[i].size; for (j = 0; j < eraser.eraseblocks[i].count; j++) {
msg_cdbg("0x%06x-0x%06x, ", start,
/* 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))
if (do_something(flash, start, len, param1, param2,
eraser.block_erase)) {
msg_cdbg("\n"); return 1;
} start += len; } }
msg_cdbg("\n"); return 0;
}
-int erase_flash(struct flashchip *flash) +int erase_and_write_flash(struct flashchip *flash, uint8_t *oldcontents, uint8_t *newcontents) { int k, ret = 0, found = 0;
uint8_t *curcontents;
unsigned long size = flash->total_size * 1024;
msg_cinfo("Erasing flash chip... ");
curcontents = (uint8_t *) malloc(size);
/* Copy oldcontents to curcontents to avoid clobbering oldcontents.
*/
memcpy(curcontents, oldcontents, size);
msg_cinfo("Erasing and writing flash chip... "); for (k = 0; k < NUM_ERASEFUNCTIONS; k++) { struct block_eraser eraser = flash->block_erasers[k];
@@ -1324,12 +1491,19 @@ } found = 1; msg_cdbg("trying... ");
ret = walk_eraseregions(flash, k, eraser.block_erase);
ret = walk_eraseregions(flash, k,
&erase_and_write_block_helper, curcontents, newcontents); msg_cdbg("\n"); /* If everything is OK, don't try another erase function. */ if (!ret) break;
/* FIXME: Reread the whole chip here so we know the current
* chip contents? curcontents might be up to date, but this
* code is only reached if something failed, and then we
don't
* know exactly what failed, and how.
*/ }
/* Free the scratchpad. */
free(curcontents); if (!found) { msg_cerr("ERROR: flashrom has no erase function for this
flash chip.\n"); return 1; @@ -1338,7 +1512,7 @@ if (ret) { msg_cerr("FAILED!\n"); } else {
msg_cinfo("SUCCESS.\n");
msg_cinfo("Done.\n"); } return ret;
} @@ -1637,6 +1811,19 @@ programmer_shutdown(); return ret; }
oldcontents = (uint8_t *) malloc(size);
/* Assume worst case: All bits are 0. */
memset(oldcontents, 0x00, size);
newcontents = (uint8_t *) malloc(size);
/* Assume best case: All bits should be 1. */
memset(newcontents, 0xff, size);
/* Side effect of the assumptions above: Default write action is
erase
* because newcontents looks like a completely erased chip, and
* oldcontents being completely 0x00 means we have to erase
everything
* before we can write.
*/
if (erase_it) { /* FIXME: Do we really want the scary warning if erase
failed? * After all, after erase the chip is either blank or partially @@ -1644,15 +1831,14 @@ * so if the user wanted erase and reboots afterwards, the user * knows very well that booting won't work. */
if (erase_flash(flash)) {
if (erase_and_write_flash(flash, oldcontents, newcontents))
{ emergency_help_message();
programmer_shutdown();
return 1;
ret = 1; }
programmer_shutdown();
return ret; }
newcontents = (uint8_t *) calloc(size, sizeof(char));
if (write_it || verify_it) { if (read_buf_from_file(newcontents, size, filename)) { programmer_shutdown();
@@ -1665,8 +1851,6 @@ #endif }
oldcontents = (uint8_t *) calloc(size, sizeof(char));
/* 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 @@ -1686,9 +1870,9 @@ // ////////////////////////////////////////////////////////////
if (write_it) {
if (erase_flash(flash)) {
msg_cerr("Uh oh. Erase failed. Checking if anything
"
"changed.\n");
if (erase_and_write_flash(flash, oldcontents, newcontents))
{
msg_cerr("Uh oh. Erase/write failed. Checking if "
"anything changed.\n"); if (!flash->read(flash, newcontents, 0, size)) { if (!memcmp(oldcontents, newcontents, size))
{ msg_cinfo("Good. It seems nothing was " @@ -1702,26 +1886,6 @@ programmer_shutdown(); return 1; }
msg_cinfo("Writing flash chip... ");
ret = flash->write(flash, newcontents, 0, size);
if (ret) {
msg_cerr("Uh oh. Write failed. Checking if anything
"
"changed.\n");
if (!flash->read(flash, newcontents, 0, size)) {
if (!memcmp(oldcontents, newcontents,
size)) {
msg_cinfo("Good. It seems nothing
was "
"changed.\n");
nonfatal_help_message();
programmer_shutdown();
return 1;
}
}
emergency_help_message();
programmer_shutdown();
return 1;
} else {
msg_cinfo("COMPLETE.\n");
} } if (verify_it) {
flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom
On 02.11.2010 04:01, David Hendricks wrote:
I think this patch is important and should go in very soon. The code looks good to me, and I've successfully tested with it several times.
Are there any outstanding issues or patches that should be submitted first?
Error recovery from a failed erase could be better, but it is pretty much on par with the current code.
I have some cleanups and patches for better error recovery which conflict with this patch, but they shouldn't hold this patch back. I hope to resend them immediately after this patch is merged (and some of them will be tiny afterwards).
To summarize, I think nothing else is left to do before committing this patch.
Regards, Carl-Daniel
On Mon, Nov 1, 2010 at 8:09 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
On 02.11.2010 04:01, David Hendricks wrote:
I think this patch is important and should go in very soon. The code
looks
good to me, and I've successfully tested with it several times.
Are there any outstanding issues or patches that should be submitted
first?
Error recovery from a failed erase could be better, but it is pretty much on par with the current code.
I have some cleanups and patches for better error recovery which conflict with this patch, but they shouldn't hold this patch back. I hope to resend them immediately after this patch is merged (and some of them will be tiny afterwards).
To summarize, I think nothing else is left to do before committing this patch.
Alrighty then! I've stared at the code as much as my eyes will allow and have tested it on a couple different platforms with SPI flash (AMD870 and Intel NM10), so:
Acked-by: David Hendricks dhendrix@google.com
Idwer's been doing a lot with this patch too, so you might want to wait for his ack as well.
Hi,
It works on SB710 and satasii. Tested using the test script and those images that behaved previously wrong.
Thanks for this! In case you need it!
Acked-by: Rudolf Marek r.marek@assembler.cz
Excellent work!
Thanks, Rudolf
2010/11/3 Rudolf Marek r.marek@assembler.cz
Hi,
It works on SB710 and satasii. Tested using the test script and those images that behaved previously wrong.
Works on a mainboard needing a board enable and on nicintel_spi, applied against r1217 and compiled with gmake on FreeBSD 8.1.
Thanks for this! In case you need it!
Acked-by: Rudolf Marek r.marek@assembler.cz
Acked-by: Idwer Vollering vidwer@gmail.com
Excellent work!
Now, up to the real stuff.
Thanks, Rudolf
flashrom mailing list flashrom@flashrom.org http://www.flashrom.org/mailman/listinfo/flashrom
On 29.10.2010 01:13, Carl-Daniel Hailfinger wrote:
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Fix a long-standing bug in need_erase() for 256 byte granularity as well.
Nice side benefit: Detailed progress printing. S means skipped E means erased W means written
Thanks to everyone who tested and reviewed this patch. I believe it is the most tested patch in flashrom history.
Committed in r1224.
Regards, Carl-Daniel
The code looks good overall, though I haven't had a chance to test it extensively yet. Thanks for doing this!
Comments below are mostly minor nits.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
On 21.10.2010 02:08, Carl-Daniel Hailfinger wrote:
I suspect that there are more bugs in the code than I'd like. Please try the patch below which has more debug output.
need_erase used an incorrect offset calculation in the write_gran_256bytes case.
This one may even work. Please make sure to test with an image generated with a random number generator (like /dev/urandom or similar).
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_rolling_erase_write/flashrom.c
--- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -793,6 +793,7 @@
- Check if the buffer @have can be programmed to the content of @want
without
- erasing. This is only possible if all chunks of size @gran are either
kept
- as-is or changed from an all-ones state to any other state.
- The following write granularities (enum @gran) are known:
- 1 bit. Each bit can be cleared individually.
- 1 byte. A byte can be written once. Further writes to an already
written @@ -803,10 +804,12 @@
- this function.
- 256 bytes. If less than 256 bytes are written, the contents of the
- unwritten bytes are undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the verified area
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no erase is needed, 1 otherwise
*/ @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++)
if (have[i] != 0xff) {
if (have[j * 256 + i] != 0xff) { result = 1; break; }
This definitely makes more sense :-)
Maybe you can just rename i and j to offset and page to make their purpose a bit more clear? (note: i generally have no problem with i/j/k/l for simple iterators)
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
@@ -846,10 +849,102 @@ break; } break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__); } return result;
}
+/**
- Check if the buffer @have needs to be programmed to get the content of
@want.
- If yes, return 1 and fill in first_start with the start address of the
- write operation and first_len with the length of the first
to-be-written
- chunk. If not, return 0 and leave first_start and first_len undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no write is needed, 1 otherwise
- @first_start offset of the first byte which needs to be written
- @first_len length of the first contiguous area which needs to be
written
Not sure what the preferred style is but @return should probably be listed below the variables, preferably separated by a newline.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
- FIXME: This function needs a parameter which tells it about coalescing
- in relation to the max write length of the programmer and the max write
- length of the chip.
- */
+static int get_next_write(uint8_t *have, uint8_t *want, int len,
int *first_start, int *first_len,
enum write_granularity gran)
+{
int result = 0;
int i, j, limit;
i and j are used for the same purpose in this function -- you can eliminate one of them.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
/* The passed in variable might be uninitialized. */
*first_len = 0;
The usage model of first_len and first_start is kind of weird... Perhaps in this case it is more straightforward to pass the info between functions using a struct. Would it make more sense to create such a struct, alloc it in get_next_write, and change the return type of the function to point at the struct if write is needed / NULL if not needed? e.g.
struct next_write { unsigned short write_needed; /* takes place of "result" flag used in get_next_write */ int start; int len; }
static struct next_write *get_next_write(uint8_t *have, uint8_t *want, int len, enum write_granularity gran) { struct next_write *next_write; ... return next_write; /* NULL if we have what we want */ }
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
switch (gran) {
case write_gran_1bit:
case write_gran_1byte:
for (i = 0; i < len; i++) {
if (have[i] != want[i]) {
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = i;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i - *first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = i - *first_start;
break;
case write_gran_256bytes:
for (j = 0; j < len / 256; j++) {
limit = min(256, len - j * 256);
/* Are 'have' and 'want' identical? */
if (memcmp(have + j * 256, want + j * 256, limit))
{
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = j * 256;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = j * 256 -
*first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = min(j * 256 - *first_start, len);
break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__);
}
return result;
+}
The write_gran_1byte and write_gran_256byte cases look practically identical -- you are really only changing the granularity, which is a simple multiplier. Perhaps you can generalize the write_gran_256byte case to include the 1byte case as well by simply replacing the granularity.
switch(gran) { case write_gran_1bit: case write_gran_1byte: stride = 1; break; case write_gran_256bytes: stride = 256; break; }
for (j = 0; j < len / stride; j++) { limit = min(stride, len - j * stride); /* Are 'have' and 'want' identical? */ if (memcmp(have + j * stride, want + j * stride, limit)) { /* same as have[i] != want[i] above */ if (!result) { /* First location where have and want * differ. */ result = 1; *first_start = j * stride; } } else { if (result) { /* First location where have and want * do not differ anymore. */ *first_len = j * stride - *first_start; break; } } } /* Did the loop terminate without setting first_len? */ if (result && ! *first_len) *first_len = min(j * stride - *first_start, len);
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
/* FIXME: Assume 256 byte granularity for now to play it safe. */
if (need_erase(oldcontents, newcontents, len, gran)) {
msg_cdbg(" E");
ret = erasefn(flash, start, len);
if (ret)
return ret;
/* Erase was successful. Adjust oldcontents. */
memset(oldcontents + start, 0xff, len);
} else
msg_cdbg(" e");
+ while (get_next_write(oldcontents + starthere,
newcontents + starthere,
len - starthere, &starthere,
&lenhere, gran)) {
msg_cdbg("W");
/* Needs the partial write function signature. */
ret = flash->write(flash, newcontents + starthere, start +
starthere, lenhere);
if (ret)
break;
starthere += lenhere;
}
return ret;
Another minor style nit: IMHO it's best to use braces consistently for both if and else portions. So even though you are technically correct, encasing the else clause in braces provides a useful visual cue that the logic is finished.
Also, it's not clear what the difference is between "E" and "e". If !need_erase(), then does that imply that you skipped the region? If so, perhaps an "s" is more appropriate.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
oldcontents = (uint8_t *) malloc(size);
/* Assume worst case: All bits are 0. */
memset(oldcontents, 0x00, size);
newcontents = (uint8_t *) malloc(size);
/* Assume best case: All bits should be 1. */
memset(newcontents, 0xff, size);
/* Side effect of the assumptions above: Default write action is
erase
* because newcontents looks like a completely erased chip, and
* oldcontents being completely 0x00 means we have to erase
everything
* before we can write.
*/
Due to my lack of experience, it seemed to me like the best thing to do is assume no action should be taken. That is, set both buffers to 0xff (equal and corresponding to an erased chip). .. .. .. But then I looked several lines down and saw that erase_it depends on newcontents being 0xff and oldcontents complementing it.
Perhaps the thing to do is move the memsets down to the erase_it block. For the other stuff, newcontents comes from a file and oldcontents comes from the chip.
On 21.10.2010 06:50, David Hendricks wrote:
The code looks good overall, though I haven't had a chance to test it extensively yet. Thanks for doing this!
Thanks for your review. Comments inline below.
Comments below are mostly minor nits.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger wrote:
need_erase used an incorrect offset calculation in the write_gran_256bytes case.
Index: flashrom-partial_write_rolling_erase_write/flashrom.c
--- flashrom-partial_write_rolling_erase_write/flashrom.c (Revision 1215) +++ flashrom-partial_write_rolling_erase_write/flashrom.c (Arbeitskopie) @@ -838,7 +841,7 @@ continue; /* have needs to be in erased state. */ for (i = 0; i < limit; i++)
if (have[i] != 0xff) {
if (have[j * 256 + i] != 0xff) { result = 1; break; }
This definitely makes more sense :-)
Maybe you can just rename i and j to offset and page to make their purpose a bit more clear?
The big problem is that page is a misnomer for chips with non-256-byte pages. base would be a more suitable name, but it may cause confusion with flashbase. The alternative would be to add some annotation ("page is not really a page, it is the write granularity length") which makes things a bit clearer. I'd like to keep i for the inner counter, though.
(note: i generally have no problem with i/j/k/l for simple iterators)
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
@@ -846,10 +849,102 @@ return result; }
+/**
- Check if the buffer @have needs to be programmed to get the content of
@want.
- If yes, return 1 and fill in first_start with the start address of the
- write operation and first_len with the length of the first
to-be-written
- chunk. If not, return 0 and leave first_start and first_len undefined.
- Warning: This function assumes that @have and @want point to naturally
- aligned regions.
- @have buffer with current content
- @want buffer with desired content
- @len length of the checked area
- @gran write granularity (enum, not count)
- @return 0 if no write is needed, 1 otherwise
- @first_start offset of the first byte which needs to be written
- @first_len length of the first contiguous area which needs to be
written
Not sure what the preferred style is but @return should probably be listed below the variables, preferably separated by a newline.
Right. first_start and first_len are some sort of return variables as well. That's the reason I had them grouped after return. I have reordered them and extended the comments. The newline separation is something I'm not so sure about given the explanation above.
That said, maybe it makes sense returning first_len as value of the function. first_len=0 would mean no write is needed. That would kill one variable and still keep full functionality.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger wrote:
- FIXME: This function needs a parameter which tells it about coalescing
- in relation to the max write length of the programmer and the max write
- length of the chip.
- */
+static int get_next_write(uint8_t *have, uint8_t *want, int len,
int *first_start, int *first_len,
enum write_granularity gran)
+{
int result = 0;
int i, j, limit;
i and j are used for the same purpose in this function -- you can eliminate one of them.
Done.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger wrote
/* The passed in variable might be uninitialized. */
*first_len = 0;
The usage model of first_len and first_start is kind of weird... Perhaps in this case it is more straightforward to pass the info between functions using a struct. Would it make more sense to create such a struct, alloc it in get_next_write, and change the return type of the function to point at the struct if write is needed / NULL if not needed? e.g.
I think using first_len as return value as suggested above makes most sense. We'd avoid dealing with a struct and only first_start would be passed in. Then again, having straightforward return-values-must-be-returned style would be good as well. Comments?
struct next_write { unsigned short write_needed; /* takes place of "result" flag used in get_next_write */ int start; int len; }
static struct next_write *get_next_write(uint8_t *have, uint8_t *want, int len, enum write_granularity gran) { struct next_write *next_write; ... return next_write; /* NULL if we have what we want */ }
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
switch (gran) {
case write_gran_1bit:
case write_gran_1byte:
for (i = 0; i < len; i++) {
if (have[i] != want[i]) {
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = i;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = i - *first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = i - *first_start;
break;
case write_gran_256bytes:
for (j = 0; j < len / 256; j++) {
limit = min(256, len - j * 256);
/* Are 'have' and 'want' identical? */
if (memcmp(have + j * 256, want + j * 256, limit))
{
if (!result) {
/* First location where have and
want
* differ.
*/
result = 1;
*first_start = j * 256;
}
} else {
if (result) {
/* First location where have and
want
* do not differ anymore.
*/
*first_len = j * 256 -
*first_start;
break;
}
}
}
/* Did the loop terminate without setting first_len? */
if (result && ! *first_len)
*first_len = min(j * 256 - *first_start, len);
break;
default:
msg_cerr("%s: Unsupported granularity! Please report a bug
at "
"flashrom@flashrom.org\n", __func__);
}
return result;
+}
The write_gran_1byte and write_gran_256byte cases look practically identical -- you are really only changing the granularity, which is a simple multiplier. Perhaps you can generalize the write_gran_256byte case to include the 1byte case as well by simply replacing the granularity.
switch(gran) { case write_gran_1bit: case write_gran_1byte: stride = 1; break; case write_gran_256bytes: stride = 256; break; }
for (j = 0; j < len / stride; j++) { limit = min(stride, len - j * stride); /* Are 'have' and 'want' identical? */ if (memcmp(have + j * stride, want + j * stride, limit)) { /* same as have[i] != want[i] above */ if (!result) { /* First location where have and want
- differ.
*/ result = 1; *first_start = j * stride; } } else { if (result) { /* First location where have and want
- do not differ anymore.
*/ *first_len = j * stride - *first_start; break; } } } /* Did the loop terminate without setting first_len? */ if (result && ! *first_len) *first_len = min(j * stride - *first_start, len);
Good point. Changed.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
/* FIXME: Assume 256 byte granularity for now to play it safe. */
if (need_erase(oldcontents, newcontents, len, gran)) {
msg_cdbg(" E");
ret = erasefn(flash, start, len);
if (ret)
return ret;
/* Erase was successful. Adjust oldcontents. */
memset(oldcontents + start, 0xff, len);
} else
msg_cdbg(" e");
while (get_next_write(oldcontents + starthere,
newcontents + starthere,
len - starthere, &starthere,
&lenhere, gran)) {
msg_cdbg("W");
/* Needs the partial write function signature. */
ret = flash->write(flash, newcontents + starthere, start +
starthere, lenhere);
if (ret)
break;
starthere += lenhere;
}
return ret;
Another minor style nit: IMHO it's best to use braces consistently for both if and else portions. So even though you are technically correct, encasing the else clause in braces provides a useful visual cue that the logic is finished.
Also, it's not clear what the difference is between "E" and "e". If !need_erase(), then does that imply that you skipped the region? If so, perhaps an "s" is more appropriate.
Both points already fixed in a version sent shortly before your review arrived. We now have E, W, S.
On Wed, Oct 20, 2010 at 5:16 PM, Carl-Daniel Hailfinger < c-d.hailfinger.devel.2006@gmx.net> wrote:
oldcontents = (uint8_t *) malloc(size);
/* Assume worst case: All bits are 0. */
memset(oldcontents, 0x00, size);
newcontents = (uint8_t *) malloc(size);
/* Assume best case: All bits should be 1. */
memset(newcontents, 0xff, size);
/* Side effect of the assumptions above: Default write action is
erase
* because newcontents looks like a completely erased chip, and
* oldcontents being completely 0x00 means we have to erase
everything
* before we can write.
*/
Due to my lack of experience, it seemed to me like the best thing to do is assume no action should be taken. That is, set both buffers to 0xff (equal and corresponding to an erased chip). .. .. .. But then I looked several lines down and saw that erase_it depends on newcontents being 0xff and oldcontents complementing it.
Perhaps the thing to do is move the memsets down to the erase_it block. For the other stuff, newcontents comes from a file and oldcontents comes from the chip.
The code flow in doit() is still very much subject to change or rather replacement. It is made more complicated by the fact that some people want to avoid the initial read at all costs, and simply run a full erase first, then write. I plan to revamp doit() shortly to finally get us libflashrom.
Regards, Carl-Daniel
Am Mittwoch, den 20.10.2010, 06:06 +0200 schrieb Carl-Daniel Hailfinger:
This patch makes flashrom use real partial writes. If you write an image full of 0xff, flashrom will erase and detect that no write is needed. If you write an image which differs only in some parts from the current flash contents, flashrom will detect that and not touch unchanged areas.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Looks mostly great - minor issues discussed on IRCs and resulted in a clean-up patch. No issues that block an Ack.
Acked-by: Michael Karcher flashrom@mkarcher.dialup.fu-berlin.de
Regards, Michael Karcher