blob: fa5556b343eacbde0a0ffe540d5b9d296e91bef5 [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>
4#include <console/console.h>
5#include <ec/google/chromeec/ec.h>
6#include <elog.h>
7#include <halt.h>
Philipp Deppenwiesed88fb362017-10-18 20:26:18 +02008#include <security/tpm/tss.h>
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -07009#include <vb2_api.h>
Kyösti Mälkki91c077f2021-11-03 18:34:14 +020010#include <security/vboot/misc.h>
Philipp Deppenwiesefea24292017-10-17 17:02:29 +020011#include <security/vboot/vboot_common.h>
Duncan Laurief131fc72019-01-23 15:01:21 -080012#include <vendorcode/google/chromeos/chromeos.h>
Raul E Rangel4693f3d2021-06-10 15:46:32 -060013#include <timestamp.h>
Duncan Laurief131fc72019-01-23 15:01:21 -080014
Keith Short00dbf442019-04-19 14:02:02 -060015#define CR50_RESET_DELAY_MS 1000
Keith Shorte0f34002019-02-05 16:15:10 -070016
17void __weak mainboard_prepare_cr50_reset(void) {}
18
19/**
20 * Check if the Cr50 TPM state requires a chip reset of the Cr50 device.
21 *
22 * Returns 0 if the Cr50 TPM state is good or if the TPM_MODE command is
Keith Short00dbf442019-04-19 14:02:02 -060023 * unsupported. Returns 1 if the Cr50 requires a reset.
Keith Shorte0f34002019-02-05 16:15:10 -070024 */
Keith Short00dbf442019-04-19 14:02:02 -060025static int cr50_is_reset_needed(void)
Keith Shorte0f34002019-02-05 16:15:10 -070026{
27 int ret;
Keith Shorte0f34002019-02-05 16:15:10 -070028 uint8_t tpm_mode;
29
30 ret = tlcl_cr50_get_tpm_mode(&tpm_mode);
31
32 if (ret == TPM_E_NO_SUCH_COMMAND) {
33 printk(BIOS_INFO,
34 "Cr50 does not support TPM mode command\n");
35 /* Older Cr50 firmware, assume no Cr50 reset is required */
36 return 0;
37 }
38
39 if (ret == TPM_E_MUST_REBOOT) {
40 /*
41 * Cr50 indicated a reboot is required to restore TPM
42 * functionality.
43 */
Keith Short00dbf442019-04-19 14:02:02 -060044 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070045 } else if (ret != TPM_SUCCESS) {
46 /* TPM command failed, continue booting. */
47 printk(BIOS_ERR,
48 "ERROR: Attempt to get CR50 TPM mode failed: %x\n", ret);
49 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
71static void enable_update(void *unused)
72{
73 int ret;
Keith Short00dbf442019-04-19 14:02:02 -060074 int cr50_reset_reqd = 0;
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070075 uint8_t num_restored_headers;
76
Edward O'Callaghan7c522832020-06-02 14:41:43 +100077 /**
78 * Never update during manually-triggered recovery to ensure update
79 * cannot interfere. Non-manual VB2_RECOVERY_TRAIN_AND_REBOOT
80 * sometimes used to update in factory.
81 */
82 if (vboot_get_context()->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE)
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070083 return;
84
85 ret = tlcl_lib_init();
86
87 if (ret != VB2_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -070088 printk(BIOS_ERR,
89 "ERROR: tlcl_lib_init() failed for CR50 update: %x\n",
90 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070091 return;
92 }
93
Raul E Rangel4693f3d2021-06-10 15:46:32 -060094 timestamp_add_now(TS_START_TPM_ENABLE_UPDATE);
95
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070096 /* Reboot in 1000 ms if necessary. */
Keith Short00dbf442019-04-19 14:02:02 -060097 ret = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS,
Keith Shorte0f34002019-02-05 16:15:10 -070098 &num_restored_headers);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070099
100 if (ret != TPM_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -0700101 printk(BIOS_ERR,
102 "ERROR: Attempt to enable CR50 update failed: %x\n",
103 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700104 return;
105 }
106
Keith Shorte0f34002019-02-05 16:15:10 -0700107 if (!num_restored_headers) {
108 /* If no headers were restored there is no reset forthcoming due
109 * to a Cr50 firmware update. Also check if the Cr50 TPM mode
110 * requires a reset.
111 *
112 * TODO: to eliminate a TPM command during every boot, the
113 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
114 * firmware to perform the TPM mode/key-ladder check in addition
115 * to the FW version check.
116 */
117
118 /*
Keith Short00dbf442019-04-19 14:02:02 -0600119 * If the Cr50 doesn't requires a reset, continue booting.
Keith Shorte0f34002019-02-05 16:15:10 -0700120 */
Keith Short00dbf442019-04-19 14:02:02 -0600121 cr50_reset_reqd = cr50_is_reset_needed();
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600122 if (!cr50_reset_reqd) {
123 timestamp_add_now(TS_END_TPM_ENABLE_UPDATE);
Keith Shorte0f34002019-02-05 16:15:10 -0700124 return;
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600125 }
Keith Shorte0f34002019-02-05 16:15:10 -0700126
127 printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
128 elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
129 } else {
130 printk(BIOS_INFO,
131 "Waiting for CR50 reset to pick up update.\n");
132 elog_add_event(ELOG_TYPE_CR50_UPDATE);
133 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700134
Duncan Laurief131fc72019-01-23 15:01:21 -0800135 /* Give mainboard a chance to take action */
Keith Shorte0f34002019-02-05 16:15:10 -0700136 mainboard_prepare_cr50_reset();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700137
138 /* clear current post code avoid chatty eventlog on subsequent boot*/
139 post_code(0);
140
Keith Short00dbf442019-04-19 14:02:02 -0600141 /*
142 * Older Cr50 firmware doesn't support the timeout parameter for the
143 * immediate reset request, so the reset request must be sent after
144 * the mainboard specific code runs.
145 */
146 if (cr50_reset_reqd) {
147 ret = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS);
148
149 if (ret != TPM_SUCCESS) {
150 /*
151 * Reset request failed due to TPM error, continue
152 * booting but the current boot will likely end up at
153 * the recovery screen.
154 */
155 printk(BIOS_ERR,
156 "ERROR: Attempt to reset CR50 failed: %x\n",
157 ret);
158 return;
159 }
160 }
161
Julius Wernercd49cce2019-03-05 16:53:33 -0800162 if (CONFIG(POWER_OFF_ON_CR50_UPDATE))
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700163 poweroff();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700164 halt();
165}
166BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);