summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico Huber <nico.huber@secunet.com>2019-01-23 17:07:49 +0100
committerNico Huber <nico.h@gmx.de>2019-03-04 15:01:59 +0000
commit6e61e0cf9cf038d5f95bcbe5b6516d58ecd81d7d (patch)
tree00ef055c999d0c6ddec61f0dd500859908e22425
parentdb7482bb72035fab8386226d1720cde09e0c700e (diff)
downloadflashrom-6e61e0cf9cf038d5f95bcbe5b6516d58ecd81d7d.tar.gz
flashrom-6e61e0cf9cf038d5f95bcbe5b6516d58ecd81d7d.tar.bz2
flashrom-6e61e0cf9cf038d5f95bcbe5b6516d58ecd81d7d.zip
Fix erasing of unaligned regions
The erase (-E) feature is somehow a brute force method, but still, if we are given a region to erase, we should make sure to restore surrounding data if the erase block expands beyond the region. It shares a lot of code with the write path. Though, experiments with common functions have shown that it would make the whole function even harder to read. Maybe we could add some abstraction if we ever need similar code on a third path. Change-Id: I5fc35310f0b090f218cd1d660e27fb39dd05c3c5 Signed-off-by: Nico Huber <nico.huber@secunet.com> Reviewed-on: https://review.coreboot.org/c/31068 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Angel Pons <th3fanbus@gmail.com> Reviewed-by: Patrick Georgi <pgeorgi@google.com>
-rw-r--r--flashrom.c69
1 files changed, 66 insertions, 3 deletions
diff --git a/flashrom.c b/flashrom.c
index e82c32e86..b9e2b413e 100644
--- a/flashrom.c
+++ b/flashrom.c
@@ -1732,17 +1732,80 @@ static int erase_block(struct flashctx *const flashctx,
const struct walk_info *const info, const erasefn_t erasefn)
{
const unsigned int erase_len = info->erase_end + 1 - info->erase_start;
+ const bool region_unaligned = info->region_start > info->erase_start ||
+ info->erase_end > info->region_end;
+ uint8_t *backup_contents = NULL, *erased_contents = NULL;
+ int ret = 2;
+
+ /*
+ * If the region is not erase-block aligned, merge current flash con-
+ * tents into a new buffer `backup_contents`.
+ */
+ if (region_unaligned) {
+ backup_contents = malloc(erase_len);
+ erased_contents = malloc(erase_len);
+ if (!backup_contents || !erased_contents) {
+ msg_cerr("Out of memory!\n");
+ ret = 1;
+ goto _free_ret;
+ }
+ memset(backup_contents, ERASED_VALUE(flashctx), erase_len);
+ memset(erased_contents, ERASED_VALUE(flashctx), erase_len);
+
+ msg_cdbg("R");
+ /* Merge data preceding the current region. */
+ if (info->region_start > info->erase_start) {
+ const chipoff_t start = info->erase_start;
+ const chipsize_t len = info->region_start - info->erase_start;
+ if (flashctx->chip->read(flashctx, backup_contents, start, len)) {
+ msg_cerr("Can't read! Aborting.\n");
+ goto _free_ret;
+ }
+ }
+ /* Merge data following the current region. */
+ if (info->erase_end > info->region_end) {
+ const chipoff_t start = info->region_end + 1;
+ const chipoff_t rel_start = start - info->erase_start; /* within this erase block */
+ const chipsize_t len = info->erase_end - info->region_end;
+ if (flashctx->chip->read(flashctx, backup_contents + rel_start, start, len)) {
+ msg_cerr("Can't read! Aborting.\n");
+ goto _free_ret;
+ }
+ }
+ }
+ ret = 1;
all_skipped = false;
msg_cdbg("E");
if (erasefn(flashctx, info->erase_start, erase_len))
- return 1;
+ goto _free_ret;
if (check_erased_range(flashctx, info->erase_start, erase_len)) {
msg_cerr("ERASE FAILED!\n");
- return 1;
+ goto _free_ret;
}
- return 0;
+
+ if (region_unaligned) {
+ unsigned int starthere = 0, lenhere = 0, writecount = 0;
+ /* get_next_write() sets starthere to a new value after the call. */
+ while ((lenhere = get_next_write(erased_contents + starthere, backup_contents + starthere,
+ erase_len - starthere, &starthere, flashctx->chip->gran))) {
+ if (!writecount++)
+ msg_cdbg("W");
+ /* Needs the partial write function signature. */
+ if (flashctx->chip->write(flashctx, backup_contents + starthere,
+ info->erase_start + starthere, lenhere))
+ goto _free_ret;
+ starthere += lenhere;
+ }
+ }
+
+ ret = 0;
+
+_free_ret:
+ free(erased_contents);
+ free(backup_contents);
+ return ret;
}
/**