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 */
42static int tpm_fd = -1;
43/* If the library should exit during an OS-level TPM failure.
44 */
45static int exit_on_failure = 1;
46
47/* Similar to VbExError, only handle the non-exit case.
48 */
49static 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 */
79static 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))
119static 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))
129static 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
136VbError_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
144VbError_t VbExTpmClose(void) {
145  if (tpm_fd != -1) {
146    close(tpm_fd);
147    tpm_fd = -1;
148  }
149  return VBERROR_SUCCESS;
150}
151
152
153VbError_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
188VbError_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