diff --git a/flashrom.c b/flashrom.c index f1edfca5987f5fabd6d70a8f4bbac0bdc3a240af..7af221ac6ec4198c75e6a859f0c52073e7e3f5cb 100644 --- a/flashrom.c +++ b/flashrom.c @@ -793,6 +793,7 @@ out_free: * 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 @@ out_free: * 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 @@ int need_erase(uint8_t *have, uint8_t *want, int len, enum write_granularity gra 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,7 +849,100 @@ int need_erase(uint8_t *have, uint8_t *want, int len, enum write_granularity gra 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; } @@ -1203,7 +1299,8 @@ out_free: 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 @@ static int selfcheck_eraseblocks(struct flashchip *flash) 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 @@ static int walk_eraseregions(struct flashchip *flash, int erasefunction, */ 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; + + curcontents = (uint8_t *) malloc(size); + /* Copy oldcontents to curcontents to avoid clobbering oldcontents. */ + memcpy(curcontents, oldcontents, size); - 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,12 +1491,19 @@ int erase_flash(struct flashchip *flash) } 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 @@ int erase_flash(struct flashchip *flash) if (ret) { msg_cerr("FAILED!\n"); } else { - msg_cinfo("SUCCESS.\n"); + msg_cinfo("Done.\n"); } return ret; } @@ -1637,6 +1811,19 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr 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 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr * 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 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr #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 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr // //////////////////////////////////////////////////////////// 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 @@ int doit(struct flashchip *flash, int force, char *filename, int read_it, int wr 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) {