blob: 9fd8d99c7c49c234b94e46d350682ba832df3a12 [file] [log] [blame]
Angel Pons3ef916f2020-04-02 23:49:13 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -07002
3#include <bootstate.h>
Martin Rothfbd13a82022-12-31 16:40:58 -07004#include <commonlib/console/post_codes.h>
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -07005#include <console/console.h>
6#include <ec/google/chromeec/ec.h>
7#include <elog.h>
8#include <halt.h>
Philipp Deppenwiesed88fb362017-10-18 20:26:18 +02009#include <security/tpm/tss.h>
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070010#include <vb2_api.h>
Kyösti Mälkki91c077f2021-11-03 18:34:14 +020011#include <security/vboot/misc.h>
Philipp Deppenwiesefea24292017-10-17 17:02:29 +020012#include <security/vboot/vboot_common.h>
Duncan Laurief131fc72019-01-23 15:01:21 -080013#include <vendorcode/google/chromeos/chromeos.h>
Raul E Rangel4693f3d2021-06-10 15:46:32 -060014#include <timestamp.h>
Duncan Laurief131fc72019-01-23 15:01:21 -080015
Keith Short00dbf442019-04-19 14:02:02 -060016#define CR50_RESET_DELAY_MS 1000
Keith Shorte0f34002019-02-05 16:15:10 -070017
18void __weak mainboard_prepare_cr50_reset(void) {}
19
20/**
21 * Check if the Cr50 TPM state requires a chip reset of the Cr50 device.
22 *
23 * Returns 0 if the Cr50 TPM state is good or if the TPM_MODE command is
Keith Short00dbf442019-04-19 14:02:02 -060024 * unsupported. Returns 1 if the Cr50 requires a reset.
Keith Shorte0f34002019-02-05 16:15:10 -070025 */
Keith Short00dbf442019-04-19 14:02:02 -060026static int cr50_is_reset_needed(void)
Keith Shorte0f34002019-02-05 16:15:10 -070027{
28 int ret;
Keith Shorte0f34002019-02-05 16:15:10 -070029 uint8_t tpm_mode;
30
31 ret = tlcl_cr50_get_tpm_mode(&tpm_mode);
32
33 if (ret == TPM_E_NO_SUCH_COMMAND) {
34 printk(BIOS_INFO,
35 "Cr50 does not support TPM mode command\n");
36 /* Older Cr50 firmware, assume no Cr50 reset is required */
37 return 0;
38 }
39
40 if (ret == TPM_E_MUST_REBOOT) {
41 /*
42 * Cr50 indicated a reboot is required to restore TPM
43 * functionality.
44 */
Keith Short00dbf442019-04-19 14:02:02 -060045 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070046 } else if (ret != TPM_SUCCESS) {
47 /* TPM command failed, continue booting. */
Julius Wernere9665952022-01-21 17:06:20 -080048 printk(BIOS_ERR, "Attempt to get CR50 TPM mode failed: %x\n", ret);
Keith Shorte0f34002019-02-05 16:15:10 -070049 return 0;
50 }
51
Keith Short00dbf442019-04-19 14:02:02 -060052 /*
53 * If the TPM mode is not enabled-tentative, then the TPM mode is locked
Keith Shorte0f34002019-02-05 16:15:10 -070054 * and cannot be changed. Perform a Cr50 reset because vboot may need
55 * to disable TPM as part of booting an untrusted OS.
56 *
57 * This is not an expected state, as the Cr50 always sets the TPM mode
58 * to TPM_MODE_ENABLED_TENTATIVE during any TPM reset action.
59 */
60 if (tpm_mode != TPM_MODE_ENABLED_TENTATIVE) {
61 printk(BIOS_NOTICE,
62 "NOTICE: Unexpected Cr50 TPM mode (%d). "
63 "A Cr50 reset is required.\n", tpm_mode);
Keith Short00dbf442019-04-19 14:02:02 -060064 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070065 }
66
67 /* If TPM state is okay, no reset needed. */
Keith Short00dbf442019-04-19 14:02:02 -060068 return 0;
Keith Shorte0f34002019-02-05 16:15:10 -070069}
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070070
Derek Huangda381222022-12-15 07:27:41 +000071static void clear_ec_ap_idle(void)
72{
73 if (!CONFIG(CR50_RESET_CLEAR_EC_AP_IDLE_FLAG))
74 return;
75
76 /* Send EC command to clear AP_IDLE flag */
77 if (!google_chromeec_reboot(EC_REBOOT_NO_OP, EC_REBOOT_FLAG_CLEAR_AP_IDLE |
78 EC_REBOOT_FLAG_ON_AP_SHUTDOWN))
79 printk(BIOS_INFO, "Successfully clear AP_IDLE flag");
80 else
81 printk(BIOS_ERR, "Failed to clear EC AP_IDLE flag");
82}
83
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070084static void enable_update(void *unused)
85{
86 int ret;
Keith Short00dbf442019-04-19 14:02:02 -060087 int cr50_reset_reqd = 0;
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070088 uint8_t num_restored_headers;
89
Edward O'Callaghan7c522832020-06-02 14:41:43 +100090 /**
91 * Never update during manually-triggered recovery to ensure update
92 * cannot interfere. Non-manual VB2_RECOVERY_TRAIN_AND_REBOOT
93 * sometimes used to update in factory.
94 */
95 if (vboot_get_context()->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE)
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070096 return;
97
98 ret = tlcl_lib_init();
99
100 if (ret != VB2_SUCCESS) {
Julius Wernere9665952022-01-21 17:06:20 -0800101 printk(BIOS_ERR, "tlcl_lib_init() failed for CR50 update: %x\n",
Keith Shorte0f34002019-02-05 16:15:10 -0700102 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700103 return;
104 }
105
Jakub Czapigaad6157e2022-02-15 11:50:31 +0100106 timestamp_add_now(TS_TPM_ENABLE_UPDATE_START);
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600107
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700108 /* Reboot in 1000 ms if necessary. */
Keith Short00dbf442019-04-19 14:02:02 -0600109 ret = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS,
Keith Shorte0f34002019-02-05 16:15:10 -0700110 &num_restored_headers);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700111
112 if (ret != TPM_SUCCESS) {
Julius Wernere9665952022-01-21 17:06:20 -0800113 printk(BIOS_ERR, "Attempt to enable CR50 update failed: %x\n",
Keith Shorte0f34002019-02-05 16:15:10 -0700114 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700115 return;
116 }
117
Keith Shorte0f34002019-02-05 16:15:10 -0700118 if (!num_restored_headers) {
119 /* If no headers were restored there is no reset forthcoming due
120 * to a Cr50 firmware update. Also check if the Cr50 TPM mode
121 * requires a reset.
122 *
123 * TODO: to eliminate a TPM command during every boot, the
124 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
125 * firmware to perform the TPM mode/key-ladder check in addition
126 * to the FW version check.
127 */
128
129 /*
Keith Short00dbf442019-04-19 14:02:02 -0600130 * If the Cr50 doesn't requires a reset, continue booting.
Keith Shorte0f34002019-02-05 16:15:10 -0700131 */
Keith Short00dbf442019-04-19 14:02:02 -0600132 cr50_reset_reqd = cr50_is_reset_needed();
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600133 if (!cr50_reset_reqd) {
Jakub Czapigaad6157e2022-02-15 11:50:31 +0100134 timestamp_add_now(TS_TPM_ENABLE_UPDATE_END);
Keith Shorte0f34002019-02-05 16:15:10 -0700135 return;
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600136 }
Keith Shorte0f34002019-02-05 16:15:10 -0700137
138 printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
139 elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
140 } else {
141 printk(BIOS_INFO,
142 "Waiting for CR50 reset to pick up update.\n");
143 elog_add_event(ELOG_TYPE_CR50_UPDATE);
144 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700145
Duncan Laurief131fc72019-01-23 15:01:21 -0800146 /* Give mainboard a chance to take action */
Keith Shorte0f34002019-02-05 16:15:10 -0700147 mainboard_prepare_cr50_reset();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700148
149 /* clear current post code avoid chatty eventlog on subsequent boot*/
lilacious40cb3fe2023-06-21 23:24:14 +0200150 post_code(POSTCODE_CODE_CLEAR);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700151
Keith Short00dbf442019-04-19 14:02:02 -0600152 /*
153 * Older Cr50 firmware doesn't support the timeout parameter for the
154 * immediate reset request, so the reset request must be sent after
155 * the mainboard specific code runs.
156 */
157 if (cr50_reset_reqd) {
158 ret = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS);
159
160 if (ret != TPM_SUCCESS) {
161 /*
162 * Reset request failed due to TPM error, continue
163 * booting but the current boot will likely end up at
164 * the recovery screen.
165 */
Julius Wernere9665952022-01-21 17:06:20 -0800166 printk(BIOS_ERR, "Attempt to reset CR50 failed: %x\n",
Keith Short00dbf442019-04-19 14:02:02 -0600167 ret);
168 return;
169 }
170 }
171
Derek Huangda381222022-12-15 07:27:41 +0000172 if (CONFIG(POWER_OFF_ON_CR50_UPDATE)) {
173 clear_ec_ap_idle();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700174 poweroff();
Derek Huangda381222022-12-15 07:27:41 +0000175 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700176 halt();
177}
178BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);