| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <bootstate.h> |
| #include <commonlib/console/post_codes.h> |
| #include <console/console.h> |
| #include <ec/google/chromeec/ec.h> |
| #include <elog.h> |
| #include <halt.h> |
| #include <security/tpm/tss.h> |
| #include <vb2_api.h> |
| #include <security/vboot/misc.h> |
| #include <security/vboot/vboot_common.h> |
| #include <vendorcode/google/chromeos/chromeos.h> |
| #include <timestamp.h> |
| |
| #define CR50_RESET_DELAY_MS 1000 |
| |
| void __weak mainboard_prepare_cr50_reset(void) {} |
| |
| /** |
| * Check if the Cr50 TPM state requires a chip reset of the Cr50 device. |
| * |
| * Returns 0 if the Cr50 TPM state is good or if the TPM_MODE command is |
| * unsupported. Returns 1 if the Cr50 requires a reset. |
| */ |
| static int cr50_is_reset_needed(void) |
| { |
| int rc; |
| uint8_t tpm_mode; |
| |
| rc = tlcl_cr50_get_tpm_mode(&tpm_mode); |
| |
| if (rc == TPM_CB_NO_SUCH_COMMAND) { |
| printk(BIOS_INFO, |
| "Cr50 does not support TPM mode command\n"); |
| /* Older Cr50 firmware, assume no Cr50 reset is required */ |
| return 0; |
| } |
| |
| if (rc == TPM_CB_MUST_REBOOT) { |
| /* |
| * Cr50 indicated a reboot is required to restore TPM |
| * functionality. |
| */ |
| return 1; |
| } else if (rc != TPM_SUCCESS) { |
| /* TPM command failed, continue booting. */ |
| printk(BIOS_ERR, "Attempt to get CR50 TPM mode failed: %#x\n", rc); |
| return 0; |
| } |
| |
| /* |
| * If the TPM mode is not enabled-tentative, then the TPM mode is locked |
| * and cannot be changed. Perform a Cr50 reset because vboot may need |
| * to disable TPM as part of booting an untrusted OS. |
| * |
| * This is not an expected state, as the Cr50 always sets the TPM mode |
| * to TPM_MODE_ENABLED_TENTATIVE during any TPM reset action. |
| */ |
| if (tpm_mode != TPM_MODE_ENABLED_TENTATIVE) { |
| printk(BIOS_NOTICE, |
| "NOTICE: Unexpected Cr50 TPM mode (%d). " |
| "A Cr50 reset is required.\n", tpm_mode); |
| return 1; |
| } |
| |
| /* If TPM state is okay, no reset needed. */ |
| return 0; |
| } |
| |
| static void enable_update(void *unused) |
| { |
| int rc; |
| int cr50_reset_reqd = 0; |
| uint8_t num_restored_headers; |
| |
| /** |
| * Never update during manually-triggered recovery to ensure update |
| * cannot interfere. Non-manual VB2_RECOVERY_TRAIN_AND_REBOOT |
| * sometimes used to update in factory. |
| */ |
| if (vboot_get_context()->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE) |
| return; |
| |
| rc = tlcl_lib_init(); |
| |
| if (rc != VB2_SUCCESS) { |
| printk(BIOS_ERR, "tlcl_lib_init() failed for CR50 update: %#x\n", |
| rc); |
| return; |
| } |
| |
| timestamp_add_now(TS_TPM_ENABLE_UPDATE_START); |
| |
| /* Reboot in 1000 ms if necessary. */ |
| rc = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS, |
| &num_restored_headers); |
| |
| if (rc != TPM_SUCCESS) { |
| printk(BIOS_ERR, "Attempt to enable CR50 update failed: %#x\n", |
| rc); |
| return; |
| } |
| |
| if (!num_restored_headers) { |
| /* If no headers were restored there is no reset forthcoming due |
| * to a Cr50 firmware update. Also check if the Cr50 TPM mode |
| * requires a reset. |
| * |
| * TODO: to eliminate a TPM command during every boot, the |
| * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50 |
| * firmware to perform the TPM mode/key-ladder check in addition |
| * to the FW version check. |
| */ |
| |
| /* |
| * If the Cr50 doesn't requires a reset, continue booting. |
| */ |
| cr50_reset_reqd = cr50_is_reset_needed(); |
| if (!cr50_reset_reqd) { |
| timestamp_add_now(TS_TPM_ENABLE_UPDATE_END); |
| return; |
| } |
| |
| printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n"); |
| elog_add_event(ELOG_TYPE_CR50_NEED_RESET); |
| } else { |
| printk(BIOS_INFO, |
| "Waiting for CR50 reset to pick up update.\n"); |
| elog_add_event(ELOG_TYPE_CR50_UPDATE); |
| } |
| |
| /* Give mainboard a chance to take action */ |
| mainboard_prepare_cr50_reset(); |
| |
| /* clear current post code avoid chatty eventlog on subsequent boot*/ |
| post_code(POSTCODE_CODE_CLEAR); |
| |
| /* |
| * Older Cr50 firmware doesn't support the timeout parameter for the |
| * immediate reset request, so the reset request must be sent after |
| * the mainboard specific code runs. |
| */ |
| if (cr50_reset_reqd) { |
| rc = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS); |
| |
| if (rc != TPM_SUCCESS) { |
| /* |
| * Reset request failed due to TPM error, continue |
| * booting but the current boot will likely end up at |
| * the recovery screen. |
| */ |
| printk(BIOS_ERR, "Attempt to reset CR50 failed: %#x\n", |
| rc); |
| return; |
| } |
| } |
| |
| if (CONFIG(POWER_OFF_ON_CR50_UPDATE)) { |
| if (CONFIG(CR50_RESET_CLEAR_EC_AP_IDLE_FLAG)) |
| google_chromeec_clear_ec_ap_idle(); |
| poweroff(); |
| } |
| halt(); |
| } |
| BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL); |