flashrom.c: Write whole layout regions at once
We used to write per erase block, right after erasing in case
we had to. To ease integration of a new erase-block selection,
the write step is postponed now, after all blocks in a region
that need erasing are erased.
This also moves the decision whether to call erase_block() on
a given block to a higher level and prepares for the new erase-
block selection algorithm.
Change-Id: I325c4873f9bde8183e9f43239929075ac31b80cc
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.coreboot.org/c/flashrom-stable/+/72550
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
diff --git a/flashrom.c b/flashrom.c
index 0330b52..b6548c0 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -920,6 +920,12 @@
chipoff_t erase_end;
};
+static bool explicit_erase(const struct walk_info *const info)
+{
+ /* For explicit erase, we are called without new contents. */
+ return !info->newcontents;
+}
+
static int write_range(struct flashctx *const flashctx, const chipoff_t flash_offset,
const uint8_t *const curcontents, const uint8_t *const newcontents,
const chipsize_t len, bool *const skipped)
@@ -968,6 +974,17 @@
if (info->region_end < info->erase_start)
break;
+ /* Check if we want to erase this block. */
+ if (!explicit_erase(info)) {
+ const chipoff_t write_start = MAX(info->region_start, info->erase_start);
+ const chipoff_t write_end = MIN(info->region_end, info->erase_end);
+ const chipsize_t write_len = write_end + 1 - write_start;
+ if (!need_erase(info->curcontents + write_start,
+ info->newcontents + write_start,
+ write_len, flashctx->chip->gran, ERASED_VALUE(flashctx)))
+ continue;
+ }
+
/* Print this for every block except the first one. */
if (first)
first = false;
@@ -999,25 +1016,44 @@
info->region_start = entry->start;
info->region_end = entry->end;
- size_t j;
- for (j = 0; j < NUM_ERASEFUNCTIONS; ++j) {
- if (j != 0)
- msg_cinfo("Looking for another erase function.\n");
- msg_cdbg("Trying erase function %zi... ", j);
- if (!check_block_eraser(flashctx, j, 1))
- break;
+ if (!(flashctx->chip->feature_bits & FEATURE_NO_ERASE) || explicit_erase(info)) {
+ size_t j;
+ for (j = 0; j < NUM_ERASEFUNCTIONS; ++j) {
+ if (j != 0)
+ msg_cinfo("Looking for another erase function.\n");
+ msg_cdbg("Trying erase function %zi... ", j);
+ if (!check_block_eraser(flashctx, j, 1))
+ break;
+ }
+
+ if (j == NUM_ERASEFUNCTIONS) {
+ msg_cinfo("No usable erase function found.\n");
+ return 1;
+ }
+
+ if (walk_eraseblocks(flashctx, info, j, per_blockfn)) {
+ msg_cerr("FAILED!\n");
+ return 1;
+ }
}
- if (j == NUM_ERASEFUNCTIONS) {
- msg_cinfo("No usable erase function found.\n");
- return 1;
+ if (info->newcontents) {
+ bool skipped = true;
+ msg_cdbg("0x%06x-0x%06x:", info->region_start, info->region_end);
+ if (write_range(flashctx, info->region_start,
+ info->curcontents + info->region_start,
+ info->newcontents + info->region_start,
+ info->region_end + 1 - info->region_start, &skipped)) {
+ msg_cerr("FAILED!\n");
+ return 1;
+ }
+ if (skipped) {
+ msg_cdbg("S\n");
+ } else {
+ msg_cdbg("\n");
+ all_skipped = false;
+ }
}
-
- if (walk_eraseblocks(flashctx, info, j, per_blockfn)) {
- msg_cerr("FAILED!\n");
- return 1;
- }
-
}
if (all_skipped)
msg_cinfo("\nWarning: Chip content is identical to the requested image.\n");
@@ -1079,10 +1115,14 @@
msg_cerr("ERASE FAILED!\n");
goto _free_ret;
}
+ if (info->curcontents)
+ memset(info->curcontents + info->erase_start, ERASED_VALUE(flashctx), erase_len);
if (region_unaligned) {
if (write_range(flashctx, info->erase_start, erased_contents, backup_contents, erase_len, NULL))
goto _free_ret;
+ if (info->curcontents)
+ memcpy(info->curcontents + info->erase_start, backup_contents, erase_len);
}
ret = 0;
@@ -1109,39 +1149,6 @@
return walk_by_layout(flashctx, &info, &erase_block);
}
-static int write_block(struct flashctx *const flashctx,
- const struct walk_info *const info, const erasefn_t erasefn)
-{
- const chipoff_t write_start = MAX(info->region_start, info->erase_start);
- const chipoff_t write_end = MIN(info->region_end, info->erase_end);
- const chipsize_t write_len = write_end + 1 - write_start;
- const uint8_t *const newcontents = info->newcontents + write_start;
- uint8_t *const curcontents = info->curcontents + write_start;
- const uint8_t erased_value = ERASED_VALUE(flashctx);
- bool skipped = true;
-
- if (!(flashctx->chip->feature_bits & FEATURE_NO_ERASE) &&
- need_erase(curcontents, newcontents, write_len, flashctx->chip->gran, erased_value)) {
- if (erase_block(flashctx, info, erasefn))
- return 1;
- /* Erase was successful. Adjust curcontents. */
- memset(curcontents, erased_value, write_len);
- skipped = false;
- }
-
- if (write_range(flashctx, write_start, curcontents, newcontents, write_len, &skipped))
- return 1;
- if (skipped)
- msg_cdbg("S");
- else
- all_skipped = false;
-
- /* Update curcontents, other regions with overlapping erase blocks
- might rely on this. */
- memcpy(curcontents, newcontents, write_len);
- return 0;
-}
-
/**
* @brief Writes the included layout regions from a given image.
*
@@ -1160,7 +1167,7 @@
struct walk_info info;
info.curcontents = curcontents;
info.newcontents = newcontents;
- return walk_by_layout(flashctx, &info, write_block);
+ return walk_by_layout(flashctx, &info, erase_block);
}
/**