1 /* Copyright (c) 2011 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 * Stub implementations of utility functions which call their linux-specific 6 * equivalents. 7 */ 8 9 #include <stdint.h> 10 11 #define _STUB_IMPLEMENTATION_ 12 #include "tlcl.h" 13 #include "tlcl_internal.h" 14 #include "utility.h" 15 #include "vboot_api.h" 16 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <stdarg.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/time.h> 24 #include <sys/types.h> 25 #include <sys/stat.h> 26 #include <time.h> 27 #include <unistd.h> 28 29 30 #define TPM_DEVICE_PATH "/dev/tpm0" 31 /* Retry failed open()s for 5 seconds in 10ms polling intervals. */ 32 #define OPEN_RETRY_DELAY_NS (10 * 1000 * 1000) 33 #define OPEN_RETRY_MAX_NUM 500 34 35 /* TODO: these functions should pass errors back rather than returning void */ 36 /* TODO: if the only callers to these are just wrappers, should just 37 * remove the wrappers and call us directly. */ 38 39 40 /* The file descriptor for the TPM device. 41 */ 42 static int tpm_fd = -1; 43 /* If the library should exit during an OS-level TPM failure. 44 */ 45 static int exit_on_failure = 1; 46 47 /* Similar to VbExError, only handle the non-exit case. 48 */ 49 static VbError_t DoError(VbError_t result, const char* format, ...) { 50 va_list ap; 51 va_start(ap, format); 52 fprintf(stderr, "ERROR: "); 53 vfprintf(stderr, format, ap); 54 va_end(ap); 55 if (exit_on_failure) 56 exit(1); 57 return result; 58 } 59 60 61 /* Print |n| bytes from array |a|, with newlines. 62 */ 63 __attribute__((unused)) static void PrintBytes(const uint8_t* a, int n) { 64 int i; 65 for (i = 0; i < n; i++) { 66 VBDEBUG(("%02x ", a[i])); 67 if ((i + 1) % 16 == 0) { 68 VBDEBUG(("\n")); 69 } 70 } 71 if (i % 16 != 0) { 72 VBDEBUG(("\n")); 73 } 74 } 75 76 77 /* Executes a command on the TPM. 78 */ 79 static VbError_t TpmExecute(const uint8_t *in, const uint32_t in_len, 80 uint8_t *out, uint32_t *pout_len) { 81 uint8_t response[TPM_MAX_COMMAND_SIZE]; 82 if (in_len <= 0) { 83 return DoError(TPM_E_INPUT_TOO_SMALL, 84 "invalid command length %d for command 0x%x\n", 85 in_len, in[9]); 86 } else if (tpm_fd < 0) { 87 return DoError(TPM_E_NO_DEVICE, 88 "the TPM device was not opened. " \ 89 "Forgot to call TlclLibInit?\n"); 90 } else { 91 int n = write(tpm_fd, in, in_len); 92 if (n != in_len) { 93 return DoError(TPM_E_WRITE_FAILURE, 94 "write failure to TPM device: %s\n", strerror(errno)); 95 } 96 n = read(tpm_fd, response, sizeof(response)); 97 if (n == 0) { 98 return DoError(TPM_E_READ_EMPTY, "null read from TPM device\n"); 99 } else if (n < 0) { 100 return DoError(TPM_E_READ_FAILURE, "read failure from TPM device: %s\n", 101 strerror(errno)); 102 } else { 103 if (n > *pout_len) { 104 return DoError(TPM_E_RESPONSE_TOO_LARGE, 105 "TPM response too long for output buffer\n"); 106 } else { 107 *pout_len = n; 108 Memcpy(out, response, n); 109 } 110 } 111 } 112 return VBERROR_SUCCESS; 113 } 114 115 116 /* Gets the tag field of a TPM command. 117 */ 118 __attribute__((unused)) 119 static inline int TpmTag(const uint8_t* buffer) { 120 uint16_t tag; 121 FromTpmUint16(buffer, &tag); 122 return (int) tag; 123 } 124 125 126 /* Gets the size field of a TPM command. 127 */ 128 __attribute__((unused)) 129 static inline int TpmResponseSize(const uint8_t* buffer) { 130 uint32_t size; 131 FromTpmUint32(buffer + sizeof(uint16_t), &size); 132 return (int) size; 133 } 134 135 136 VbError_t VbExTpmInit(void) { 137 char *no_exit = getenv("TPM_NO_EXIT"); 138 if (no_exit) 139 exit_on_failure = !atoi(no_exit); 140 return VbExTpmOpen(); 141 } 142 143 144 VbError_t VbExTpmClose(void) { 145 if (tpm_fd != -1) { 146 close(tpm_fd); 147 tpm_fd = -1; 148 } 149 return VBERROR_SUCCESS; 150 } 151 152 153 VbError_t VbExTpmOpen(void) { 154 char* device_path; 155 struct timespec delay; 156 int retries, saved_errno; 157 158 if (tpm_fd >= 0) 159 return VBERROR_SUCCESS; /* Already open */ 160 161 device_path = getenv("TPM_DEVICE_PATH"); 162 if (device_path == NULL) { 163 device_path = TPM_DEVICE_PATH; 164 } 165 166 /* Retry TPM opens on EBUSY failures. */ 167 for (retries = 0; retries < OPEN_RETRY_MAX_NUM; ++ retries) { 168 errno = 0; 169 tpm_fd = open(device_path, O_RDWR); 170 saved_errno = errno; 171 if (tpm_fd >= 0) 172 return VBERROR_SUCCESS; 173 if (saved_errno != EBUSY) 174 break; 175 176 VBDEBUG(("TPM: retrying %s: %s\n", device_path, strerror(errno))); 177 178 /* Stall until TPM comes back. */ 179 delay.tv_sec = 0; 180 delay.tv_nsec = OPEN_RETRY_DELAY_NS; 181 nanosleep(&delay, NULL); 182 } 183 return DoError(TPM_E_NO_DEVICE, "TPM: Cannot open TPM device %s: %s\n", 184 device_path, strerror(saved_errno)); 185 } 186 187 188 VbError_t VbExTpmSendReceive(const uint8_t* request, uint32_t request_length, 189 uint8_t* response, uint32_t* response_length) { 190 /* 191 * In a real firmware implementation, this function should contain 192 * the equivalent API call for the firmware TPM driver which takes a 193 * raw sequence of bytes as input command and a pointer to the 194 * output buffer for putting in the results. 195 * 196 * For EFI firmwares, this can make use of the EFI TPM driver as 197 * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20 198 * availaible from the TCG website): 199 * 200 * EFI_STATUS status; 201 * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM(TpmCommandSize(request), 202 * request, 203 * max_length, 204 * response); 205 * // Error checking depending on the value of the status above 206 */ 207 #ifndef NDEBUG 208 int tag, response_tag; 209 #endif 210 VbError_t result; 211 212 struct timeval before, after; 213 gettimeofday(&before, NULL); 214 result = TpmExecute(request, request_length, response, response_length); 215 if (result != VBERROR_SUCCESS) 216 return result; 217 gettimeofday(&after, NULL); 218 219 #ifdef VBOOT_DEBUG 220 { 221 int x = request_length; 222 int y = *response_length; 223 VBDEBUG(("request (%d bytes): ", x)); 224 PrintBytes(request, 10); 225 PrintBytes(request + 10, x - 10); 226 VBDEBUG(("response (%d bytes): ", y)); 227 PrintBytes(response, 10); 228 PrintBytes(response + 10, y - 10); 229 VBDEBUG(("execution time: %dms\n", 230 (int) ((after.tv_sec - before.tv_sec) * 1000 + 231 (after.tv_usec - before.tv_usec) / 1000))); 232 } 233 #endif 234 235 #ifndef NDEBUG 236 /* sanity checks */ 237 tag = TpmTag(request); 238 response_tag = TpmTag(response); 239 assert( 240 (tag == TPM_TAG_RQU_COMMAND && 241 response_tag == TPM_TAG_RSP_COMMAND) || 242 (tag == TPM_TAG_RQU_AUTH1_COMMAND && 243 response_tag == TPM_TAG_RSP_AUTH1_COMMAND) || 244 (tag == TPM_TAG_RQU_AUTH2_COMMAND && 245 response_tag == TPM_TAG_RSP_AUTH2_COMMAND)); 246 assert(*response_length == TpmResponseSize(response)); 247 #endif 248 249 return VBERROR_SUCCESS; 250 } 251