OLD | NEW |
1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 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 | 2 * Use of this source code is governed by a BSD-style license that can be |
3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
4 * | 4 * |
5 * Functions for querying, manipulating and locking rollback indices | 5 * Functions for querying, manipulating and locking rollback indices |
6 * stored in the TPM NVRAM. | 6 * stored in the TPM NVRAM. |
7 */ | 7 */ |
8 | 8 |
9 #include "rollback_index.h" | 9 #include "rollback_index.h" |
10 | 10 |
11 #include <stdint.h> | 11 #include <stdint.h> |
12 | 12 |
13 #include "utility.h" | 13 #include "utility.h" |
14 #include "tlcl.h" | 14 #include "tlcl.h" |
15 #include "tss_constants.h" | 15 #include "tss_constants.h" |
16 | 16 |
17 uint16_t g_firmware_key_version = 0; | 17 uint16_t g_firmware_key_version = 0; |
18 uint16_t g_firmware_version = 0; | 18 uint16_t g_firmware_version = 0; |
19 uint16_t g_kernel_key_version = 0; | 19 uint16_t g_kernel_key_version = 0; |
20 uint16_t g_kernel_version = 0; | 20 uint16_t g_kernel_version = 0; |
21 | 21 |
22 static void InitializeSpaces(void) { | 22 static int InitializeSpaces(void) { |
23 uint16_t zero = 0; | 23 uint32_t zero = 0; |
| 24 uint32_t space_holder; |
24 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE; | 25 uint32_t firmware_perm = TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE; |
25 uint32_t kernel_perm = TPM_NV_PER_PPWRITE; | 26 uint32_t kernel_perm = TPM_NV_PER_PPWRITE; |
26 | 27 |
27 debug("Initializing spaces\n"); | 28 debug("Initializing spaces\n"); |
28 TlclSetNvLocked(); /* useful only the first time */ | |
29 | 29 |
30 TlclDefineSpace(FIRMWARE_KEY_VERSION_NV_INDEX, | 30 if (TlclRead(TPM_IS_INITIALIZED_NV_INDEX, |
31 firmware_perm, sizeof(uint16_t)); | 31 (uint8_t*) &space_holder, sizeof(space_holder)) == TPM_SUCCESS) { |
32 TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); | 32 /* Spaces are already initialized, so this is an error */ |
| 33 return 0; |
| 34 } |
| 35 |
| 36 TlclSetNvLocked(); |
33 | 37 |
34 TlclDefineSpace(FIRMWARE_VERSION_NV_INDEX, firmware_perm, sizeof(uint16_t)); | 38 TlclDefineSpace(FIRMWARE_VERSIONS_NV_INDEX, firmware_perm, sizeof(uint32_t)); |
35 TlclWrite(FIRMWARE_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); | 39 TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); |
36 | 40 |
37 TlclDefineSpace(KERNEL_KEY_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t)); | 41 TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, kernel_perm, sizeof(uint32_t)); |
38 TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); | 42 TlclWrite(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &zero, sizeof(uint32_t)); |
39 | 43 |
40 TlclDefineSpace(KERNEL_VERSION_NV_INDEX, kernel_perm, sizeof(uint16_t)); | 44 /* The space KERNEL_VERSIONS_BACKUP_NV_INDEX is used to protect the kernel |
41 TlclWrite(KERNEL_VERSION_NV_INDEX, (uint8_t*) &zero, sizeof(uint16_t)); | 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; |
42 } | 65 } |
43 | 66 |
44 static void EnterRecovery(void) { | 67 /* Enters the recovery mode. If |unlocked| is true, there is some problem with |
45 /* Temporary recovery stub. Currently just initalizes spaces. */ | 68 * the TPM, so do not attempt to do any more TPM operations, and particularly |
46 InitializeSpaces(); | 69 * do not set bGlobalLock. |
| 70 */ |
| 71 static void EnterRecovery(int unlocked) { |
| 72 uint32_t combined_versions; |
| 73 uint32_t one = 1; |
| 74 |
| 75 if (!unlocked) { |
| 76 /* Saves the kernel versions and indicates that we should trust the saved |
| 77 * ones. |
| 78 */ |
| 79 TlclRead(KERNEL_VERSIONS_NV_INDEX, (uint8_t*) &combined_versions, |
| 80 sizeof(uint32_t)); |
| 81 TlclWrite(KERNEL_VERSIONS_BACKUP_NV_INDEX, (uint8_t*) &combined_versions, |
| 82 sizeof(uint32_t)); |
| 83 TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &one, |
| 84 sizeof(uint32_t)); |
| 85 /* Protects the firmware and backup kernel versions. */ |
| 86 LockFirmwareVersions(); |
| 87 } |
| 88 debug("entering recovery mode"); |
| 89 |
| 90 /* and then what? */ |
47 } | 91 } |
48 | 92 |
49 static int GetTPMRollbackIndices(void) { | 93 static int GetTPMRollbackIndices(void) { |
50 /* We just perform the reads, making sure they succeed. A failure means that | 94 uint32_t backup_is_valid; |
51 * the rollback index locations are some how messed up and we must jump to | 95 uint32_t firmware_versions; |
52 * recovery */ | 96 uint32_t kernel_versions; |
53 if (TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, | 97 |
54 (uint8_t*) &g_firmware_key_version, | 98 if (TlclRead(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &backup_is_valid, |
55 sizeof(g_firmware_key_version)) || | 99 sizeof(uint32_t)) != TPM_SUCCESS) { |
56 TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, | 100 EnterRecovery(1); |
57 (uint8_t*) &g_firmware_key_version, | 101 } |
58 sizeof(g_firmware_key_version)) || | 102 if (backup_is_valid) { |
59 TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, | 103 /* We reach this path if the previous boot went into recovery mode and we |
60 (uint8_t*) &g_firmware_key_version, | 104 * made a copy of the kernel versions to protect them. |
61 sizeof(g_firmware_key_version)) || | 105 */ |
62 TPM_SUCCESS != TlclRead(FIRMWARE_KEY_VERSION_NV_INDEX, | 106 uint32_t protected_combined_versions; |
63 (uint8_t*) &g_firmware_key_version, | 107 uint32_t unsafe_combined_versions; |
64 sizeof(g_firmware_key_version))) | 108 uint32_t result; |
| 109 uint32_t zero = 0; |
| 110 if (TlclRead(KERNEL_VERSIONS_BACKUP_NV_INDEX, |
| 111 (uint8_t*) &protected_combined_versions, |
| 112 sizeof(uint32_t)) != TPM_SUCCESS) { |
| 113 EnterRecovery(1); |
| 114 } |
| 115 result = TlclRead(KERNEL_VERSIONS_NV_INDEX, |
| 116 (uint8_t*) &unsafe_combined_versions, sizeof(uint32_t)); |
| 117 if (result == TPM_E_BADINDEX) { |
| 118 /* Jeez, someone removed the space. This is either hostile or extremely |
| 119 * incompetent. Foo to them. Politeness and lack of an adequate |
| 120 * character set prevent me from expressing my true feelings. |
| 121 */ |
| 122 TlclDefineSpace(KERNEL_VERSIONS_NV_INDEX, TPM_NV_PER_PPWRITE, |
| 123 sizeof(uint32_t)); |
| 124 } else if (result != TPM_SUCCESS) { |
| 125 EnterRecovery(1); |
| 126 } |
| 127 if (result == TPM_E_BADINDEX || |
| 128 protected_combined_versions != unsafe_combined_versions) { |
| 129 TlclWrite(KERNEL_VERSIONS_NV_INDEX, |
| 130 (uint8_t*) &protected_combined_versions, sizeof(uint32_t)); |
| 131 } |
| 132 /* We recovered and now we can reset the BACKUP_IS_VALID flag. |
| 133 */ |
| 134 TlclWrite(KERNEL_BACKUP_IS_VALID_NV_INDEX, (uint8_t*) &zero, 0); |
| 135 } |
| 136 |
| 137 /* We perform the reads, making sure they succeed. A failure means that the |
| 138 * rollback index locations are missing or somehow messed up. We let the |
| 139 * caller deal with that. |
| 140 */ |
| 141 if (TPM_SUCCESS != TlclRead(FIRMWARE_VERSIONS_NV_INDEX, |
| 142 (uint8_t*) &firmware_versions, |
| 143 sizeof(firmware_versions)) || |
| 144 TPM_SUCCESS != TlclRead(KERNEL_VERSIONS_NV_INDEX, |
| 145 (uint8_t*) &kernel_versions, |
| 146 sizeof(kernel_versions))) |
65 return 0; | 147 return 0; |
| 148 |
| 149 g_firmware_key_version = firmware_versions >> 16; |
| 150 g_firmware_version = firmware_versions && 0xffff; |
| 151 g_kernel_key_version = kernel_versions >> 16; |
| 152 g_kernel_version = kernel_versions && 0xffff; |
| 153 |
66 return 1; | 154 return 1; |
67 } | 155 } |
68 | 156 |
69 | 157 |
70 void SetupTPM(void) { | 158 void SetupTPM(void) { |
71 uint8_t disable; | 159 uint8_t disable; |
72 uint8_t deactivated; | 160 uint8_t deactivated; |
73 TlclLibinit(); | 161 TlclLibinit(); |
74 TlclStartup(); | 162 TlclStartup(); |
75 /* TODO(gauravsh): The call to self test should probably be deferred. | 163 /* TODO(gauravsh): The call to self test should probably be deferred. |
76 * As per semenzato@chromium.org - | 164 * As per semenzato@chromium.org - |
77 * TlclStartup should be called before the firmware initializes the memory | 165 * TlclStartup should be called before the firmware initializes the memory |
78 * controller, so the selftest can run in parallel with that. Here we should | 166 * controller, so the selftest can run in parallel with that. Here we should |
79 * just call TlclSelftestFull to make sure the self test has | 167 * just call TlclSelftestFull to make sure the self test has |
80 * completed---unless we want to rely on the NVRAM operations being available | 168 * completed---unless we want to rely on the NVRAM operations being available |
81 * before the selftest completes. */ | 169 * before the selftest completes. */ |
82 TlclSelftestfull(); | 170 TlclSelftestfull(); |
83 TlclAssertPhysicalPresence(); | 171 TlclAssertPhysicalPresence(); |
84 /* Check that the TPM is enabled and activated. */ | 172 /* Check that the TPM is enabled and activated. */ |
85 if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) { | 173 if(TlclGetFlags(&disable, &deactivated) != TPM_SUCCESS) { |
86 debug("failed to get TPM flags"); | 174 debug("failed to get TPM flags"); |
87 EnterRecovery(); | 175 EnterRecovery(1); |
88 } | 176 } |
89 if (disable || deactivated) { | 177 if (disable || deactivated) { |
90 TlclSetEnable(); | 178 TlclSetEnable(); |
91 if (TlclSetDeactivated(0) != TPM_SUCCESS) { | 179 if (TlclSetDeactivated(0) != TPM_SUCCESS) { |
92 debug("failed to activate TPM"); | 180 debug("failed to activate TPM"); |
93 EnterRecovery(); | 181 EnterRecovery(1); |
94 } | 182 } |
95 } | 183 } |
| 184 /* We expect this to fail the first time we run on a device, indicating that |
| 185 * the TPM has not been initialized yet. */ |
96 if (!GetTPMRollbackIndices()) { | 186 if (!GetTPMRollbackIndices()) { |
97 debug("failed to get rollback indices"); | 187 debug("failed to get rollback indices"); |
98 EnterRecovery(); | 188 if (!InitializeSpaces()) { |
| 189 /* If InitializeSpaces() fails (possibly because it had been executed |
| 190 * already), something is wrong. */ |
| 191 EnterRecovery(1); |
| 192 } |
99 } | 193 } |
100 } | 194 } |
101 | 195 |
| 196 void GetStoredVersions(int type, uint16_t* key_version, uint16_t* version) { |
| 197 switch (type) { |
| 198 case FIRMWARE_VERSIONS: |
| 199 *key_version = g_firmware_key_version; |
| 200 *version = g_firmware_version; |
| 201 break; |
| 202 case KERNEL_VERSIONS: |
| 203 *key_version = g_kernel_key_version; |
| 204 *version = g_kernel_version; |
| 205 break; |
| 206 } |
| 207 } |
102 | 208 |
103 uint16_t GetStoredVersion(int type) { | 209 int WriteStoredVersions(int type, uint16_t key_version, uint16_t version) { |
| 210 uint32_t combined_version = (key_version << 16) & version; |
104 switch (type) { | 211 switch (type) { |
105 case FIRMWARE_KEY_VERSION: | 212 case FIRMWARE_VERSIONS: |
106 return g_firmware_key_version; | 213 return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSIONS_NV_INDEX, |
| 214 (uint8_t*) &combined_version, |
| 215 sizeof(uint32_t))); |
107 break; | 216 break; |
108 case FIRMWARE_VERSION: | 217 case KERNEL_VERSIONS: |
109 return g_firmware_version; | 218 return (TPM_SUCCESS == TlclWrite(KERNEL_VERSIONS_NV_INDEX, |
110 break; | 219 (uint8_t*) &combined_version, |
111 case KERNEL_KEY_VERSION: | 220 sizeof(uint32_t))); |
112 return g_kernel_key_version; | |
113 break; | |
114 case KERNEL_VERSION: | |
115 return g_kernel_version; | |
116 break; | 221 break; |
117 } | 222 } |
118 return 0; | 223 return 0; |
119 } | |
120 | |
121 int WriteStoredVersion(int type, uint16_t version) { | |
122 switch (type) { | |
123 case FIRMWARE_KEY_VERSION: | |
124 return (TPM_SUCCESS == TlclWrite(FIRMWARE_KEY_VERSION_NV_INDEX, | |
125 (uint8_t*) &version, | |
126 sizeof(uint16_t))); | |
127 break; | |
128 case FIRMWARE_VERSION: | |
129 return (TPM_SUCCESS == TlclWrite(FIRMWARE_VERSION_NV_INDEX, | |
130 (uint8_t*) &version, | |
131 sizeof(uint16_t))); | |
132 break; | |
133 case KERNEL_KEY_VERSION: | |
134 return (TPM_SUCCESS == TlclWrite(KERNEL_KEY_VERSION_NV_INDEX, | |
135 (uint8_t*) &version, | |
136 sizeof(uint16_t))); | |
137 break; | |
138 case KERNEL_VERSION: | |
139 return (TPM_SUCCESS == TlclWrite(KERNEL_VERSION_NV_INDEX, | |
140 (uint8_t*) &version, | |
141 sizeof(uint16_t))); | |
142 break; | |
143 } | |
144 return 0; | |
145 } | 224 } |
146 | 225 |
147 void LockFirmwareVersions() { | 226 void LockFirmwareVersions() { |
148 if (TlclSetGlobalLock() != TPM_SUCCESS) { | 227 if (TlclSetGlobalLock() != TPM_SUCCESS) { |
149 debug("failed to set global lock"); | 228 debug("failed to set global lock"); |
150 EnterRecovery(); | 229 EnterRecovery(1); |
151 } | 230 } |
152 } | 231 } |
153 | 232 |
154 void LockKernelVersionsByLockingPP() { | 233 void LockKernelVersionsByLockingPP() { |
155 if (TlclLockPhysicalPresence() != TPM_SUCCESS) { | 234 if (TlclLockPhysicalPresence() != TPM_SUCCESS) { |
156 debug("failed to turn off PP"); | 235 debug("failed to turn off PP"); |
157 EnterRecovery(); | 236 EnterRecovery(1); |
158 } | 237 } |
159 } | 238 } |
OLD | NEW |