Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 1 | /* 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 Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 12 | |
Gaurav Shah | f3dd1a6 | 2010-04-05 15:50:00 -0700 | [diff] [blame] | 13 | #include "utility.h" |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 14 | #include "tlcl.h" |
Gaurav Shah | 887e3d4 | 2010-04-27 16:26:48 -0700 | [diff] [blame] | 15 | #include "tss_constants.h" |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 16 | |
| 17 | uint16_t g_firmware_key_version = 0; |
| 18 | uint16_t g_firmware_version = 0; |
| 19 | uint16_t g_kernel_key_version = 0; |
| 20 | uint16_t g_kernel_version = 0; |
| 21 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 22 | static int InitializeSpaces(void) { |
| 23 | uint32_t zero = 0; |
| 24 | uint32_t space_holder; |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 25 | uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE; |
| 26 | uint32_t kernel_perm = TPM_NV_PER_PPWRITE; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 27 | |
Gaurav Shah | f3dd1a6 | 2010-04-05 15:50:00 -0700 | [diff] [blame] | 28 | debug("Initializing spaces\n"); |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 29 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 30 | 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 Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 35 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 36 | TlclSetNvLocked(); |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 37 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 38 | TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t)); |
| 39 | TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 40 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 41 | TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, kernel_perm, sizeof(uint32_t)); |
| 42 | TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 43 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 44 | /* 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 Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 65 | } |
| 66 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 67 | /* 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 | */ |
| 71 | static void EnterRecovery(int unlocked) { |
| 72 | uint32_t combined_versions; |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 73 | uint32_t backup_versions; |
| 74 | uint32_t backup_is_valid; |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 75 | |
| 76 | if (!unlocked) { |
| 77 | /* Saves the kernel versions and indicates that we should trust the saved |
| 78 | * ones. |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 79 | */ |
| 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 Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 99 | /* Protects the firmware and backup kernel versions. */ |
| 100 | LockFirmwareVersions(); |
| 101 | } |
| 102 | debug("entering recovery mode"); |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 103 | |
| 104 | /* TODO(nelson): code for entering recovery mode. */ |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | static int GetTPMRollbackIndices(void) { |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 108 | 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 Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 146 | /* We recovered the backed-up versions and now we can reset the |
| 147 | * BACKUP_IS_VALID flag. |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 148 | */ |
| 149 | TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0); |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 150 | |
| 151 | /* TODO(nelson): ForceClear and reboot if unowned. */ |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 152 | } |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 153 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 154 | /* 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 Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 164 | return 0; |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 165 | |
| 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 Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 170 | |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 171 | return 1; |
| 172 | } |
| 173 | |
| 174 | |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 175 | int SetupTPM(void) { |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 176 | uint8_t disable; |
| 177 | uint8_t deactivated; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 178 | 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 Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 189 | /* Check that the TPM is enabled and activated. */ |
| 190 | if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) { |
| 191 | debug("failed to get TPM flags"); |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 192 | return 1; |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 193 | } |
| 194 | if (disable || deactivated) { |
| 195 | TlclSetEnable(); |
| 196 | if (TlclSetDeactivated(0) != TPM_SUCCESS) { |
| 197 | debug("failed to activate TPM"); |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 198 | return 1; |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 199 | } |
| 200 | } |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 201 | /* We expect this to fail the first time we run on a device, indicating that |
| 202 | * the TPM has not been initialized yet. */ |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 203 | if (!GetTPMRollbackIndices()) { |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 204 | debug("failed to get rollback indices"); |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 205 | if (!InitializeSpaces()) { |
| 206 | /* If InitializeSpaces() fails (possibly because it had been executed |
| 207 | * already), something is wrong. */ |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 208 | return 1; |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 209 | } |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 210 | } |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 211 | |
| 212 | return 0; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 213 | } |
| 214 | |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 215 | int 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 Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 224 | switch (type) { |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 225 | case FIRMWARE_VERSIONS: |
| 226 | *key_version = g_firmware_key_version; |
| 227 | *version = g_firmware_version; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 228 | break; |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 229 | case KERNEL_VERSIONS: |
| 230 | *key_version = g_kernel_key_version; |
| 231 | *version = g_kernel_version; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 232 | break; |
| 233 | } |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 234 | |
| 235 | return 0; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 236 | } |
| 237 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 238 | int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { |
| 239 | uint32_t combined_version = (key_version << 16) & version; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 240 | switch (type) { |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 241 | case FIRMWARE_VERSIONS: |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 242 | return (TPM_SUCCESS != TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 243 | (uint8_t*) &combined_version, |
| 244 | sizeof(uint32_t))); |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 245 | |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 246 | case KERNEL_VERSIONS: |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 247 | return (TPM_SUCCESS != TlclWrite(KERNEL_VERSIONS_NV_INDEX, |
Luigi Semenzato | 52a8d2d | 2010-05-28 10:34:31 -0700 | [diff] [blame] | 248 | (uint8_t*) &combined_version, |
| 249 | sizeof(uint32_t))); |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 250 | } |
Luigi Semenzato | 0a48681 | 2010-06-04 13:34:43 -0700 | [diff] [blame] | 251 | /* TODO(nelson): ForceClear and reboot if unowned. */ |
| 252 | |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 253 | return 1; |
| 254 | } |
| 255 | |
| 256 | int LockFirmwareVersions() { |
| 257 | if (TlclSetGlobalLock() != TPM_SUCCESS) { |
| 258 | debug("failed to set global lock"); |
| 259 | return 1; |
| 260 | } |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 261 | return 0; |
| 262 | } |
| 263 | |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 264 | int LockKernelVersionsByLockingPP() { |
Luigi Semenzato | 596b640 | 2010-05-27 14:04:52 -0700 | [diff] [blame] | 265 | if (TlclLockPhysicalPresence() != TPM_SUCCESS) { |
| 266 | debug("failed to turn off PP"); |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 267 | return 1; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 268 | } |
Randall Spangler | 4a7a9e3 | 2010-06-08 15:44:24 -0700 | [diff] [blame^] | 269 | return 0; |
Gaurav Shah | ce0cc30 | 2010-03-24 13:48:55 -0700 | [diff] [blame] | 270 | } |