Karthik Ramasubramanian has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/44277 )
Change subject: drivers/spi/tpm: Add support to cache the TPM firmware version ......................................................................
drivers/spi/tpm: Add support to cache the TPM firmware version
TPM firmware version is required for couple of use-cases during ramstage - GPIO PM configuration for Intel SoCs, Board specific reset when CSE Lite jumps from RO to RW. This change adds support to read the TPM firmware version and cache it.
BUG=b:154333137, b:162290856 TEST=Ensure that the DUT boots to OS. Ensure that the firmware version is cached and matches with the firmware version logged in the bootup logs.
Change-Id: I2c894d619f6b986be196a9da6ee4556bc440424e --- M src/drivers/spi/tpm/tpm.c M src/drivers/spi/tpm/tpm.h 2 files changed, 148 insertions(+), 6 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/77/44277/1
diff --git a/src/drivers/spi/tpm/tpm.c b/src/drivers/spi/tpm/tpm.c index b44c87e..dac405a 100644 --- a/src/drivers/spi/tpm/tpm.c +++ b/src/drivers/spi/tpm/tpm.c @@ -35,6 +35,7 @@ #define CR50_TIMEOUT_INIT_MS 30000 /* Very long timeout for TPM init */
#define TPM_FW_VER_STRLEN 301 +#define TPM_FW_VER_COUNT 2 /* One for RO and one for RW */
/* SPI slave structure for TPM device. */ static struct spi_slave spi_slave; @@ -42,6 +43,9 @@ /* Cached TPM device identification. */ static struct tpm2_info tpm_info;
+/* Cached TPM FW Version. */ +static struct tpm2_fw_ver tpm_fw_ver[TPM_FW_VER_COUNT]; + /* * TODO(vbendeb): make CONFIG(DEBUG_TPM) an int to allow different level of * debug traces. Right now it is either 0 or 1. @@ -423,20 +427,96 @@ return 0; }
+/** + * Extract the TPM firmware version from the tokenized firmware version string. + * @fw_ver : Tokenized firmware version string. + * @cache : Buffer to cache the firmware version. + * + * @return: 0 on success, -1 on error. + * + * The RO/RW tokenized firmware version string is of the below format: + * "<region_type>_<region_label>:<epoch>.<major_ver>.<minor_ver>/<hash>" + * where region_type : "RO" or "RW" + * region_label : "A" or "B" + */ +static int cache_tpm_fw_version(char *fw_ver, struct tpm2_fw_ver *cache) +{ + /* + */ + char *str = fw_ver + 5; /* Skip past the colon. */ + enum tpm2_fw_ver_type type = TPM2_FW_VER_TYPE_INIT; + + if (!strncmp(fw_ver, "RO_", strlen("RO_"))) + type = TPM2_FW_VER_TYPE_RO; + else if (!strncmp(fw_ver, "RW_", strlen("RW_"))) + type = TPM2_FW_VER_TYPE_RW; + + if (type == TPM2_FW_VER_TYPE_INIT) + return -1; + + cache->epoch = skip_atoi(&str); + if (*str++ != '.') { + printk(BIOS_ERR, "Error parsing after epoch\n"); + return -1; + } + cache->major_ver = skip_atoi(&str); + if (*str++ != '.') { + printk(BIOS_ERR, "Error parsing after major version\n"); + return -1; + } + cache->minor_ver = skip_atoi(&str); + if (*str != '/') { + printk(BIOS_ERR, "Error parsing after minor version\n"); + return -1; + } + + /* Caching the type marks successful caching of firmware version. */ + cache->type = type; + return 0; +} + +/** + * Helper to parse the firmware version string read from TPM. + * @vstr : Firmware version string read from TPM. + * + * The firmware version string read from TPM is of the following format: + * "<chip_info> <RO_firmware_info> <RW_firmware_info>". + * Use the space delimiter to tokenize the TPM firmware version string + * and extract the firmware version. + */ +static void parse_tpm_fw_version(char *vstr) +{ + char *str = vstr; + char *fw_ver, *ctx; + uint32_t count = 0; + + do { + fw_ver = strtok_r(str, " ", &ctx); + if (fw_ver && !cache_tpm_fw_version(fw_ver, &tpm_fw_ver[count])) + count++; + str = NULL; + } while (fw_ver && count < TPM_FW_VER_COUNT); +} + static void read_tpm_fw_version(void) { char vstr[TPM_FW_VER_STRLEN]; - int chunk_count = 0; - /* - * let's read 50 bytes at a time; leave room for the trailing - * zero. - */ + int chunk_count = 0, i; + /* Read 50 bytes at a time; leave room for the trailing zero. */ size_t chunk_size = 50;
/* Only cr50 reports device FW version */ if (tpm_info.vendor_id != 0x1ae0) return;
+ /* If all the FW entries are cached, no need to read again */ + for (i = 0; i < TPM_FW_VER_COUNT; i++) { + if (tpm_fw_ver[i].type == TPM2_FW_VER_TYPE_INIT) + break; + } + if (i == TPM_FW_VER_COUNT) + return; + /* * Does not really matter what's written, this just makes sure * the version is reported from the beginning. @@ -451,6 +531,7 @@ (chunk_count + 1) * chunk_size < sizeof(vstr)); vstr[chunk_count * chunk_size] = 0; printk(BIOS_INFO, "Firmware version: %s\n", vstr); + parse_tpm_fw_version(vstr); }
/* Device/vendor ID values of the TPM devices this driver supports. */ @@ -502,7 +583,15 @@ uint32_t did_vid, status; uint8_t cmd;
- memcpy(&spi_slave, spi_if, sizeof(*spi_if)); + /* + * Ensure SPI controller is set up - either already or passed as the + * paramter. + */ + if (!spi_if && !spi_slave.ctrlr) + return -1; + + if (spi_if) + memcpy(&spi_slave, spi_if, sizeof(*spi_if));
/* clear any pending IRQs */ tis_plat_irq_status(); @@ -749,3 +838,34 @@
return payload_size; } + +bool is_tpm2_fw_version(struct tpm2_fw_ver *match) +{ + int i; + + /* + * Setup the SPI controller if it is not already set up. Also initialize + * TPM so that firmware version is read and cached if not done already. + * If SPI controller is already set up, then the firmware version is + * already read and cached. + */ + if (!spi_slave.ctrlr) { + if (spi_setup_slave(CONFIG_DRIVER_TPM_SPI_BUS, + CONFIG_DRIVER_TPM_SPI_CHIP, &spi_slave)) { + printk(BIOS_ERR, "Failed to setup TPM SPI slave\n"); + return false; + } + + if (tpm2_init(NULL)) + return false; + } + + for (i = 0; i < TPM_FW_VER_COUNT; i++) { + if (tpm_fw_ver[i].type == match->type && + tpm_fw_ver[i].epoch == match->epoch && + tpm_fw_ver[i].major_ver == match->major_ver && + tpm_fw_ver[i].minor_ver == match->minor_ver) + return true; + } + return false; +} diff --git a/src/drivers/spi/tpm/tpm.h b/src/drivers/spi/tpm/tpm.h index be98ed0..3baceab 100644 --- a/src/drivers/spi/tpm/tpm.h +++ b/src/drivers/spi/tpm/tpm.h @@ -16,6 +16,20 @@ uint16_t revision; };
+enum tpm2_fw_ver_type { + TPM2_FW_VER_TYPE_INIT, + TPM2_FW_VER_TYPE_RO, + TPM2_FW_VER_TYPE_RW, +}; + +/* Data structure defining the indivdual elements of TPM firmware version. */ +struct tpm2_fw_ver { + enum tpm2_fw_ver_type type; + uint32_t epoch; + uint32_t major_ver; + uint32_t minor_ver; +}; + /* * Initialize a TPM2 device: read its id, claim locality of zero, verify that * this indeed is a TPM2 device. Use the passed in handle to access the right @@ -41,4 +55,12 @@ /* Get information about previously initialized TPM device. */ void tpm2_get_info(struct tpm2_info *info);
+/** + * is_tpm2_fw_version() - Check if the TPM is running a firmware version. + * @match : Firmware version to be matched against. + * + * @return: true if TPM is running the concerned firmware version, else false. + */ +bool is_tpm2_fw_version(struct tpm2_fw_ver *match); + #endif /* ! __COREBOOT_SRC_DRIVERS_SPI_TPM_TPM_H */