| /* Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * The board-specific quirks needed by firmware updater. |
| */ |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| |
| #include "crossystem.h" |
| #include "futility.h" |
| #include "host_misc.h" |
| #include "updater.h" |
| |
| struct quirks_record { |
| const char * const match; |
| const char * const quirks; |
| }; |
| |
| /* |
| * The 'match by firmware name' is now deprecated. Please do not add any |
| * new records below. We now support reading quirks from CBFS, which is |
| * easier and more reliable. To do that, create a text file 'updater_quirks' |
| * and install to the CBFS. |
| * |
| * Examples: CL:*3365287, CL:*3351831, CL:*4441527 |
| */ |
| static const struct quirks_record quirks_records[] = { |
| { .match = "Google_Whirlwind.", .quirks = "enlarge_image" }, |
| { .match = "Google_Arkham.", .quirks = "enlarge_image" }, |
| { .match = "Google_Storm.", .quirks = "enlarge_image" }, |
| { .match = "Google_Gale.", .quirks = "enlarge_image" }, |
| |
| { .match = "Google_Chell.", .quirks = "unlock_me_for_update" }, |
| { .match = "Google_Lars.", .quirks = "unlock_me_for_update" }, |
| { .match = "Google_Sentry.", .quirks = "unlock_me_for_update" }, |
| { .match = "Google_Asuka.", .quirks = "unlock_me_for_update" }, |
| { .match = "Google_Caroline.", .quirks = "unlock_me_for_update" }, |
| { .match = "Google_Cave.", .quirks = "unlock_me_for_update" }, |
| |
| { .match = "Google_Eve.", |
| .quirks = "unlock_me_for_update,eve_smm_store" }, |
| |
| { .match = "Google_Sarien.", |
| .quirks = "unlock_wilco_me_for_update" }, |
| { .match = "Google_Arcada.", |
| .quirks = "unlock_wilco_me_for_update" }, |
| { .match = "Google_Drallion.", |
| .quirks = "unlock_wilco_me_for_update" }, |
| |
| { .match = "Google_Poppy.", .quirks = "min_platform_version=6" }, |
| { .match = "Google_Scarlet.", .quirks = "min_platform_version=1" }, |
| { .match = "Google_Trogdor.", .quirks = "min_platform_version=2" }, |
| |
| /* Legacy custom label units. */ |
| { .match = "Google_Hana.", .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Reks.", .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Relm.", .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Wizpig.", .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Enguarde.", |
| .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Expresso.", |
| .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Veyron_Jaq.", |
| .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Veyron_Jerry.", |
| .quirks = "allow_empty_custom_label_tag" }, |
| { .match = "Google_Veyron_Mighty.", |
| .quirks = "allow_empty_custom_label_tag" }, |
| |
| { .match = "Google_Phaser.", .quirks = "override_signature_id" }, |
| }; |
| |
| /* Preserves meta data and reload image contents from given file path. */ |
| static int reload_firmware_image(const char *file_path, |
| struct firmware_image *image) |
| { |
| free_firmware_image(image); |
| return load_firmware_image(image, file_path, NULL); |
| } |
| |
| /* |
| * 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 *ec_ro_path; |
| uint8_t *ec_ro_data; |
| uint32_t ec_ro_len; |
| int is_same_ec_ro; |
| struct firmware_section ec_ro_sec; |
| const char *tmp_path = get_firmware_image_temp_file( |
| &cfg->image, &cfg->tempfiles); |
| |
| if (!tmp_path) |
| 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(tmp_path, FMAP_RO_CBFS, "ecro", |
| &cfg->tempfiles); |
| if (!ec_ro_path || |
| !cbfs_file_exists(tmp_path, FMAP_RO_CBFS, "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. |
| * Returns 0 on success, otherwise failure. |
| */ |
| static int quirk_enlarge_image(struct updater_config *cfg) |
| { |
| struct firmware_image *image_from = &cfg->image_current, |
| *image_to = &cfg->image; |
| const char *tmp_path; |
| size_t to_write; |
| FILE *fp; |
| |
| if (image_from->size <= image_to->size) |
| return 0; |
| |
| tmp_path = get_firmware_image_temp_file(image_to, &cfg->tempfiles); |
| if (!tmp_path) |
| return -1; |
| |
| VB2_DEBUG("Resize image from %u to %u.\n", |
| image_to->size, image_from->size); |
| to_write = image_from->size - image_to->size; |
| fp = fopen(tmp_path, "ab"); |
| if (!fp) { |
| ERROR("Cannot open temporary file %s.\n", tmp_path); |
| return -1; |
| } |
| while (to_write-- > 0) |
| fputc('\xff', fp); |
| fclose(fp); |
| return reload_firmware_image(tmp_path, image_to); |
| } |
| |
| /* |
| * Quirk to unlock a firmware image with SI_ME (management engine) when updating |
| * so the system has a chance to make sure SI_ME won't be corrupted on next boot |
| * before locking the Flash Master values in SI_DESC. |
| * Returns 0 on success, otherwise failure. |
| */ |
| static int quirk_unlock_me_for_update(struct updater_config *cfg) |
| { |
| struct firmware_section section; |
| struct firmware_image *image_to = &cfg->image; |
| const int flash_master_offset = 128; |
| const uint8_t flash_master[] = { |
| 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, |
| 0xff, 0xff |
| }; |
| |
| find_firmware_section(§ion, image_to, FMAP_SI_DESC); |
| if (section.size < flash_master_offset + ARRAY_SIZE(flash_master)) |
| return 0; |
| if (memcmp(section.data + flash_master_offset, flash_master, |
| ARRAY_SIZE(flash_master)) == 0) { |
| VB2_DEBUG("Target ME not locked.\n"); |
| return 0; |
| } |
| /* |
| * b/35568719: We should only update with unlocked ME and let |
| * board-postinst lock it. |
| */ |
| INFO("Changed Flash Master Values to unlocked.\n"); |
| memcpy(section.data + flash_master_offset, flash_master, |
| ARRAY_SIZE(flash_master)); |
| return 0; |
| } |
| |
| /* |
| * Quirk to unlock a firmware image with SI_ME (management engine) when updating |
| * so the system has a chance to make sure SI_ME won't be corrupted on next boot |
| * before locking the Flash Master values in SI_DESC. |
| * Returns 0 on success, otherwise failure. |
| */ |
| static int quirk_unlock_wilco_me_for_update(struct updater_config *cfg) |
| { |
| struct firmware_section section; |
| struct firmware_image *image_to = &cfg->image; |
| const int flash_master_offset = 128; |
| const uint8_t flash_master[] = { |
| 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, |
| 0xff, 0xff |
| }; |
| |
| find_firmware_section(§ion, image_to, FMAP_SI_DESC); |
| if (section.size < flash_master_offset + ARRAY_SIZE(flash_master)) |
| return 0; |
| if (memcmp(section.data + flash_master_offset, flash_master, |
| ARRAY_SIZE(flash_master)) == 0) { |
| VB2_DEBUG("Target ME not locked.\n"); |
| return 0; |
| } |
| INFO("Changed Flash Master Values to unlocked.\n"); |
| memcpy(section.data + flash_master_offset, flash_master, |
| ARRAY_SIZE(flash_master)); |
| return 0; |
| } |
| |
| /* |
| * Checks and returns 0 if the platform version of current system is larger |
| * or equal to given number, otherwise non-zero. |
| */ |
| static int quirk_min_platform_version(struct updater_config *cfg) |
| { |
| int min_version = get_config_quirk(QUIRK_MIN_PLATFORM_VERSION, cfg); |
| int platform_version = get_system_property(SYS_PROP_PLATFORM_VER, cfg); |
| |
| VB2_DEBUG("Minimum required version=%d, current platform version=%d\n", |
| min_version, platform_version); |
| |
| if (platform_version >= min_version) |
| return 0; |
| ERROR("Need platform version >= %d (current is %d). " |
| "This firmware will only run on newer systems.\n", |
| min_version, platform_version); |
| return -1; |
| } |
| |
| /* |
| * Quirk to help preserving SMM store on devices without a dedicated "SMMSTORE" |
| * FMAP section. These devices will store "smm_store" file in same CBFS where |
| * the legacy boot loader lives (i.e, FMAP RW_LEGACY). |
| * Note this currently has dependency on external program "cbstool". |
| * Returns 0 if the SMM store is properly preserved, or if the system is not |
| * available to do that (problem in cbfstool, or no "smm_store" in current |
| * system firmware). Otherwise non-zero as failure. |
| */ |
| static int quirk_eve_smm_store(struct updater_config *cfg) |
| { |
| const char *smm_store_name = "smm_store"; |
| const char *old_store; |
| char *command; |
| const char *temp_image = get_firmware_image_temp_file( |
| &cfg->image_current, &cfg->tempfiles); |
| |
| if (!temp_image) |
| return -1; |
| |
| old_store = cbfs_extract_file(temp_image, FMAP_RW_LEGACY, |
| smm_store_name, &cfg->tempfiles); |
| if (!old_store) { |
| VB2_DEBUG("cbfstool failure or SMM store not available. " |
| "Don't preserve.\n"); |
| return 0; |
| } |
| |
| /* Reuse temp_image */ |
| temp_image = get_firmware_image_temp_file(&cfg->image, &cfg->tempfiles); |
| if (!temp_image) |
| return -1; |
| |
| /* crosreview.com/1165109: The offset is fixed at 0x1bf000. */ |
| ASPRINTF(&command, |
| "cbfstool \"%s\" remove -r %s -n \"%s\" 2>/dev/null; " |
| "cbfstool \"%s\" add -r %s -n \"%s\" -f \"%s\" " |
| " -t raw -b 0x1bf000", temp_image, FMAP_RW_LEGACY, |
| smm_store_name, temp_image, FMAP_RW_LEGACY, |
| smm_store_name, old_store); |
| free(host_shell(command)); |
| free(command); |
| |
| return reload_firmware_image(temp_image, &cfg->image); |
| } |
| |
| /* |
| * 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. |
| * |
| * Note: EC RO software sync was not fully tested and may cause problems |
| * (b/218612817, b/187789991). |
| * RO-update (without extra sysjump) needs support from flashrom and is |
| * currently disabled. |
| * |
| * 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) { |
| /* Need full update. */ |
| } 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; |
| } |
| |
| /* |
| * Preserve ME during firmware update. |
| * |
| * Updating ME region while SoC is in S0 state is an unsupported use-case. On |
| * recent platforms, we are seeing issues more frequently because of this use- |
| * case. For the firmware updates performed using firmware update archive, |
| * preserve the ME region so that it gets updated in the successive boot. |
| * |
| * Returns: |
| * 1 to signal ME needs to be preserved. |
| * 0 to signal ME does not need to be preserved. |
| */ |
| static int quirk_preserve_me(struct updater_config *cfg) |
| { |
| /* For a factory update donot preserve ME. */ |
| if (cfg->factory_update) { |
| WARN("Factory update. Not preserving ME.\n"); |
| return 0; |
| } |
| |
| /* |
| * For a non-archive update, the post boot script that updates ME |
| * does not have access to the firmware image after the boot. Hence |
| * donot preserve ME. |
| */ |
| if (!cfg->archive) { |
| WARN("Update using a non-archive image. Not preserving ME.\n"); |
| return 0; |
| } |
| |
| /* |
| * b/213706510: subratabanik@ confirmed CSE may modify itself while we |
| * are doing system update, and currently the 'preserve' is done by |
| * flashing the same (e.g., "previously read") contents to skip erasing |
| * and writing; so we have to use the diff image to prevent contents |
| * being changed when writing. |
| */ |
| cfg->use_diff_image = 1; |
| |
| return 1; |
| } |
| |
| /* |
| * Disable checking platform compatibility. |
| */ |
| static int quirk_no_check_platform(struct updater_config *cfg) |
| { |
| WARN("Disabled checking platform. You are on your own.\n"); |
| cfg->check_platform = 0; |
| return 0; |
| } |
| |
| /* |
| * Disable verifying contents after flashing. |
| */ |
| static int quirk_no_verify(struct updater_config *cfg) |
| { |
| WARN("Disabled verifying flashed contents. You are on your own.\n"); |
| cfg->do_verify = 0; |
| return 0; |
| } |
| |
| /* |
| * Registers known quirks to a updater_config object. |
| */ |
| void updater_register_quirks(struct updater_config *cfg) |
| { |
| struct quirk_entry *quirks; |
| |
| assert(ARRAY_SIZE(cfg->quirks) == QUIRK_MAX); |
| quirks = &cfg->quirks[QUIRK_ENLARGE_IMAGE]; |
| quirks->name = "enlarge_image"; |
| quirks->help = "Enlarge firmware image by flash size."; |
| quirks->apply = quirk_enlarge_image; |
| |
| quirks = &cfg->quirks[QUIRK_MIN_PLATFORM_VERSION]; |
| quirks->name = "min_platform_version"; |
| quirks->help = "Minimum compatible platform version " |
| "(also known as Board ID version)."; |
| quirks->apply = quirk_min_platform_version; |
| |
| quirks = &cfg->quirks[QUIRK_UNLOCK_WILCO_ME_FOR_UPDATE]; |
| quirks->name = "unlock_wilco_me_for_update"; |
| quirks->help = "Unlock ME for safe lockdown."; |
| quirks->apply = quirk_unlock_wilco_me_for_update; |
| |
| quirks = &cfg->quirks[QUIRK_UNLOCK_ME_FOR_UPDATE]; |
| quirks->name = "unlock_me_for_update"; |
| quirks->help = "b/35568719; only lock management engine in " |
| "board-postinst."; |
| quirks->apply = quirk_unlock_me_for_update; |
| |
| quirks = &cfg->quirks[QUIRK_EVE_SMM_STORE]; |
| quirks->name = "eve_smm_store"; |
| quirks->help = "b/70682365; preserve UEFI SMM store without " |
| "dedicated FMAP section."; |
| quirks->apply = quirk_eve_smm_store; |
| |
| quirks = &cfg->quirks[QUIRK_ALLOW_EMPTY_CUSTOM_LABEL_TAG]; |
| quirks->name = "allow_empty_custom_label_tag"; |
| quirks->help = "chromium/906962; allow devices without custom 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 = &cfg->quirks[QUIRK_OVERRIDE_SIGNATURE_ID]; |
| quirks->name = "override_signature_id"; |
| quirks->help = "chromium/146876241; override signature id for " |
| "devices shipped with different root key."; |
| quirks->apply = NULL; /* Simple config. */ |
| |
| quirks = &cfg->quirks[QUIRK_PRESERVE_ME]; |
| quirks->name = "preserve_me"; |
| quirks->help = "b/165590952; Preserve ME during firmware update except " |
| "for factory update or developer images."; |
| quirks->apply = quirk_preserve_me; |
| |
| quirks = &cfg->quirks[QUIRK_NO_CHECK_PLATFORM]; |
| quirks->name = "no_check_platform"; |
| quirks->help = "Do not check platform name."; |
| quirks->apply = quirk_no_check_platform; |
| |
| quirks = &cfg->quirks[QUIRK_NO_VERIFY]; |
| quirks->name = "no_verify"; |
| quirks->help = "Do not verify when flashing."; |
| quirks->apply = quirk_no_verify; |
| |
| quirks = &cfg->quirks[QUIRK_EXTRA_RETRIES]; |
| quirks->name = "extra_retries"; |
| quirks->help = "Extra retries when writing to system firmware."; |
| quirks->apply = NULL; /* Simple config. */ |
| |
| quirks = &cfg->quirks[QUIRK_EXTERNAL_FLASHROM]; |
| quirks->name = "external_flashrom"; |
| quirks->help = "Use external flashrom to access the system firmware."; |
| quirks->apply = NULL; /* Simple config. */ |
| } |
| |
| /* |
| * Gets the default quirk config string from target image name. |
| * Returns a string (in same format as --quirks) to load or NULL if no quirks. |
| */ |
| const char * const updater_get_model_quirks(struct updater_config *cfg) |
| { |
| const char *pattern = cfg->image.ro_version; |
| int i; |
| |
| if (!pattern) { |
| VB2_DEBUG("Cannot identify system for default quirks.\n"); |
| return NULL; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(quirks_records); i++) { |
| const struct quirks_record *r = &quirks_records[i]; |
| if (strncmp(r->match, pattern, strlen(r->match)) != 0) |
| continue; |
| VB2_DEBUG("Found system default quirks: %s\n", r->quirks); |
| return r->quirks; |
| } |
| return NULL; |
| } |
| |
| /* |
| * Gets the quirk config string from target image CBFS. |
| * Returns a string (in same format as --quirks) to load or NULL if no quirks. |
| */ |
| char *updater_get_cbfs_quirks(struct updater_config *cfg) |
| { |
| const char *entry_name = "updater_quirks"; |
| const char *cbfs_region = "FW_MAIN_A"; |
| struct firmware_section cbfs_section; |
| |
| /* Before invoking cbfstool, try to search for CBFS file name. */ |
| find_firmware_section(&cbfs_section, &cfg->image, cbfs_region); |
| if (!cbfs_section.size || !memmem(cbfs_section.data, cbfs_section.size, |
| entry_name, strlen(entry_name))) { |
| if (!cbfs_section.size) |
| VB2_DEBUG("Missing region: %s\n", cbfs_region); |
| else |
| VB2_DEBUG("Cannot find entry: %s\n", entry_name); |
| return NULL; |
| } |
| |
| const char *tmp_path = get_firmware_image_temp_file( |
| &cfg->image, &cfg->tempfiles); |
| uint8_t *data = NULL; |
| uint32_t size = 0; |
| |
| /* Although the name exists, it may not be a real file. */ |
| if (!cbfs_file_exists(tmp_path, cbfs_region, entry_name)) { |
| VB2_DEBUG("Found string '%s' but not a file.\n", entry_name); |
| return NULL; |
| } |
| |
| VB2_DEBUG("Found %s from CBFS %s\n", entry_name, cbfs_region); |
| tmp_path = cbfs_extract_file(tmp_path, cbfs_region, entry_name, |
| &cfg->tempfiles); |
| if (!tmp_path || |
| vb2_read_file(tmp_path, &data, &size) != VB2_SUCCESS) { |
| ERROR("Failed to read [%s] from CBFS [%s].\n", |
| entry_name, cbfs_region); |
| return NULL; |
| } |
| VB2_DEBUG("Got quirks (%u bytes): %s\n", size, data); |
| return (char *)data; |
| } |
| |
| /* |
| * Overrides signature id if the device was shipped with known |
| * special rootkey. |
| */ |
| int quirk_override_signature_id(struct updater_config *cfg, |
| struct model_config *model, |
| const char **signature_id) |
| { |
| const char * const DOPEFISH_KEY_HASH = |
| "9a1f2cc319e2f2e61237dc51125e35ddd4d20984"; |
| |
| /* b/146876241 */ |
| assert(model); |
| if (strcmp(model->name, "phaser360") == 0) { |
| struct firmware_image *image = &cfg->image_current; |
| const char *key_hash = get_firmware_rootkey_hash(image); |
| if (key_hash && strcmp(key_hash, DOPEFISH_KEY_HASH) == 0) { |
| const char * const sig_dopefish = "phaser360-dopefish"; |
| WARN("A Phaser360 with Dopefish rootkey - " |
| "override signature_id to '%s'.\n", sig_dopefish); |
| *signature_id = sig_dopefish; |
| } |
| } |
| |
| return 0; |
| } |