summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2019-10-07 11:34:20 +0800
committerCommit Bot <commit-bot@chromium.org>2019-10-29 17:09:42 +0000
commit6d43a1925a3f88b4fb8ac3fc9dcfc42ccd934063 (patch)
treeeb763e629022537e3b7702e23b234605feb9593b
parent8f0989e7bfc7f56a40f1dede727999934a6dc6e0 (diff)
downloadvboot-6d43a1925a3f88b4fb8ac3fc9dcfc42ccd934063.tar.gz
futility: update: Try EC-RO software sync if available
For devices with EC that will reset TCPC when updating RO, dogfood units without write protection will always see failure in recovery process. To fix this, we want to apply EC RO software sync if available. BRANCH=None BUG=b:141965252 TEST=Boot device in recovery mode, same EC RO and run futility -i image.bin -e ec.bin # see EC RO in next boot Change-Id: I60552facc059b894a4922738207fd885008cb40c Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1864534 Reviewed-by: Hung-Te Lin <hungte@chromium.org> Tested-by: Hung-Te Lin <hungte@chromium.org> Commit-Queue: Hung-Te Lin <hungte@chromium.org> Auto-Submit: Hung-Te Lin <hungte@chromium.org>
-rw-r--r--futility/updater.c195
1 files changed, 154 insertions, 41 deletions
diff --git a/futility/updater.c b/futility/updater.c
index 18fc339f..a360f2c8 100644
--- a/futility/updater.c
+++ b/futility/updater.c
@@ -893,16 +893,12 @@ static int write_firmware(struct updater_config *cfg,
}
/*
- * Write a section from given firmware image to system firmware if possible.
- * If section_name is NULL, write whole image. If the image has no data or if
- * the section does not exist, ignore and return success.
- * Returns 0 if success, non-zero if error.
+ * Returns True if we should start the update process for given image.
*/
-static int write_optional_firmware(struct updater_config *cfg,
- const struct firmware_image *image,
- const char *section_name,
- int check_programmer_wp,
- int is_host)
+static int has_valid_update(struct updater_config *cfg,
+ const struct firmware_image *image,
+ const char *section_name,
+ int is_host)
{
if (!image->data) {
VB2_DEBUG("No data in <%s> image.\n", image->programmer);
@@ -921,7 +917,23 @@ static int write_optional_firmware(struct updater_config *cfg,
image->file_name, image->programmer, image->size);
return 0;
}
+ return 1;
+}
+/*
+ * Write a section from given firmware image to system firmware if possible.
+ * If section_name is NULL, write whole image. If the image has no data or if
+ * the section does not exist, ignore and return success.
+ * Returns 0 if success, non-zero if error.
+ */
+static int write_optional_firmware(struct updater_config *cfg,
+ const struct firmware_image *image,
+ const char *section_name,
+ int check_programmer_wp,
+ int is_host)
+{
+ if (!has_valid_update(cfg, image, section_name, is_host))
+ return 0;
/*
* EC & PD may have different WP settings and we want to write
* only if it is OK.
@@ -1395,6 +1407,23 @@ static int cbfs_file_exists(const char *image_file,
return !r;
}
+static int cbfs_extract_file(const char *image_file,
+ const char *section_name,
+ const char *cbfs_entry_name,
+ const char *output_name)
+{
+ char *cmd;
+ int r;
+
+ ASPRINTF(&cmd,
+ "cbfstool '%s' extract -r %s -n '%s' -f '%s' "
+ "2>/dev/null", image_file, section_name, cbfs_entry_name,
+ output_name);
+ r = system(cmd);
+ free(cmd);
+ return r;
+}
+
/*
* Returns non-zero if the RW_LEGACY needs to be updated, otherwise 0.
*/
@@ -1488,44 +1517,129 @@ static int check_compatible_tpm_keys(struct updater_config *cfg,
}
/*
- * Returns the name of EC section to update in recovery (whole update).
+ * Returns True if the system has EC software sync enabled.
+ */
+static int is_ec_software_sync_enabled(struct updater_config *cfg)
+{
+ const struct vb2_gbb_header *gbb;
+
+ /* Check if current system has disabled software sync or no support. */
+ if (!(VbGetSystemPropertyInt("vdat_flags") & VBSD_EC_SOFTWARE_SYNC)) {
+ INFO("EC Software Sync is not available.\n");
+ return 0;
+ }
+
+ /* Check if the system has been updated to disable software sync. */
+ gbb = find_gbb(&cfg->image);
+ if (!gbb) {
+ WARN("Invalid AP firmware image.\n");
+ return 0;
+ }
+ if (gbb->flags & VB2_GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC) {
+ INFO("EC Software Sync will be disabled in next boot.\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Schedules an EC RO software sync (in next boot) if applicable.
+ */
+static int ec_ro_software_sync(struct updater_config *cfg)
+{
+ const char *tmp_path = updater_create_temp_file(cfg);
+ const char *ec_ro_path = updater_create_temp_file(cfg);
+ uint8_t *ec_ro_data;
+ uint32_t ec_ro_len;
+ int is_same_ec_ro;
+ struct firmware_section ec_ro_sec;
+
+ if (!tmp_path || !ec_ro_path ||
+ vb2_write_file(tmp_path, cfg->image.data, cfg->image.size)) {
+ ERROR("Failed to create temporary file for image contents.\n");
+ return 1;
+ }
+ find_firmware_section(&ec_ro_sec, &cfg->ec_image, "EC_RO");
+ if (!ec_ro_sec.data || !ec_ro_sec.size) {
+ ERROR("EC image has invalid section '%s'.\n", "EC_RO");
+ return 1;
+ }
+ if (cbfs_extract_file(tmp_path, FMAP_RO_SECTION, "ec_ro", ec_ro_path) ||
+ !cbfs_file_exists(tmp_path, FMAP_RO_SECTION, "ec_ro.hash")) {
+ INFO("No valid EC RO for software sync in AP firmware.\n");
+ return 1;
+ }
+ if (vb2_read_file(ec_ro_path, &ec_ro_data, &ec_ro_len) != VB2_SUCCESS) {
+ ERROR("Failed to read EC RO.\n");
+ return 1;
+ }
+
+ is_same_ec_ro = (ec_ro_len <= ec_ro_sec.size &&
+ memcmp(ec_ro_sec.data, ec_ro_data, ec_ro_len) == 0);
+ free(ec_ro_data);
+
+ if (!is_same_ec_ro) {
+ /* TODO(hungte) If change AP RO is not a problem (hash will be
+ * different, which may be a problem to factory and HWID), or if
+ * we can be be sure this is for developers, extract EC RO and
+ * update AP RO CBFS to trigger EC RO sync with new EC.
+ */
+ ERROR("The EC RO contents specified from AP (--image) and EC "
+ "(--ec_image) firmware images are different, cannot "
+ "update by EC RO software sync.\n");
+ return 1;
+ }
+ VbSetSystemPropertyInt("try_ro_sync", 1);
+ return 0;
+}
+
+/*
+ * Returns True if EC is running in RW.
+ */
+static int is_ec_in_rw(void)
+{
+ char buf[VB_MAX_STRING_PROPERTY];
+ return (VbGetSystemPropertyString("ecfw_act", buf, sizeof(buf)) &&
+ strcasecmp(buf, "RW") == 0);
+}
+
+/*
+ * Update EC (RO+RW) in most reliable way.
*
* Some EC will reset TCPC when doing sysjump, and will make rootfs unavailable
- * if the system was boot from USB. There is no solution if EC already in RO
- * (recovery). But for developers who run updater manually in developer mode
- * (Ctrl-U, EC in RW) and have software sync enabled, we may update only EC RO
- * (skip RW).
+ * if the system was boot from USB, or other unexpected issues even if the
+ * system was boot from internal disk. To prevent that, try to partial update
+ * only RO and expect EC software sync to update RW later, or perform EC RO
+ * software sync.
+ *
+ * Returns 0 if success, non-zero if error.
*/
-static const char * get_ec_section_to_recover(struct updater_config *cfg)
+static int update_ec_firmware(struct updater_config *cfg)
{
- const char * const ec_ro = "EC_RO";
- char buf[VB_MAX_STRING_PROPERTY];
+ const char *ec_ro = "EC_RO";
+ struct firmware_image *ec_image = &cfg->ec_image;
- /* For devices without Chrome OS EC image, update all. */
- if (!cfg->ec_image.data ||
- !firmware_section_exists(&cfg->ec_image, ec_ro))
- return NULL;
+ /* TODO(hungte) Check if we have EC RO in AP image without --ec_image */
+ if (!has_valid_update(cfg, ec_image, NULL, 0))
+ return 0;
- /* For devices where EC is not running in RW, try to update all. */
- if (!VbGetSystemPropertyString("ecfw_act", buf, sizeof(buf)) ||
- strcasecmp(buf, "RW") != 0) {
- WARN("EC is not running in RW so updating RO may cause sysjump "
- "and you may see 'Input/output error' after that. "
- "See http://crbug.com/782427#c4 for more information.\n");
- return NULL;
+ if (!firmware_section_exists(ec_image, ec_ro)) {
+ INFO("EC image does not have section '%s'.\n", ec_ro);
+ } else if (!is_ec_software_sync_enabled(cfg)) {
+ /* Message already printed. */
+ } else if (is_ec_in_rw()) {
+ WARN("EC Software Sync detected, will only update EC RO. "
+ "The contents in EC RW will be updated after reboot.\n");
+ return write_optional_firmware(cfg, ec_image, ec_ro, 1, 0);
+ } else if (ec_ro_software_sync(cfg) == 0) {
+ INFO("EC RO and RW should be updated after reboot.\n");
+ return 0;
}
- /* If software sync is disabled or not available, try to update all. */
- if (!(VbGetSystemPropertyInt("vdat_flags") & VBSD_EC_SOFTWARE_SYNC))
- return NULL;
-
- /*
- * This is the only case that we "may" safely update EC RO without
- * sysjump, and have RW updated in next boot.
- */
- WARN("EC Software Sync detected, will only update EC RO. "
- "The contents in EC RW will be updated after reboot.\n");
- return ec_ro;
+ /* Do full update. */
+ WARN("Update EC RO+RW and may cause unexpected error later. "
+ "See http://crbug.com/782427#c4 for more information.\n");
+ return write_optional_firmware(cfg, ec_image, NULL, 1, 0);
}
const char * const updater_error_messages[] = {
@@ -1712,8 +1826,7 @@ static enum updater_error_codes update_whole_firmware(
/* FMAP may be different so we should just update all. */
if (write_firmware(cfg, image_to, NULL) ||
- write_optional_firmware(cfg, &cfg->ec_image,
- get_ec_section_to_recover(cfg), 1, 0) ||
+ update_ec_firmware(cfg) ||
write_optional_firmware(cfg, &cfg->pd_image, NULL, 1, 0))
return UPDATE_ERR_WRITE_FIRMWARE;