blob: f2cdbfd39c5b8482d5bfbe944d828d0fbe9e9000 [file] [log] [blame]
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2017 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <bootstate.h>
17#include <console/console.h>
18#include <ec/google/chromeec/ec.h>
19#include <elog.h>
20#include <halt.h>
Philipp Deppenwiesed88fb362017-10-18 20:26:18 +020021#include <security/tpm/tss.h>
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070022#include <vb2_api.h>
Philipp Deppenwiesefea24292017-10-17 17:02:29 +020023#include <security/vboot/vboot_common.h>
Duncan Laurief131fc72019-01-23 15:01:21 -080024#include <vendorcode/google/chromeos/chromeos.h>
25
Keith Short00dbf442019-04-19 14:02:02 -060026#define CR50_RESET_DELAY_MS 1000
Keith Shorte0f34002019-02-05 16:15:10 -070027
28void __weak mainboard_prepare_cr50_reset(void) {}
29
30/**
31 * Check if the Cr50 TPM state requires a chip reset of the Cr50 device.
32 *
33 * Returns 0 if the Cr50 TPM state is good or if the TPM_MODE command is
Keith Short00dbf442019-04-19 14:02:02 -060034 * unsupported. Returns 1 if the Cr50 requires a reset.
Keith Shorte0f34002019-02-05 16:15:10 -070035 */
Keith Short00dbf442019-04-19 14:02:02 -060036static int cr50_is_reset_needed(void)
Keith Shorte0f34002019-02-05 16:15:10 -070037{
38 int ret;
Keith Shorte0f34002019-02-05 16:15:10 -070039 uint8_t tpm_mode;
40
41 ret = tlcl_cr50_get_tpm_mode(&tpm_mode);
42
43 if (ret == TPM_E_NO_SUCH_COMMAND) {
44 printk(BIOS_INFO,
45 "Cr50 does not support TPM mode command\n");
46 /* Older Cr50 firmware, assume no Cr50 reset is required */
47 return 0;
48 }
49
50 if (ret == TPM_E_MUST_REBOOT) {
51 /*
52 * Cr50 indicated a reboot is required to restore TPM
53 * functionality.
54 */
Keith Short00dbf442019-04-19 14:02:02 -060055 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070056 } else if (ret != TPM_SUCCESS) {
57 /* TPM command failed, continue booting. */
58 printk(BIOS_ERR,
59 "ERROR: Attempt to get CR50 TPM mode failed: %x\n", ret);
60 return 0;
61 }
62
Keith Short00dbf442019-04-19 14:02:02 -060063 /*
64 * If the TPM mode is not enabled-tentative, then the TPM mode is locked
Keith Shorte0f34002019-02-05 16:15:10 -070065 * and cannot be changed. Perform a Cr50 reset because vboot may need
66 * to disable TPM as part of booting an untrusted OS.
67 *
68 * This is not an expected state, as the Cr50 always sets the TPM mode
69 * to TPM_MODE_ENABLED_TENTATIVE during any TPM reset action.
70 */
71 if (tpm_mode != TPM_MODE_ENABLED_TENTATIVE) {
72 printk(BIOS_NOTICE,
73 "NOTICE: Unexpected Cr50 TPM mode (%d). "
74 "A Cr50 reset is required.\n", tpm_mode);
Keith Short00dbf442019-04-19 14:02:02 -060075 return 1;
Keith Shorte0f34002019-02-05 16:15:10 -070076 }
77
78 /* If TPM state is okay, no reset needed. */
Keith Short00dbf442019-04-19 14:02:02 -060079 return 0;
Keith Shorte0f34002019-02-05 16:15:10 -070080}
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070081
82static void enable_update(void *unused)
83{
84 int ret;
Keith Short00dbf442019-04-19 14:02:02 -060085 int cr50_reset_reqd = 0;
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070086 uint8_t num_restored_headers;
87
88 /* Nothing to do on recovery mode. */
89 if (vboot_recovery_mode_enabled())
90 return;
91
92 ret = tlcl_lib_init();
93
94 if (ret != VB2_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -070095 printk(BIOS_ERR,
96 "ERROR: tlcl_lib_init() failed for CR50 update: %x\n",
97 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070098 return;
99 }
100
101 /* Reboot in 1000 ms if necessary. */
Keith Short00dbf442019-04-19 14:02:02 -0600102 ret = tlcl_cr50_enable_update(CR50_RESET_DELAY_MS,
Keith Shorte0f34002019-02-05 16:15:10 -0700103 &num_restored_headers);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700104
105 if (ret != TPM_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -0700106 printk(BIOS_ERR,
107 "ERROR: Attempt to enable CR50 update failed: %x\n",
108 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700109 return;
110 }
111
Keith Shorte0f34002019-02-05 16:15:10 -0700112 if (!num_restored_headers) {
113 /* If no headers were restored there is no reset forthcoming due
114 * to a Cr50 firmware update. Also check if the Cr50 TPM mode
115 * requires a reset.
116 *
117 * TODO: to eliminate a TPM command during every boot, the
118 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
119 * firmware to perform the TPM mode/key-ladder check in addition
120 * to the FW version check.
121 */
122
123 /*
Keith Short00dbf442019-04-19 14:02:02 -0600124 * If the Cr50 doesn't requires a reset, continue booting.
Keith Shorte0f34002019-02-05 16:15:10 -0700125 */
Keith Short00dbf442019-04-19 14:02:02 -0600126 cr50_reset_reqd = cr50_is_reset_needed();
127 if (!cr50_reset_reqd)
Keith Shorte0f34002019-02-05 16:15:10 -0700128 return;
129
130 printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
131 elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
132 } else {
133 printk(BIOS_INFO,
134 "Waiting for CR50 reset to pick up update.\n");
135 elog_add_event(ELOG_TYPE_CR50_UPDATE);
136 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700137
Duncan Laurief131fc72019-01-23 15:01:21 -0800138 /* Give mainboard a chance to take action */
Keith Shorte0f34002019-02-05 16:15:10 -0700139 mainboard_prepare_cr50_reset();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700140
141 /* clear current post code avoid chatty eventlog on subsequent boot*/
142 post_code(0);
143
Keith Short00dbf442019-04-19 14:02:02 -0600144 /*
145 * Older Cr50 firmware doesn't support the timeout parameter for the
146 * immediate reset request, so the reset request must be sent after
147 * the mainboard specific code runs.
148 */
149 if (cr50_reset_reqd) {
150 ret = tlcl_cr50_immediate_reset(CR50_RESET_DELAY_MS);
151
152 if (ret != TPM_SUCCESS) {
153 /*
154 * Reset request failed due to TPM error, continue
155 * booting but the current boot will likely end up at
156 * the recovery screen.
157 */
158 printk(BIOS_ERR,
159 "ERROR: Attempt to reset CR50 failed: %x\n",
160 ret);
161 return;
162 }
163 }
164
Julius Wernercd49cce2019-03-05 16:53:33 -0800165 if (CONFIG(POWER_OFF_ON_CR50_UPDATE))
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700166 poweroff();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700167 halt();
168}
169BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);