summaryrefslogtreecommitdiffstats
path: root/flashrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'flashrom.c')
-rw-r--r--flashrom.c86
1 files changed, 63 insertions, 23 deletions
diff --git a/flashrom.c b/flashrom.c
index 630c69db8..dbf06cf47 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -379,7 +379,7 @@ void get_flash_region(const struct flashctx *flash, int addr, struct flash_regio
} else {
region->name = strdup("");
region->start = 0;
- region->end = flashrom_flash_getsize(flash);
+ region->end = flashrom_flash_getsize(flash) - 1;
region->read_prot = false;
region->write_prot = false;
}
@@ -388,12 +388,12 @@ void get_flash_region(const struct flashctx *flash, int addr, struct flash_regio
int check_for_unwritable_regions(const struct flashctx *flash, unsigned int start, unsigned int len)
{
struct flash_region region;
- for (unsigned int addr = start; addr < start + len; addr = region.end) {
+ for (unsigned int addr = start; addr < start + len; addr = region.end + 1) {
get_flash_region(flash, addr, &region);
if (region.write_prot) {
msg_gerr("%s: cannot write/erase inside %s region (%#08"PRIx32"..%#08"PRIx32").\n",
- __func__, region.name, region.start, region.end - 1);
+ __func__, region.name, region.start, region.end);
free(region.name);
return -1;
}
@@ -402,8 +402,10 @@ int check_for_unwritable_regions(const struct flashctx *flash, unsigned int star
return 0;
}
-/* special unit-test hook */
-erasefunc_t *g_test_erase_injector;
+#ifdef FLASHROM_TEST
+/* special unit-test hooks */
+erasefunc_t *g_test_erase_injector[NUM_TEST_ERASE_INJECTORS];
+#endif
erasefunc_t *lookup_erase_func_ptr(const struct block_eraser *const eraser)
{
@@ -442,7 +444,14 @@ erasefunc_t *lookup_erase_func_ptr(const struct block_eraser *const eraser)
case ERASE_SECTOR_49LFXXXC: return &erase_sector_49lfxxxc;
case STM50_SECTOR_ERASE: return &erase_sector_stm50; // TODO rename to &stm50_sector_erase;
case EDI_CHIP_BLOCK_ERASE: return &edi_chip_block_erase;
- case TEST_ERASE_INJECTOR: return g_test_erase_injector;
+#ifdef FLASHROM_TEST
+ case TEST_ERASE_INJECTOR_1:
+ case TEST_ERASE_INJECTOR_2:
+ case TEST_ERASE_INJECTOR_3:
+ case TEST_ERASE_INJECTOR_4:
+ case TEST_ERASE_INJECTOR_5:
+ return g_test_erase_injector[eraser->block_erase - TEST_ERASE_INJECTOR_1];
+#endif
/* default: total function, 0 indicates no erase function set.
* We explicitly do not want a default catch-all case in the switch
* to ensure unhandled enum's are compiler warnings.
@@ -540,8 +549,10 @@ int check_erased_range(struct flashctx *flash, unsigned int start, unsigned int
return ret;
}
+#ifdef FLASHROM_TEST
/* special unit-test hook */
read_func_t *g_test_read_injector;
+#endif
static read_func_t *lookup_read_func_ptr(const struct flashchip *chip)
{
@@ -552,7 +563,9 @@ static read_func_t *lookup_read_func_ptr(const struct flashchip *chip)
case EDI_CHIP_READ: return &edi_chip_read;
case SPI_READ_AT45DB: return spi_read_at45db;
case SPI_READ_AT45DB_E8: return spi_read_at45db_e8;
+#ifdef FLASHROM_TEST
case TEST_READ_INJECTOR: return g_test_read_injector;
+#endif
/* default: total function, 0 indicates no read function set.
* We explicitly do not want a default catch-all case in the switch
* to ensure unhandled enum's are compiler warnings.
@@ -583,14 +596,14 @@ int read_flash(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigne
struct flash_region region;
get_flash_region(flash, addr, &region);
- read_len = min(start + len, region.end) - addr;
+ read_len = min(start + len, region.end + 1) - addr;
uint8_t *rbuf = buf + addr - start;
if (region.read_prot) {
if (flash->flags.skip_unreadable_regions) {
msg_gdbg("%s: cannot read inside %s region (%#08"PRIx32"..%#08"PRIx32"), "
"filling (%#08x..%#08x) with erased value instead.\n",
- __func__, region.name, region.start, region.end - 1,
+ __func__, region.name, region.start, region.end,
addr, addr + read_len - 1);
free(region.name);
@@ -599,12 +612,12 @@ int read_flash(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigne
}
msg_gerr("%s: cannot read inside %s region (%#08"PRIx32"..%#08"PRIx32").\n",
- __func__, region.name, region.start, region.end - 1);
+ __func__, region.name, region.start, region.end);
free(region.name);
return -1;
}
msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is readable, reading range (%#08x..%#08x).\n",
- __func__, region.name, region.start, region.end - 1, addr, addr + read_len - 1);
+ __func__, region.name, region.start, region.end, addr, addr + read_len - 1);
free(region.name);
read_func_t *read_func = lookup_read_func_ptr(flash->chip);
@@ -652,25 +665,25 @@ int verify_range(struct flashctx *flash, const uint8_t *cmpbuf, unsigned int sta
for (size_t addr = start; addr < start + len; addr += read_len) {
struct flash_region region;
get_flash_region(flash, addr, &region);
- read_len = min(start + len, region.end) - addr;
+ read_len = min(start + len, region.end + 1) - addr;
if ((region.write_prot && flash->flags.skip_unwritable_regions) ||
(region.read_prot && flash->flags.skip_unreadable_regions)) {
msg_gdbg("%s: Skipping verification of %s region (%#08"PRIx32"..%#08"PRIx32")\n",
- __func__, region.name, region.start, region.end - 1);
+ __func__, region.name, region.start, region.end);
free(region.name);
continue;
}
if (region.read_prot) {
msg_gerr("%s: Verification imposible because %s region (%#08"PRIx32"..%#08"PRIx32") is unreadable.\n",
- __func__, region.name, region.start, region.end - 1);
+ __func__, region.name, region.start, region.end);
free(region.name);
goto out_free;
}
msg_gdbg("%s: Verifying %s region (%#08"PRIx32"..%#08"PRIx32")\n",
- __func__, region.name, region.start, region.end - 1);
+ __func__, region.name, region.start, region.end);
free(region.name);
ret = read_flash(flash, readbuf, addr, read_len);
@@ -976,7 +989,9 @@ static int init_default_layout(struct flashctx *flash)
}
/* special unit-test hook */
+#ifdef FLASHROM_TEST
write_func_t *g_test_write_injector;
+#endif
static write_func_t *lookup_write_func_ptr(const struct flashchip *chip)
{
@@ -992,7 +1007,9 @@ static write_func_t *lookup_write_func_ptr(const struct flashchip *chip)
case WRITE_82802AB: return &write_82802ab;
case WRITE_EN29LV640B: return &write_en29lv640b;
case EDI_CHIP_WRITE: return &edi_chip_write;
+#ifdef FLASHROM_TEST
case TEST_WRITE_INJECTOR: return g_test_write_injector;
+#endif
/* default: total function, 0 indicates no write function set.
* We explicitly do not want a default catch-all case in the switch
* to ensure unhandled enum's are compiler warnings.
@@ -1029,18 +1046,18 @@ int write_flash(struct flashctx *flash, const uint8_t *buf,
struct flash_region region;
get_flash_region(flash, addr, &region);
- write_len = min(start + len, region.end) - addr;
+ write_len = min(start + len, region.end + 1) - addr;
const uint8_t *rbuf = buf + addr - start;
if (region.write_prot) {
msg_gdbg("%s: cannot write inside %s region (%#08"PRIx32"..%#08"PRIx32"), skipping (%#08x..%#08x).\n",
- __func__, region.name, region.start, region.end - 1, addr, addr + write_len - 1);
+ __func__, region.name, region.start, region.end, addr, addr + write_len - 1);
free(region.name);
continue;
}
msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is writable, writing range (%#08x..%#08x).\n",
- __func__, region.name, region.start, region.end - 1, addr, addr + write_len - 1);
+ __func__, region.name, region.start, region.end, addr, addr + write_len - 1);
write_func_t *write_func = lookup_write_func_ptr(flash->chip);
int ret = write_func(flash, rbuf, addr, write_len);
@@ -1494,17 +1511,17 @@ static int erase_block(struct flashctx *const flashctx,
struct flash_region region;
get_flash_region(flashctx, addr, &region);
- len = min(info->erase_start + erase_len, region.end) - addr;
+ len = min(info->erase_start + erase_len, region.end + 1) - addr;
if (region.write_prot) {
msg_gdbg("%s: cannot erase inside %s region (%#08"PRIx32"..%#08"PRIx32"), skipping range (%#08x..%#08x).\n",
- __func__, region.name, region.start, region.end - 1, addr, addr + len - 1);
+ __func__, region.name, region.start, region.end, addr, addr + len - 1);
free(region.name);
continue;
}
msg_gdbg("%s: %s region (%#08"PRIx32"..%#08"PRIx32") is writable, erasing range (%#08x..%#08x).\n",
- __func__, region.name, region.start, region.end - 1, addr, addr + len - 1);
+ __func__, region.name, region.start, region.end, addr, addr + len - 1);
free(region.name);
if (erasefn(flashctx, addr, len))
@@ -2092,7 +2109,7 @@ static int unlock_flash_wp(struct flashctx *const flash,
warn_out:
if (ret)
- msg_cerr("Failed to unlock flash status reg with wp support.\n");
+ msg_cwarn("Failed to unlock flash status reg with wp support.\n");
return ret;
}
@@ -2330,8 +2347,31 @@ int flashrom_image_write(struct flashctx *const flashctx, void *const buffer, co
if (verify && !all_skipped) {
msg_cinfo("Verifying flash... ");
- /* Work around chips which need some time to calm down. */
- programmer_delay(flashctx, 1000*1000);
+ /*
+ * Work around chips which "need some time to calm down."
+ *
+ * Frankly, it's not 100% clear why this delay is here at all,
+ * except for a terse message from 2009 of "a few reports where
+ * verify directly after erase had unpleasant side effects like
+ * corrupting flash or at least getting incorrect verify
+ * results". Ideally, if there were a few known problematic
+ * chips or programmers, we could add quirks flags for those
+ * specific implementations without penalizing all other
+ * flashrom users. But alas, we don't know which systems
+ * experienced those issues.
+ *
+ * Out of an extreme abundance of caution, we retain this
+ * delay, but only for a few non-SPI bus types that were the
+ * likely prevalent targets at the time. This is a complete
+ * guess, which conveniently avoids wasting time on common
+ * BUS_SPI and BUS_PROG systems.
+ *
+ * Background thread:
+ * Subject: RFC: removing 1 second verification delay
+ * https://mail.coreboot.org/hyperkitty/list/flashrom@flashrom.org/thread/SFV3OJBVVMDKRLI3FQA3DDDGEXJ7W4ED/
+ */
+ if (flashctx->chip->bustype & (BUS_PARALLEL | BUS_LPC | BUS_FWH))
+ programmer_delay(flashctx, 1000*1000);
if (verify_all)
combine_image_by_layout(flashctx, newcontents, oldcontents);