blob: 0a24d0f71a1c3f5df49bc3eaf6ec1f2c01243e0b [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. */
Julius Wernere9665952022-01-21 17:06:20 -080047 printk(BIOS_ERR, "Attempt to get CR50 TPM mode failed: %x\n", ret);
Keith Shorte0f34002019-02-05 16:15:10 -070048 return 0;
49 }
50
Keith Short00dbf442019-04-19 14:02:02 -060051 /*
52 * If the TPM mode is not enabled-tentative, then the TPM mode is locked
Keith Shorte0f34002019-02-05 16:15:10 -070053 * and cannot be changed. Perform a Cr50 reset because vboot may need
54 * to disable TPM as part of booting an untrusted OS.
55 *
56 * This is not an expected state, as the Cr50 always sets the TPM mode
57 * to TPM_MODE_ENABLED_TENTATIVE during any TPM reset action.
58 */
59 if (tpm_mode != TPM_MODE_ENABLED_TENTATIVE) {
60 printk(BIOS_NOTICE,
61 "NOTICE: Unexpected Cr50 TPM mode (%d). "
62 "A Cr50 reset is required.\n", tpm_mode);
Keith Short00dbf442019-04-19 14:02:02 -060063 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070064 }
65
66 /* If TPM state is okay, no reset needed. */
Keith Short00dbf442019-04-19 14:02:02 -060067 return 0;
Keith Shorte0f34002019-02-05 16:15:10 -070068}
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070069
70static void enable_update(void *unused)
71{
72 int ret;
Keith Short00dbf442019-04-19 14:02:02 -060073 int cr50_reset_reqd = 0;
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070074 uint8_t num_restored_headers;
75
Edward O'Callaghan7c522832020-06-02 14:41:43 +100076 /**
77 * Never update during manually-triggered recovery to ensure update
78 * cannot interfere. Non-manual VB2_RECOVERY_TRAIN_AND_REBOOT
79 * sometimes used to update in factory.
80 */
81 if (vboot_get_context()->flags & VB2_CONTEXT_FORCE_RECOVERY_MODE)
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070082 return;
83
84 ret = tlcl_lib_init();
85
86 if (ret != VB2_SUCCESS) {
Julius Wernere9665952022-01-21 17:06:20 -080087 printk(BIOS_ERR, "tlcl_lib_init() failed for CR50 update: %x\n",
Keith Shorte0f34002019-02-05 16:15:10 -070088 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070089 return;
90 }
91
Raul E Rangel4693f3d2021-06-10 15:46:32 -060092 timestamp_add_now(TS_START_TPM_ENABLE_UPDATE);
93
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070094 /* Reboot in 1000 ms if necessary. */
Keith Short00dbf442019-04-19 14:02:02 -060095 ret = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS,
Keith Shorte0f34002019-02-05 16:15:10 -070096 &num_restored_headers);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070097
98 if (ret != TPM_SUCCESS) {
Julius Wernere9665952022-01-21 17:06:20 -080099 printk(BIOS_ERR, "Attempt to enable CR50 update failed: %x\n",
Keith Shorte0f34002019-02-05 16:15:10 -0700100 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700101 return;
102 }
103
Keith Shorte0f34002019-02-05 16:15:10 -0700104 if (!num_restored_headers) {
105 /* If no headers were restored there is no reset forthcoming due
106 * to a Cr50 firmware update. Also check if the Cr50 TPM mode
107 * requires a reset.
108 *
109 * TODO: to eliminate a TPM command during every boot, the
110 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
111 * firmware to perform the TPM mode/key-ladder check in addition
112 * to the FW version check.
113 */
114
115 /*
Keith Short00dbf442019-04-19 14:02:02 -0600116 * If the Cr50 doesn't requires a reset, continue booting.
Keith Shorte0f34002019-02-05 16:15:10 -0700117 */
Keith Short00dbf442019-04-19 14:02:02 -0600118 cr50_reset_reqd = cr50_is_reset_needed();
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600119 if (!cr50_reset_reqd) {
120 timestamp_add_now(TS_END_TPM_ENABLE_UPDATE);
Keith Shorte0f34002019-02-05 16:15:10 -0700121 return;
Raul E Rangel4693f3d2021-06-10 15:46:32 -0600122 }
Keith Shorte0f34002019-02-05 16:15:10 -0700123
124 printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
125 elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
126 } else {
127 printk(BIOS_INFO,
128 "Waiting for CR50 reset to pick up update.\n");
129 elog_add_event(ELOG_TYPE_CR50_UPDATE);
130 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700131
Duncan Laurief131fc72019-01-23 15:01:21 -0800132 /* Give mainboard a chance to take action */
Keith Shorte0f34002019-02-05 16:15:10 -0700133 mainboard_prepare_cr50_reset();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700134
135 /* clear current post code avoid chatty eventlog on subsequent boot*/
136 post_code(0);
137
Keith Short00dbf442019-04-19 14:02:02 -0600138 /*
139 * Older Cr50 firmware doesn't support the timeout parameter for the
140 * immediate reset request, so the reset request must be sent after
141 * the mainboard specific code runs.
142 */
143 if (cr50_reset_reqd) {
144 ret = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS);
145
146 if (ret != TPM_SUCCESS) {
147 /*
148 * Reset request failed due to TPM error, continue
149 * booting but the current boot will likely end up at
150 * the recovery screen.
151 */
Julius Wernere9665952022-01-21 17:06:20 -0800152 printk(BIOS_ERR, "Attempt to reset CR50 failed: %x\n",
Keith Short00dbf442019-04-19 14:02:02 -0600153 ret);
154 return;
155 }
156 }
157
Julius Wernercd49cce2019-03-05 16:53:33 -0800158 if (CONFIG(POWER_OFF_ON_CR50_UPDATE))
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700159 poweroff();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700160 halt();
161}
162BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);