/* Fill in active EC firmware information. */ static int set_active_ec_firmware(crossystem_data_t* cdata) { int in_rw = 0; int rv; /* If software sync is disabled, just leave this as original value. */ if (!cros_fdtdec_config_has_prop(gd->fdt_blob, "ec-software-sync")) { cdata->active_ec_firmware = ACTIVE_EC_FIRMWARE_UNCHANGE; return 0; } rv = VbExEcRunningRW(&in_rw); if (rv != VBERROR_SUCCESS) return rv; cdata->active_ec_firmware = (in_rw ? ACTIVE_EC_FIRMWARE_RW : ACTIVE_EC_FIRMWARE_RO); return 0; }
int crossystem_setup(void) { chromeos_acpi_t *acpi_table = (chromeos_acpi_t *)lib_sysinfo.vdat_addr; VbSharedDataHeader *vboot_handoff_shared_data; VbSharedDataHeader *vdat = (VbSharedDataHeader *)&acpi_table->vdat; int size; if (vdat->magic != VB_SHARED_DATA_MAGIC) { printf("Bad magic value in vboot shared data header.\n"); return 1; } acpi_table->boot_reason = BOOT_REASON_OTHER; int main_fw; const char *fwid; int fwid_size; int fw_index = vdat->firmware_index; fwid = get_fw_id(fw_index); if (fwid == NULL) { printf("Unrecognized firmware index %d.\n", fw_index); return 1; } fwid_size = get_fw_size(fw_index); const struct { int vdat_fw_index; int main_fw_index; } main_fw_arr[] = { { VDAT_RW_A, BINF_RW_A }, { VDAT_RW_B, BINF_RW_B }, { VDAT_RECOVERY, BINF_RECOVERY }, }; int i; for (i = 0; i < ARRAY_SIZE(main_fw_arr); i++) { if (fw_index == main_fw_arr[i].vdat_fw_index) { main_fw = main_fw_arr[i].main_fw_index; break; } } assert(i < ARRAY_SIZE(main_fw_arr)); acpi_table->main_fw = main_fw; // Use the value set by coreboot if we don't want to change it. if (CONFIG_EC_SOFTWARE_SYNC) { int in_rw = 0; if (VbExEcRunningRW(0, &in_rw)) { printf("Couldn't tell if the EC firmware is RW.\n"); return 1; } acpi_table->ec_fw = in_rw ? ACTIVE_ECFW_RW : ACTIVE_ECFW_RO; } uint16_t chsw = 0; if (flag_fetch(FLAG_WPSW)) chsw |= CHSW_FIRMWARE_WP_DIS; if (flag_fetch(FLAG_RECSW)) chsw |= CHSW_RECOVERY_X86; if (flag_fetch(FLAG_DEVSW)) chsw |= CHSW_DEVELOPER_SWITCH; acpi_table->chsw = chsw; GoogleBinaryBlockHeader *gbb = cparams.gbb_data; if (memcmp(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE)) { printf("Bad signature on GBB.\n"); return 1; } char *hwid = (char *)((uintptr_t)cparams.gbb_data + gbb->hwid_offset); size = MIN(gbb->hwid_size, sizeof(acpi_table->hwid)); memcpy(acpi_table->hwid, hwid, size); size = MIN(fwid_size, sizeof(acpi_table->fwid)); memcpy(acpi_table->fwid, fwid, size); size = get_ro_fw_size(); if (size) { size = MIN(size, sizeof(acpi_table->frid)); memcpy(acpi_table->frid, get_ro_fw_id(), size); } if (main_fw == BINF_RECOVERY) acpi_table->main_fw_type = FIRMWARE_TYPE_RECOVERY; else if (vdat->flags & VBSD_BOOT_DEV_SWITCH_ON) acpi_table->main_fw_type = FIRMWARE_TYPE_DEVELOPER; else acpi_table->main_fw_type = FIRMWARE_TYPE_NORMAL; acpi_table->recovery_reason = vdat->recovery_reason; acpi_table->fmap_base = (uintptr_t)fmap_base(); size = MIN(fwid_size, strnlen(fwid, ACPI_FWID_SIZE)); uint8_t *dest = (uint8_t *)(uintptr_t)acpi_table->fwid_ptr; memcpy(dest, fwid, size); dest[size] = 0; // Synchronize the value in vboot_handoff back to acpi vdat. if (find_common_params((void**)(&vboot_handoff_shared_data), &size) == 0) memcpy(vdat, vboot_handoff_shared_data, size); else { printf("Can't find common params.\n"); return 1; } return 0; }
VbError_t VbEcSoftwareSync(int devidx, VbCommonParams *cparams) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; int in_rw = 0; int rv; const uint8_t *ec_hash = NULL; int ec_hash_size; const uint8_t *rw_hash = NULL; int rw_hash_size; const uint8_t *expected = NULL; int expected_size; uint8_t expected_hash[SHA256_DIGEST_SIZE]; int need_update = 0; int i; VBDEBUG(("VbEcSoftwareSync(devidx=%d)\n", devidx)); /* Determine whether the EC is in RO or RW */ rv = VbExEcRunningRW(devidx, &in_rw); if (shared->recovery_reason) { /* Recovery mode; just verify the EC is in RO code */ if (rv == VBERROR_SUCCESS && in_rw == 1) { /* * EC is definitely in RW firmware. We want it in * read-only code, so preserve the current recovery * reason and reboot. * * We don't reboot on error or unknown EC code, because * we could end up in an endless reboot loop. If we * had some way to track that we'd already rebooted for * this reason, we could retry only once. */ VBDEBUG(("VbEcSoftwareSync() - " "want recovery but got EC-RW\n")); VbSetRecoveryRequest(shared->recovery_reason); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("VbEcSoftwareSync() in recovery; EC-RO\n")); return VBERROR_SUCCESS; } /* * Not in recovery. If we couldn't determine where the EC was, * reboot to recovery. */ if (rv != VBERROR_SUCCESS) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcRunningRW() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_UNKNOWN_IMAGE); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } /* If AP is read-only normal, EC should be in its RO code also. */ if (shared->flags & VBSD_LF_USE_RO_NORMAL) { /* If EC is in RW code, request reboot back to RO */ if (in_rw == 1) { VBDEBUG(("VbEcSoftwareSync() - " "want RO-normal but got EC-RW\n")); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } /* Protect the RW flash and stay in EC-RO */ rv = EcProtectRW(devidx); if (rv != VBERROR_SUCCESS) return rv; rv = VbExEcDisableJump(devidx); if (rv != VBERROR_SUCCESS) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcDisableJump() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("VbEcSoftwareSync() in RO-Normal; EC-RO\n")); return VBERROR_SUCCESS; } /* Get hash of EC-RW */ rv = VbExEcHashRW(devidx, &ec_hash, &ec_hash_size); if (rv) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcHashRW() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } if (ec_hash_size != SHA256_DIGEST_SIZE) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcHashRW() says size %d, not %d\n", ec_hash_size, SHA256_DIGEST_SIZE)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("EC hash:")); for (i = 0; i < SHA256_DIGEST_SIZE; i++) VBDEBUG(("%02x", ec_hash[i])); VBDEBUG(("\n")); /* * Get expected EC-RW hash. Note that we've already checked for * RO_NORMAL, so we know that the BIOS must be RW-A or RW-B, and * therefore the EC must match. */ rv = VbExEcGetExpectedRWHash(devidx, shared->firmware_index ? VB_SELECT_FIRMWARE_B : VB_SELECT_FIRMWARE_A, &rw_hash, &rw_hash_size); if (rv == VBERROR_EC_GET_EXPECTED_HASH_FROM_IMAGE) { /* * BIOS has verified EC image but doesn't have a precomputed * hash for it, so we must compute the hash ourselves. */ rw_hash = NULL; } else if (rv) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcGetExpectedRWHash() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } else if (rw_hash_size != SHA256_DIGEST_SIZE) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcGetExpectedRWHash() says size %d, not %d\n", rw_hash_size, SHA256_DIGEST_SIZE)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } else { VBDEBUG(("Expected hash:")); for (i = 0; i < SHA256_DIGEST_SIZE; i++) VBDEBUG(("%02x", rw_hash[i])); VBDEBUG(("\n")); need_update = SafeMemcmp(ec_hash, rw_hash, SHA256_DIGEST_SIZE); } /* * Get expected EC-RW image if we're sure we need to update (because the * expected hash didn't match the EC) or we still don't know (because * there was no expected hash and we need the image to compute one * ourselves). */ if (need_update || !rw_hash) { /* Get expected EC-RW image */ rv = VbExEcGetExpectedRW(devidx, shared->firmware_index ? VB_SELECT_FIRMWARE_B : VB_SELECT_FIRMWARE_A, &expected, &expected_size); if (rv) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcGetExpectedRW() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_IMAGE); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("VbEcSoftwareSync() - expected len = %d\n", expected_size)); /* Hash expected image */ internal_SHA256(expected, expected_size, expected_hash); VBDEBUG(("Computed hash of expected image:")); for (i = 0; i < SHA256_DIGEST_SIZE; i++) VBDEBUG(("%02x", expected_hash[i])); VBDEBUG(("\n")); } if (!rw_hash) { /* * BIOS didn't have expected EC hash, so check if we need * update by comparing EC hash to the one we just computed. */ need_update = SafeMemcmp(ec_hash, expected_hash, SHA256_DIGEST_SIZE); } else if (need_update && SafeMemcmp(rw_hash, expected_hash, SHA256_DIGEST_SIZE)) { /* * We need to update, but the expected EC image doesn't match * the expected EC hash we were given. */ VBDEBUG(("VbEcSoftwareSync() - " "VbExEcGetExpectedRW() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_MISMATCH); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } /* * TODO: GBB flag to override whether we need update; needed for EC * development. */ if (in_rw) { if (need_update) { /* * Check if BIOS should also load VGA Option ROM when * rebooting to save another reboot if possible. */ if ((shared->flags & VBSD_EC_SLOW_UPDATE) && (shared->flags & VBSD_OPROM_MATTERS) && !(shared->flags & VBSD_OPROM_LOADED)) { VBDEBUG(("VbEcSoftwareSync() - Reboot to " "load VGA Option ROM\n")); VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); } /* * EC is running the wrong RW image. Reboot the EC to * RO so we can update it on the next boot. */ VBDEBUG(("VbEcSoftwareSync() - " "in RW, need to update RW, so reboot\n")); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("VbEcSoftwareSync() in EC-RW and it matches\n")); return VBERROR_SUCCESS; } /* Update EC if necessary */ if (need_update) { VBDEBUG(("VbEcSoftwareSync() updating EC-RW...\n")); if (shared->flags & VBSD_EC_SLOW_UPDATE) { VBDEBUG(("VbEcSoftwareSync() - " "EC is slow. Show WAIT screen.\n")); /* Ensure the VGA Option ROM is loaded */ if ((shared->flags & VBSD_OPROM_MATTERS) && !(shared->flags & VBSD_OPROM_LOADED)) { VBDEBUG(("VbEcSoftwareSync() - Reboot to " "load VGA Option ROM\n")); VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); return VBERROR_VGA_OPROM_MISMATCH; } VbDisplayScreen(cparams, VB_SCREEN_WAIT, 0, &vnc); } rv = VbExEcUpdateRW(devidx, expected, expected_size); if (rv != VBERROR_SUCCESS) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcUpdateRW() returned %d\n", rv)); /* * The EC may know it needs a reboot. It may need to * unprotect RW before updating, or may need to reboot * after RW updated. Either way, it's not an error * requiring recovery mode. * * If we fail for any other reason, trigger recovery * mode. */ if (rv != VBERROR_EC_REBOOT_TO_RO_REQUIRED) VbSetRecoveryRequest(VBNV_RECOVERY_EC_UPDATE); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } /* * TODO: should ask EC to recompute its hash to verify it's * correct before continuing? */ } /* Protect EC-RW flash */ rv = EcProtectRW(devidx); if (rv != VBERROR_SUCCESS) return rv; /* Tell EC to jump to its RW image */ VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n")); rv = VbExEcJumpToRW(devidx); if (rv != VBERROR_SUCCESS) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcJumpToRW() returned %d\n", rv)); /* * If the EC booted RO-normal and a previous AP boot has called * VbExEcStayInRO(), we need to reboot the EC to unlock the * ability to jump to the RW firmware. * * All other errors trigger recovery mode. */ if (rv != VBERROR_EC_REBOOT_TO_RO_REQUIRED) VbSetRecoveryRequest(VBNV_RECOVERY_EC_JUMP_RW); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } VBDEBUG(("VbEcSoftwareSync() jumped to EC-RW\n")); rv = VbExEcDisableJump(devidx); if (rv != VBERROR_SUCCESS) { VBDEBUG(("VbEcSoftwareSync() - " "VbExEcDisableJump() returned %d\n", rv)); VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); return VBERROR_EC_REBOOT_TO_RO_REQUIRED; } /* * Reboot to unload VGA Option ROM if: * - RW update was done * - the system is NOT in developer mode * - the system has slow EC update flag set * - the VGA Option ROM was needed and loaded */ if (need_update && !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && (shared->flags & VBSD_EC_SLOW_UPDATE) && (shared->flags & VBSD_OPROM_MATTERS) && (shared->flags & VBSD_OPROM_LOADED)) { VBDEBUG(("VbEcSoftwareSync() - Reboot to " "unload VGA Option ROM\n")); return VBERROR_VGA_OPROM_MISMATCH; } VBDEBUG(("VbEcSoftwareSync() in RW; done\n")); return VBERROR_SUCCESS; }