tlcl: automatically detect if platform hierarchy is disabled
[vboot.git] / firmware / lib / tpm_lite / tlcl.c
1 /* Copyright (c) 2012 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
6 /* A lightweight TPM command library.
7  *
8  * The general idea is that TPM commands are array of bytes whose
9  * fields are mostly compile-time constant.  The goal is to build much
10  * of the commands at compile time (or build time) and change some of
11  * the fields at run time as needed.  The code in
12  * utility/tlcl_generator.c builds structures containing the commands,
13  * as well as the offsets of the fields that need to be set at run
14  * time.
15  */
16
17 #include "sysincludes.h"
18
19 #include "tlcl.h"
20 #include "tlcl_internal.h"
21 #include "tlcl_structures.h"
22 #include "utility.h"
23 #include "vboot_api.h"
24
25 #ifdef FOR_TEST
26 /* Allow unit testing implementation of TlclSendReceive() */
27 #undef CHROMEOS_ENVIRONMENT
28 #endif
29
30 /* Sets the size field of a TPM command. */
31 static inline void SetTpmCommandSize(uint8_t* buffer, uint32_t size) {
32   ToTpmUint32(buffer + sizeof(uint16_t), size);
33 }
34
35 /* Gets the size field of a TPM command. */
36 __attribute__((unused))
37 static inline int TpmCommandSize(const uint8_t* buffer) {
38   uint32_t size;
39   FromTpmUint32(buffer + sizeof(uint16_t), &size);
40   return (int) size;
41 }
42
43 /* Gets the size field of a TPM request or response. */
44 int TlclPacketSize(const uint8_t* packet) {
45   return TpmCommandSize(packet);
46 }
47
48 /* Gets the code field of a TPM command. */
49 static inline int TpmCommandCode(const uint8_t* buffer) {
50   uint32_t code;
51   FromTpmUint32(buffer + sizeof(uint16_t) + sizeof(uint32_t), &code);
52   return code;
53 }
54
55 /* Gets the return code field of a TPM result. */
56 static inline int TpmReturnCode(const uint8_t* buffer) {
57   return TpmCommandCode(buffer);
58 }
59
60 /* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or
61  * DOING_SELFTEST errors are returned.
62  */
63 static uint32_t TlclSendReceiveNoRetry(const uint8_t* request,
64                                        uint8_t* response, int max_length) {
65
66   uint32_t response_length = max_length;
67   uint32_t result;
68
69 #ifdef EXTRA_LOGGING
70   VBDEBUG(("TPM: command: %x%x %x%x%x%x %x%x%x%x\n",
71            request[0], request[1],
72            request[2], request[3], request[4], request[5],
73            request[6], request[7], request[8], request[9]));
74 #endif
75
76   result = VbExTpmSendReceive(request, TpmCommandSize(request),
77                               response, &response_length);
78   if (0 != result) {
79     /* Communication with TPM failed, so response is garbage */
80     VBDEBUG(("TPM: command 0x%x send/receive failed: 0x%x\n",
81              TpmCommandCode(request), result));
82     return result;
83   }
84   /* Otherwise, use the result code from the response */
85   result = TpmReturnCode(response);
86
87   /* TODO: add paranoia about returned response_length vs. max_length
88    * (and possibly expected length from the response header).  See
89    * crosbug.com/17017 */
90
91 #ifdef EXTRA_LOGGING
92   VBDEBUG(("TPM: response: %x%x %x%x%x%x %x%x%x%x\n",
93            response[0], response[1],
94            response[2], response[3], response[4], response[5],
95            response[6], response[7], response[8], response[9]));
96 #endif
97
98   VBDEBUG(("TPM: command 0x%x returned 0x%x\n",
99            TpmCommandCode(request), result));
100
101   return result;
102 }
103
104
105 /* Sends a TPM command and gets a response.  Returns 0 if success or the TPM
106  * error code if error. In the firmware, waits for the self test to complete
107  * if needed. In the host, reports the first error without retries. */
108 uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response,
109                          int max_length) {
110   uint32_t result = TlclSendReceiveNoRetry(request, response, max_length);
111   /* When compiling for the firmware, hide command failures due to the self
112    * test not having run or completed. */
113 #ifndef CHROMEOS_ENVIRONMENT
114   /* If the command fails because the self test has not completed, try it
115    * again after attempting to ensure that the self test has completed. */
116   if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) {
117     result = TlclContinueSelfTest();
118     if (result != TPM_SUCCESS) {
119       return result;
120     }
121 #if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE)
122     /* Retry only once */
123     result = TlclSendReceiveNoRetry(request, response, max_length);
124 #else
125     /* This needs serious testing.  The TPM specification says: "iii. The
126      * caller MUST wait for the actions of TPM_ContinueSelfTest to complete
127      * before reissuing the command C1."  But, if ContinueSelfTest is
128      * non-blocking, how do we know that the actions have completed other than
129      * trying again? */
130     do {
131       result = TlclSendReceiveNoRetry(request, response, max_length);
132     } while (result == TPM_E_DOING_SELFTEST);
133 #endif
134   }
135 #endif  /* ! defined(CHROMEOS_ENVIRONMENT) */
136   return result;
137 }
138
139 /* Sends a command and returns the error code. */
140 static uint32_t Send(const uint8_t* command) {
141   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
142   return TlclSendReceive(command, response, sizeof(response));
143 }
144
145 /* Exported functions. */
146
147 uint32_t TlclLibInit(void) {
148   return VbExTpmInit();
149 }
150
151 uint32_t TlclLibClose(void) {
152   return VbExTpmClose();
153 }
154
155 uint32_t TlclStartup(void) {
156   VBDEBUG(("TPM: Startup\n"));
157   return Send(tpm_startup_cmd.buffer);
158 }
159
160 uint32_t TlclSaveState(void) {
161   VBDEBUG(("TPM: SaveState\n"));
162   return Send(tpm_savestate_cmd.buffer);
163 }
164
165 uint32_t TlclResume(void) {
166   VBDEBUG(("TPM: Resume\n"));
167   return Send(tpm_resume_cmd.buffer);
168 }
169
170 uint32_t TlclSelfTestFull(void) {
171   VBDEBUG(("TPM: Self test full\n"));
172   return Send(tpm_selftestfull_cmd.buffer);
173 }
174
175 uint32_t TlclContinueSelfTest(void) {
176   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
177   VBDEBUG(("TPM: Continue self test\n"));
178   /* Call the No Retry version of SendReceive to avoid recursion. */
179   return TlclSendReceiveNoRetry(tpm_continueselftest_cmd.buffer,
180                                 response, sizeof(response));
181 }
182
183 uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) {
184   struct s_tpm_nv_definespace_cmd cmd;
185   VBDEBUG(("TPM: TlclDefineSpace(0x%x, 0x%x, %d)\n", index, perm, size));
186   Memcpy(&cmd, &tpm_nv_definespace_cmd, sizeof(cmd));
187   ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.index, index);
188   ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.perm, perm);
189   ToTpmUint32(cmd.buffer + tpm_nv_definespace_cmd.size, size);
190   return Send(cmd.buffer);
191 }
192
193 uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) {
194   struct s_tpm_nv_write_cmd cmd;
195   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
196   const int total_length =
197     kTpmRequestHeaderLength + kWriteInfoLength + length;
198
199   VBDEBUG(("TPM: TlclWrite(0x%x, %d)\n", index, length));
200   Memcpy(&cmd, &tpm_nv_write_cmd, sizeof(cmd));
201   VbAssert(total_length <= TPM_LARGE_ENOUGH_COMMAND_SIZE);
202   SetTpmCommandSize(cmd.buffer, total_length);
203
204   ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.index, index);
205   ToTpmUint32(cmd.buffer + tpm_nv_write_cmd.length, length);
206   Memcpy(cmd.buffer + tpm_nv_write_cmd.data, data, length);
207
208   return TlclSendReceive(cmd.buffer, response, sizeof(response));
209 }
210
211 uint32_t TlclRead(uint32_t index, void* data, uint32_t length) {
212   struct s_tpm_nv_read_cmd cmd;
213   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
214   uint32_t result_length;
215   uint32_t result;
216
217   VBDEBUG(("TPM: TlclRead(0x%x, %d)\n", index, length));
218   Memcpy(&cmd, &tpm_nv_read_cmd, sizeof(cmd));
219   ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.index, index);
220   ToTpmUint32(cmd.buffer + tpm_nv_read_cmd.length, length);
221
222   result = TlclSendReceive(cmd.buffer, response, sizeof(response));
223   if (result == TPM_SUCCESS && length > 0) {
224     uint8_t* nv_read_cursor = response + kTpmResponseHeaderLength;
225     FromTpmUint32(nv_read_cursor, &result_length);
226     nv_read_cursor += sizeof(uint32_t);
227     Memcpy(data, nv_read_cursor, result_length);
228   }
229
230   return result;
231 }
232
233 uint32_t TlclPCRRead(uint32_t index, void* data, uint32_t length) {
234   struct s_tpm_pcr_read_cmd cmd;
235   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
236   uint32_t result;
237
238   VBDEBUG(("TPM: TlclPCRRead(0x%x, %d)\n", index, length));
239   if (length < kPcrDigestLength) {
240     return TPM_E_IOERROR;
241   }
242   Memcpy(&cmd, &tpm_pcr_read_cmd, sizeof(cmd));
243   ToTpmUint32(cmd.buffer + tpm_pcr_read_cmd.pcrNum, index);
244
245   result = TlclSendReceive(cmd.buffer, response, sizeof(response));
246   if (result == TPM_SUCCESS) {
247     uint8_t* pcr_read_cursor = response + kTpmResponseHeaderLength;
248     Memcpy(data, pcr_read_cursor, kPcrDigestLength);
249   }
250
251   return result;
252 }
253
254 uint32_t TlclWriteLock(uint32_t index) {
255   VBDEBUG(("TPM: Write lock 0x%x\n", index));
256   return TlclWrite(index, NULL, 0);
257 }
258
259 uint32_t TlclReadLock(uint32_t index) {
260   VBDEBUG(("TPM: Read lock 0x%x\n", index));
261   return TlclRead(index, NULL, 0);
262 }
263
264 uint32_t TlclAssertPhysicalPresence(void) {
265   VBDEBUG(("TPM: Asserting physical presence\n"));
266   return Send(tpm_ppassert_cmd.buffer);
267 }
268
269 uint32_t TlclPhysicalPresenceCMDEnable(void) {
270   VBDEBUG(("TPM: Enable the physical presence command\n"));
271   return Send(tpm_ppenable_cmd.buffer);
272 }
273
274 uint32_t TlclFinalizePhysicalPresence(void) {
275   VBDEBUG(("TPM: Enable PP cmd, disable HW pp, and set lifetime lock\n"));
276   return Send(tpm_finalizepp_cmd.buffer);
277 }
278
279 uint32_t TlclAssertPhysicalPresenceResult(void) {
280   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
281   return TlclSendReceive(tpm_ppassert_cmd.buffer, response, sizeof(response));
282 }
283
284 uint32_t TlclLockPhysicalPresence(void) {
285   VBDEBUG(("TPM: Lock physical presence\n"));
286   return Send(tpm_pplock_cmd.buffer);
287 }
288
289 uint32_t TlclSetNvLocked(void) {
290   VBDEBUG(("TPM: Set NV locked\n"));
291   return TlclDefineSpace(TPM_NV_INDEX_LOCK, 0, 0);
292 }
293
294 int TlclIsOwned(void) {
295   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE + TPM_PUBEK_SIZE];
296   uint32_t result;
297   result = TlclSendReceive(tpm_readpubek_cmd.buffer,
298                            response, sizeof(response));
299   return (result != TPM_SUCCESS);
300 }
301
302 uint32_t TlclForceClear(void) {
303   VBDEBUG(("TPM: Force clear\n"));
304   return Send(tpm_forceclear_cmd.buffer);
305 }
306
307 uint32_t TlclSetEnable(void) {
308   VBDEBUG(("TPM: Enabling TPM\n"));
309   return Send(tpm_physicalenable_cmd.buffer);
310 }
311
312 uint32_t TlclClearEnable(void) {
313   VBDEBUG(("TPM: Disabling TPM\n"));
314   return Send(tpm_physicaldisable_cmd.buffer);
315 }
316
317 uint32_t TlclSetDeactivated(uint8_t flag) {
318   struct s_tpm_physicalsetdeactivated_cmd cmd;
319   VBDEBUG(("TPM: SetDeactivated(%d)\n", flag));
320   Memcpy(&cmd, &tpm_physicalsetdeactivated_cmd, sizeof(cmd));
321   *(cmd.buffer + cmd.deactivated) = flag;
322   return Send(cmd.buffer);
323 }
324
325 uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) {
326   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
327   uint32_t size;
328   uint32_t result =
329     TlclSendReceive(tpm_getflags_cmd.buffer, response, sizeof(response));
330   if (result != TPM_SUCCESS)
331     return result;
332   FromTpmUint32(response + kTpmResponseHeaderLength, &size);
333   /* TODO(crbug.com/379255): This fails. Find out why.
334    * VbAssert(size == sizeof(TPM_PERMANENT_FLAGS));
335    */
336   Memcpy(pflags,
337          response + kTpmResponseHeaderLength + sizeof(size),
338          sizeof(TPM_PERMANENT_FLAGS));
339   return result;
340 }
341
342 uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS* vflags) {
343   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
344   uint32_t size;
345   uint32_t result =
346     TlclSendReceive(tpm_getstclearflags_cmd.buffer, response, sizeof(response));
347   if (result != TPM_SUCCESS)
348     return result;
349   FromTpmUint32(response + kTpmResponseHeaderLength, &size);
350   /* Ugly assertion, but the struct is padded up by one byte. */
351   /* TODO(crbug.com/379255): This fails. Find out why.
352    * VbAssert(size == 7 && sizeof(TPM_STCLEAR_FLAGS) - 1 == 7);
353    */
354   Memcpy(vflags,
355          response + kTpmResponseHeaderLength + sizeof(size),
356          sizeof(TPM_STCLEAR_FLAGS));
357   return result;
358 }
359
360 uint32_t TlclGetFlags(uint8_t* disable,
361                       uint8_t* deactivated,
362                       uint8_t *nvlocked) {
363   TPM_PERMANENT_FLAGS pflags;
364   uint32_t result = TlclGetPermanentFlags(&pflags);
365   if (result == TPM_SUCCESS) {
366     if (disable)
367       *disable = pflags.disable;
368     if (deactivated)
369       *deactivated = pflags.deactivated;
370     if (nvlocked)
371       *nvlocked = pflags.nvLocked;
372     VBDEBUG(("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
373              pflags.disable, pflags.deactivated, pflags.nvLocked));
374   }
375   return result;
376 }
377
378 uint32_t TlclSetGlobalLock(void) {
379   uint32_t x;
380   VBDEBUG(("TPM: Set global lock\n"));
381   return TlclWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0);
382 }
383
384 uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest,
385                     uint8_t* out_digest) {
386   struct s_tpm_extend_cmd cmd;
387   uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength];
388   uint32_t result;
389
390   Memcpy(&cmd, &tpm_extend_cmd, sizeof(cmd));
391   ToTpmUint32(cmd.buffer + tpm_extend_cmd.pcrNum, pcr_num);
392   Memcpy(cmd.buffer + cmd.inDigest, in_digest, kPcrDigestLength);
393
394   result = TlclSendReceive(cmd.buffer, response, sizeof(response));
395   if (result != TPM_SUCCESS)
396     return result;
397
398   Memcpy(out_digest, response + kTpmResponseHeaderLength, kPcrDigestLength);
399   return result;
400 }
401
402 uint32_t TlclGetPermissions(uint32_t index, uint32_t* permissions) {
403   struct s_tpm_getpermissions_cmd cmd;
404   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
405   uint8_t* nvdata;
406   uint32_t result;
407   uint32_t size;
408
409   Memcpy(&cmd, &tpm_getpermissions_cmd, sizeof(cmd));
410   ToTpmUint32(cmd.buffer + tpm_getpermissions_cmd.index, index);
411   result = TlclSendReceive(cmd.buffer, response, sizeof(response));
412   if (result != TPM_SUCCESS)
413     return result;
414
415   nvdata = response + kTpmResponseHeaderLength + sizeof(size);
416   FromTpmUint32(nvdata + kNvDataPublicPermissionsOffset, permissions);
417   return result;
418 }
419
420 uint32_t TlclGetOwnership(uint8_t* owned) {
421   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
422   uint32_t size;
423   uint32_t result =
424     TlclSendReceive(tpm_getownership_cmd.buffer, response, sizeof(response));
425   if (result != TPM_SUCCESS)
426     return result;
427   FromTpmUint32(response + kTpmResponseHeaderLength, &size);
428   /* TODO(crbug.com/379255): This fails. Find out why.
429    * VbAssert(size == sizeof(*owned));
430    */
431   Memcpy(owned,
432          response + kTpmResponseHeaderLength + sizeof(size),
433          sizeof(*owned));
434   return result;
435 }
436
437 uint32_t TlclGetRandom(uint8_t* data, uint32_t length, uint32_t *size) {
438   struct s_tpm_get_random_cmd cmd;
439   uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
440   uint32_t result;
441
442   VBDEBUG(("TPM: TlclGetRandom(%d)\n", length));
443   Memcpy(&cmd, &tpm_get_random_cmd, sizeof(cmd));
444   ToTpmUint32(cmd.buffer + tpm_get_random_cmd.bytesRequested, length);
445   /* There must be room in the response buffer for the bytes. */
446   if (length > TPM_LARGE_ENOUGH_COMMAND_SIZE - kTpmResponseHeaderLength
447                - sizeof(uint32_t)) {
448     return TPM_E_IOERROR;
449   }
450
451   result = TlclSendReceive(cmd.buffer, response, sizeof(response));
452   if (result == TPM_SUCCESS) {
453     uint8_t* get_random_cursor;
454     FromTpmUint32(response + kTpmResponseHeaderLength, size);
455
456     /* There must be room in the target buffer for the bytes. */
457     if (*size > length) {
458       return TPM_E_RESPONSE_TOO_LARGE;
459     }
460     get_random_cursor = response + kTpmResponseHeaderLength
461                                  + sizeof(uint32_t);
462     Memcpy(data, get_random_cursor, *size);
463   }
464
465   return result;
466 }