CHROMIUM: cros_ec.c: Avoid sysjumps where possible

Avoid unconditional sysjumps to allow software sync to do
its job.

BUG=b:278652302,b:152925812
BRANCH=none
TEST=`futility update --quirks=ec_partial_recovery` does not sysjump from RW for EC.

Signed-off-by: Edward O'Callaghan <quasisec@google.com>
Change-Id: I3313a540b98d8e87f84ab6fc25b1e0f4d703dbff
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/flashrom/+/4445816
Reviewed-by: Sam McNally <sammc@chromium.org>
Commit-Queue: Edward O'Callaghan <quasisec@chromium.org>
Tested-by: Sam McNally <sammc@chromium.org>
Auto-Submit: Edward O'Callaghan <quasisec@chromium.org>
Tested-by: Edward O'Callaghan <quasisec@chromium.org>
diff --git a/cros_ec.c b/cros_ec.c
index 3595b14..89b39d8 100644
--- a/cros_ec.c
+++ b/cros_ec.c
@@ -406,6 +406,7 @@
 		const struct fmap_area *fa = &fmap->areas[i];
 
 		for (unsigned int j = EC_IMAGE_RO; j < ARRAY_SIZE(sections); j++) {
+			/* skip fmap sections unrelated to cros_ec sections. */
 			if (strcmp(sections[j], (const char *) fa->name))
 				continue;
 
@@ -417,6 +418,30 @@
 	free(fmap);
 }
 
+/**
+ * iff layout region is one of the supported cros_ec
+ * sections then modify the region_type bit-feild.
+ * 00 - no RO or RW.
+ * 01 - RO found.
+ * 10 - RW found.
+ * 11 - RO+RW found.
+ */
+static enum ec_current_image parse_layout(const struct flashrom_layout *const layout)
+{
+	enum ec_current_image region_type = EC_IMAGE_UNKNOWN; /* no RO or RW found yet. */
+	const struct romentry *entry = NULL;
+
+	while ((entry = layout_next_included(layout, entry))) {
+		const struct flash_region *region = &entry->region;
+		if (!strcmp("WP_RO", (const char *) region->name))
+			region_type |= EC_IMAGE_RO;
+		if (!strcmp(sections[EC_IMAGE_RW], (const char *) region->name))
+			region_type |= EC_IMAGE_RW;
+	}
+	/* iff neither RO or RW was found in the layout then assume a full image of both. */
+	return region_type == EC_IMAGE_UNKNOWN ? (EC_IMAGE_RO | EC_IMAGE_RW) : region_type;
+}
+
 /*
  * Prepare EC for update:
  * - Disable soft WP if needed.
@@ -437,13 +462,33 @@
 		return 1;
 
 	parse_fmap(image, flash_size);
+	/* check layout to determine what sysjumps we are required to do. */
+	const struct flashrom_layout *const layout = get_layout(flash);
+	const enum ec_current_image region_typ = parse_layout(layout);
+	const uint8_t ec_subtype = cros_ec_priv->subtype; /* non-zero denotes non-ec path. */
 
 	if (ec_check_features(EC_FEATURE_EXEC_IN_RAM) <= 0) {
 		/* Warning: before update, we jump the EC to RO copy. If you
 		 * want to change this behavior, please also check the
 		 * cros_ec_finish().
 		 */
-		msg_pwarn("EXEC_IN_RAM unsupported - jumping to RO\n");
+		msg_pwarn("EXEC_IN_RAM unsupported..");
+
+		if (ec_subtype) {
+			msg_pwarn(" legacy component, unconditional jump to RO.\n");
+			return cros_ec_jump_copy(EC_IMAGE_RO);
+		}
+
+		if (!(region_typ & EC_IMAGE_RO) && cros_ec_get_current_image() == EC_IMAGE_RO) {
+			msg_pwarn(" image contains RW and already in RO, skipping jump.\n");
+			return 0;
+		}
+		if (!(region_typ & EC_IMAGE_RW) && cros_ec_get_current_image() == EC_IMAGE_RW) {
+			msg_pwarn(" image contains RO and already in RW, skipping jump.\n");
+			return 0;
+		}
+
+		msg_pwarn(" unconditional jump to RO.\n");
 		return cros_ec_jump_copy(EC_IMAGE_RO);
 	}
 	msg_pwarn("EXEC_IN_RAM supported - skip jumping to RO\n");
@@ -483,19 +528,23 @@
 }
 
 
-/* Returns 0 for success.
- *
+/**
+ * Returns 0 for success.
  * Try latest firmware: B > A > RO
- *
- * This function assumes the EC jumps to RO at cros_ec_prepare() so that
- * the fwcopy[RO].flags is old (0) and A/B are new. Please also refine
- * this code logic if you change the cros_ec_prepare() behavior.
  */
 int cros_ec_finish(void)
 {
 	if (!(cros_ec_priv && cros_ec_priv->detected))
           return 0;
 
+	/*
+	 * Check that the EC had jumped to RO at cros_ec_prepare() so that
+	 * the fwcopy[RO].flags is old (0) and A/B are new otherwise return.
+	 */
+	const uint8_t ec_subtype = cros_ec_priv->subtype; /* non-zero denotes non-ec path. */
+	if (cros_ec_get_current_image() != EC_IMAGE_RO && !ec_subtype)
+		return 0;
+
 	/* For EC with RWSIG enabled. We need a cold reboot to enable
 	 * EC_FLASH_PROTECT_ALL_NOW and make sure RWSIG check is performed.
 	 */
diff --git a/cros_ec.h b/cros_ec.h
index 787af26..65f2f7d 100644
--- a/cros_ec.h
+++ b/cros_ec.h
@@ -40,6 +40,9 @@
 	 */
 	const char* dev;
 
+	/* technical debt to deal with non-EC components (pd, tp, fp). */
+	uint8_t subtype;
+
 	/*
 	 * Some CrOS ECs support page write mode for their flash memory. This
 	 * represents the ideal size of a data payload to write to flash.
diff --git a/cros_ec_dev.c b/cros_ec_dev.c
index 11ebf58..90378c7 100644
--- a/cros_ec_dev.c
+++ b/cros_ec_dev.c
@@ -430,6 +430,7 @@
 		priv->dev = ec_type[index];
 		if (!strcmp(priv->dev, "fp"))
 			msg_perr("\t\033[31;1;5;7m >> The fp subtype is deprecated! Remove call site NOW as this WILL be deleted! <<\033[0m\n");
+		priv->subtype = index;
 		msg_pdbg("Target %s used\n", priv->dev);
 	}
 	free(p);
@@ -515,6 +516,7 @@
 	.region = NULL,
 	.ec_command = cros_ec_command_dev,
 	.dev = "ec",
+	.subtype = 0,
 	.ideal_write_size = 0,
 	.erase_block_size = 0,
 	.max_response_size = 0