Home | History | Annotate | Download | only in stub
      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