futility: updater: add new quirk 'extra_retries'

In early bring up, some devices may have problem when flashrom is
reading or writing to the system flash. To unblock dogfood process,
we want to have a special quirk to retry flashing.

BUG=b:213706510
TEST=make; build and run test
BRANCH=None

Change-Id: I58788f620fb32f7c886d1e5638f4a3605ea77953
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3452846
Reviewed-by: YH Lin <yueherngl@chromium.org>
Commit-Queue: YH Lin <yueherngl@chromium.org>
Reviewed-by: Yu-Ping Wu <yupingso@chromium.org>
(cherry picked from commit 707b839fadfd600af4300bc451946f8e8ccf840e)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3906634
Commit-Queue: Nick Vaccaro <nvaccaro@google.com>
Tested-by: Nick Vaccaro <nvaccaro@google.com>
diff --git a/futility/updater.c b/futility/updater.c
index 46f8878..5790ace 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -384,6 +384,14 @@
 }
 
 /*
+ * Returns the number of retries when reading or writing to flash.
+ */
+static int get_io_retries(struct updater_config *cfg)
+{
+	return 1 + get_config_quirk(QUIRK_EXTRA_RETRIES, cfg);
+}
+
+/*
  * Writes a section from given firmware image to system firmware.
  * If section_name is NULL, write whole image.
  * Returns 0 if success, non-zero if error.
@@ -410,7 +418,7 @@
 
 	return write_system_firmware(image, diff_image, section_name,
 				     &cfg->tempfiles, cfg->do_verify,
-				     cfg->verbosity + 1);
+				     get_io_retries(cfg), cfg->verbosity + 1);
 }
 
 /*
@@ -1207,9 +1215,10 @@
 	}
 	if (!image_from->data) {
 		int ret;
+
 		INFO("Loading current system firmware...\n");
 		ret = load_system_firmware(image_from, &cfg->tempfiles,
-					   cfg->verbosity);
+					   get_io_retries(cfg), cfg->verbosity);
 		if (ret == IMAGE_PARSE_FAILURE && cfg->force_update) {
 			WARN("No compatible firmware in system.\n");
 			cfg->check_platform = 0;
@@ -1392,7 +1401,9 @@
 		if (!cfg->image_current.data) {
 			INFO("Loading system firmware for white label...\n");
 			load_system_firmware(&cfg->image_current,
-					     &cfg->tempfiles, cfg->verbosity);
+					     &cfg->tempfiles,
+					     get_io_retries(cfg),
+					     cfg->verbosity);
 		}
 		tmp_image = get_firmware_image_temp_file(
 				&cfg->image_current, &cfg->tempfiles);
diff --git a/futility/updater.h b/futility/updater.h
index 2562782..6dda928 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -48,6 +48,7 @@
 	QUIRK_PRESERVE_ME,
 	QUIRK_NO_CHECK_PLATFORM,
 	QUIRK_NO_VERIFY,
+	QUIRK_EXTRA_RETRIES,
 	QUIRK_MAX,
 };
 
diff --git a/futility/updater_quirks.c b/futility/updater_quirks.c
index 1f7fada..6d6ccf8 100644
--- a/futility/updater_quirks.c
+++ b/futility/updater_quirks.c
@@ -512,6 +512,11 @@
 	quirks->name = "no_verify";
 	quirks->help = "Do not verify when flashing.";
 	quirks->apply = quirk_no_verify;
+
+	quirks = &cfg->quirks[QUIRK_EXTRA_RETRIES];
+	quirks->name = "extra_retries";
+	quirks->help = "Extra retries when writing to system firmware.";
+	quirks->apply = NULL;  /* Simple config. */
 }
 
 /*
diff --git a/futility/updater_utils.c b/futility/updater_utils.c
index 2221a87..a846ef2 100644
--- a/futility/updater_utils.c
+++ b/futility/updater_utils.c
@@ -523,11 +523,16 @@
  * Returns 0 if success, non-zero if error.
  */
 int load_system_firmware(struct firmware_image *image,
-			 struct tempfile *tempfiles, int verbosity)
+			 struct tempfile *tempfiles,
+			 int retries, int verbosity)
 {
-	int r;
+	int r, i;
 
-	r = flashrom_read_image(image, NULL, (verbosity + 1));
+	for (i = 1, r = -1; i <= retries && r != 0; i++) {
+		if (i > 1)
+			WARN("Retry reading firmware (%d/%d)...\n", i, retries);
+		r = flashrom_read_image(image, NULL, verbosity + 1);
+	}
 	if (!r)
 		r = parse_firmware_image(image);
 	return r;
@@ -542,10 +547,17 @@
 			  const struct firmware_image *diff_image,
 			  const char *section_name,
 			  struct tempfile *tempfiles,
-			  int do_verify, int verbosity)
+			  int do_verify, int retries, int verbosity)
 {
-	return flashrom_write_image(image, section_name, diff_image,
-				    do_verify, (verbosity + 1));
+	int r, i;
+
+	for (i = 1, r = -1; i <= retries && r != 0; i++) {
+		if (i > 1)
+			WARN("Retry writing firmware (%d/%d)...\n", i, retries);
+		r = flashrom_write_image(image, section_name, diff_image,
+					 do_verify, verbosity + 1);
+	}
+	return r;
 }
 
 /* Helper function to return host software write protection status. */
diff --git a/futility/updater_utils.h b/futility/updater_utils.h
index ae5574f..8ac0683 100644
--- a/futility/updater_utils.h
+++ b/futility/updater_utils.h
@@ -85,7 +85,8 @@
  * Returns 0 if success, non-zero if error.
  */
 int load_system_firmware(struct firmware_image *image,
-			 struct tempfile *tempfiles, int verbosity);
+			 struct tempfile *tempfiles,
+			 int retries, int verbosity);
 
 /* Frees the allocated resource from a firmware image object. */
 void free_firmware_image(struct firmware_image *image);
@@ -107,7 +108,7 @@
 			  const struct firmware_image *diff_image,
 			  const char *section_name,
 			  struct tempfile *tempfiles,
-			  int do_verify, int verbosity);
+			  int do_verify, int retries, int verbosity);
 
 struct firmware_section {
 	uint8_t *data;