diff options
-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. */ } /* |