blob: 950e9301331b7e68b1e78bed565e45bcab4985e1 [file] [log] [blame]
Philipp Deppenwiesec07f8fb2018-02-27 19:40:52 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
5 * Copyright 2017 Facebook Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <console/cbmem_console.h>
18#include <console/console.h>
19#include <reset.h>
20#include <security/tpm/tspi.h>
21#include <security/tpm/tss.h>
22#include <stdlib.h>
23#include <string.h>
24
25#if IS_ENABLED(CONFIG_TPM1)
26static uint32_t tpm1_invoke_state_machine(void)
27{
28 uint8_t disable;
29 uint8_t deactivated;
30 uint32_t result = TPM_SUCCESS;
31
32 /* Check that the TPM is enabled and activated. */
33 result = tlcl_get_flags(&disable, &deactivated, NULL);
34 if (result != TPM_SUCCESS) {
35 printk(BIOS_ERR, "TPM: Can't read capabilities.\n");
36 return result;
37 }
38
39 if (!!deactivated != IS_ENABLED(CONFIG_TPM_DEACTIVATE)) {
40 printk(BIOS_INFO,
41 "TPM: Unexpected TPM deactivated state. Toggling...\n");
42 result = tlcl_set_deactivated(!deactivated);
43 if (result != TPM_SUCCESS) {
44 printk(BIOS_ERR,
45 "TPM: Can't toggle deactivated state.\n");
46 return result;
47 }
48
49 deactivated = !deactivated;
50 result = TPM_E_MUST_REBOOT;
51 }
52
53 if (disable && !deactivated) {
54 printk(BIOS_INFO, "TPM: disabled (%d). Enabling...\n", disable);
55
56 result = tlcl_set_enable();
57 if (result != TPM_SUCCESS) {
58 printk(BIOS_ERR, "TPM: Can't set enabled state.\n");
59 return result;
60 }
61
62 printk(BIOS_INFO, "TPM: Must reboot to re-enable\n");
63 result = TPM_E_MUST_REBOOT;
64 }
65
66 return result;
67}
68#endif
69
70/*
71 * tpm_setup starts the TPM and establishes the root of trust for the
72 * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a
73 * TPM hardware failure. 3 An unexpected TPM state due to some attack. In
74 * general we cannot easily distinguish the kind of failure, so our strategy is
75 * to reboot in recovery mode in all cases. The recovery mode calls SetupTPM
76 * again, which executes (almost) the same sequence of operations. There is a
77 * good chance that, if recovery mode was entered because of a TPM failure, the
78 * failure will repeat itself. (In general this is impossible to guarantee
79 * because we have no way of creating the exact TPM initial state at the
80 * previous boot.) In recovery mode, we ignore the failure and continue, thus
81 * giving the recovery kernel a chance to fix things (that's why we don't set
82 * bGlobalLock). The choice is between a knowingly insecure device and a
83 * bricked device.
84 *
85 * As a side note, observe that we go through considerable hoops to avoid using
86 * the STCLEAR permissions for the index spaces. We do this to avoid writing
87 * to the TPM flashram at every reboot or wake-up, because of concerns about
88 * the durability of the NVRAM.
89 */
90uint32_t tpm_setup(int s3flag)
91{
92 uint32_t result;
93
94 result = tlcl_lib_init();
95 if (result != TPM_SUCCESS) {
96 printk(BIOS_ERR, "TPM: Can't initialize.\n");
97 goto out;
98 }
99
100 /* Handle special init for S3 resume path */
101 if (s3flag) {
102 result = tlcl_resume();
103 if (result == TPM_E_INVALID_POSTINIT)
104 printk(BIOS_INFO, "TPM: Already initialized.\n");
105
106 return TPM_SUCCESS;
107 }
108
109 result = tlcl_startup();
110 if (result != TPM_SUCCESS) {
111 printk(BIOS_ERR, "TPM: Can't run startup command.\n");
112 goto out;
113 }
114
115 result = tlcl_assert_physical_presence();
116 if (result != TPM_SUCCESS) {
117 /*
118 * It is possible that the TPM was delivered with the physical
119 * presence command disabled. This tries enabling it, then
120 * tries asserting PP again.
121 */
122 result = tlcl_physical_presence_cmd_enable();
123 if (result != TPM_SUCCESS) {
124 printk(
125 BIOS_ERR,
126 "TPM: Can't enable physical presence command.\n");
127 goto out;
128 }
129
130 result = tlcl_assert_physical_presence();
131 if (result != TPM_SUCCESS) {
132 printk(BIOS_ERR,
133 "TPM: Can't assert physical presence.\n");
134 goto out;
135 }
136 }
137
138#if IS_ENABLED(CONFIG_TPM1)
139 result = tpm1_invoke_state_machine();
140 if (result != TPM_SUCCESS)
141 return result;
142#endif
143
144out:
145 if (result != TPM_SUCCESS)
146 post_code(POST_TPM_FAILURE);
147 else
148 printk(BIOS_INFO, "TPM: setup succeeded\n");
149
150 return result;
151}
152
153uint32_t tpm_clear_and_reenable(void)
154{
155 uint32_t result;
156
157 printk(BIOS_INFO, "TPM: Clear and re-enable\n");
158 result = tlcl_force_clear();
159 if (result != TPM_SUCCESS) {
160 printk(BIOS_ERR, "TPM: Can't initiate a force clear.\n");
161 return result;
162 }
163
164#if IS_ENABLED(CONFIG_TPM1)
165 result = tlcl_set_enable();
166 if (result != TPM_SUCCESS) {
167 printk(BIOS_ERR, "TPM: Can't set enabled state.\n");
168 return result;
169 }
170
171 result = tlcl_set_deactivated(0);
172 if (result != TPM_SUCCESS) {
173 printk(BIOS_ERR, "TPM: Can't set deactivated state.\n");
174 return result;
175 }
176#endif
177
178 return TPM_SUCCESS;
179}
180
Philipp Deppenwiesef8499722018-07-30 01:27:47 +0200181uint32_t tpm_extend_pcr(int pcr, uint8_t *digest,
182 size_t digest_len, const char *name)
Philipp Deppenwiesec07f8fb2018-02-27 19:40:52 +0100183{
Philipp Deppenwiesef8499722018-07-30 01:27:47 +0200184 uint32_t result;
185
Philipp Deppenwiesec07f8fb2018-02-27 19:40:52 +0100186 if (!digest)
187 return TPM_E_IOERROR;
188
Philipp Deppenwiesef8499722018-07-30 01:27:47 +0200189 result = tlcl_extend(pcr, digest, NULL);
190 if (result != TPM_SUCCESS)
191 return result;
Philipp Deppenwiesec07f8fb2018-02-27 19:40:52 +0100192
Furquan Shaikh38f3ffa2018-07-31 14:26:39 -0700193 tcpa_log_add_table_entry(name, pcr, digest, digest_len);
Philipp Deppenwiesef8499722018-07-30 01:27:47 +0200194
Furquan Shaikh38f3ffa2018-07-31 14:26:39 -0700195 return TPM_SUCCESS;
Philipp Deppenwiesec07f8fb2018-02-27 19:40:52 +0100196}