flashrom had an implicit erase-on-write for most flash chip and programmer drivers, but it was not entirely consistent. Some drivers had their own hand-rolled partial update functionality which made handling partial updates from generic code impossible.
Move implicit erase out of chip drivers, clean up the chip write functions at the same time. A full chip erase is now performed in the generic code for all flash chips on write, and after that the whole chip is written. This is needed before we can change write function signature of the write functions to take start+len.
Side note: With this patch and one or two followup patches, more functions will be easily recognizable as duplicates and can be eliminated.
Compile tested and proofread, but that's it. The patch may cause flashrom to eat your dog or it may cause your dog to eat burned flash chips. Please test on real hardware. Thanks!
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-partial_write_no_autoerase/flash.h =================================================================== --- flashrom-partial_write_no_autoerase/flash.h (Revision 1005) +++ flashrom-partial_write_no_autoerase/flash.h (Arbeitskopie) @@ -676,7 +676,7 @@ int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt, const unsigned char *writearr, unsigned char *readarr); int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); -int sb600_spi_write_1(struct flashchip *flash, uint8_t *buf); +int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf); extern uint8_t *sb600_spibar;
/* wbsio_spi.c */ Index: flashrom-partial_write_no_autoerase/spi25.c =================================================================== --- flashrom-partial_write_no_autoerase/spi25.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/spi25.c (Arbeitskopie) @@ -875,6 +875,7 @@
/* * Read a part of the flash chip. + * FIXME: Use the chunk code from Michael Karcher instead. * Each page is read separately in chunks with a maximum size of chunksize. */ int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize) @@ -914,6 +915,7 @@
/* * Write a part of the flash chip. + * FIXME: Use the chunk code from Michael Karcher instead. * Each page is written separately in chunks with a maximum size of chunksize. */ int spi_write_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize) @@ -964,20 +966,13 @@ * and for chips where memory mapped programming is impossible * (e.g. due to size constraints in IT87* for over 512 kB) */ +/* real chunksize is 1, logical chunksize is 1 */ int spi_chip_write_1(struct flashchip *flash, uint8_t *buf) { - int total_size = 1024 * flash->total_size; int i, result = 0;
spi_disable_blockprotect(); - /* Erase first */ - msg_cinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - msg_cinfo("done.\n"); - for (i = 0; i < total_size; i++) { + for (i = 0; i < flash->total_size * 1024; i++) { result = spi_byte_program(i, buf[i]); if (result) return 1; @@ -988,6 +983,7 @@ return 0; }
+/* FIXME: Unused function, not easily convertable to partial write. */ int spi_aai_write(struct flashchip *flash, uint8_t *buf) { uint32_t pos = 2, size = flash->total_size * 1024; @@ -1004,10 +1000,6 @@ default: break; } - if (erase_flash(flash)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } /* FIXME: This will fail on ICH/VIA SPI. */ result = spi_write_enable(); if (result) Index: flashrom-partial_write_no_autoerase/it87spi.c =================================================================== --- flashrom-partial_write_no_autoerase/it87spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/it87spi.c (Arbeitskopie) @@ -326,25 +326,19 @@
int it8716f_spi_chip_write_256(struct flashchip *flash, uint8_t *buf) { - int total_size = 1024 * flash->total_size; int i;
/* * IT8716F only allows maximum of 512 kb SPI chip size for memory * mapped access. */ - if ((programmer == PROGRAMMER_IT87SPI) || (total_size > 512 * 1024)) { + if ((programmer == PROGRAMMER_IT87SPI) || (flash->total_size * 1024 > 512 * 1024)) { spi_chip_write_1(flash, buf); } else { spi_disable_blockprotect(); - /* Erase first */ - msg_pinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_perr("ERASE FAILED!\n"); - return -1; - } - msg_pinfo("done.\n"); - for (i = 0; i < total_size / 256; i++) { + + /* FIXME: Handle chips which have max writechunk size >1 and <256. */ + for (i = 0; i < flash->total_size * 1024 / 256; i++) { it8716f_spi_page_program(flash, i, buf); } } Index: flashrom-partial_write_no_autoerase/buspirate_spi.c =================================================================== --- flashrom-partial_write_no_autoerase/buspirate_spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/buspirate_spi.c (Arbeitskopie) @@ -318,16 +318,6 @@
int buspirate_spi_write_256(struct flashchip *flash, uint8_t *buf) { - int total_size = 1024 * flash->total_size; - spi_disable_blockprotect(); - /* Erase first. */ - msg_pinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_perr("ERASE FAILED!\n"); - return -1; - } - msg_pinfo("done.\n"); - - return spi_write_chunked(flash, buf, 0, total_size, 12); + return spi_write_chunked(flash, buf, 0, flash->total_size * 1024, 12); } Index: flashrom-partial_write_no_autoerase/jedec.c =================================================================== --- flashrom-partial_write_no_autoerase/jedec.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/jedec.c (Arbeitskopie) @@ -393,6 +393,7 @@ } }
+/* real chunksize is page_size, logical chunksize is page_size */ int write_jedec(struct flashchip *flash, uint8_t *buf) { int mask; @@ -402,52 +403,23 @@
mask = getaddrmask(flash);
- if (erase_chip_jedec(flash)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - - msg_cinfo("Programming page: "); for (i = 0; i < total_size / page_size; i++) { - msg_cinfo("%04d at address: 0x%08x", i, i * page_size); - if (write_page_write_jedec_common(flash, buf + i * page_size, - i * page_size, page_size, mask)) + if (write_page_write_jedec_common(flash, buf + i * page_size, i * page_size, page_size, mask)) failed = 1; - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); } - msg_cinfo("DONE!\n");
return failed; }
+/* real chunksize is 1, logical chunksize is 1 */ int write_jedec_1(struct flashchip *flash, uint8_t * buf) { - int i; - chipaddr bios = flash->virtual_memory; - chipaddr dst = bios; + chipaddr dst = flash->virtual_memory; int mask;
mask = getaddrmask(flash);
- programmer_delay(10); - if (erase_flash(flash)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - - msg_cinfo("Programming page: "); - for (i = 0; i < flash->total_size; i++) { - if ((i & 0x3) == 0) - msg_cinfo("address: 0x%08lx", (unsigned long)i * 1024); - - write_sector_jedec_common(flash, buf + i * 1024, dst + i * 1024, 1024, mask); - - if ((i & 0x3) == 0) - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); - } - - msg_cinfo("DONE!\n"); - return 0; + return write_sector_jedec_common(flash, buf, dst, flash->total_size * 1024, mask); }
/* erase chip with block_erase() prototype */ Index: flashrom-partial_write_no_autoerase/bitbang_spi.c =================================================================== --- flashrom-partial_write_no_autoerase/bitbang_spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/bitbang_spi.c (Arbeitskopie) @@ -141,8 +141,5 @@
int bitbang_spi_write_256(struct flashchip *flash, uint8_t *buf) { - int total_size = 1024 * flash->total_size; - - msg_pdbg("total_size is %d\n", total_size); - return spi_write_chunked(flash, buf, 0, total_size, 256); + return spi_write_chunked(flash, buf, 0, flash->total_size * 1024, 256); } Index: flashrom-partial_write_no_autoerase/sst49lfxxxc.c =================================================================== --- flashrom-partial_write_no_autoerase/sst49lfxxxc.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/sst49lfxxxc.c (Arbeitskopie) @@ -92,6 +92,7 @@ return 0; }
+/* real chunksize is 1, logical chunksize is page_size */ int write_49lfxxxc(struct flashchip *flash, uint8_t *buf) { int i; @@ -100,21 +101,10 @@ chipaddr bios = flash->virtual_memory;
write_lockbits_49lfxxxc(flash, 0); - msg_cinfo("Programming page: "); for (i = 0; i < total_size / page_size; i++) { - /* erase the page before programming */ - if (erase_sector_49lfxxxc(flash, i * page_size, flash->page_size)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - }
- /* write to the sector */ - msg_cinfo("%04d at address: 0x%08x", i, i * page_size); - write_page_82802ab(bios, buf + i * page_size, - bios + i * page_size, page_size); - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + write_page_82802ab(bios, buf + i * page_size, bios + i * page_size, page_size); } - msg_cinfo("\n");
chip_writeb(0xFF, bios);
Index: flashrom-partial_write_no_autoerase/spi.c =================================================================== --- flashrom-partial_write_no_autoerase/spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/spi.c (Arbeitskopie) @@ -67,7 +67,7 @@ .command = sb600_spi_send_command, .multicommand = default_spi_send_multicommand, .read = sb600_spi_read, - .write_256 = sb600_spi_write_1, + .write_256 = sb600_spi_write_256, },
{ /* SPI_CONTROLLER_VIA */ @@ -194,6 +194,7 @@ * Program chip using page (256 bytes) programming. * Some SPI masters can't do this, they use single byte programming instead. */ +/* real chunksize is up to 256, logical chunksize is 256 */ int spi_chip_write_256(struct flashchip *flash, uint8_t *buf) { if (!spi_programmer[spi_controller].write_256) { Index: flashrom-partial_write_no_autoerase/ft2232_spi.c =================================================================== --- flashrom-partial_write_no_autoerase/ft2232_spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/ft2232_spi.c (Arbeitskopie) @@ -287,18 +287,8 @@
int ft2232_spi_write_256(struct flashchip *flash, uint8_t *buf) { - int total_size = 1024 * flash->total_size; - spi_disable_blockprotect(); - /* Erase first. */ - msg_pinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_perr("ERASE FAILED!\n"); - return -1; - } - msg_pinfo("done.\n"); - msg_pdbg("total_size is %d\n", total_size); - return spi_write_chunked(flash, buf, 0, total_size, 256); + return spi_write_chunked(flash, buf, 0, flash->total_size * 1024, 256); }
#endif Index: flashrom-partial_write_no_autoerase/sst28sf040.c =================================================================== --- flashrom-partial_write_no_autoerase/sst28sf040.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/sst28sf040.c (Arbeitskopie) @@ -111,6 +111,7 @@ return 0; }
+/* real chunksize is 1, logical chunksize is page_size */ int write_28sf040(struct flashchip *flash, uint8_t *buf) { int i; @@ -120,21 +121,9 @@
unprotect_28sf040(bios);
- msg_cinfo("Programming page: "); for (i = 0; i < total_size / page_size; i++) { - /* erase the page before programming */ - if (erase_sector_28sf040(flash, i * page_size, page_size)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - - /* write to the sector */ - msg_cinfo("%04d at address: 0x%08x", i, i * page_size); - write_sector_28sf040(bios, buf + i * page_size, - bios + i * page_size, page_size); - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + write_sector_28sf040(bios, buf + i * page_size, bios + i * page_size, page_size); } - msg_cinfo("\n");
protect_28sf040(bios);
Index: flashrom-partial_write_no_autoerase/sb600spi.c =================================================================== --- flashrom-partial_write_no_autoerase/sb600spi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/sb600spi.c (Arbeitskopie) @@ -47,25 +47,10 @@ return spi_read_chunked(flash, buf, start, len, 8); }
-/* FIXME: SB600 can write 5 bytes per transaction. */ -int sb600_spi_write_1(struct flashchip *flash, uint8_t *buf) +int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf) { - int total_size = flash->total_size * 1024; - int result = 0; - spi_disable_blockprotect(); - /* Erase first */ - msg_pinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_perr("ERASE FAILED!\n"); - return -1; - } - msg_pinfo("done.\n"); - - msg_pinfo("Programming flash"); - result = spi_write_chunked(flash, buf, 0, total_size, 5); - msg_pinfo(" done.\n"); - return result; + return spi_write_chunked(flash, buf, 0, flash->total_size * 1024, 5); }
static void reset_internal_fifo_pointer(void) Index: flashrom-partial_write_no_autoerase/flashrom.c =================================================================== --- flashrom-partial_write_no_autoerase/flashrom.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/flashrom.c (Arbeitskopie) @@ -1319,6 +1319,7 @@
/* This function signature is horrible. We need to design a better interface, * but right now it allows us to split off the CLI code. + * Besides that, the function itself is a textbook example of abysmal code flow. */ int doit(struct flashchip *flash, int force, char *filename, int read_it, int write_it, int erase_it, int verify_it) { @@ -1358,12 +1359,7 @@ programmer_shutdown(); return 1; } - } else { - struct stat image_stat; - - if (flash->unlock) - flash->unlock(flash); - + } else if (write_it) { if (flash->tested & TEST_BAD_ERASE) { msg_cerr("Erase is not working on this chip " "and erase is needed for write. "); @@ -1385,6 +1381,18 @@ msg_cerr("Continuing anyway.\n"); } } + if (!flash->write) { + msg_cerr("Error: flashrom has no write function for this flash chip.\n"); + programmer_shutdown(); + return 1; + } + if (flash->unlock) + flash->unlock(flash); + + } + if (write_it || verify_it) { + struct stat image_stat; + if ((image = fopen(filename, "rb")) == NULL) { perror(filename); programmer_shutdown(); @@ -1420,12 +1428,12 @@ // ////////////////////////////////////////////////////////////
if (write_it) { - msg_cinfo("Writing flash chip... "); - if (!flash->write) { - msg_cerr("Error: flashrom has no write function for this flash chip.\n"); + if (erase_flash(flash)) { + emergency_help_message(); programmer_shutdown(); return 1; } + msg_cinfo("Writing flash chip... "); ret = flash->write(flash, buf); if (ret) { msg_cerr("FAILED!\n"); Index: flashrom-partial_write_no_autoerase/ichspi.c =================================================================== --- flashrom-partial_write_no_autoerase/ichspi.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/ichspi.c (Arbeitskopie) @@ -646,34 +646,13 @@
int ich_spi_write_256(struct flashchip *flash, uint8_t * buf) { - int i, ret = 0; - int total_size = flash->total_size * 1024; - int erase_size = 64 * 1024; int maxdata = 64;
if (spi_controller == SPI_CONTROLLER_VIA) maxdata = 16;
spi_disable_blockprotect(); - /* Erase first */ - msg_pinfo("Erasing flash before programming... "); - if (erase_flash(flash)) { - msg_perr("ERASE FAILED!\n"); - return -1; - } - msg_pinfo("done.\n"); - - msg_pinfo("Programming page: \n"); - for (i = 0; i < total_size / erase_size; i++) { - ret = spi_write_chunked(flash, buf + (i * erase_size), - i * erase_size, erase_size, maxdata); - if (ret) - break; - } - - msg_pinfo("\n"); - - return ret; + return spi_write_chunked(flash, buf, 0, flash->total_size * 1024, maxdata); }
int ich_spi_send_command(unsigned int writecnt, unsigned int readcnt, Index: flashrom-partial_write_no_autoerase/82802ab.c =================================================================== --- flashrom-partial_write_no_autoerase/82802ab.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/82802ab.c (Arbeitskopie) @@ -176,28 +176,16 @@ } }
+/* real chunksize is 1, logical chunksize is 1024 */ int write_82802ab(struct flashchip *flash, uint8_t *buf) { int i; chipaddr bios = flash->virtual_memory;
- if (erase_flash(flash)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - - msg_cinfo("Programming at: "); for (i = 0; i < flash->total_size; i++) { - if ((i & 0x3) == 0) - msg_cinfo("address: 0x%08lx", (unsigned long)i * 1024); - write_page_82802ab(bios, buf + i * 1024, bios + i * 1024, 1024); - - if ((i & 0x3) == 0) - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); }
- msg_cinfo("DONE!\n"); return 0; }
Index: flashrom-partial_write_no_autoerase/m29f400bt.c =================================================================== --- flashrom-partial_write_no_autoerase/m29f400bt.c (Revision 1005) +++ flashrom-partial_write_no_autoerase/m29f400bt.c (Arbeitskopie) @@ -39,8 +39,6 @@
/* transfer data from source to destination */ chip_writeb(*src, dst); - //chip_writeb(0xF0, bios); - //programmer_delay(5); toggle_ready_jedec(dst); msg_cerr("Value in the flash at address 0x%lx = %#x, want %#x\n", (dst - bios), chip_readb(dst), *src); @@ -113,7 +111,6 @@
chip_writeb(0xAA, bios + 0xAAA); chip_writeb(0x55, bios + 0x555); - //chip_writeb(0x10, bios + 0xAAA); chip_writeb(0x30, dst);
programmer_delay(10); @@ -136,6 +133,7 @@ return erase_m29f400bt(flash); }
+/* real chunksize is 1, logical chunksize is 8k-64k */ int write_m29f400bt(struct flashchip *flash, uint8_t *buf) { int i; @@ -143,117 +141,27 @@ int page_size = flash->page_size; chipaddr bios = flash->virtual_memory;
- //erase_m29f400bt (flash); - msg_cinfo("Programming page:\n "); - /********************************* - *Pages for M29F400BT: - * 16 0x7c000 0x7ffff TOP - * 8 0x7a000 0x7bfff - * 8 0x78000 0x79fff - * 32 0x70000 0x77fff - * 64 0x60000 0x6ffff - * 64 0x50000 0x5ffff - * 64 0x40000 0x4ffff - *--------------------------------- - * 64 0x30000 0x3ffff - * 64 0x20000 0x2ffff - * 64 0x10000 0x1ffff - * 64 0x00000 0x0ffff BOTTOM - *********************************/ - msg_cinfo("total_size/page_size = %d\n", total_size / page_size); for (i = 0; i < (total_size / page_size) - 1; i++) { - msg_cinfo("%04d at address: 0x%08x\n", i, i * page_size); - if (block_erase_m29f400bt(flash, i * page_size, page_size)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } - write_page_m29f400bt(bios, buf + i * page_size, - bios + i * page_size, page_size); - msg_cinfo("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); + write_page_m29f400bt(bios, buf + i * page_size, bios + i * page_size, page_size); }
- msg_cinfo("%04d at address: 0x%08x\n", 7, 0x70000); - if (block_erase_m29f400bt(flash, 0x70000, 32 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x70000, bios + 0x70000, 32 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 8, 0x78000); - if (block_erase_m29f400bt(flash, 0x78000, 8 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x78000, bios + 0x78000, 8 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 9, 0x7a000); - if (block_erase_m29f400bt(flash, 0x7a000, 8 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x7a000, bios + 0x7a000, 8 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 10, 0x7c000); - if (block_erase_m29f400bt(flash, 0x7c000, 16 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x7c000, bios + 0x7c000, 16 * 1024);
- msg_cinfo("\n"); - return 0; }
+/* real chunksize is 1, logical chunksize is 64k */ int write_coreboot_m29f400bt(struct flashchip *flash, uint8_t *buf) { chipaddr bios = flash->virtual_memory;
- msg_cinfo("Programming page:\n "); - /********************************* - *Pages for M29F400BT: - * 16 0x7c000 0x7ffff TOP - * 8 0x7a000 0x7bfff - * 8 0x78000 0x79fff - * 32 0x70000 0x77fff - * 64 0x60000 0x6ffff - * 64 0x50000 0x5ffff - * 64 0x40000 0x4ffff - *--------------------------------- - * 64 0x30000 0x3ffff - * 64 0x20000 0x2ffff - * 64 0x10000 0x1ffff - * 64 0x00000 0x0ffff BOTTOM - *********************************/ - msg_cinfo("%04d at address: 0x%08x\n", 7, 0x00000); - if (block_erase_m29f400bt(flash, 0x00000, 64 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x00000, bios + 0x00000, 64 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 7, 0x10000); - if (block_erase_m29f400bt(flash, 0x10000, 64 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x10000, bios + 0x10000, 64 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 7, 0x20000); - if (block_erase_m29f400bt(flash, 0x20000, 64 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x20000, bios + 0x20000, 64 * 1024); - - msg_cinfo("%04d at address: 0x%08x\n", 7, 0x30000); - if (block_erase_m29f400bt(flash, 0x30000, 64 * 1024)) { - msg_cerr("ERASE FAILED!\n"); - return -1; - } write_page_m29f400bt(bios, buf + 0x30000, bios + 0x30000, 64 * 1024);
- msg_cinfo("\n"); - return 0; }