/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * * Stub implementations of utility functions which call their linux-specific * equivalents. */ #define _STUB_IMPLEMENTATION_ #include "tlcl.h" #include "tlcl_internal.h" #include "utility.h" #include "vboot_api.h" #include #include #include #include #include #include #include #include #include #include #define TPM_DEVICE_PATH "/dev/tpm0" /* TODO: these functions should pass errors back rather than returning void */ /* TODO: if the only callers to these are just wrappers, should just * remove the wrappers and call us directly. */ /* The file descriptor for the TPM device. */ static int tpm_fd = -1; /* Print |n| bytes from array |a|, with newlines. */ POSSIBLY_UNUSED static void PrintBytes(const uint8_t* a, int n) { int i; for (i = 0; i < n; i++) { VBDEBUG(("%02x ", a[i])); if ((i + 1) % 16 == 0) { VBDEBUG(("\n")); } } if (i % 16 != 0) { VBDEBUG(("\n")); } } /* Executes a command on the TPM. */ static void TpmExecute(const uint8_t *in, const uint32_t in_len, uint8_t *out, uint32_t *pout_len) { uint8_t response[TPM_MAX_COMMAND_SIZE]; if (in_len <= 0) { VbExError("invalid command length %d for command 0x%x\n", in_len, in[9]); } else if (tpm_fd < 0) { VbExError("the TPM device was not opened. Forgot to call TlclLibInit?\n"); } else { int n = write(tpm_fd, in, in_len); if (n != in_len) { VbExError("write failure to TPM device: %s\n", strerror(errno)); } n = read(tpm_fd, response, sizeof(response)); if (n == 0) { VbExError("null read from TPM device\n"); } else if (n < 0) { VbExError("read failure from TPM device: %s\n", strerror(errno)); } else { if (n > *pout_len) { VbExError("TPM response too long for output buffer\n"); } else { *pout_len = n; Memcpy(out, response, n); } } } } /* Gets the tag field of a TPM command. */ POSSIBLY_UNUSED static INLINE int TpmTag(const uint8_t* buffer) { uint16_t tag; FromTpmUint16(buffer, &tag); return (int) tag; } /* Gets the size field of a TPM command. */ POSSIBLY_UNUSED static INLINE int TpmResponseSize(const uint8_t* buffer) { uint32_t size; FromTpmUint32(buffer + sizeof(uint16_t), &size); return (int) size; } VbError_t VbExTpmInit(void) { return VbExTpmOpen(); } VbError_t VbExTpmClose(void) { if (tpm_fd != -1) { close(tpm_fd); tpm_fd = -1; } return VBERROR_SUCCESS; } VbError_t VbExTpmOpen(void) { char* device_path; if (tpm_fd >= 0) return VBERROR_SUCCESS; /* Already open */ device_path = getenv("TPM_DEVICE_PATH"); if (device_path == NULL) { device_path = TPM_DEVICE_PATH; } tpm_fd = open(device_path, O_RDWR); if (tpm_fd < 0) { VbExError("TPM: Cannot open TPM device %s: %s\n", device_path, strerror(errno)); } return VBERROR_SUCCESS; } VbError_t VbExTpmSendReceive(const uint8_t* request, uint32_t request_length, uint8_t* response, uint32_t* response_length) { /* * In a real firmware implementation, this function should contain * the equivalent API call for the firmware TPM driver which takes a * raw sequence of bytes as input command and a pointer to the * output buffer for putting in the results. * * For EFI firmwares, this can make use of the EFI TPM driver as * follows (based on page 16, of TCG EFI Protocol Specs Version 1.20 * availaible from the TCG website): * * EFI_STATUS status; * status = TcgProtocol->EFI_TCG_PASS_THROUGH_TO_TPM(TpmCommandSize(request), * request, * max_length, * response); * // Error checking depending on the value of the status above */ #ifndef NDEBUG int tag, response_tag; #endif struct timeval before, after; gettimeofday(&before, NULL); TpmExecute(request, request_length, response, response_length); gettimeofday(&after, NULL); #ifdef VBOOT_DEBUG { int x = request_length; int y = *response_length; VBDEBUG(("request (%d bytes): ", x)); PrintBytes(request, 10); PrintBytes(request + 10, x - 10); VBDEBUG(("response (%d bytes): ", y)); PrintBytes(response, 10); PrintBytes(response + 10, y - 10); VBDEBUG(("execution time: %dms\n", (int) ((after.tv_sec - before.tv_sec) * 1000 + (after.tv_usec - before.tv_usec) / 1000))); } #endif #ifndef NDEBUG /* sanity checks */ tag = TpmTag(request); response_tag = TpmTag(response); assert( (tag == TPM_TAG_RQU_COMMAND && response_tag == TPM_TAG_RSP_COMMAND) || (tag == TPM_TAG_RQU_AUTH1_COMMAND && response_tag == TPM_TAG_RSP_AUTH1_COMMAND) || (tag == TPM_TAG_RQU_AUTH2_COMMAND && response_tag == TPM_TAG_RSP_AUTH2_COMMAND)); assert(*response_length == TpmResponseSize(response)); #endif return VBERROR_SUCCESS; }