blob: fe0caffbf8c97a3ebbac8bb2b9a1df96cc6a0ba9 [file] [log] [blame]
Gaurav Shahce0cc302010-03-24 13:48:55 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Functions for querying, manipulating and locking rollback indices
6 * stored in the TPM NVRAM.
7 */
8
9#include "rollback_index.h"
10
11#include <stdint.h>
Gaurav Shahce0cc302010-03-24 13:48:55 -070012
Gaurav Shahf3dd1a62010-04-05 15:50:00 -070013#include "utility.h"
Gaurav Shahce0cc302010-03-24 13:48:55 -070014#include "tlcl.h"
Gaurav Shah887e3d42010-04-27 16:26:48 -070015#include "tss_constants.h"
Gaurav Shahce0cc302010-03-24 13:48:55 -070016
17uint16_t g_firmware_key_version = 0;
18uint16_t g_firmware_version = 0;
19uint16_t g_kernel_key_version = 0;
20uint16_t g_kernel_version = 0;
21
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070022static int InitializeSpaces(void) {
23 uint32_t zero = 0;
24 uint32_t space_holder;
Luigi Semenzato596b6402010-05-27 14:04:52 -070025 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE;
26 uint32_t kernel_perm = TPM_NV_PER_PPWRITE;
Gaurav Shahce0cc302010-03-24 13:48:55 -070027
Gaurav Shahf3dd1a62010-04-05 15:50:00 -070028 debug("Initializing spaces\n");
Gaurav Shahce0cc302010-03-24 13:48:55 -070029
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070030 if (TlclRead(TPM_IS_INITIALIZED_NV_INDEX,
31 (uint8_t*) &space_holder, sizeof(space_holder)) == TPM_SUCCESS) {
32 /* Spaces are already initialized, so this is an error */
33 return 0;
34 }
Luigi Semenzato0a486812010-06-04 13:34:43 -070035
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070036 TlclSetNvLocked();
Gaurav Shahce0cc302010-03-24 13:48:55 -070037
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070038 TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t));
39 TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t));
Gaurav Shahce0cc302010-03-24 13:48:55 -070040
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070041 TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, kernel_perm, sizeof(uint32_t));
42 TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t));
Gaurav Shahce0cc302010-03-24 13:48:55 -070043
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070044 /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel
45 * versions when entering recovery mode. The content of space
46 * KERNEL_BACKUP_IS_VALID determines whether the backup value (1) or the
47 * regular value (0) should be trusted.
48 */
49 TlclDefineSpace(KERNEL_VERSIONS_BACKUP_NV_INDEX,
50 firmware_perm, sizeof(uint32_t));
51 TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
52 (uint8_t*) &zero, sizeof(uint32_t));
53 TlclDefineSpace(KERNEL_BACKUP_IS_VALID_NV_INDEX,
54 firmware_perm, sizeof(uint32_t));
55 TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX,
56 (uint8_t*) &zero, sizeof(uint32_t));
57
58 /* The space TPM_IS_INITIALIZED_NV_INDEX is used to indicate that the TPM
59 * initialization has completed. Without it we cannot be sure that the last
60 * space to be created was also initialized (power could have been lost right
61 * after its creation).
62 */
63 TlclDefineSpace(TPM_IS_INITIALIZED_NV_INDEX, firmware_perm, sizeof(uint32_t));
64 return 1;
Gaurav Shahce0cc302010-03-24 13:48:55 -070065}
66
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070067/* Enters the recovery mode. If |unlocked| is true, there is some problem with
68 * the TPM, so do not attempt to do any more TPM operations, and particularly
69 * do not set bGlobalLock.
70 */
71static void EnterRecovery(int unlocked) {
72 uint32_t combined_versions;
Luigi Semenzato0a486812010-06-04 13:34:43 -070073 uint32_t backup_versions;
74 uint32_t backup_is_valid;
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070075
76 if (!unlocked) {
77 /* Saves the kernel versions and indicates that we should trust the saved
78 * ones.
Luigi Semenzato0a486812010-06-04 13:34:43 -070079 */
80 TlclRead(KERNEL_VERSIONS_NV_INDEX,
81 (uint8_t*) &combined_versions, sizeof(uint32_t));
82 TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
83 (uint8_t*) &backup_versions, sizeof(uint32_t));
84 /* We could unconditional writes of both KERNEL_VERSIONS_BACKUP and
85 * KERNEL_BACKUP_IS_VALID, but this is more robust.
86 */
87 if (combined_versions != backup_versions) {
88 TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX,
89 (uint8_t*) &combined_versions, sizeof(uint32_t));
90 }
91
92 TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX,
93 (uint8_t*) &backup_is_valid, sizeof(uint32_t));
94 if (backup_is_valid != 1) {
95 backup_is_valid = 1;
96 TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid,
97 sizeof(uint32_t));
98 }
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -070099 /* Protects the firmware and backup kernel versions. */
100 LockFirmwareVersions();
101 }
102 debug("entering recovery mode");
Luigi Semenzato0a486812010-06-04 13:34:43 -0700103
104 /* TODO(nelson): code for entering recovery mode. */
Gaurav Shahce0cc302010-03-24 13:48:55 -0700105}
106
107static int GetTPMRollbackIndices(void) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700108 uint32_t backup_is_valid;
109 uint32_t firmware_versions;
110 uint32_t kernel_versions;
111
112 if (TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid,
113 sizeof(uint32_t)) != TPM_SUCCESS) {
114 EnterRecovery(1);
115 }
116 if (backup_is_valid) {
117 /* We reach this path if the previous boot went into recovery mode and we
118 * made a copy of the kernel versions to protect them.
119 */
120 uint32_t protected_combined_versions;
121 uint32_t unsafe_combined_versions;
122 uint32_t result;
123 uint32_t zero = 0;
124 if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX,
125 (uint8_t*) &protected_combined_versions,
126 sizeof(uint32_t)) != TPM_SUCCESS) {
127 EnterRecovery(1);
128 }
129 result = TlclRead(KERNEL_VERSIONS_NV_INDEX,
130 (uint8_t*) &unsafe_combined_versions, sizeof(uint32_t));
131 if (result == TPM_E_BADINDEX) {
132 /* Jeez, someone removed the space. This is either hostile or extremely
133 * incompetent. Foo to them. Politeness and lack of an adequate
134 * character set prevent me from expressing my true feelings.
135 */
136 TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, TPM_NV_PER_PPWRITE,
137 sizeof(uint32_t));
138 } else if (result != TPM_SUCCESS) {
139 EnterRecovery(1);
140 }
141 if (result == TPM_E_BADINDEX ||
142 protected_combined_versions != unsafe_combined_versions) {
143 TlclWrite(KERNEL_VERSIONS_NV_INDEX,
144 (uint8_t*) &protected_combined_versions, sizeof(uint32_t));
145 }
Luigi Semenzato0a486812010-06-04 13:34:43 -0700146 /* We recovered the backed-up versions and now we can reset the
147 * BACKUP_IS_VALID flag.
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700148 */
149 TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0);
Luigi Semenzato0a486812010-06-04 13:34:43 -0700150
151 /* TODO(nelson): ForceClear and reboot if unowned. */
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700152 }
Luigi Semenzato0a486812010-06-04 13:34:43 -0700153
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700154 /* We perform the reads, making sure they succeed. A failure means that the
155 * rollback index locations are missing or somehow messed up. We let the
156 * caller deal with that.
157 */
158 if (TPM_SUCCESS != TlclRead(FIRMWARE_VERSIONS_NV_INDEX,
159 (uint8_t*) &firmware_versions,
160 sizeof(firmware_versions)) ||
161 TPM_SUCCESS != TlclRead(KERNEL_VERSIONS_NV_INDEX,
162 (uint8_t*) &kernel_versions,
163 sizeof(kernel_versions)))
Gaurav Shahce0cc302010-03-24 13:48:55 -0700164 return 0;
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700165
166 g_firmware_key_version = firmware_versions >> 16;
167 g_firmware_version = firmware_versions && 0xffff;
168 g_kernel_key_version = kernel_versions >> 16;
169 g_kernel_version = kernel_versions && 0xffff;
Luigi Semenzato0a486812010-06-04 13:34:43 -0700170
Gaurav Shahce0cc302010-03-24 13:48:55 -0700171 return 1;
172}
173
174
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700175int SetupTPM(void) {
Luigi Semenzato596b6402010-05-27 14:04:52 -0700176 uint8_t disable;
177 uint8_t deactivated;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700178 TlclLibinit();
179 TlclStartup();
180 /* TODO(gauravsh): The call to self test should probably be deferred.
181 * As per semenzato@chromium.org -
182 * TlclStartup should be called before the firmware initializes the memory
183 * controller, so the selftest can run in parallel with that. Here we should
184 * just call TlclSelftestFull to make sure the self test has
185 * completed---unless we want to rely on the NVRAM operations being available
186 * before the selftest completes. */
187 TlclSelftestfull();
188 TlclAssertPhysicalPresence();
Luigi Semenzato596b6402010-05-27 14:04:52 -0700189 /* Check that the TPM is enabled and activated. */
190 if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) {
191 debug("failed to get TPM flags");
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700192 return 1;
Luigi Semenzato596b6402010-05-27 14:04:52 -0700193 }
194 if (disable || deactivated) {
195 TlclSetEnable();
196 if (TlclSetDeactivated(0) != TPM_SUCCESS) {
197 debug("failed to activate TPM");
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700198 return 1;
Luigi Semenzato596b6402010-05-27 14:04:52 -0700199 }
200 }
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700201 /* We expect this to fail the first time we run on a device, indicating that
202 * the TPM has not been initialized yet. */
Gaurav Shahce0cc302010-03-24 13:48:55 -0700203 if (!GetTPMRollbackIndices()) {
Luigi Semenzato596b6402010-05-27 14:04:52 -0700204 debug("failed to get rollback indices");
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700205 if (!InitializeSpaces()) {
206 /* If InitializeSpaces() fails (possibly because it had been executed
207 * already), something is wrong. */
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700208 return 1;
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700209 }
Gaurav Shahce0cc302010-03-24 13:48:55 -0700210 }
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700211
212 return 0;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700213}
214
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700215int GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) {
216
217 /* TODO: should verify that SetupTPM() has been called. Note that
218 * SetupTPM() does hardware setup AND sets global variables. When we
219 * get down into kernel verification, the hardware setup persists, but
220 * we don't have access to the global variables. So I guess we DO need
221 * to call SetupTPM() there, and have it be smart enough not to redo the
222 * hardware init, but it still needs to re-read the flags... */
223
Gaurav Shahce0cc302010-03-24 13:48:55 -0700224 switch (type) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700225 case FIRMWARE_VERSIONS:
226 *key_version = g_firmware_key_version;
227 *version = g_firmware_version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700228 break;
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700229 case KERNEL_VERSIONS:
230 *key_version = g_kernel_key_version;
231 *version = g_kernel_version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700232 break;
233 }
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700234
235 return 0;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700236}
237
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700238int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) {
239 uint32_t combined_version = (key_version << 16) & version;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700240 switch (type) {
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700241 case FIRMWARE_VERSIONS:
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700242 return (TPM_SUCCESS != TlclWrite(FIRMWARE_VERSIONS_NV_INDEX,
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700243 (uint8_t*) &combined_version,
244 sizeof(uint32_t)));
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700245
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700246 case KERNEL_VERSIONS:
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700247 return (TPM_SUCCESS != TlclWrite(KERNEL_VERSIONS_NV_INDEX,
Luigi Semenzato52a8d2d2010-05-28 10:34:31 -0700248 (uint8_t*) &combined_version,
249 sizeof(uint32_t)));
Gaurav Shahce0cc302010-03-24 13:48:55 -0700250 }
Luigi Semenzato0a486812010-06-04 13:34:43 -0700251 /* TODO(nelson): ForceClear and reboot if unowned. */
252
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700253 return 1;
254}
255
256int LockFirmwareVersions() {
257 if (TlclSetGlobalLock() != TPM_SUCCESS) {
258 debug("failed to set global lock");
259 return 1;
260 }
Gaurav Shahce0cc302010-03-24 13:48:55 -0700261 return 0;
262}
263
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700264int LockKernelVersionsByLockingPP() {
Luigi Semenzato596b6402010-05-27 14:04:52 -0700265 if (TlclLockPhysicalPresence() != TPM_SUCCESS) {
266 debug("failed to turn off PP");
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700267 return 1;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700268 }
Randall Spangler4a7a9e32010-06-08 15:44:24 -0700269 return 0;
Gaurav Shahce0cc302010-03-24 13:48:55 -0700270}