diff options
author | Tim Wawrzynczak <twawrzynczak@chromium.org> | 2022-02-14 16:04:21 -0700 |
---|---|---|
committer | Tim Wawrzynczak <twawrzynczak@chromium.org> | 2022-03-07 18:06:24 +0000 |
commit | 6b8599f29a888c0946ff44e97c32ecd4cec2a151 (patch) | |
tree | ca38ae8682e63f5abc66e9475798b5614a05c0fe /src/drivers/tpm | |
parent | f019d986b154cb95db541be9c71eafde51ec9294 (diff) |
drivers/tpm/spi: Refactor out some cr50-specific logic
Mainboards accessing the cr50 over an I2C bus may want to reuse some of
the same firmware version and BOARD_CFG logic, therefore refactor this
logic out into a bus-agnostic file, drivers/tpm/cr50.c. This file uses
the new tis_vendor_read/write() functions in order to access the cr50
regardless of the bus which is physically used. In order to leave SPI
devices intact, the tis_vendor_* functions are added to the SPI driver.
BUG=b:202246591
TEST=boot to OS on google/dratini, see the same FW version and board_cfg
console prints as before the change.
Signed-off-by: Tim Wawrzynczak <twawrzynczak@chromium.org>
Change-Id: Ie68618cbe026a2b9221f93d0fe41d0b2054e8091
Reviewed-on: https://review.coreboot.org/c/coreboot/+/61977
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Subrata Banik <subratabanik@google.com>
Diffstat (limited to 'src/drivers/tpm')
-rw-r--r-- | src/drivers/tpm/Makefile.inc | 6 | ||||
-rw-r--r-- | src/drivers/tpm/cr50.c | 195 | ||||
-rw-r--r-- | src/drivers/tpm/cr50.h | 24 |
3 files changed, 225 insertions, 0 deletions
diff --git a/src/drivers/tpm/Makefile.inc b/src/drivers/tpm/Makefile.inc index 9833eb4a2f..a56c02b666 100644 --- a/src/drivers/tpm/Makefile.inc +++ b/src/drivers/tpm/Makefile.inc @@ -5,3 +5,9 @@ ramstage-$(CONFIG_HAVE_ACPI_TABLES) += ppi.c else ramstage-$(CONFIG_HAVE_ACPI_TABLES) += ppi_stub.c endif + +bootblock-$(CONFIG_TPM_CR50) += cr50.c +verstage-$(CONFIG_TPM_CR50) += cr50.c +romstage-$(CONFIG_TPM_CR50) += cr50.c +ramstage-$(CONFIG_TPM_CR50) += cr50.c +postcar-$(CONFIG_TPM_CR50) += cr50.c diff --git a/src/drivers/tpm/cr50.c b/src/drivers/tpm/cr50.c new file mode 100644 index 0000000000..c8ab5e4a55 --- /dev/null +++ b/src/drivers/tpm/cr50.c @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#include <drivers/spi/tpm/tpm.h> +#include <security/tpm/tis.h> +#include <string.h> +#include <types.h> + +#define CR50_BOARD_CFG_LOCKBIT_MASK 0x80000000U +#define CR50_BOARD_CFG_FEATUREBITS_MASK 0x3FFFFFFFU + +#define CR50_BOARD_CFG_100US_READY_PULSE 0x00000001U +#define CR50_BOARD_CFG_VALUE \ + (CONFIG(CR50_USE_LONG_INTERRUPT_PULSES) \ + ? CR50_BOARD_CFG_100US_READY_PULSE : 0) + +static struct cr50_firmware_version cr50_firmware_version; + +enum cr50_register { + CR50_FW_VER_REG, + CR50_BOARD_CFG_REG, +}; + +#define CR50_FW_VER_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xf90) +#define CR50_BOARD_CFG_REG_SPI (TPM_LOCALITY_0_SPI_BASE + 0xfe0) + +/* Return register address, which depends on the bus type, or -1 for error. */ +static int get_reg_addr(enum cr50_register reg) +{ + if (CONFIG(SPI_TPM)) { + switch (reg) { + case CR50_FW_VER_REG: + return CR50_FW_VER_REG_SPI; + case CR50_BOARD_CFG_REG: + return CR50_BOARD_CFG_REG_SPI; + default: + return -1; + } + } + + + return -1; +} + +static bool cr50_fw_supports_board_cfg(struct cr50_firmware_version *version) +{ + /* Cr50 supports the CR50_BOARD_CFG register from version 0.5.5 / 0.6.5 + * and onwards. */ + if (version->epoch > 0 || version->major >= 7 + || (version->major >= 5 && version->minor >= 5)) + return true; + + printk(BIOS_INFO, "Cr50 firmware does not support CR50_BOARD_CFG, version: %d.%d.%d\n", + version->epoch, version->major, version->minor); + + return false; +} + +/* + * Expose method to read the CR50_BOARD_CFG register, will return zero if + * register not supported by Cr50 firmware. + */ +static uint32_t cr50_get_board_cfg(void) +{ + uint32_t value; + + if (!cr50_fw_supports_board_cfg(&cr50_firmware_version)) + return 0; + + const cb_err_t ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value, + sizeof(value)); + if (ret != CB_SUCCESS) { + printk(BIOS_INFO, "Error reading from cr50\n"); + return 0; + } + + return value & CR50_BOARD_CFG_FEATUREBITS_MASK; +} + +/** + * Set the BOARD_CFG register on the TPM chip to a particular compile-time constant value. + */ +cb_err_t cr50_set_board_cfg(void) +{ + uint32_t value; + cb_err_t ret; + + if (!cr50_fw_supports_board_cfg(&cr50_firmware_version)) + return CB_ERR; + + /* Set the CR50_BOARD_CFG register, for e.g. asking cr50 to use longer ready pulses. */ + ret = tis_vendor_read(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value)); + if (ret != CB_SUCCESS) { + printk(BIOS_INFO, "Error reading from cr50\n"); + return CB_ERR; + } + + if ((value & CR50_BOARD_CFG_FEATUREBITS_MASK) == CR50_BOARD_CFG_VALUE) { + printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, matches desired = 0x%08x\n", + value, CR50_BOARD_CFG_VALUE); + return CB_SUCCESS; + } + + if (value & CR50_BOARD_CFG_LOCKBIT_MASK) { + /* The high bit is set, meaning that the Cr50 is already locked on a particular + * value for the register, but not the one we wanted. */ + printk(BIOS_ERR, "Current CR50_BOARD_CFG = 0x%08x, does not match" + "desired = 0x%08x\n", value, CR50_BOARD_CFG_VALUE); + return CB_ERR; + } + + printk(BIOS_INFO, "Current CR50_BOARD_CFG = 0x%08x, setting to 0x%08x\n", + value, CR50_BOARD_CFG_VALUE); + value = CR50_BOARD_CFG_VALUE; + + ret = tis_vendor_write(get_reg_addr(CR50_BOARD_CFG_REG), &value, sizeof(value)); + if (ret != CB_SUCCESS) { + printk(BIOS_ERR, "Error writing to cr50\n"); + return ret; + } + + return CB_SUCCESS; +} + +bool cr50_is_long_interrupt_pulse_enabled(void) +{ + return !!(cr50_get_board_cfg() & CR50_BOARD_CFG_100US_READY_PULSE); +} + +static cb_err_t cr50_parse_fw_version(const char *version_str, + struct cr50_firmware_version *ver) +{ + int epoch, major, minor; + + char *number = strstr(version_str, " RW_A:"); + if (!number) + number = strstr(version_str, " RW_B:"); + if (!number) + return CB_ERR_ARG; + number += 6; /* Skip past the colon. */ + + epoch = skip_atoi(&number); + if (*number++ != '.') + return CB_ERR_ARG; + major = skip_atoi(&number); + if (*number++ != '.') + return CB_ERR_ARG; + minor = skip_atoi(&number); + + ver->epoch = epoch; + ver->major = major; + ver->minor = minor; + return CB_SUCCESS; +} + +cb_err_t cr50_get_firmware_version(struct cr50_firmware_version *version) +{ + if (cr50_firmware_version.epoch || cr50_firmware_version.major || + cr50_firmware_version.minor) + goto success; + + int chunk_count = 0; + size_t chunk_size = 50; + char version_str[301]; + int addr = get_reg_addr(CR50_FW_VER_REG); + + /* + * Does not really matter what's written, this just makes sure + * the version is reported from the beginning. + */ + tis_vendor_write(addr, &chunk_size, 1); + + /* + * Read chunk_size bytes at a time, last chunk will be zero padded. + */ + do { + uint8_t *buf = (uint8_t *)version_str + chunk_count * chunk_size; + tis_vendor_read(addr, buf, chunk_size); + if (!version_str[++chunk_count * chunk_size - 1]) + /* Zero padding detected: end of string. */ + break; + /* Check if there is enough room for reading one more chunk. */ + } while (chunk_count * chunk_size < sizeof(version_str) - chunk_size); + + version_str[chunk_count * chunk_size] = '\0'; + printk(BIOS_INFO, "Firmware version: %s\n", version_str); + + if (cr50_parse_fw_version(version_str, &cr50_firmware_version) != CB_SUCCESS) { + printk(BIOS_ERR, "Did not recognize Cr50 version format\n"); + return CB_ERR; + } + +success: + *version = cr50_firmware_version; + return CB_SUCCESS; +} diff --git a/src/drivers/tpm/cr50.h b/src/drivers/tpm/cr50.h new file mode 100644 index 0000000000..4fdd06b50b --- /dev/null +++ b/src/drivers/tpm/cr50.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ + +#ifndef __DRIVERS_TPM_CR50_H__ +#define __DRIVERS_TPM_CR50_H__ + +#include <types.h> + +/* Structure describing the elements of Cr50 firmware version. */ +struct cr50_firmware_version { + int epoch; + int major; + int minor; +}; + +/* Indicates whether Cr50 ready pulses are guaranteed to be at least 100us. */ +bool cr50_is_long_interrupt_pulse_enabled(void); + +/* Get the Cr50 firmware version information. */ +cb_err_t cr50_get_firmware_version(struct cr50_firmware_version *version); + +/* Set the BOARD_CFG register depending on Cr50 Kconfigs */ +cb_err_t cr50_set_board_cfg(void); + +#endif /* __DRIVERS_TPM_CR50_H__ */ |