blob: 660fe2e86fc9e1ccfd97a6623d055390499699b1 [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 Shorte0f34002019-02-05 16:15:10 -070026#define C50_RESET_DELAY_MS 1000
27
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
34 * unsupported. Returns 1 if the Cr50 was reset.
35 */
36static int cr50_reset_if_needed(uint16_t timeout_ms)
37{
38 int ret;
39 int cr50_must_reset = 0;
40 uint8_t tpm_mode;
41
42 ret = tlcl_cr50_get_tpm_mode(&tpm_mode);
43
44 if (ret == TPM_E_NO_SUCH_COMMAND) {
45 printk(BIOS_INFO,
46 "Cr50 does not support TPM mode command\n");
47 /* Older Cr50 firmware, assume no Cr50 reset is required */
48 return 0;
49 }
50
51 if (ret == TPM_E_MUST_REBOOT) {
52 /*
53 * Cr50 indicated a reboot is required to restore TPM
54 * functionality.
55 */
56 cr50_must_reset = 1;
57 } else if (ret != TPM_SUCCESS) {
58 /* TPM command failed, continue booting. */
59 printk(BIOS_ERR,
60 "ERROR: Attempt to get CR50 TPM mode failed: %x\n", ret);
61 return 0;
62 }
63
64 /* If the TPM mode is not enabled-tentative, then the TPM mode is locked
65 * 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);
75 cr50_must_reset = 1;
76 }
77
78 /* If TPM state is okay, no reset needed. */
79 if (!cr50_must_reset)
80 return 0;
81
82 ret = tlcl_cr50_immediate_reset(timeout_ms);
83
84 if (ret != TPM_SUCCESS) {
85 /* TPM command failed, continue booting. */
86 printk(BIOS_ERR,
87 "ERROR: Attempt to reset CR50 failed: %x\n",
88 ret);
89 return 0;
90 }
91
92 /* Cr50 is about to be reset, caller needs to prepare */
93 return 1;
94}
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -070095
96static void enable_update(void *unused)
97{
98 int ret;
99 uint8_t num_restored_headers;
100
101 /* Nothing to do on recovery mode. */
102 if (vboot_recovery_mode_enabled())
103 return;
104
105 ret = tlcl_lib_init();
106
107 if (ret != VB2_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -0700108 printk(BIOS_ERR,
109 "ERROR: tlcl_lib_init() failed for CR50 update: %x\n",
110 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700111 return;
112 }
113
114 /* Reboot in 1000 ms if necessary. */
Keith Shorte0f34002019-02-05 16:15:10 -0700115 ret = tlcl_cr50_enable_update(C50_RESET_DELAY_MS,
116 &num_restored_headers);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700117
118 if (ret != TPM_SUCCESS) {
Keith Shorte0f34002019-02-05 16:15:10 -0700119 printk(BIOS_ERR,
120 "ERROR: Attempt to enable CR50 update failed: %x\n",
121 ret);
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700122 return;
123 }
124
Keith Shorte0f34002019-02-05 16:15:10 -0700125 if (!num_restored_headers) {
126 /* If no headers were restored there is no reset forthcoming due
127 * to a Cr50 firmware update. Also check if the Cr50 TPM mode
128 * requires a reset.
129 *
130 * TODO: to eliminate a TPM command during every boot, the
131 * TURN_UPDATE_ON command could be enhanced/replaced in the Cr50
132 * firmware to perform the TPM mode/key-ladder check in addition
133 * to the FW version check.
134 */
135
136 /*
137 * If the Cr50 was not reset, continue booting.
138 */
139 if (!cr50_reset_if_needed(C50_RESET_DELAY_MS))
140 return;
141
142 printk(BIOS_INFO, "Waiting for CR50 reset to enable TPM.\n");
143 elog_add_event(ELOG_TYPE_CR50_NEED_RESET);
144 } else {
145 printk(BIOS_INFO,
146 "Waiting for CR50 reset to pick up update.\n");
147 elog_add_event(ELOG_TYPE_CR50_UPDATE);
148 }
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700149
Duncan Laurief131fc72019-01-23 15:01:21 -0800150 /* Give mainboard a chance to take action */
Keith Shorte0f34002019-02-05 16:15:10 -0700151 mainboard_prepare_cr50_reset();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700152
153 /* clear current post code avoid chatty eventlog on subsequent boot*/
154 post_code(0);
155
Aaron Durbin6db1b2f2018-10-12 12:33:34 -0600156 if (IS_ENABLED(CONFIG_POWER_OFF_ON_CR50_UPDATE))
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700157 poweroff();
Vadim Bendeburyb9126fe2017-03-22 16:16:34 -0700158 halt();
159}
160BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_ENTRY, enable_update, NULL);