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.
6 /* A lightweight TPM command library.
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
17 #include "sysincludes.h"
20 #include "tlcl_internal.h"
21 #include "tlcl_structures.h"
23 #include "vboot_api.h"
26 /* Allow unit testing implementation of TlclSendReceive() */
27 #undef CHROMEOS_ENVIRONMENT
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);
35 /* Gets the size field of a TPM command. */
36 __attribute__((unused))
37 static inline int TpmCommandSize(const uint8_t* buffer) {
39 FromTpmUint32(buffer + sizeof(uint16_t), &size);
43 /* Gets the size field of a TPM request or response. */
44 int TlclPacketSize(const uint8_t* packet) {
45 return TpmCommandSize(packet);
48 /* Gets the code field of a TPM command. */
49 static inline int TpmCommandCode(const uint8_t* buffer) {
51 FromTpmUint32(buffer + sizeof(uint16_t) + sizeof(uint32_t), &code);
55 /* Gets the return code field of a TPM result. */
56 static inline int TpmReturnCode(const uint8_t* buffer) {
57 return TpmCommandCode(buffer);
60 /* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or
61 * DOING_SELFTEST errors are returned.
63 static uint32_t TlclSendReceiveNoRetry(const uint8_t* request,
64 uint8_t* response, int max_length) {
66 uint32_t response_length = max_length;
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]));
76 result = VbExTpmSendReceive(request, TpmCommandSize(request),
77 response, &response_length);
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));
84 /* Otherwise, use the result code from the response */
85 result = TpmReturnCode(response);
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 */
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]));
98 VBDEBUG(("TPM: command 0x%x returned 0x%x\n",
99 TpmCommandCode(request), result));
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,
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) {
121 #if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE)
122 /* Retry only once */
123 result = TlclSendReceiveNoRetry(request, response, max_length);
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
131 result = TlclSendReceiveNoRetry(request, response, max_length);
132 } while (result == TPM_E_DOING_SELFTEST);
135 #endif /* ! defined(CHROMEOS_ENVIRONMENT) */
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));
145 /* Exported functions. */
147 uint32_t TlclLibInit(void) {
148 return VbExTpmInit();
151 uint32_t TlclLibClose(void) {
152 return VbExTpmClose();
155 uint32_t TlclStartup(void) {
156 VBDEBUG(("TPM: Startup\n"));
157 return Send(tpm_startup_cmd.buffer);
160 uint32_t TlclSaveState(void) {
161 VBDEBUG(("TPM: SaveState\n"));
162 return Send(tpm_savestate_cmd.buffer);
165 uint32_t TlclResume(void) {
166 VBDEBUG(("TPM: Resume\n"));
167 return Send(tpm_resume_cmd.buffer);
170 uint32_t TlclSelfTestFull(void) {
171 VBDEBUG(("TPM: Self test full\n"));
172 return Send(tpm_selftestfull_cmd.buffer);
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));
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);
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;
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);
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);
208 return TlclSendReceive(cmd.buffer, response, sizeof(response));
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;
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);
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);
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];
238 VBDEBUG(("TPM: TlclPCRRead(0x%x, %d)\n", index, length));
239 if (length < kPcrDigestLength) {
240 return TPM_E_IOERROR;
242 Memcpy(&cmd, &tpm_pcr_read_cmd, sizeof(cmd));
243 ToTpmUint32(cmd.buffer + tpm_pcr_read_cmd.pcrNum, index);
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);
254 uint32_t TlclWriteLock(uint32_t index) {
255 VBDEBUG(("TPM: Write lock 0x%x\n", index));
256 return TlclWrite(index, NULL, 0);
259 uint32_t TlclReadLock(uint32_t index) {
260 VBDEBUG(("TPM: Read lock 0x%x\n", index));
261 return TlclRead(index, NULL, 0);
264 uint32_t TlclAssertPhysicalPresence(void) {
265 VBDEBUG(("TPM: Asserting physical presence\n"));
266 return Send(tpm_ppassert_cmd.buffer);
269 uint32_t TlclPhysicalPresenceCMDEnable(void) {
270 VBDEBUG(("TPM: Enable the physical presence command\n"));
271 return Send(tpm_ppenable_cmd.buffer);
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);
279 uint32_t TlclAssertPhysicalPresenceResult(void) {
280 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
281 return TlclSendReceive(tpm_ppassert_cmd.buffer, response, sizeof(response));
284 uint32_t TlclLockPhysicalPresence(void) {
285 VBDEBUG(("TPM: Lock physical presence\n"));
286 return Send(tpm_pplock_cmd.buffer);
289 uint32_t TlclSetNvLocked(void) {
290 VBDEBUG(("TPM: Set NV locked\n"));
291 return TlclDefineSpace(TPM_NV_INDEX_LOCK, 0, 0);
294 int TlclIsOwned(void) {
295 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE + TPM_PUBEK_SIZE];
297 result = TlclSendReceive(tpm_readpubek_cmd.buffer,
298 response, sizeof(response));
299 return (result != TPM_SUCCESS);
302 uint32_t TlclForceClear(void) {
303 VBDEBUG(("TPM: Force clear\n"));
304 return Send(tpm_forceclear_cmd.buffer);
307 uint32_t TlclSetEnable(void) {
308 VBDEBUG(("TPM: Enabling TPM\n"));
309 return Send(tpm_physicalenable_cmd.buffer);
312 uint32_t TlclClearEnable(void) {
313 VBDEBUG(("TPM: Disabling TPM\n"));
314 return Send(tpm_physicaldisable_cmd.buffer);
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);
325 uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) {
326 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
329 TlclSendReceive(tpm_getflags_cmd.buffer, response, sizeof(response));
330 if (result != TPM_SUCCESS)
332 FromTpmUint32(response + kTpmResponseHeaderLength, &size);
333 /* TODO(crbug.com/379255): This fails. Find out why.
334 * VbAssert(size == sizeof(TPM_PERMANENT_FLAGS));
337 response + kTpmResponseHeaderLength + sizeof(size),
338 sizeof(TPM_PERMANENT_FLAGS));
342 uint32_t TlclGetSTClearFlags(TPM_STCLEAR_FLAGS* vflags) {
343 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
346 TlclSendReceive(tpm_getstclearflags_cmd.buffer, response, sizeof(response));
347 if (result != TPM_SUCCESS)
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);
355 response + kTpmResponseHeaderLength + sizeof(size),
356 sizeof(TPM_STCLEAR_FLAGS));
360 uint32_t TlclGetFlags(uint8_t* disable,
361 uint8_t* deactivated,
363 TPM_PERMANENT_FLAGS pflags;
364 uint32_t result = TlclGetPermanentFlags(&pflags);
365 if (result == TPM_SUCCESS) {
367 *disable = pflags.disable;
369 *deactivated = pflags.deactivated;
371 *nvlocked = pflags.nvLocked;
372 VBDEBUG(("TPM: Got flags disable=%d, deactivated=%d, nvlocked=%d\n",
373 pflags.disable, pflags.deactivated, pflags.nvLocked));
378 uint32_t TlclSetGlobalLock(void) {
380 VBDEBUG(("TPM: Set global lock\n"));
381 return TlclWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0);
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];
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);
394 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
395 if (result != TPM_SUCCESS)
398 Memcpy(out_digest, response + kTpmResponseHeaderLength, kPcrDigestLength);
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];
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)
415 nvdata = response + kTpmResponseHeaderLength + sizeof(size);
416 FromTpmUint32(nvdata + kNvDataPublicPermissionsOffset, permissions);
420 uint32_t TlclGetOwnership(uint8_t* owned) {
421 uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
424 TlclSendReceive(tpm_getownership_cmd.buffer, response, sizeof(response));
425 if (result != TPM_SUCCESS)
427 FromTpmUint32(response + kTpmResponseHeaderLength, &size);
428 /* TODO(crbug.com/379255): This fails. Find out why.
429 * VbAssert(size == sizeof(*owned));
432 response + kTpmResponseHeaderLength + sizeof(size),
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];
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;
451 result = TlclSendReceive(cmd.buffer, response, sizeof(response));
452 if (result == TPM_SUCCESS) {
453 uint8_t* get_random_cursor;
454 FromTpmUint32(response + kTpmResponseHeaderLength, size);
456 /* There must be room in the target buffer for the bytes. */
457 if (*size > length) {
458 return TPM_E_RESPONSE_TOO_LARGE;
460 get_random_cursor = response + kTpmResponseHeaderLength
462 Memcpy(data, get_random_cursor, *size);