diff options
-rw-r--r-- | src/Kconfig | 4 | ||||
-rw-r--r-- | src/arch/x86/boot/acpi.c | 8 | ||||
-rw-r--r-- | src/include/pc80/tpm.h | 29 | ||||
-rw-r--r-- | src/pc80/Makefile.inc | 1 | ||||
-rw-r--r-- | src/pc80/tpm.c | 554 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot.c | 201 |
6 files changed, 797 insertions, 0 deletions
diff --git a/src/Kconfig b/src/Kconfig index ba1af089cd..f7180a98c0 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -254,6 +254,10 @@ config IOAPIC bool default n +config TPM + bool + default n + # TODO: Can probably be removed once all chipsets have kconfig options for it. config VIDEO_MB int diff --git a/src/arch/x86/boot/acpi.c b/src/arch/x86/boot/acpi.c index 168933a726..33d730bc8c 100644 --- a/src/arch/x86/boot/acpi.c +++ b/src/arch/x86/boot/acpi.c @@ -32,6 +32,9 @@ #include <device/pci.h> #include <cbmem.h> #include <cpu/x86/lapic_def.h> +#if CONFIG_CHROMEOS +#include <vendorcode/google/chromeos/chromeos.h> +#endif u8 acpi_checksum(u8 *table, u32 length) { @@ -524,6 +527,11 @@ void *acpi_find_wakeup_vector(void) if (!acpi_is_wakeup()) return NULL; +#if CONFIG_CHROMEOS + printk(BIOS_DEBUG, "Verified boot TPM initialization.\n"); + init_vboot(); +#endif + printk(BIOS_DEBUG, "Trying to find the wakeup vector...\n"); /* Find RSDP. */ diff --git a/src/include/pc80/tpm.h b/src/include/pc80/tpm.h new file mode 100644 index 0000000000..2eff15a09a --- /dev/null +++ b/src/include/pc80/tpm.h @@ -0,0 +1,29 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef TPM_H_ +#define TPM_H_ + +int tis_init(void); +int tis_open(void); +int tis_close(void); +int tis_sendrecv(const u8 *sendbuf, size_t send_size, u8 *recvbuf, + size_t *recv_len); + +#endif /* TPM_H_ */ diff --git a/src/pc80/Makefile.inc b/src/pc80/Makefile.inc index 2c8a80ee15..cd6ea33483 100644 --- a/src/pc80/Makefile.inc +++ b/src/pc80/Makefile.inc @@ -4,6 +4,7 @@ ramstage-y += i8254.c ramstage-y += i8259.c ramstage-$(CONFIG_UDELAY_IO) += udelay_io.c ramstage-y += keyboard.c +ramstage-$(CONFIG_TPM) += tpm.c romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c subdirs-y += vga diff --git a/src/pc80/tpm.c b/src/pc80/tpm.c new file mode 100644 index 0000000000..1cbf800571 --- /dev/null +++ b/src/pc80/tpm.c @@ -0,0 +1,554 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * The code in this file has been heavily based on the article "Writing a TPM + * Device Driver" published on http://ptgmedia.pearsoncmg.com and the + * submission by Stefan Berger on Qemu-devel mailing list. + * + * One principal difference is that in the simplest config the other than 0 + * TPM localities do not get mapped by some devices (for instance, by + * Infineon slb9635), so this driver provides access to locality 0 only. + */ + +/* #define DEBUG */ +#include <stdlib.h> +#include <string.h> +#include <delay.h> +#include <arch/io.h> +#include <arch/byteorder.h> +#include <console/console.h> +#include <pc80/tpm.h> + +#ifdef DEBUG +#define TPM_DEBUG_ON 1 +#else +#define TPM_DEBUG_ON 0 +#endif + +#define PREFIX "lpc_tpm: " + +/* coreboot wrapper for TPM driver (start) */ +#define TPM_DEBUG(fmt, args...) \ + if (TPM_DEBUG_ON) { \ + printk(BIOS_DEBUG, PREFIX); \ + printk(BIOS_DEBUG, fmt , ##args); \ + } +#define printf(x...) printk(BIOS_ERR, x) + +#define min(a,b) MIN(a,b) +#define max(a,b) MAX(a,b) +#define readb(_a) (*(volatile unsigned char *) (_a)) +#define writeb(_v, _a) (*(volatile unsigned char *) (_a) = (_v)) +#define readl(_a) (*(volatile unsigned long *) (_a)) +#define writel(_v, _a) (*(volatile unsigned long *) (_a) = (_v)) +/* coreboot wrapper for TPM driver (end) */ + +#ifndef CONFIG_TPM_TIS_BASE_ADDRESS +/* Base TPM address standard for x86 systems */ +#define CONFIG_TPM_TIS_BASE_ADDRESS 0xfed40000 +#endif + +/* the macro accepts the locality value, but only locality 0 is operational */ +#define TIS_REG(LOCALITY, REG) \ + (void *)(CONFIG_TPM_TIS_BASE_ADDRESS + (LOCALITY << 12) + REG) + +/* hardware registers' offsets */ +#define TIS_REG_ACCESS 0x0 +#define TIS_REG_INT_ENABLE 0x8 +#define TIS_REG_INT_VECTOR 0xc +#define TIS_REG_INT_STATUS 0x10 +#define TIS_REG_INTF_CAPABILITY 0x14 +#define TIS_REG_STS 0x18 +#define TIS_REG_DATA_FIFO 0x24 +#define TIS_REG_DID_VID 0xf00 +#define TIS_REG_RID 0xf04 + +/* Some registers' bit field definitions */ +#define TIS_STS_VALID (1 << 7) /* 0x80 */ +#define TIS_STS_COMMAND_READY (1 << 6) /* 0x40 */ +#define TIS_STS_TPM_GO (1 << 5) /* 0x20 */ +#define TIS_STS_DATA_AVAILABLE (1 << 4) /* 0x10 */ +#define TIS_STS_EXPECT (1 << 3) /* 0x08 */ +#define TIS_STS_RESPONSE_RETRY (1 << 1) /* 0x02 */ + +#define TIS_ACCESS_TPM_REG_VALID_STS (1 << 7) /* 0x80 */ +#define TIS_ACCESS_ACTIVE_LOCALITY (1 << 5) /* 0x20 */ +#define TIS_ACCESS_BEEN_SEIZED (1 << 4) /* 0x10 */ +#define TIS_ACCESS_SEIZE (1 << 3) /* 0x08 */ +#define TIS_ACCESS_PENDING_REQUEST (1 << 2) /* 0x04 */ +#define TIS_ACCESS_REQUEST_USE (1 << 1) /* 0x02 */ +#define TIS_ACCESS_TPM_ESTABLISHMENT (1 << 0) /* 0x01 */ + +#define TIS_STS_BURST_COUNT_MASK (0xffff) +#define TIS_STS_BURST_COUNT_SHIFT (8) + +/* + * Error value returned if a tpm register does not enter the expected state + * after continuous polling. No actual TPM register reading ever returns ~0, + * so this value is a safe error indication to be mixed with possible status + * register values. + */ +#define TPM_TIMEOUT_ERR (~0) + +/* Error value returned on various TPM driver errors */ +#define TPM_DRIVER_ERR (~0) + + /* 1 second is plenty for anything TPM does.*/ +#define MAX_DELAY_US (1000 * 1000) + +/* Retrieve burst count value out of the status register contents. */ +#define BURST_COUNT(status) ((u16)(((status) >> TIS_STS_BURST_COUNT_SHIFT) & \ + TIS_STS_BURST_COUNT_MASK)) + +/* + * Structures defined below allow creating descriptions of TPM vendor/device + * ID information for run time discovery. The only device the system knows + * about at this time is Infineon slb9635 + */ +struct device_name { + u16 dev_id; + const char * const dev_name; +}; + +struct vendor_name { + u16 vendor_id; + const char * vendor_name; + struct device_name* dev_names; +}; + +static struct device_name infineon_devices[] = { + {0xb, "SLB9635 TT 1.2"}, + {0} +}; + +static const struct vendor_name vendor_names[] = { + {0x15d1, "Infineon", infineon_devices}, +}; + +/* + * Cached vendor/device ID pair to indicate that the device has been already + * discovered + */ +static u32 vendor_dev_id; + +static int is_byte_reg(u32 reg) +{ + /* + * These TPM registers are 8 bits wide and as such require byte access + * on writes and truncated value on reads. + */ + return ((reg == TIS_REG_ACCESS) || + (reg == TIS_REG_INT_VECTOR) || + (reg == TIS_REG_DATA_FIFO)); +} + +/* TPM access functions are carved out to make tracing easier. */ +static u32 tpm_read(int locality, u32 reg) +{ + u32 value; + /* + * Data FIFO register must be read and written in byte access mode, + * otherwise the FIFO values are returned 4 bytes at a time. + */ + if (is_byte_reg(reg)) + value = readb(TIS_REG(locality, reg)); + else + value = readl(TIS_REG(locality, reg)); + + TPM_DEBUG("Read reg 0x%x returns 0x%x\n", reg, value); + return value; +} + +static void tpm_write(u32 value, int locality, u32 reg) +{ + TPM_DEBUG("Write reg 0x%x with 0x%x\n", reg, value); + + if (is_byte_reg(reg)) + writeb(value & 0xff, TIS_REG(locality, reg)); + else + writel(value, TIS_REG(locality, reg)); +} + +/* + * tis_wait_reg() + * + * Wait for at least a second for a register to change its state to match the + * expected state. Normally the transition happens within microseconds. + * + * @reg - the TPM register offset + * @locality - locality + * @mask - bitmask for the bitfield(s) to watch + * @expected - value the field(s) are supposed to be set to + * + * Returns the register contents in case the expected value was found in the + * appropriate register bits, or TPM_TIMEOUT_ERR on timeout. + */ +static u32 tis_wait_reg(u8 reg, u8 locality, u8 mask, u8 expected) +{ + u32 time_us = MAX_DELAY_US; + while (time_us > 0) { + u32 value = tpm_read(locality, reg); + if ((value & mask) == expected) + return value; + udelay(1); /* 1 us */ + time_us--; + } + return TPM_TIMEOUT_ERR; +} + +/* + * Probe the TPM device and try determining its manufacturer/device name. + * + * Returns 0 on success (the device is found or was found during an earlier + * invocation) or TPM_DRIVER_ERR if the device is not found. + */ +static u32 tis_probe(void) +{ + u32 didvid = tpm_read(0, TIS_REG_DID_VID); + int i; + const char *device_name = "unknown"; + const char *vendor_name = device_name; + u16 vid, did; + + if (vendor_dev_id) + return 0; /* Already probed. */ + + if (!didvid || (didvid == 0xffffffff)) { + printf("%s: No TPM device found\n", __FUNCTION__); + return TPM_DRIVER_ERR; + } + + vendor_dev_id = didvid; + + vid = didvid & 0xffff; + did = (didvid >> 16) & 0xffff; + for (i = 0; i < ARRAY_SIZE(vendor_names); i++) { + int j = 0; + u16 known_did; + if (vid == vendor_names[i].vendor_id) { + vendor_name = vendor_names[i].vendor_name; + } + while ((known_did = vendor_names[i].dev_names[j].dev_id) != 0) { + if (known_did == did) { + device_name = + vendor_names[i].dev_names[j].dev_name; + break; + } + j++; + } + break; + } + /* this will have to be converted into debug printout */ + TPM_DEBUG("Found TPM %s by %s\n", device_name, vendor_name); + return 0; +} + +/* + * tis_senddata() + * + * send the passed in data to the TPM device. + * + * @data - address of the data to send, byte by byte + * @len - length of the data to send + * + * Returns 0 on success, TPM_DRIVER_ERR on error (in case the device does + * not accept the entire command). + */ +static u32 tis_senddata(const u8 * const data, u32 len) +{ + u32 offset = 0; + u16 burst = 0; + u32 max_cycles = 0; + u8 locality = 0; + u32 value; + + value = tis_wait_reg(TIS_REG_STS, locality, TIS_STS_COMMAND_READY, + TIS_STS_COMMAND_READY); + if (value == TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to get 'command_ready' status\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + burst = BURST_COUNT(value); + + while (1) { + unsigned count; + + /* Wait till the device is ready to accept more data. */ + while (!burst) { + if (max_cycles++ == MAX_DELAY_US) { + printf("%s:%d failed to feed %d bytes of %d\n", + __FILE__, __LINE__, len - offset, len); + return TPM_DRIVER_ERR; + } + udelay(1); + burst = BURST_COUNT(tpm_read(locality, TIS_REG_STS)); + } + + max_cycles = 0; + + /* + * Calculate number of bytes the TPM is ready to accept in one + * shot. + * + * We want to send the last byte outside of the loop (hence + * the -1 below) to make sure that the 'expected' status bit + * changes to zero exactly after the last byte is fed into the + * FIFO. + */ + count = min(burst, len - offset - 1); + while (count--) + tpm_write(data[offset++], locality, TIS_REG_DATA_FIFO); + + value = tis_wait_reg(TIS_REG_STS, locality, + TIS_STS_VALID, TIS_STS_VALID); + + if ((value == TPM_TIMEOUT_ERR) || !(value & TIS_STS_EXPECT)) { + printf("%s:%d TPM command feed overflow\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + burst = BURST_COUNT(value); + if ((offset == (len - 1)) && burst) + /* + * We need to be able to send the last byte to the + * device, so burst size must be nonzero before we + * break out. + */ + break; + } + + /* Send the last byte. */ + tpm_write(data[offset++], locality, TIS_REG_DATA_FIFO); + + /* + * Verify that TPM does not expect any more data as part of this + * command. + */ + value = tis_wait_reg(TIS_REG_STS, locality, + TIS_STS_VALID, TIS_STS_VALID); + if ((value == TPM_TIMEOUT_ERR) || (value & TIS_STS_EXPECT)) { + printf("%s:%d unexpected TPM status 0x%x\n", + __FILE__, __LINE__, value); + return TPM_DRIVER_ERR; + } + + /* OK, sitting pretty, let's start the command execution. */ + tpm_write(TIS_STS_TPM_GO, locality, TIS_REG_STS); + + return 0; +} + +/* + * tis_readresponse() + * + * read the TPM device response after a command was issued. + * + * @buffer - address where to read the response, byte by byte. + * @len - pointer to the size of buffer + * + * On success stores the number of received bytes to len and returns 0. On + * errors (misformatted TPM data or synchronization problems) returns + * TPM_DRIVER_ERR. + */ +static u32 tis_readresponse(u8 *buffer, size_t *len) +{ + u16 burst_count; + u32 status; + u32 offset = 0; + u8 locality = 0; + const u32 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; + u32 expected_count = *len; + int max_cycles = 0; + + /* Wait for the TPM to process the command */ + status = tis_wait_reg(TIS_REG_STS, locality, has_data, has_data); + if (status == TPM_TIMEOUT_ERR) { + printf("%s:%d failed processing command\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + do { + while ((burst_count = BURST_COUNT(status)) == 0) { + if (max_cycles++ == MAX_DELAY_US) { + printf("%s:%d TPM stuck on read\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + udelay(1); + status = tpm_read(locality, TIS_REG_STS); + } + + max_cycles = 0; + + while (burst_count-- && (offset < expected_count)) { + buffer[offset++] = (u8) tpm_read(locality, + TIS_REG_DATA_FIFO); + if (offset == 6) { + /* + * We got the first six bytes of the reply, + * let's figure out how many bytes to expect + * total - it is stored as a 4 byte number in + * network order, starting with offset 2 into + * the body of the reply. + */ + u32 real_length; + memcpy(&real_length, + buffer + 2, + sizeof(real_length)); + expected_count = be32_to_cpu(real_length); + + if ((expected_count < offset) || + (expected_count > *len)) { + printf("%s:%d bad response size %d\n", + __FILE__, __LINE__, + expected_count); + return TPM_DRIVER_ERR; + } + } + } + + /* Wait for the next portion */ + status = tis_wait_reg(TIS_REG_STS, locality, + TIS_STS_VALID, TIS_STS_VALID); + if (status == TPM_TIMEOUT_ERR) { + printf("%s:%d failed to read response\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + if (offset == expected_count) + break; /* We got all we need */ + + } while ((status & has_data) == has_data); + + /* + * Make sure we indeed read all there was. The TIS_STS_VALID bit is + * known to be set. + */ + if (status & TIS_STS_DATA_AVAILABLE) { + printf("%s:%d wrong receive status %x\n", + __FILE__, __LINE__, status); + return TPM_DRIVER_ERR; + } + + /* Tell the TPM that we are done. */ + tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS); + + *len = offset; + return 0; +} + +/* + * tis_init() + * + * Initialize the TPM device. Returns 0 on success or TPM_DRIVER_ERR on + * failure (in case device probing did not succeed). + */ +int tis_init(void) +{ + if (tis_probe()) + return TPM_DRIVER_ERR; + return 0; +} + +/* + * tis_open() + * + * Requests access to locality 0 for the caller. After all commands have been + * completed the caller is supposed to call tis_close(). + * + * Returns 0 on success, TPM_DRIVER_ERR on failure. + */ +int tis_open(void) +{ + u8 locality = 0; /* we use locality zero for everything */ + + if (tis_close()) + return TPM_DRIVER_ERR; + + /* now request access to locality */ + tpm_write(TIS_ACCESS_REQUEST_USE, locality, TIS_REG_ACCESS); + + /* did we get a lock? */ + if (tis_wait_reg(TIS_REG_ACCESS, locality, + TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY) == TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to lock locality %d\n", + __FILE__, __LINE__, locality); + return TPM_DRIVER_ERR; + } + + tpm_write(TIS_STS_COMMAND_READY, locality, TIS_REG_STS); + + return 0; +} + +/* + * tis_close() + * + * terminate the currect session with the TPM by releasing the locked + * locality. Returns 0 on success of TPM_DRIVER_ERR on failure (in case lock + * removal did not succeed). + */ +int tis_close(void) +{ + u8 locality = 0; + if (tpm_read(locality, TIS_REG_ACCESS) & + TIS_ACCESS_ACTIVE_LOCALITY) { + tpm_write(TIS_ACCESS_ACTIVE_LOCALITY, locality, TIS_REG_ACCESS); + + if (tis_wait_reg(TIS_REG_ACCESS, locality, + TIS_ACCESS_ACTIVE_LOCALITY, 0) == + TPM_TIMEOUT_ERR) { + printf("%s:%d - failed to release locality %d\n", + __FILE__, __LINE__, locality); + return TPM_DRIVER_ERR; + } + } + return 0; +} + +/* + * tis_sendrecv() + * + * Send the requested data to the TPM and then try to get its response + * + * @sendbuf - buffer of the data to send + * @send_size size of the data to send + * @recvbuf - memory to save the response to + * @recv_len - pointer to the size of the response buffer + * + * Returns 0 on success (and places the number of response bytes at recv_len) + * or TPM_DRIVER_ERR on failure. + */ +int tis_sendrecv(const uint8_t *sendbuf, size_t send_size, + uint8_t *recvbuf, size_t *recv_len) +{ + if (tis_senddata(sendbuf, send_size)) { + printf("%s:%d failed sending data to TPM\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + return tis_readresponse(recvbuf, recv_len); +} diff --git a/src/vendorcode/google/chromeos/vboot.c b/src/vendorcode/google/chromeos/vboot.c new file mode 100644 index 0000000000..e0a8c9b336 --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot.c @@ -0,0 +1,201 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <types.h> +#include <console/console.h> +#include <arch/acpi.h> +#include <pc80/tpm.h> +#include "chromeos.h" + +//#define EXTRA_LOGGING + +#define TPM_LARGE_ENOUGH_COMMAND_SIZE 256 /* saves space in the firmware */ + +#define TPM_SUCCESS ((u32)0x00000000) + +#define TPM_E_IOERROR ((u32)0x0000001f) +#define TPM_E_COMMUNICATION_ERROR ((u32)0x00005004) +#define TPM_E_NON_FATAL ((u32)0x00000800) +#define TPM_E_INVALID_POSTINIT ((u32)0x00000026) + +#define TPM_E_NEEDS_SELFTEST ((u32)(TPM_E_NON_FATAL + 1)) +#define TPM_E_DOING_SELFTEST ((u32)(TPM_E_NON_FATAL + 2)) + +static const struct { + u8 buffer[12]; +} tpm_resume_cmd = { + { 0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x2 } +}; + +static const struct { + u8 buffer[10]; +} tpm_continueselftest_cmd = { + { 0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x53 } +}; + +static inline void FromTpmUint32(const u8 * buffer, u32 * x) +{ + *x = ((buffer[0] << 24) | + (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); +} + +static inline int TpmCommandSize(const u8 * buffer) +{ + u32 size; + FromTpmUint32(buffer + sizeof(u16), &size); + return (int)size; +} + +/* Gets the code field of a TPM command. */ +static inline int TpmCommandCode(const u8 * buffer) +{ + u32 code; + FromTpmUint32(buffer + sizeof(u16) + sizeof(u32), &code); + return code; +} + +/* Gets the return code field of a TPM result. */ +static inline int TpmReturnCode(const u8 * buffer) +{ + return TpmCommandCode(buffer); +} + +/* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or + * DOING_SELFTEST errors are returned. + */ +static u32 TlclSendReceiveNoRetry(const u8 * request, + u8 * response, int max_length) +{ + size_t response_length = max_length; + u32 result; + +#ifdef EXTRA_LOGGING + printk(BIOS_DEBUG, "TPM: command: %x%x %x%x%x%x %x%x%x%x\n", + request[0], request[1], + request[2], request[3], request[4], request[5], + request[6], request[7], request[8], request[9]); +#endif + + result = TPM_SUCCESS; + if (tis_sendrecv + (request, TpmCommandSize(request), response, &response_length)) + result = TPM_E_IOERROR; + + if (0 != result) { + /* Communication with TPM failed, so response is garbage */ + printk(BIOS_DEBUG, + "TPM: command 0x%x send/receive failed: 0x%x\n", + TpmCommandCode(request), result); + return TPM_E_COMMUNICATION_ERROR; + } + /* Otherwise, use the result code from the response */ + result = TpmReturnCode(response); + +/* TODO: add paranoia about returned response_length vs. max_length + * (and possibly expected length from the response header). See + * crosbug.com/17017 */ + +#ifdef EXTRA_LOGGING + printk(BIOS_DEBUG, "TPM: response: %x%x %x%x%x%x %x%x%x%x\n", + response[0], response[1], + response[2], response[3], response[4], response[5], + response[6], response[7], response[8], response[9]); +#endif + + printk(BIOS_DEBUG, "TPM: command 0x%x returned 0x%x\n", + TpmCommandCode(request), result); + + return result; +} + +static inline u32 TlclContinueSelfTest(void) +{ + u8 response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + printk(BIOS_DEBUG, "TPM: Continue self test\n"); + /* Call the No Retry version of SendReceive to avoid recursion. */ + return TlclSendReceiveNoRetry(tpm_continueselftest_cmd.buffer, + response, sizeof(response)); +} + +/* Sends a TPM command and gets a response. Returns 0 if success or the TPM + * error code if error. In the firmware, waits for the self test to complete + * if needed. In the host, reports the first error without retries. */ +static u32 TlclSendReceive(const u8 * request, u8 * response, int max_length) +{ + u32 result = TlclSendReceiveNoRetry(request, response, max_length); + /* When compiling for the firmware, hide command failures due to the self + * test not having run or completed. */ + /* If the command fails because the self test has not completed, try it + * again after attempting to ensure that the self test has completed. */ + if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) { + result = TlclContinueSelfTest(); + if (result != TPM_SUCCESS) { + return result; + } +#if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE) + /* Retry only once */ + result = TlclSendReceiveNoRetry(request, response, max_length); +#else + /* This needs serious testing. The TPM specification says: + * "iii. The caller MUST wait for the actions of + * TPM_ContinueSelfTest to complete before reissuing the + * command C1." But, if ContinueSelfTest is non-blocking, how + * do we know that the actions have completed other than trying + * again? */ + do { + result = + TlclSendReceiveNoRetry(request, response, + max_length); + } while (result == TPM_E_DOING_SELFTEST); +#endif + } + + return result; +} + +void init_vboot(void) +{ + u32 result; + u8 response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + + printk(BIOS_DEBUG, "TPM: Init\n"); + if (tis_init()) + return; + + printk(BIOS_DEBUG, "TPM: Open\n"); + if (tis_open()) + return; + + printk(BIOS_DEBUG, "TPM: Resume\n"); + + result = + TlclSendReceive(tpm_resume_cmd.buffer, response, sizeof(response)); + + if (result == TPM_E_INVALID_POSTINIT) { + /* We're on a platform where the TPM maintains power in S3, so + * it's already initialized. */ + printk(BIOS_DEBUG, "TPM: Already initialized.\n"); + return; + } + if (result == TPM_SUCCESS) { + printk(BIOS_DEBUG, "TPM: OK.\n"); + return; + } + // TODO(reinauer) hard reboot? +} |