diff options
author | Hung-Te Lin <hungte@chromium.org> | 2019-11-20 15:50:13 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-22 22:09:23 +0000 |
commit | 10c0559dd45092c26fbab9d2c9663c2d51382bae (patch) | |
tree | 80f9787acae79c0f159e229eb5fd98b9f1b00b8e /futility | |
parent | e8618380056e338d563501e2c9e03e9ff7102cc5 (diff) | |
download | vboot-10c0559dd45092c26fbab9d2c9663c2d51382bae.tar.gz |
futility: updater: move "EC partial recovery" to be a quirk.
The "EC update" (either calling flashrom or using EC RO software sync)
logic has been bloated and is really not a typical updater feature (that
only makes sense for dogfooders). And we have seen enough special cases
that some boards may not want to use it (or causing problems).
Move that to a quirk so we can turn on or off in a more flexible way.
BRANCH=none
BUG=chromium:1024401
TEST=make clean && make runtests
Change-Id: I2d4fe0d9ee0d98ad41b8cbdcaff848846d120d07
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/1926010
Reviewed-by: Joel Kitching <kitching@chromium.org>
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>
Diffstat (limited to 'futility')
-rw-r--r-- | futility/updater.c | 134 | ||||
-rw-r--r-- | futility/updater.h | 8 | ||||
-rw-r--r-- | futility/updater_quirks.c | 154 |
3 files changed, 174 insertions, 122 deletions
diff --git a/futility/updater.c b/futility/updater.c index 92020095..14f7ccfc 100644 --- a/futility/updater.c +++ b/futility/updater.c @@ -923,140 +923,30 @@ static int check_compatible_tpm_keys(struct updater_config *cfg, return 0; } -/* - * 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; - uint8_t *ec_ro_data; - uint32_t ec_ro_len; - int is_same_ec_ro; - struct firmware_section ec_ro_sec; - - if (!tmp_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 (ec_ro_sec.data != cfg->ec_image.data) { - /* http://crbug.com/1024401: EC_RO is not enough. */ - ERROR("EC may need to update data outside EC RO Sync."); - return 1; - } - ec_ro_path = cbfs_extract_file(cfg, tmp_path, FMAP_RO_SECTION, "ecro"); - if (!ec_ro_path || - !cbfs_file_exists(tmp_path, FMAP_RO_SECTION, "ecro.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, 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. - * + * Update EC (RO+RW) firmware. * Returns 0 if success, non-zero if error. */ static int update_ec_firmware(struct updater_config *cfg) { - /* - * http://crbug.com/1024401: Some EC needs extra header outside EC_RO so - * we have to update whole WP_RO, not just EC_RO. - */ - const char *ec_ro = "WP_RO"; struct firmware_image *ec_image = &cfg->ec_image; - - /* 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; - 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"); + int r = try_apply_quirk(QUIRK_EC_PARTIAL_RECOVERY, cfg); + switch (r) { + case EC_RECOVERY_FULL: + return write_optional_firmware(cfg, ec_image, NULL, 1, 0); + + case EC_RECOVERY_RO: + return write_optional_firmware(cfg, ec_image, "WP_RO", 1, 0); + + case EC_RECOVERY_DONE: + /* Done by some quirks, for example EC RO software sync. */ return 0; } - - /* 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); + return r; } const char * const updater_error_messages[] = { diff --git a/futility/updater.h b/futility/updater.h index b74b67f0..91d3286a 100644 --- a/futility/updater.h +++ b/futility/updater.h @@ -42,9 +42,17 @@ enum quirk_types { QUIRK_DAISY_SNOW_DUAL_MODEL, QUIRK_EVE_SMM_STORE, QUIRK_ALLOW_EMPTY_WLTAG, + QUIRK_EC_PARTIAL_RECOVERY, QUIRK_MAX, }; +/* Return values from QUIRK_EC_PARTIAL_RECOVERY. */ +enum { + EC_RECOVERY_FULL = 0, /* Must be 0 as default value of quirks. */ + EC_RECOVERY_RO, + EC_RECOVERY_DONE +}; + struct updater_config { struct firmware_image image, image_current; struct firmware_image ec_image, pd_image; diff --git a/futility/updater_quirks.c b/futility/updater_quirks.c index 9f037ea1..aba07359 100644 --- a/futility/updater_quirks.c +++ b/futility/updater_quirks.c @@ -11,6 +11,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include "crossystem.h" #include "futility.h" #include "host_misc.h" #include "updater.h" @@ -76,6 +77,94 @@ static int reload_firmware_image(const char *file_path, } /* + * 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; + uint8_t *ec_ro_data; + uint32_t ec_ro_len; + int is_same_ec_ro; + struct firmware_section ec_ro_sec; + + if (!tmp_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; + } + ec_ro_path = cbfs_extract_file(cfg, tmp_path, FMAP_RO_SECTION, "ecro"); + if (!ec_ro_path || + !cbfs_file_exists(tmp_path, FMAP_RO_SECTION, "ecro.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); +} + +/* * Quirk to enlarge a firmware image to match flash size. This is needed by * devices using multiple SPI flash with different sizes, for example 8M and * 16M. The image_to will be padded with 0xFF using the size of image_from. @@ -319,6 +408,65 @@ static int quirk_eve_smm_store(struct updater_config *cfg) } /* + * 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, 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: + * EC_RECOVERY_FULL to indicate a full recovery is needed. + * EC_RECOVERY_RO to indicate partial update (WP_RO) is needed. + * EC_RECOVERY_DONE to indicate EC RO software sync is applied. + * Other values to report failure. + */ +static int quirk_ec_partial_recovery(struct updater_config *cfg) +{ + /* + * http://crbug.com/1024401: Some EC needs extra header outside EC_RO so + * we have to update whole WP_RO, not just EC_RO. + */ + const char *ec_ro = "WP_RO"; + struct firmware_image *ec_image = &cfg->ec_image; + + int do_partial = get_config_quirk(QUIRK_EC_PARTIAL_RECOVERY, cfg); + if (do_partial == -1) { + char arch[VB_MAX_STRING_PROPERTY]; + /* + * Don't do partial update if can't decide arch (usually implies + * running outside DUT). + */ + do_partial = 0; + if (VbGetSystemPropertyString("arch", arch, sizeof(arch)) > 0) { + /* By default disabled for x86, otherwise enabled. */ + do_partial = !!strcmp(arch, "x86"); + } + } + + if (!do_partial) { + return EC_RECOVERY_FULL; + } else if (!firmware_section_exists(ec_image, ec_ro)) { + INFO("EC image does not have section '%s'.\n", ec_ro); + /* Need full update. */ + } else if (!is_ec_software_sync_enabled(cfg)) { + /* Message already printed, need full update. */ + } 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 EC_RECOVERY_RO; + } else if (ec_ro_software_sync(cfg) == 0) { + INFO("EC RO and RW should be updated after reboot.\n"); + return EC_RECOVERY_DONE; + } + + WARN("Update EC RO+RW and may cause unexpected error later. " + "See http://crbug.com/782427#c4 for more information.\n"); + return EC_RECOVERY_FULL; +} + +/* * Registers known quirks to a updater_config object. */ void updater_register_quirks(struct updater_config *cfg) @@ -364,6 +512,12 @@ void updater_register_quirks(struct updater_config *cfg) quirks->help = "chromium/906962; allow devices without white label " "tags set to use default keys."; quirks->apply = NULL; /* Simple config. */ + + quirks = &cfg->quirks[QUIRK_EC_PARTIAL_RECOVERY]; + quirks->name = "ec_partial_recovery"; + quirks->help = "chromium/1024401; recover EC by partial RO update."; + quirks->apply = quirk_ec_partial_recovery; + quirks->value = -1; /* Decide at runtime. */ } /* |