Kyösti Mälkki (kyosti.malkki@gmail.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/5644
-gerrit
commit 85543bd5299d5dbf69b1eec37d84f57da88e147e Author: Kyösti Mälkki kyosti.malkki@gmail.com Date: Sat May 3 17:30:30 2014 +0300
Trusted Platform: Make TPM init available without ChromeOS
The function does TPM hardware initialisation but is not really related to use of ChromeOS.
Change-Id: Ie67c6e2b07485bdf861f74f9a8768882d9495a01 Signed-off-by: Kyösti Mälkki kyosti.malkki@gmail.com --- src/cpu/intel/haswell/romstage.c | 8 +- src/drivers/Kconfig | 1 + src/drivers/Makefile.inc | 1 + src/drivers/pc80/Kconfig | 8 - src/drivers/pc80/Makefile.inc | 1 - src/drivers/pc80/tpm.c | 677 -------------------------- src/drivers/tpm/Kconfig | 16 + src/drivers/tpm/Makefile.inc | 8 + src/drivers/tpm/tpm.c | 221 +++++++++ src/drivers/tpm/tpm_drv.c | 677 ++++++++++++++++++++++++++ src/include/bootmode.h | 2 + src/include/pc80/tpm.h | 29 -- src/include/tpm.h | 36 ++ src/lib/bootmode.c | 25 + src/mainboard/google/butterfly/romstage.c | 8 +- src/mainboard/google/link/romstage.c | 8 +- src/mainboard/google/parrot/romstage.c | 8 +- src/mainboard/google/stout/romstage.c | 7 +- src/mainboard/intel/emeraldlake2/romstage.c | 8 +- src/mainboard/samsung/lumpy/romstage.c | 7 +- src/mainboard/samsung/stumpy/romstage.c | 7 +- src/soc/intel/baytrail/romstage/romstage.c | 15 +- src/vendorcode/google/chromeos/Kconfig | 8 - src/vendorcode/google/chromeos/Makefile.inc | 7 - src/vendorcode/google/chromeos/chromeos.h | 3 - src/vendorcode/google/chromeos/vboot.c | 244 ---------- src/vendorcode/google/chromeos/vboot_loader.c | 2 +- 27 files changed, 1006 insertions(+), 1036 deletions(-)
diff --git a/src/cpu/intel/haswell/romstage.c b/src/cpu/intel/haswell/romstage.c index 9e27668..a77cb0c 100644 --- a/src/cpu/intel/haswell/romstage.c +++ b/src/cpu/intel/haswell/romstage.c @@ -36,9 +36,7 @@ #include <ramstage_cache.h> #include <romstage_handoff.h> #include <reset.h> -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif +#include <bootmode.h> #if CONFIG_EC_GOOGLE_CHROMEEC #include <ec/google/chromeec/ec.h> #endif @@ -279,9 +277,7 @@ void romstage_common(const struct romstage_params *params) printk(BIOS_DEBUG, "Romstage handoff structure not added!\n");
post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); }
diff --git a/src/drivers/Kconfig b/src/drivers/Kconfig index 874ec75..8c152e1 100644 --- a/src/drivers/Kconfig +++ b/src/drivers/Kconfig @@ -34,6 +34,7 @@ endif source src/drivers/sil/Kconfig source src/drivers/spi/Kconfig source src/drivers/ti/Kconfig +source src/drivers/tpm/Kconfig source src/drivers/trident/Kconfig source src/drivers/uart/Kconfig source src/drivers/usb/Kconfig diff --git a/src/drivers/Makefile.inc b/src/drivers/Makefile.inc index 66fe7b8..df98c90 100644 --- a/src/drivers/Makefile.inc +++ b/src/drivers/Makefile.inc @@ -27,6 +27,7 @@ subdirs-y += maxim subdirs-y += net subdirs-y += parade subdirs-y += sil +subdirs-$(CONFIG_TPM) += tpm subdirs-y += trident subdirs-$(CONFIG_DRIVERS_UART) += uart subdirs-y += usb diff --git a/src/drivers/pc80/Kconfig b/src/drivers/pc80/Kconfig index 3572bc2..5ee5c2a 100644 --- a/src/drivers/pc80/Kconfig +++ b/src/drivers/pc80/Kconfig @@ -14,11 +14,3 @@ config DRIVERS_PS2_KEYBOARD If you know you will only use a payload which does not require this option, then you can say N here to speed up boot time. Otherwise say Y. - -config TPM - bool - default n - help - Enable this option to enable TPM support in coreboot. - - If unsure, say N. diff --git a/src/drivers/pc80/Makefile.inc b/src/drivers/pc80/Makefile.inc index 4d0a280..bfdc11a 100644 --- a/src/drivers/pc80/Makefile.inc +++ b/src/drivers/pc80/Makefile.inc @@ -7,7 +7,6 @@ ramstage-y += keyboard.c ramstage-$(CONFIG_SPKMODEM) += spkmodem.c
romstage-$(CONFIG_USE_OPTION_TABLE) += mc146818rtc_early.c -romstage-$(CONFIG_TPM) += tpm.c romstage-$(CONFIG_SPKMODEM) += spkmodem.c
subdirs-y += vga diff --git a/src/drivers/pc80/tpm.c b/src/drivers/pc80/tpm.c deleted file mode 100644 index f581ab4..0000000 --- a/src/drivers/pc80/tpm.c +++ /dev/null @@ -1,677 +0,0 @@ -/* - * 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. - */ - -#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> -#include <arch/early_variables.h> - -#define PREFIX "lpc_tpm: " - -/* coreboot wrapper for TPM driver (start) */ -#define TPM_DEBUG(fmt, args...) \ - if (CONFIG_DEBUG_TPM) { \ - printk(BIOS_DEBUG, PREFIX); \ - printk(BIOS_DEBUG, fmt , ##args); \ - } -#define TPM_DEBUG_IO_READ(reg_, val_) \ - TPM_DEBUG("Read reg 0x%x returns 0x%x\n", (reg_), (val_)) -#define TPM_DEBUG_IO_WRITE(reg_, val_) \ - TPM_DEBUG("Write reg 0x%x with 0x%x\n", (reg_), (val_)) -#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_BURST_COUNT 0x19 -#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 */ - -/* - * 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) - -/* - * 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; - const struct device_name* dev_names; -}; - -static const struct device_name atmel_devices[] = { - {0x3204, "AT97SC3204"}, - {0xffff} -}; - -static const struct device_name infineon_devices[] = { - {0x000b, "SLB9635 TT 1.2"}, - {0xffff} -}; - -static const struct device_name nuvoton_devices[] = { - {0x00fe, "NPCT420AA V2"}, - {0xffff} -}; - -static const struct device_name stmicro_devices[] = { - {0x0000, "ST33ZP24" }, - {0xffff} -}; - -static const struct vendor_name vendor_names[] = { - {0x1114, "Atmel", atmel_devices}, - {0x15d1, "Infineon", infineon_devices}, - {0x1050, "Nuvoton", nuvoton_devices}, - {0x104a, "ST Microelectronics", stmicro_devices}, -}; - -/* - * Cached vendor/device ID pair to indicate that the device has been already - * discovered - */ -static u32 vendor_dev_id CAR_GLOBAL; - -static inline u8 tpm_read_status(int locality) -{ - u8 value = readb(TIS_REG(locality, TIS_REG_STS)); - TPM_DEBUG_IO_READ(TIS_REG_STS, value); - return value; -} - -static inline void tpm_write_status(u8 sts, int locality) -{ - TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts); - writeb(sts, TIS_REG(locality, TIS_REG_STS)); -} - -static inline u8 tpm_read_data(int locality) -{ - u8 value = readb(TIS_REG(locality, TIS_REG_DATA_FIFO)); - TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value); - return value; -} - -static inline void tpm_write_data(u8 data, int locality) -{ - TPM_DEBUG_IO_WRITE(TIS_REG_STS, data); - writeb(data, TIS_REG(locality, TIS_REG_DATA_FIFO)); -} - -static inline u16 tpm_read_burst_count(int locality) -{ - u16 count; - count = readb(TIS_REG(locality, TIS_REG_BURST_COUNT)); - count |= readb(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8; - TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count); - return count; -} - -static inline u8 tpm_read_access(int locality) -{ - u8 value = readb(TIS_REG(locality, TIS_REG_ACCESS)); - TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value); - return value; -} - -static inline void tpm_write_access(u8 data, int locality) -{ - TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data); - writeb(data, TIS_REG(locality, TIS_REG_ACCESS)); -} - -static inline u32 tpm_read_did_vid(int locality) -{ - u32 value = readl(TIS_REG(locality, TIS_REG_DID_VID)); - TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value); - return value; -} - -/* - * tis_wait_sts() - * - * Wait for at least a second for a status to change its state to match the - * expected state. Normally the transition happens within microseconds. - * - * @locality - locality - * @mask - bitmask for the bitfield(s) to watch - * @expected - value the field(s) are supposed to be set to - * - * Returns 0 on success or TPM_TIMEOUT_ERR on timeout. - */ -static int tis_wait_sts(int locality, u8 mask, u8 expected) -{ - u32 time_us = MAX_DELAY_US; - while (time_us > 0) { - u8 value = tpm_read_status(locality); - if ((value & mask) == expected) - return 0; - udelay(1); /* 1 us */ - time_us--; - } - return TPM_TIMEOUT_ERR; -} - -static inline int tis_wait_ready(int locality) -{ - return tis_wait_sts(locality, TIS_STS_COMMAND_READY, - TIS_STS_COMMAND_READY); -} - -static inline int tis_wait_valid(int locality) -{ - return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID); -} - -static inline int tis_wait_valid_data(int locality) -{ - const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; - return tis_wait_sts(locality, has_data, has_data); -} - -static inline int tis_has_valid_data(int locality) -{ - const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; - return (tpm_read_status(locality) & has_data) == has_data; -} - -static inline int tis_expect_data(int locality) -{ - return !!(tpm_read_status(locality) & TIS_STS_EXPECT); -} - -/* - * tis_wait_access() - * - * Wait for at least a second for a access to change its state to match the - * expected state. Normally the transition happens within microseconds. - * - * @locality - locality - * @mask - bitmask for the bitfield(s) to watch - * @expected - value the field(s) are supposed to be set to - * - * Returns 0 on success or TPM_TIMEOUT_ERR on timeout. - */ -static int tis_wait_access(int locality, u8 mask, u8 expected) -{ - u32 time_us = MAX_DELAY_US; - while (time_us > 0) { - u8 value = tpm_read_access(locality); - if ((value & mask) == expected) - return 0; - udelay(1); /* 1 us */ - time_us--; - } - return TPM_TIMEOUT_ERR; -} - -static inline int tis_wait_dropped_access(int locality) -{ - return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, 0); -} - -static inline int tis_wait_received_access(int locality) -{ - return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, - TIS_ACCESS_ACTIVE_LOCALITY); -} - -static inline int tis_has_access(int locality) -{ - return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY); -} - -static inline void tis_request_access(int locality) -{ - tpm_write_access(TIS_ACCESS_REQUEST_USE, locality); -} - -static inline void tis_drop_access(int locality) -{ - tpm_write_access(TIS_ACCESS_ACTIVE_LOCALITY, locality); -} - -/* - * PC Client Specific TPM Interface Specification section 11.2.12: - * - * Software must be prepared to send two writes of a "1" to command ready - * field: the first to indicate successful read of all the data, thus - * clearing the data from the ReadFIFO and freeing the TPM's resources, - * and the second to indicate to the TPM it is about to send a new command. - * - * In practice not all TPMs behave the same so it is necessary to be - * flexible when trying to set command ready. - * - * Returns 0 on success if the TPM is ready for transactions. - * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set. - */ -static int tis_command_ready(u8 locality) -{ - u32 status; - - /* 1st attempt to set command ready */ - tpm_write_status(TIS_STS_COMMAND_READY, locality); - - /* Wait for response */ - status = tpm_read_status(locality); - - /* Check if command ready is set yet */ - if (status & TIS_STS_COMMAND_READY) - return 0; - - /* 2nd attempt to set command ready */ - tpm_write_status(TIS_STS_COMMAND_READY, locality); - - return tis_wait_ready(locality); -} - -/* - * 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) -{ - const char *device_name = "unknown"; - const char *vendor_name = device_name; - const struct device_name *dev; - u32 didvid; - u16 vid, did; - int i; - - if (car_get_var(vendor_dev_id)) - return 0; /* Already probed. */ - - didvid = tpm_read_did_vid(0); - if (!didvid || (didvid == 0xffffffff)) { - printf("%s: No TPM device found\n", __FUNCTION__); - return TPM_DRIVER_ERR; - } - - car_set_var(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; - } else { - continue; - } - dev = &vendor_names[i].dev_names[j]; - while ((known_did = dev->dev_id) != 0xffff) { - if (known_did == did) { - device_name = dev->dev_name; - break; - } - j++; - } - break; - } - /* this will have to be converted into debug printout */ - printf("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; - - if (tis_wait_ready(locality)) { - printf("%s:%d - failed to get 'command_ready' status\n", - __FILE__, __LINE__); - return TPM_DRIVER_ERR; - } - burst = tpm_read_burst_count(locality); - - 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 = tpm_read_burst_count(locality); - } - - 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(data[offset++], locality); - - if (tis_wait_valid(locality) || !tis_expect_data(locality)) { - printf("%s:%d TPM command feed overflow\n", - __FILE__, __LINE__); - return TPM_DRIVER_ERR; - } - - burst = tpm_read_burst_count(locality); - 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(data[offset++], locality); - - /* - * Verify that TPM does not expect any more data as part of this - * command. - */ - if (tis_wait_valid(locality) || tis_expect_data(locality)) { - printf("%s:%d unexpected TPM status 0x%x\n", - __FILE__, __LINE__, tpm_read_status(locality)); - return TPM_DRIVER_ERR; - } - - /* OK, sitting pretty, let's start the command execution. */ - tpm_write_status(TIS_STS_TPM_GO, locality); - - 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 offset = 0; - u8 locality = 0; - u32 expected_count = *len; - int max_cycles = 0; - - /* Wait for the TPM to process the command */ - if (tis_wait_valid_data(locality)) { - printf("%s:%d failed processing command\n", __FILE__, __LINE__); - return TPM_DRIVER_ERR; - } - - do { - while ((burst_count = tpm_read_burst_count(locality)) == 0) { - if (max_cycles++ == MAX_DELAY_US) { - printf("%s:%d TPM stuck on read\n", - __FILE__, __LINE__); - return TPM_DRIVER_ERR; - } - udelay(1); - } - - max_cycles = 0; - - while (burst_count-- && (offset < expected_count)) { - buffer[offset++] = tpm_read_data(locality); - 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 */ - if (tis_wait_valid(locality)) { - 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 (tis_has_valid_data(locality)); - - /* * Make sure we indeed read all there was. */ - if (tis_has_valid_data(locality)) { - printf("%s:%d wrong receive status: %x %d bytes left\n", - __FILE__, __LINE__, tpm_read_status(locality), - tpm_read_burst_count(locality)); - return TPM_DRIVER_ERR; - } - - /* Tell the TPM that we are done. */ - if (tis_command_ready(locality) == TPM_TIMEOUT_ERR) - return TPM_DRIVER_ERR; - - *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 */ - tis_request_access(locality); - - /* did we get a lock? */ - if (tis_wait_received_access(locality)) { - printf("%s:%d - failed to lock locality %d\n", - __FILE__, __LINE__, locality); - return TPM_DRIVER_ERR; - } - - /* Certain TPMs seem to need some delay here or they hang... */ - udelay(10); - - if (tis_command_ready(locality) == TPM_TIMEOUT_ERR) - return TPM_DRIVER_ERR; - - return 0; -} - -/* - * tis_close() - * - * terminate the current 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 (tis_has_access(locality)) { - tis_drop_access(locality); - if (tis_wait_dropped_access(locality)) { - 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/drivers/tpm/Kconfig b/src/drivers/tpm/Kconfig new file mode 100644 index 0000000..d5a9236 --- /dev/null +++ b/src/drivers/tpm/Kconfig @@ -0,0 +1,16 @@ +config TPM + bool "Initialise TPM hardware" + default n + help + Enable this option to enable TPM support in coreboot. + + If unsure, say N. + +config NO_TPM_RESUME + bool + default n + depends on TPM + help + On some boards the TPM stays powered up in S3. On those + boards, booting Windows will break if the TPM resume command + is sent during an S3 resume. diff --git a/src/drivers/tpm/Makefile.inc b/src/drivers/tpm/Makefile.inc new file mode 100644 index 0000000..9a9f0dd --- /dev/null +++ b/src/drivers/tpm/Makefile.inc @@ -0,0 +1,8 @@ +romstage-y += tpm.c +romstage-$(CONFIG_ARCH_ROMSTAGE_X86_32) += tpm_drv.c + +ifeq ($(MOCK_TPM),1) +CFLAGS_common += -DMOCK_TPM=1 +else +CFLAGS_common += -DMOCK_TPM=0 +endif diff --git a/src/drivers/tpm/tpm.c b/src/drivers/tpm/tpm.c new file mode 100644 index 0000000..1622332 --- /dev/null +++ b/src/drivers/tpm/tpm.c @@ -0,0 +1,221 @@ +/* + * 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 <tpm.h> +#include <reset.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[12]; +} tpm_startup_cmd = { + {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x1 } +}; + +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(int bootmode) +{ + u32 result; + u8 response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; + + printk(BIOS_DEBUG, "Verified boot TPM initialization.\n"); + + printk(BIOS_SPEW, "TPM: Init\n"); + if (tis_init()) + return; + + printk(BIOS_SPEW, "TPM: Open\n"); + if (tis_open()) + return; + + + if (bootmode == 2) { + /* S3 Resume */ + printk(BIOS_SPEW, "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; + } + } else { + printk(BIOS_SPEW, "TPM: Startup\n"); + result = TlclSendReceive(tpm_startup_cmd.buffer, + response, sizeof(response)); + } + + if (result == TPM_SUCCESS) { + printk(BIOS_SPEW, "TPM: OK.\n"); + return; + } + +#if !MOCK_TPM + printk(BIOS_ERR, "TPM: Error code 0x%x. Hard reset!\n", result); + hard_reset(); +#endif +} diff --git a/src/drivers/tpm/tpm_drv.c b/src/drivers/tpm/tpm_drv.c new file mode 100644 index 0000000..ea3b5f3 --- /dev/null +++ b/src/drivers/tpm/tpm_drv.c @@ -0,0 +1,677 @@ +/* + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <delay.h> +#include <arch/io.h> +#include <arch/byteorder.h> +#include <console/console.h> +#include <tpm.h> +#include <arch/early_variables.h> + +#define PREFIX "lpc_tpm: " + +/* coreboot wrapper for TPM driver (start) */ +#define TPM_DEBUG(fmt, args...) \ + if (CONFIG_DEBUG_TPM) { \ + printk(BIOS_DEBUG, PREFIX); \ + printk(BIOS_DEBUG, fmt , ##args); \ + } +#define TPM_DEBUG_IO_READ(reg_, val_) \ + TPM_DEBUG("Read reg 0x%x returns 0x%x\n", (reg_), (val_)) +#define TPM_DEBUG_IO_WRITE(reg_, val_) \ + TPM_DEBUG("Write reg 0x%x with 0x%x\n", (reg_), (val_)) +#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_BURST_COUNT 0x19 +#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 */ + +/* + * 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) + +/* + * 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; + const struct device_name* dev_names; +}; + +static const struct device_name atmel_devices[] = { + {0x3204, "AT97SC3204"}, + {0xffff} +}; + +static const struct device_name infineon_devices[] = { + {0x000b, "SLB9635 TT 1.2"}, + {0xffff} +}; + +static const struct device_name nuvoton_devices[] = { + {0x00fe, "NPCT420AA V2"}, + {0xffff} +}; + +static const struct device_name stmicro_devices[] = { + {0x0000, "ST33ZP24" }, + {0xffff} +}; + +static const struct vendor_name vendor_names[] = { + {0x1114, "Atmel", atmel_devices}, + {0x15d1, "Infineon", infineon_devices}, + {0x1050, "Nuvoton", nuvoton_devices}, + {0x104a, "ST Microelectronics", stmicro_devices}, +}; + +/* + * Cached vendor/device ID pair to indicate that the device has been already + * discovered + */ +static u32 vendor_dev_id CAR_GLOBAL; + +static inline u8 tpm_read_status(int locality) +{ + u8 value = readb(TIS_REG(locality, TIS_REG_STS)); + TPM_DEBUG_IO_READ(TIS_REG_STS, value); + return value; +} + +static inline void tpm_write_status(u8 sts, int locality) +{ + TPM_DEBUG_IO_WRITE(TIS_REG_STS, sts); + writeb(sts, TIS_REG(locality, TIS_REG_STS)); +} + +static inline u8 tpm_read_data(int locality) +{ + u8 value = readb(TIS_REG(locality, TIS_REG_DATA_FIFO)); + TPM_DEBUG_IO_READ(TIS_REG_DATA_FIFO, value); + return value; +} + +static inline void tpm_write_data(u8 data, int locality) +{ + TPM_DEBUG_IO_WRITE(TIS_REG_STS, data); + writeb(data, TIS_REG(locality, TIS_REG_DATA_FIFO)); +} + +static inline u16 tpm_read_burst_count(int locality) +{ + u16 count; + count = readb(TIS_REG(locality, TIS_REG_BURST_COUNT)); + count |= readb(TIS_REG(locality, TIS_REG_BURST_COUNT + 1)) << 8; + TPM_DEBUG_IO_READ(TIS_REG_BURST_COUNT, count); + return count; +} + +static inline u8 tpm_read_access(int locality) +{ + u8 value = readb(TIS_REG(locality, TIS_REG_ACCESS)); + TPM_DEBUG_IO_READ(TIS_REG_ACCESS, value); + return value; +} + +static inline void tpm_write_access(u8 data, int locality) +{ + TPM_DEBUG_IO_WRITE(TIS_REG_ACCESS, data); + writeb(data, TIS_REG(locality, TIS_REG_ACCESS)); +} + +static inline u32 tpm_read_did_vid(int locality) +{ + u32 value = readl(TIS_REG(locality, TIS_REG_DID_VID)); + TPM_DEBUG_IO_READ(TIS_REG_DID_VID, value); + return value; +} + +/* + * tis_wait_sts() + * + * Wait for at least a second for a status to change its state to match the + * expected state. Normally the transition happens within microseconds. + * + * @locality - locality + * @mask - bitmask for the bitfield(s) to watch + * @expected - value the field(s) are supposed to be set to + * + * Returns 0 on success or TPM_TIMEOUT_ERR on timeout. + */ +static int tis_wait_sts(int locality, u8 mask, u8 expected) +{ + u32 time_us = MAX_DELAY_US; + while (time_us > 0) { + u8 value = tpm_read_status(locality); + if ((value & mask) == expected) + return 0; + udelay(1); /* 1 us */ + time_us--; + } + return TPM_TIMEOUT_ERR; +} + +static inline int tis_wait_ready(int locality) +{ + return tis_wait_sts(locality, TIS_STS_COMMAND_READY, + TIS_STS_COMMAND_READY); +} + +static inline int tis_wait_valid(int locality) +{ + return tis_wait_sts(locality, TIS_STS_VALID, TIS_STS_VALID); +} + +static inline int tis_wait_valid_data(int locality) +{ + const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; + return tis_wait_sts(locality, has_data, has_data); +} + +static inline int tis_has_valid_data(int locality) +{ + const u8 has_data = TIS_STS_DATA_AVAILABLE | TIS_STS_VALID; + return (tpm_read_status(locality) & has_data) == has_data; +} + +static inline int tis_expect_data(int locality) +{ + return !!(tpm_read_status(locality) & TIS_STS_EXPECT); +} + +/* + * tis_wait_access() + * + * Wait for at least a second for a access to change its state to match the + * expected state. Normally the transition happens within microseconds. + * + * @locality - locality + * @mask - bitmask for the bitfield(s) to watch + * @expected - value the field(s) are supposed to be set to + * + * Returns 0 on success or TPM_TIMEOUT_ERR on timeout. + */ +static int tis_wait_access(int locality, u8 mask, u8 expected) +{ + u32 time_us = MAX_DELAY_US; + while (time_us > 0) { + u8 value = tpm_read_access(locality); + if ((value & mask) == expected) + return 0; + udelay(1); /* 1 us */ + time_us--; + } + return TPM_TIMEOUT_ERR; +} + +static inline int tis_wait_dropped_access(int locality) +{ + return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, 0); +} + +static inline int tis_wait_received_access(int locality) +{ + return tis_wait_access(locality, TIS_ACCESS_ACTIVE_LOCALITY, + TIS_ACCESS_ACTIVE_LOCALITY); +} + +static inline int tis_has_access(int locality) +{ + return !!(tpm_read_access(locality) & TIS_ACCESS_ACTIVE_LOCALITY); +} + +static inline void tis_request_access(int locality) +{ + tpm_write_access(TIS_ACCESS_REQUEST_USE, locality); +} + +static inline void tis_drop_access(int locality) +{ + tpm_write_access(TIS_ACCESS_ACTIVE_LOCALITY, locality); +} + +/* + * PC Client Specific TPM Interface Specification section 11.2.12: + * + * Software must be prepared to send two writes of a "1" to command ready + * field: the first to indicate successful read of all the data, thus + * clearing the data from the ReadFIFO and freeing the TPM's resources, + * and the second to indicate to the TPM it is about to send a new command. + * + * In practice not all TPMs behave the same so it is necessary to be + * flexible when trying to set command ready. + * + * Returns 0 on success if the TPM is ready for transactions. + * Returns TPM_TIMEOUT_ERR if the command ready bit does not get set. + */ +static int tis_command_ready(u8 locality) +{ + u32 status; + + /* 1st attempt to set command ready */ + tpm_write_status(TIS_STS_COMMAND_READY, locality); + + /* Wait for response */ + status = tpm_read_status(locality); + + /* Check if command ready is set yet */ + if (status & TIS_STS_COMMAND_READY) + return 0; + + /* 2nd attempt to set command ready */ + tpm_write_status(TIS_STS_COMMAND_READY, locality); + + return tis_wait_ready(locality); +} + +/* + * 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) +{ + const char *device_name = "unknown"; + const char *vendor_name = device_name; + const struct device_name *dev; + u32 didvid; + u16 vid, did; + int i; + + if (car_get_var(vendor_dev_id)) + return 0; /* Already probed. */ + + didvid = tpm_read_did_vid(0); + if (!didvid || (didvid == 0xffffffff)) { + printf("%s: No TPM device found\n", __FUNCTION__); + return TPM_DRIVER_ERR; + } + + car_set_var(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; + } else { + continue; + } + dev = &vendor_names[i].dev_names[j]; + while ((known_did = dev->dev_id) != 0xffff) { + if (known_did == did) { + device_name = dev->dev_name; + break; + } + j++; + } + break; + } + /* this will have to be converted into debug printout */ + printf("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; + + if (tis_wait_ready(locality)) { + printf("%s:%d - failed to get 'command_ready' status\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + burst = tpm_read_burst_count(locality); + + 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 = tpm_read_burst_count(locality); + } + + 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(data[offset++], locality); + + if (tis_wait_valid(locality) || !tis_expect_data(locality)) { + printf("%s:%d TPM command feed overflow\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + burst = tpm_read_burst_count(locality); + 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(data[offset++], locality); + + /* + * Verify that TPM does not expect any more data as part of this + * command. + */ + if (tis_wait_valid(locality) || tis_expect_data(locality)) { + printf("%s:%d unexpected TPM status 0x%x\n", + __FILE__, __LINE__, tpm_read_status(locality)); + return TPM_DRIVER_ERR; + } + + /* OK, sitting pretty, let's start the command execution. */ + tpm_write_status(TIS_STS_TPM_GO, locality); + + 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 offset = 0; + u8 locality = 0; + u32 expected_count = *len; + int max_cycles = 0; + + /* Wait for the TPM to process the command */ + if (tis_wait_valid_data(locality)) { + printf("%s:%d failed processing command\n", __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + + do { + while ((burst_count = tpm_read_burst_count(locality)) == 0) { + if (max_cycles++ == MAX_DELAY_US) { + printf("%s:%d TPM stuck on read\n", + __FILE__, __LINE__); + return TPM_DRIVER_ERR; + } + udelay(1); + } + + max_cycles = 0; + + while (burst_count-- && (offset < expected_count)) { + buffer[offset++] = tpm_read_data(locality); + 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 */ + if (tis_wait_valid(locality)) { + 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 (tis_has_valid_data(locality)); + + /* * Make sure we indeed read all there was. */ + if (tis_has_valid_data(locality)) { + printf("%s:%d wrong receive status: %x %d bytes left\n", + __FILE__, __LINE__, tpm_read_status(locality), + tpm_read_burst_count(locality)); + return TPM_DRIVER_ERR; + } + + /* Tell the TPM that we are done. */ + if (tis_command_ready(locality) == TPM_TIMEOUT_ERR) + return TPM_DRIVER_ERR; + + *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 */ + tis_request_access(locality); + + /* did we get a lock? */ + if (tis_wait_received_access(locality)) { + printf("%s:%d - failed to lock locality %d\n", + __FILE__, __LINE__, locality); + return TPM_DRIVER_ERR; + } + + /* Certain TPMs seem to need some delay here or they hang... */ + udelay(10); + + if (tis_command_ready(locality) == TPM_TIMEOUT_ERR) + return TPM_DRIVER_ERR; + + return 0; +} + +/* + * tis_close() + * + * terminate the current 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 (tis_has_access(locality)) { + tis_drop_access(locality); + if (tis_wait_dropped_access(locality)) { + 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/include/bootmode.h b/src/include/bootmode.h index 323668b..3a0809c 100644 --- a/src/include/bootmode.h +++ b/src/include/bootmode.h @@ -37,4 +37,6 @@ static inline int recovery_mode_enabled(void) { return 0; } static inline int developer_mode_enabled(void) { return 0; } #endif
+/* FIXME: Parameter boot_mode is obscure. 0 for cold boot, 2 for S3 resume. */ +void init_trusted_platform(int boot_mode); #endif /* __BOOTMODE_H__ */ diff --git a/src/include/pc80/tpm.h b/src/include/pc80/tpm.h deleted file mode 100644 index 2eff15a..0000000 --- a/src/include/pc80/tpm.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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/include/tpm.h b/src/include/tpm.h new file mode 100644 index 0000000..b6cc5ce --- /dev/null +++ b/src/include/tpm.h @@ -0,0 +1,36 @@ +/* + * 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_ + +#include <stddef.h> + +#if CONFIG_TPM +void init_vboot(int bootmode); +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); +#else +static inline void init_vboot(int bootmode) {}; +#endif + +#endif /* TPM_H_ */ diff --git a/src/lib/bootmode.c b/src/lib/bootmode.c index 9818490..8f8cb3a 100644 --- a/src/lib/bootmode.c +++ b/src/lib/bootmode.c @@ -18,7 +18,9 @@ */
#include <rules.h> +#include <console/console.h> #include <bootmode.h> +#include <tpm.h> #if CONFIG_CHROMEOS || CONFIG_VBOOT_VERIFY_FIRMWARE #include <vendorcode/google/chromeos/chromeos.h> #endif @@ -78,3 +80,26 @@ void gfx_set_init_done(int done) gfx_init_done = done; } #endif + +#if ENV_ROMSTAGE +void init_trusted_platform(int boot_mode) +{ + /* With CONFIG_CHROMEOS, u-boot does TPM init on power-on. + * Doing TPM startup when we're not coming in on the S3 resume path + * saves us roughly 20ms in boot time only. This does not seem to + * be worth an API change to vboot_reference-firmware right now, so + * just bail out early: + */ + if (IS_ENABLED(CONFIG_VBOOT_VERIFY_FIRMWARE) && (boot_mode != 2)) + return; + + /* On some boards the TPM stays powered up in S3. */ + if (IS_ENABLED(CONFIG_NO_TPM_RESUME) && (boot_mode == 2)) + return; + + if (IS_ENABLED(CONFIG_TPM)) + init_vboot(boot_mode); + else + printk(BIOS_DEBUG, "TPM: support not enabled in build.\n"); +} +#endif diff --git a/src/mainboard/google/butterfly/romstage.c b/src/mainboard/google/butterfly/romstage.c index d32f3f0..0b2f5c9 100644 --- a/src/mainboard/google/butterfly/romstage.c +++ b/src/mainboard/google/butterfly/romstage.c @@ -38,9 +38,7 @@ #include <cpu/x86/bist.h> #include <cpu/x86/msr.h> #include "gpio.h" -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif +#include <bootmode.h> #include <cbfs.h>
static void pch_enable_lpc(void) @@ -273,8 +271,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/google/link/romstage.c b/src/mainboard/google/link/romstage.c index 2f17880..76a0943 100644 --- a/src/mainboard/google/link/romstage.c +++ b/src/mainboard/google/link/romstage.c @@ -40,9 +40,7 @@ #include <cpu/x86/bist.h> #include <cpu/x86/msr.h> #include "gpio.h" -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif +#include <bootmode.h> #include <cbfs.h>
#include <southbridge/intel/bd82x6x/chip.h> @@ -317,8 +315,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/google/parrot/romstage.c b/src/mainboard/google/parrot/romstage.c index 1799aec..59cb329 100644 --- a/src/mainboard/google/parrot/romstage.c +++ b/src/mainboard/google/parrot/romstage.c @@ -38,9 +38,7 @@ #include <cpu/x86/bist.h> #include <cpu/x86/msr.h> #include "gpio.h" -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif +#include <bootmode.h> #include <cbfs.h> #include "ec/compal/ene932/ec.h"
@@ -273,8 +271,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/google/stout/romstage.c b/src/mainboard/google/stout/romstage.c index f53c07d..011ff7d 100644 --- a/src/mainboard/google/stout/romstage.c +++ b/src/mainboard/google/stout/romstage.c @@ -39,9 +39,6 @@ #include <cpu/x86/msr.h> #include "gpio.h" #include <bootmode.h> -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif #include <cbfs.h> #include <ec/quanta/it8518/ec.h> #include "ec.h" @@ -327,8 +324,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/intel/emeraldlake2/romstage.c b/src/mainboard/intel/emeraldlake2/romstage.c index a7ab482..0af2016 100644 --- a/src/mainboard/intel/emeraldlake2/romstage.c +++ b/src/mainboard/intel/emeraldlake2/romstage.c @@ -38,9 +38,7 @@ #include <cpu/x86/bist.h> #include <cpu/x86/msr.h> #include "gpio.h" -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif +#include <bootmode.h>
#define SIO_PORT 0x164e
@@ -325,8 +323,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/samsung/lumpy/romstage.c b/src/mainboard/samsung/lumpy/romstage.c index ef5a836..1882efe 100644 --- a/src/mainboard/samsung/lumpy/romstage.c +++ b/src/mainboard/samsung/lumpy/romstage.c @@ -45,9 +45,6 @@ #include "superio/smsc/lpc47n207/lpc47n207.h" #include "superio/smsc/lpc47n207/early_serial.c" #endif -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif
static void pch_enable_lpc(void) { @@ -345,8 +342,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/mainboard/samsung/stumpy/romstage.c b/src/mainboard/samsung/stumpy/romstage.c index 0067cb5..bff06b4 100644 --- a/src/mainboard/samsung/stumpy/romstage.c +++ b/src/mainboard/samsung/stumpy/romstage.c @@ -45,9 +45,6 @@ #include "superio/smsc/lpc47n207/lpc47n207.h" #include "superio/smsc/lpc47n207/early_serial.c" #endif -#if CONFIG_CHROMEOS -#include <vendorcode/google/chromeos/chromeos.h> -#endif
/* Stumpy USB Reset Disable defined in cmos.layout */ #if CONFIG_USE_OPTION_TABLE @@ -352,8 +349,6 @@ void main(unsigned long bist) } #endif post_code(0x3f); -#if CONFIG_CHROMEOS - init_chromeos(boot_mode); -#endif + init_trusted_platform(boot_mode); timestamp_add_now(TS_END_ROMSTAGE); } diff --git a/src/soc/intel/baytrail/romstage/romstage.c b/src/soc/intel/baytrail/romstage/romstage.c index baa62a5..690ae47 100644 --- a/src/soc/intel/baytrail/romstage/romstage.c +++ b/src/soc/intel/baytrail/romstage/romstage.c @@ -33,7 +33,7 @@ #include <ramstage_cache.h> #include <romstage_handoff.h> #include <timestamp.h> -#include <vendorcode/google/chromeos/chromeos.h> +#include <bootmode.h> #include <baytrail/gpio.h> #include <baytrail/iomap.h> #include <baytrail/lpc.h> @@ -222,14 +222,6 @@ static int chipset_prev_sleep_state(struct chipset_power_state *ps) return prev_sleep_state; }
-static inline void chromeos_init(int prev_sleep_state) -{ -#if CONFIG_CHROMEOS - /* Normalize the sleep state to what init_chromeos() wants for S3: 2. */ - init_chromeos(prev_sleep_state == 3 ? 2 : 0); -#endif -} - /* Entry from the mainboard. */ void romstage_common(struct romstage_params *params) { @@ -259,7 +251,10 @@ void romstage_common(struct romstage_params *params) else printk(BIOS_DEBUG, "Romstage handoff structure not added!\n");
- chromeos_init(prev_sleep_state); + /* Normalize the sleep state to what init_trusted_platform() + * wants for S3: 2. + */ + init_trusted_platform(prev_sleep_state == 3 ? 2 : 0);
/* Save timestamp information. */ timestamp_init(ts64_to_tsc(params->ts.times[0])); diff --git a/src/vendorcode/google/chromeos/Kconfig b/src/vendorcode/google/chromeos/Kconfig index a1a9d9a..6c074c7 100644 --- a/src/vendorcode/google/chromeos/Kconfig +++ b/src/vendorcode/google/chromeos/Kconfig @@ -124,13 +124,5 @@ config VBOOT_REFCODE_INDEX This is the index of the reference code component in the verified firmware block.
-config NO_TPM_RESUME - bool - default n - help - On some boards the TPM stays powered up in S3. On those - boards, booting Windows will break if the TPM resume command - is sent during an S3 resume. - endif endmenu diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index 9bd5091..76202c7 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -19,19 +19,12 @@
romstage-$(CONFIG_ARCH_ROMSTAGE_X86_32) += vbnv.c ramstage-$(CONFIG_ARCH_RAMSTAGE_X86_32) += vbnv.c -romstage-$(CONFIG_ARCH_ROMSTAGE_X86_32) += vboot.c ramstage-y += gnvs.c romstage-y += fmap.c ramstage-y += fmap.c ramstage-$(CONFIG_CHROMEOS_RAMOOPS) += ramoops.c smm-y += fmap.c
-ifeq ($(MOCK_TPM),1) -CFLAGS_common += -DMOCK_TPM=1 -else -CFLAGS_common += -DMOCK_TPM=0 -endif - ifeq ($(CONFIG_VBOOT_VERIFY_FIRMWARE),y) romstage-y += vboot_handoff.c ramstage-y += vboot_handoff.c diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h index dc34dc3..ce884b1 100644 --- a/src/vendorcode/google/chromeos/chromeos.h +++ b/src/vendorcode/google/chromeos/chromeos.h @@ -31,9 +31,6 @@ int vboot_wants_oprom(void); void read_vbnv(uint8_t *vbnv_copy); void save_vbnv(const uint8_t *vbnv_copy);
-/* functions implemented in vboot.c */ -void init_chromeos(int bootmode); - #if CONFIG_VBOOT_VERIFY_FIRMWARE /* Returns 0 on success < 0 on error. */ int vboot_get_handoff_info(void **addr, uint32_t *size); diff --git a/src/vendorcode/google/chromeos/vboot.c b/src/vendorcode/google/chromeos/vboot.c deleted file mode 100644 index 03d759c..0000000 --- a/src/vendorcode/google/chromeos/vboot.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * 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 <reset.h> -#include "chromeos.h" - -//#define EXTRA_LOGGING -#define UBOOT_DOES_TPM_STARTUP - -#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)) - -#if CONFIG_NO_TPM_RESUME -static void init_vboot(int bootmode) -{ -} -#else -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[12]; -} tpm_startup_cmd = { - {0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x1 } -}; - -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; -} - -static void init_vboot(int bootmode) -{ - u32 result; - u8 response[TPM_LARGE_ENOUGH_COMMAND_SIZE]; - -#ifdef UBOOT_DOES_TPM_STARTUP - /* Doing TPM startup when we're not coming in on the S3 resume path - * saves us roughly 20ms in boot time only. This does not seem to - * be worth an API change to vboot_reference-firmware right now, so - * let's keep the code around, but just bail out early: - */ - if (bootmode != 2) - return; -#endif - - printk(BIOS_DEBUG, "Verified boot TPM initialization.\n"); - - printk(BIOS_SPEW, "TPM: Init\n"); - if (tis_init()) - return; - - printk(BIOS_SPEW, "TPM: Open\n"); - if (tis_open()) - return; - - - if (bootmode == 2) { - /* S3 Resume */ - printk(BIOS_SPEW, "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; - } - } else { - printk(BIOS_SPEW, "TPM: Startup\n"); - result = TlclSendReceive(tpm_startup_cmd.buffer, - response, sizeof(response)); - } - - if (result == TPM_SUCCESS) { - printk(BIOS_SPEW, "TPM: OK.\n"); - return; - } - -#if !MOCK_TPM - printk(BIOS_ERR, "TPM: Error code 0x%x. Hard reset!\n", result); - hard_reset(); -#endif -} -#endif - -void init_chromeos(int bootmode) -{ - init_vboot(bootmode); -} diff --git a/src/vendorcode/google/chromeos/vboot_loader.c b/src/vendorcode/google/chromeos/vboot_loader.c index 943ad17..2f786ad 100644 --- a/src/vendorcode/google/chromeos/vboot_loader.c +++ b/src/vendorcode/google/chromeos/vboot_loader.c @@ -24,7 +24,7 @@ #include <cbmem.h> #include <console/console.h> #include <console/vtxprintf.h> -#include <pc80/tpm.h> +#include <tpm.h> #include <reset.h> #include <ramstage_loader.h> #include <romstage_handoff.h>