summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2018-08-24 12:25:42 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-08-31 11:19:59 -0700
commitad02d85e4fe8c2f96cf7abd3db25eafda056952e (patch)
tree5cb2b64d743a7de8fb3b9ecf82e6c27bcf32a165
parent75e3c5bb621cf26d7fd774a6603e224ad4120f59 (diff)
downloadvboot-ad02d85e4fe8c2f96cf7abd3db25eafda056952e.tar.gz
futility: cmd_update: Implement updater logic "TRY-RW" (--try)
In vboot2, to try one RW (unused) section on next boot, we have to: - Find mainfw_act - Select and update to the "other" slot - Set system property fw_try_{next,count} values to try in next boot. The new '--try' (-t) option can trigger the mode if available. BUG=chromium:875551 TEST=make futil; tests/futility/run_test_scripts.sh $(pwd)/build/futility futility update --emulation FILE -i IMAGE -t --sys_prop 0; # Updates to B. futility update --emulation FILE -i IMAGE -t --sys_prop 1; # Updates to A. BRANCH=None Change-Id: I4b4662616a7181d2f37307238b7b80ae82369768 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1188017 Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r--futility/cmd_update.c117
-rwxr-xr-xtests/futility/test_update.sh15
2 files changed, 129 insertions, 3 deletions
diff --git a/futility/cmd_update.c b/futility/cmd_update.c
index c319275a..529510e5 100644
--- a/futility/cmd_update.c
+++ b/futility/cmd_update.c
@@ -31,8 +31,8 @@ static const char * const FMAP_RO_FRID = "RO_FRID",
* const FMAP_RW_LEGACY = "RW_LEGACY";
/* System environment values. */
-static CONST_STRING FWACT_A = "A",
- FWACT_B = "B";
+static const char * const FWACT_A = "A",
+ * const FWACT_B = "B";
/* flashrom programmers. */
static const char * const PROG_HOST = "host",
@@ -92,6 +92,7 @@ enum system_property_type {
struct updater_config {
struct firmware_image image, image_current;
struct firmware_image ec_image, pd_image;
+ int try_update;
int emulate;
struct system_property system_properties[SYS_PROP_MAX];
};
@@ -442,6 +443,65 @@ static void free_image(struct firmware_image *image)
}
/*
+ * Decides which target in RW firmware to manipulate.
+ * The `target` argument specifies if we want to know "the section to be
+ * update" (TARGET_UPDATE), or "the (active) section * to check" (TARGET_SELF).
+ * Returns the section name if success, otherwise NULL.
+ */
+static const char *decide_rw_target(struct updater_config *cfg,
+ enum target_type target)
+{
+ const char *a = FMAP_RW_SECTION_A, *b = FMAP_RW_SECTION_B;
+ int slot = get_system_property(SYS_PROP_MAINFW_ACT, cfg);
+
+ switch (slot) {
+ case SLOT_A:
+ return target == TARGET_UPDATE ? b : a;
+
+ case SLOT_B:
+ return target == TARGET_UPDATE ? a : b;
+ }
+
+ return NULL;
+}
+
+/*
+ * Sets any needed system properties to indicate system should try the new
+ * firmware on next boot.
+ * The `target` argument is an FMAP section name indicating which to try.
+ * Returns 0 if success, non-zero if error.
+ */
+static int set_try_cookies(struct updater_config *cfg, const char *target)
+{
+ int tries = 6;
+ const char *slot;
+
+ /* EC Software Sync needs few more reboots. */
+ if (cfg->ec_image.data)
+ tries += 2;
+
+ /* Find new slot according to target (section) name. */
+ if (strcmp(target, FMAP_RW_SECTION_A) == 0)
+ slot = FWACT_A;
+ else if (strcmp(target, FMAP_RW_SECTION_B) == 0)
+ slot = FWACT_B;
+ else {
+ Error("%s: Unknown target: %s\n", __FUNCTION__, target);
+ return -1;
+ }
+
+ if (cfg->emulate) {
+ printf("(emulation) Setting try_next to %s, try_count to %d.\n",
+ slot, tries);
+ return 0;
+ }
+
+ RETURN_ON_FAILURE(VbSetSystemPropertyString("fw_try_next", slot));
+ RETURN_ON_FAILURE(VbSetSystemPropertyInt("fw_try_count", tries));
+ return 0;
+}
+
+/*
* Emulates writing to firmware.
* Returns 0 if success, non-zero if error.
*/
@@ -586,21 +646,56 @@ static int is_write_protection_enabled(struct updater_config *cfg)
}
enum updater_error_codes {
UPDATE_ERR_DONE,
+ UPDATE_ERR_NEED_RO_UPDATE,
UPDATE_ERR_NO_IMAGE,
UPDATE_ERR_SYSTEM_IMAGE,
+ UPDATE_ERR_SET_COOKIES,
UPDATE_ERR_WRITE_FIRMWARE,
+ UPDATE_ERR_TARGET,
UPDATE_ERR_UNKNOWN,
};
static const char * const updater_error_messages[] = {
[UPDATE_ERR_DONE] = "Done (no error)",
+ [UPDATE_ERR_NEED_RO_UPDATE] = "RO changed and no WP. Need full update.",
[UPDATE_ERR_NO_IMAGE] = "No image to update; try specify with -i.",
[UPDATE_ERR_SYSTEM_IMAGE] = "Cannot load system active firmware.",
+ [UPDATE_ERR_SET_COOKIES] = "Failed writing system flags to try update.",
[UPDATE_ERR_WRITE_FIRMWARE] = "Failed writing firmware.",
+ [UPDATE_ERR_TARGET] = "Cannot find target section to update.",
[UPDATE_ERR_UNKNOWN] = "Unknown error.",
};
/*
+ * The main updater for "Try-RW update", to update only one RW section
+ * and try if it can boot properly on reboot.
+ * This was also known as --mode=autoupdate,--wp=1 in legacy updater.
+ * Returns UPDATE_ERR_DONE if success, otherwise error.
+ */
+static enum updater_error_codes update_try_rw_firmware(
+ struct updater_config *cfg,
+ struct firmware_image *image_from,
+ struct firmware_image *image_to,
+ int wp_enabled)
+{
+ const char *target;
+
+ /* TODO(hungte): Support vboot1. */
+ target = decide_rw_target(cfg, TARGET_UPDATE);
+ if (!target) {
+ Error("TRY-RW update needs system to boot in RW firmware.\n");
+ return UPDATE_ERR_TARGET;
+ }
+ printf(">> TRY-RW UPDATE: Updating %s to try on reboot.\n", target);
+ if (write_firmware(cfg, image_to, target))
+ return UPDATE_ERR_WRITE_FIRMWARE;
+ if (set_try_cookies(cfg, target))
+ return UPDATE_ERR_SET_COOKIES;
+
+ return UPDATE_ERR_DONE;
+}
+
+/*
* The main updater for "RW update".
* This was also known as --mode=recovery, --wp=1 in legacy updater.
* Returns UPDATE_ERR_DONE if success, otherwise error.
@@ -685,6 +780,15 @@ static enum updater_error_codes update_firmware(struct updater_config *cfg)
if (debugging_enabled)
print_system_properties(cfg);
+ if (cfg->try_update) {
+ enum updater_error_codes r;
+ r = update_try_rw_firmware(cfg, image_from, image_to,
+ wp_enabled);
+ if (r != UPDATE_ERR_NEED_RO_UPDATE)
+ return r;
+ printf("Warning: %s\n", updater_error_messages[r]);
+ }
+
if (wp_enabled)
return update_rw_firmrware(cfg, image_from, image_to);
else
@@ -714,6 +818,7 @@ static struct option const long_opts[] = {
{"image", 1, NULL, 'i'},
{"ec_image", 1, NULL, 'e'},
{"pd_image", 1, NULL, 'P'},
+ {"try", 0, NULL, 't'},
{"wp", 1, NULL, 'W'},
{"emulate", 1, NULL, 'E'},
{"sys_props", 1, NULL, 'S'},
@@ -721,7 +826,7 @@ static struct option const long_opts[] = {
{NULL, 0, NULL, 0},
};
-static const char * const short_opts = "hi:e:";
+static const char * const short_opts = "hi:e:t";
static void print_help(int argc, char *argv[])
{
@@ -731,6 +836,7 @@ static void print_help(int argc, char *argv[])
"-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"
+ "-t, --try \tTry A/B update on reboot if possible\n"
"\n"
"Debugging and testing options:\n"
" --wp=1|0 \tSpecify write protection status\n"
@@ -748,6 +854,7 @@ static int do_update(int argc, char *argv[])
.image_current = { .programmer = PROG_HOST, },
.ec_image = { .programmer = PROG_EC, },
.pd_image = { .programmer = PROG_PD, },
+ .try_update = 0,
.emulate = 0,
.system_properties = {
[SYS_PROP_MAINFW_ACT] = {.getter = host_get_mainfw_act},
@@ -769,6 +876,10 @@ static int do_update(int argc, char *argv[])
break;
case 'P':
errorcnt += load_image(optarg, &cfg.pd_image);
+ break;
+ case 't':
+ cfg.try_update = 1;
+ break;
case 'W':
r = strtol(optarg, NULL, 0);
override_system_property(SYS_PROP_WP_HW, &cfg, r);
diff --git a/tests/futility/test_update.sh b/tests/futility/test_update.sh
index 873a6f56..9e8a6aa5 100755
--- a/tests/futility/test_update.sh
+++ b/tests/futility/test_update.sh
@@ -47,11 +47,17 @@ unpack_image "from" "${FROM_IMAGE}"
# Generate expected results.
cp -f "${TO_IMAGE}" "${TMP}.expected.full"
cp -f "${FROM_IMAGE}" "${TMP}.expected.rw"
+cp -f "${FROM_IMAGE}" "${TMP}.expected.a"
+cp -f "${FROM_IMAGE}" "${TMP}.expected.b"
"${FUTILITY}" load_fmap "${TMP}.expected.rw" \
RW_SECTION_A:${TMP}.to/RW_SECTION_A \
RW_SECTION_B:${TMP}.to/RW_SECTION_B \
RW_SHARED:${TMP}.to/RW_SHARED \
RW_LEGACY:${TMP}.to/RW_LEGACY
+"${FUTILITY}" load_fmap "${TMP}.expected.a" \
+ RW_SECTION_A:${TMP}.to/RW_SECTION_A
+"${FUTILITY}" load_fmap "${TMP}.expected.b" \
+ RW_SECTION_B:${TMP}.to/RW_SECTION_B
test_update() {
local test_name="$1"
@@ -78,3 +84,12 @@ test_update "Full update" \
test_update "RW update" \
"${FROM_IMAGE}" "${TMP}.expected.rw" \
-i "${TO_IMAGE}" --wp=1
+
+# Test Try-RW update (vboot2).
+test_update "RW update (A->B)" \
+ "${FROM_IMAGE}" "${TMP}.expected.b" \
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 0
+
+test_update "RW update (B->A)" \
+ "${FROM_IMAGE}" "${TMP}.expected.a" \
+ -i "${TO_IMAGE}" -t --wp=1 --sys_props 1