futility: update: Add '--gbb_flags FLAGS' to override GBB flags

Developers may want to use the new GBB flags when flashing a firmware
image. That can be done by --factory, but it's also more convenient to
have a new parameter for overriding the flags with a new value.

BRANCH=none
BUG=b:166569397
TEST=make runtests

Change-Id: If9dce9b1f2fbb27655ad2a111ba75ab83375fb7a
Signed-off-by: Hung-Te Lin <hungte@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/2382991
Reviewed-by: Julius Werner <jwerner@chromium.org>
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index db29db2..3d99664 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -21,6 +21,7 @@
 	OPT_FACTORY,
 	OPT_FAST,
 	OPT_FORCE,
+	OPT_GBB_FLAGS,
 	OPT_HOST_ONLY,
 	OPT_MANIFEST,
 	OPT_MODEL,
@@ -58,6 +59,7 @@
 	{"factory", 0, NULL, OPT_FACTORY},
 	{"fast", 0, NULL, OPT_FAST},
 	{"force", 0, NULL, OPT_FORCE},
+	{"gbb_flags", 1, NULL, OPT_GBB_FLAGS},
 	{"host_only", 0, NULL, OPT_HOST_ONLY},
 	{"list-quirks", 0, NULL, OPT_QUIRKS_LIST},
 	{"manifest", 0, NULL, OPT_MANIFEST},
@@ -90,6 +92,17 @@
 	printf("\n"
 		"Usage:  " MYNAME " %s [OPTIONS]\n"
 		"\n"
+		"Updates firmware in one of the following modes (default to recovery):\n"
+		"  autoupdate:\tUpdate RW[A|B], or recovery if RO changed.\n"
+		"  recovery:  \tUpdate RW[A&B], (RO, RO:GBB[keys] - if RO changed)\n"
+		"  factory:   \tUpdate RW[A&B],  RO, RO:GBB[keys,flags]\n"
+		"\n"
+		"Note: firmware sections with PRESERVE flags like VPD and\n"
+		"      HWID in GBB are always preserved.\n"
+		"      GBB flags are preserved in autoupdate and recovery modes.\n"
+		"\n"
+		"OPTIONS:\n"
+		"\n"
 		"-i, --image=FILE    \tAP (host) firmware image (image.bin)\n"
 		"-e, --ec_image=FILE \tEC firmware image (i.e, ec.bin)\n"
 		"    --pd_image=FILE \tPD firmware image (i.e, pd.bin)\n"
@@ -102,9 +115,9 @@
 		"    --fast          \tReduce read cycles and do not verify\n"
 		"    --quirks=LIST   \tSpecify the quirks to apply\n"
 		"    --list-quirks   \tPrint all available quirks\n"
+		"-m, --mode=MODE     \tRun updater in specified mode\n"
 		"\n"
 		"Legacy and compatibility options:\n"
-		"-m, --mode=MODE     \tRun updater in given mode\n"
 		"    --factory       \tAlias for --mode=factory\n"
 		"    --force         \tForce update (skip checking contents)\n"
 		"    --output_dir=DIR\tSpecify the target for --mode=output\n"
@@ -114,6 +127,7 @@
 		"    --host_only     \tUpdate only AP (host) firmware\n"
 		"    --emulate=FILE  \tEmulate system firmware using file\n"
 		"    --model=MODEL   \tOverride system model for images\n"
+		"    --gbb_flags=FLAG\tOverride new GBB flags\n"
 		"    --ccd           \tDo fast,force,wp=0,p=raiden_debug_spi\n"
 		"    --servo         \tFlash using Servo (v2, v4, micro, ...)\n"
 		"    --servo_port=PRT\tOverride servod port, implies --servo\n"
@@ -132,6 +146,7 @@
 	int i, errorcnt = 0, do_update = 1;
 	int detect_servo = 0, do_servo_cpu_fw_spi = 0;
 	char *servo_programmer = NULL;
+	char *endptr;
 
 	cfg = updater_new_config();
 	assert(cfg);
@@ -218,6 +233,15 @@
 		case OPT_FAST:
 			args.fast_update = 1;
 			break;
+		case OPT_GBB_FLAGS:
+			args.gbb_flags = strtoul(optarg, &endptr, 0);
+			if (*endptr) {
+				ERROR("Invalid flags: %s\n", optarg);
+				errorcnt++;
+			} else {
+				args.override_gbb_flags = 1;
+			}
+			break;
 		case OPT_CCD:
 			args.fast_update = 1;
 			args.force_update = 1;
diff --git a/futility/updater.c b/futility/updater.c
index a8dfb8d..8048a5b 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -472,7 +472,8 @@
  */
 static int preserve_gbb(const struct firmware_image *image_from,
 			struct firmware_image *image_to,
-			int preserve_flags)
+			int preserve_flags, int override_flags,
+			uint64_t override_value)
 {
 	const struct vb2_gbb_header *gbb_from;
 	struct vb2_gbb_header *gbb_to;
@@ -484,8 +485,10 @@
 	if (!gbb_from || !gbb_to)
 		return -1;
 
-	/* Preserve flags (for non-factory mode). */
-	if (preserve_flags)
+	/* Preserve (for non-factory mode) or override flags. */
+	if (override_flags)
+		gbb_to->flags = override_value;
+	else if (preserve_flags)
 		gbb_to->flags = gbb_from->flags;
 
 	/* Preserve HWID. */
@@ -583,7 +586,8 @@
 	int errcnt = 0, found;
 	struct firmware_image *from = &cfg->image_current, *to = &cfg->image;
 
-	errcnt += preserve_gbb(from, to, !cfg->factory_update);
+	errcnt += preserve_gbb(from, to, !cfg->factory_update,
+			       cfg->override_gbb_flags, cfg->gbb_flags);
 	errcnt += preserve_management_engine(cfg, from, to);
 	errcnt += preserve_fmap_sections(from, to, &found);
 
@@ -998,7 +1002,7 @@
 	int has_update = 1;
 	int is_vboot2 = get_system_property(SYS_PROP_FW_VBOOT2, cfg);
 
-	preserve_gbb(image_from, image_to, 1);
+	preserve_gbb(image_from, image_to, 1, 0, 0);
 	if (!wp_enabled && section_needs_update(
 			image_from, image_to, FMAP_RO_SECTION))
 		return UPDATE_ERR_NEED_RO_UPDATE;
@@ -1502,6 +1506,8 @@
 		check_wp_disabled = 1;
 		cfg->try_update = 0;
 	}
