futility: cmd_update: Add quirk 'min_platform_version'

Many device may have some minor difference in early builds, for example
(board id) rev 0 and rev 1 may have GPIO pins connected to different
components. Usually the firmware should read board identifier and do the
right mapping, but sometimes the firmware may be totally incompatible and
no way to workaround (for example even the CPU may be different).

The min_platform_version is introduced so we can prevent updating to
incompatible systems, by reading $(mosys platform version).

BUG=chromium:875551
TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility
BRANCH=None

Change-Id: I418fee1aad884551b38ac25c340b2797b8503596
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1198815
Reviewed-by: Randall Spangler <rspangler@chromium.org>
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index b4f57f7..7e57b97 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -50,6 +50,7 @@
 /* System environment values. */
 static const char * const FWACT_A = "A",
 		  * const FWACT_B = "B",
+		  * const STR_REV = "rev",
 		  * const FLASHROM_OUTPUT_WP_ENABLED =
 			  FLASHROM_OUTPUT_WP_PATTERN "enabled",
 		  * const FLASHROM_OUTPUT_WP_DISABLED =
@@ -108,6 +109,7 @@
 	SYS_PROP_MAINFW_ACT,
 	SYS_PROP_TPM_FWVER,
 	SYS_PROP_FW_VBOOT2,
+	SYS_PROP_PLATFORM_VER,
 	SYS_PROP_WP_HW,
 	SYS_PROP_WP_SW,
 	SYS_PROP_MAX
@@ -124,6 +126,7 @@
 enum quirk_types {
 	QUIRK_ENLARGE_IMAGE,
 	QUIRK_UNLOCK_ME_FOR_UPDATE,
+	QUIRK_MIN_PLATFORM_VERSION,
 	QUIRK_MAX,
 };
 
@@ -287,6 +290,21 @@
 	return VbGetSystemPropertyInt("fw_vboot2");
 }
 
+/* A help function to get $(mosys platform version). */
+static int host_get_platform_version()
+{
+	char *result = host_shell("mosys platform version");
+	int rev = -1;
+
+	/* Result should be 'revN' */
+	if (strncmp(result, STR_REV, strlen(STR_REV)) == 0)
+		rev = strtol(result + strlen(STR_REV), NULL, 0);
+	DEBUG("Raw data = [%s], parsed version is %d", result, rev);
+
+	free(result);
+	return rev;
+}
+
 /*
  * A helper function to invoke flashrom(8) command.
  * Returns 0 if success, non-zero if error.
@@ -1479,6 +1497,26 @@
 	return 0;
 }
 
+/*
+ * Checks and returns 0 if the platform version of current system is larger
+ * or equal to given number, otherwise non-zero.
+ */
+static int quirk_min_platform_version(struct updater_config *cfg)
+{
+	int min_version = get_config_quirk(QUIRK_MIN_PLATFORM_VERSION, cfg);
+	int platform_version = get_system_property(SYS_PROP_PLATFORM_VER, cfg);
+
+	DEBUG("Minimum required version=%d, current platform version=%d",
+	      min_version, platform_version);
+
+	if (platform_version >= min_version)
+		return 0;
+	ERROR("Need platform version >= %d (current is %d). "
+	      "This firmware will only run on newer systems.",
+	      min_version, platform_version);
+	return -1;
+}
+
 enum updater_error_codes {
 	UPDATE_ERR_DONE,
 	UPDATE_ERR_NEED_RO_UPDATE,
@@ -1671,6 +1709,9 @@
 	       image_to->file_name, image_to->ro_version,
 	       image_to->rw_version_a, image_to->rw_version_b);
 
+	if (try_apply_quirk(QUIRK_MIN_PLATFORM_VERSION, cfg))
+		return UPDATE_ERR_PLATFORM;
+
 	if (!image_from->data) {
 		/*
 		 * TODO(hungte) Read only RO_SECTION, VBLOCK_A, VBLOCK_B,
@@ -1794,6 +1835,8 @@
 			[SYS_PROP_MAINFW_ACT] = {.getter = host_get_mainfw_act},
 			[SYS_PROP_TPM_FWVER] = {.getter = host_get_tpm_fwver},
 			[SYS_PROP_FW_VBOOT2] = {.getter = host_get_fw_vboot2},
+			[SYS_PROP_PLATFORM_VER] = {
+				.getter = host_get_platform_version},
 			[SYS_PROP_WP_HW] = {.getter = host_get_wp_hw},
 			[SYS_PROP_WP_SW] = {.getter = host_get_wp_sw},
 		},
@@ -1809,6 +1852,12 @@
 				      "by board-postinst.",
 				.apply=quirk_unlock_me_for_update,
 			},
+			[QUIRK_MIN_PLATFORM_VERSION] = {
+				.name="min_platform_version",
+				.help="Minimum compatible platform version "
+				      "(also known as Board ID version).",
+				.apply=quirk_min_platform_version,
+			},
 		},
 	};
 
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 6902a0c..44530ab 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -137,7 +137,7 @@
 	fi
 }
 
-# --sys_props: mainfw_act, tpm_fwver, is_vboot2, [wp_hw, wp_sw]
+# --sys_props: mainfw_act, tpm_fwver, is_vboot2, platform_ver, [wp_hw, wp_sw]
 # tpm_fwver = <data key version:16><firmware version:16>.
 # TO_IMAGE is signed with data key version = 1, firmware version = 4 => 0x10004.
 
@@ -238,3 +238,13 @@
 	"${FROM_IMAGE}" "${TMP}.expected.me_unlocked" \
 	--quirks unlock_me_for_update \
 	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1
+
+test_update "Full update (failure by --quirks min_platform_version)" \
+	"${FROM_IMAGE}" "!Need platform version >= 3 (current is 2)" \
+	--quirks min_platform_version=3 \
+	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1,2
+
+test_update "Full update (--quirks min_platform_version)" \
+	"${FROM_IMAGE}" "${TMP}.expected.full" \
+	--quirks min_platform_version=3 \
+	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1,3