+	cfg->gbb_flags = arg->gbb_flags;
+	cfg->override_gbb_flags = arg->override_gbb_flags;
 
 	/* Setup properties and fields that do not have external dependency. */
 	if (arg->programmer) {
diff --git a/futility/updater.h b/futility/updater.h
index e11cc74..57829cb 100644
--- a/futility/updater.h
+++ b/futility/updater.h
@@ -69,6 +69,8 @@
 	int fast_update;
 	int verbosity;
 	const char *emulation;
+	int override_gbb_flags;
+	uint32_t gbb_flags;
 };
 
 struct updater_config_arguments {
@@ -82,6 +84,8 @@
 	int is_factory, try_update, force_update, do_manifest, host_only;
 	int fast_update;
 	int verbosity;
+	int override_gbb_flags;
+	uint32_t gbb_flags;
 };
 
 struct patch_config {
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 002f105..57b3f46 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -133,6 +133,8 @@
 "${FUTILITY}" gbb -s --flags=0 "${TMP}.expected.full.gbb0"
 cp -f "${FROM_IMAGE}" "${FROM_IMAGE}.gbb0"
 "${FUTILITY}" gbb -s --flags=0 "${FROM_IMAGE}.gbb0"
+cp -f "${TMP}.expected.full" "${TMP}.expected.full.gbb0x27"
+"${FUTILITY}" gbb -s --flags=0x27 "${TMP}.expected.full.gbb0x27"
 cp -f "${TMP}.expected.full" "${TMP}.expected.large"
 dd if=/dev/zero bs=8388608 count=1 | tr '\000' '\377' >>"${TMP}.expected.large"
 cp -f "${TMP}.expected.full" "${TMP}.expected.me_unlocked"
@@ -209,6 +211,10 @@
 	"${FROM_IMAGE}.gbb0" "${TMP}.expected.full.gbb0" \
 	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1
 
+test_update "Full update (GBB flags -> 0x27)" \
+	"${FROM_IMAGE}" "${TMP}.expected.full.gbb0x27" \
+	-i "${TO_IMAGE}" --gbb_flags=0x27 --wp=0 --sys_props 0,0x10001,1
+
 test_update "Full update (--host_only)" \
 	"${FROM_IMAGE}" "${TMP}.expected.full" \
 	-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1 \
@@ -302,7 +308,7 @@
 	"${FROM_IMAGE}" "!remove write protection for factory mode" \
 	--factory -i "${TO_IMAGE}" --wp=1 --sys_props 0,0x10001,1
 
-test_update "Factory mode update (GBB=0 -> 39)" \
+test_update "Factory mode update (GBB=0 -> 0x39)" \
 	"${FROM_IMAGE}.gbb0" "${TMP}.expected.full" \
 	--factory -i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,1