From 36e3d877cd6caf51155a74936f15b461cc9b814c Mon Sep 17 00:00:00 2001 From: "Abhi.Singh" Date: Wed, 28 Aug 2024 14:17:52 -0500 Subject: [PATCH] feat(tpm): add tpm drivers and framework Add tpm2 drivers to tf-a with adequate framework -implement a fifo spi interface that works with discrete tpm chip. -implement tpm command layer interfaces that are used to initialize, start and make measurements and close the interface. -tpm drivers are built using their own make file to allow for ease in porting across platforms, and across different interfaces. Signed-off-by: Tushar Khandelwal Signed-off-by: Abhi Singh Change-Id: Ie1a189f45c80f26f4dea16c3bd71b1503709e0ea --- Makefile | 2 + changelog.yaml | 3 + drivers/measured_boot/event_log/event_log.mk | 17 +- drivers/tpm/tpm2.mk | 26 ++ drivers/tpm/tpm2_chip.c | 21 ++ drivers/tpm/tpm2_cmds.c | 222 +++++++++++++ drivers/tpm/tpm2_fifo.c | 322 +++++++++++++++++++ drivers/tpm/tpm2_fifo_spi.c | 161 ++++++++++ include/drivers/tpm/tpm2.h | 103 ++++++ include/drivers/tpm/tpm2_chip.h | 24 ++ include/drivers/tpm/tpm2_interface.h | 28 ++ make_helpers/defaults.mk | 3 + 12 files changed, 926 insertions(+), 6 deletions(-) create mode 100644 drivers/tpm/tpm2.mk create mode 100644 drivers/tpm/tpm2_chip.c create mode 100644 drivers/tpm/tpm2_cmds.c create mode 100644 drivers/tpm/tpm2_fifo.c create mode 100644 drivers/tpm/tpm2_fifo_spi.c create mode 100644 include/drivers/tpm/tpm2.h create mode 100644 include/drivers/tpm/tpm2_chip.h create mode 100644 include/drivers/tpm/tpm2_interface.h diff --git a/Makefile b/Makefile index e111be20a..99e91890b 100644 --- a/Makefile +++ b/Makefile @@ -1232,6 +1232,7 @@ $(eval $(call assert_booleans,\ HARDEN_SLS \ HW_ASSISTED_COHERENCY \ MEASURED_BOOT \ + DISCRETE_TPM \ DICE_PROTECTION_ENVIRONMENT \ RMMD_ENABLE_EL3_TOKEN_SIGN \ DRTM_SUPPORT \ @@ -1418,6 +1419,7 @@ $(eval $(call add_defines,\ HW_ASSISTED_COHERENCY \ LOG_LEVEL \ MEASURED_BOOT \ + DISCRETE_TPM \ DICE_PROTECTION_ENVIRONMENT \ DRTM_SUPPORT \ NS_TIMER_SWITCH \ diff --git a/changelog.yaml b/changelog.yaml index 422b9dafe..3f0636941 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -962,6 +962,9 @@ subsections: - drivers/scmi-msg - scmi-msg + - title: TPM + scope: tpm + - title: UFS scope: ufs diff --git a/drivers/measured_boot/event_log/event_log.mk b/drivers/measured_boot/event_log/event_log.mk index 5ea4c554a..9e0d6c4f6 100644 --- a/drivers/measured_boot/event_log/event_log.mk +++ b/drivers/measured_boot/event_log/event_log.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2020-2022, Arm Limited. All rights reserved. +# Copyright (c) 2020-2025, Arm Limited. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -7,15 +7,20 @@ # Default log level to dump the event log (LOG_LEVEL_INFO) EVENT_LOG_LEVEL ?= 40 -# Measured Boot hash algorithm. -# SHA-256 (or stronger) is required for all devices that are TPM 2.0 compliant. -ifdef TPM_HASH_ALG - $(warning "TPM_HASH_ALG is deprecated. Please use MBOOT_EL_HASH_ALG instead.") - MBOOT_EL_HASH_ALG := ${TPM_HASH_ALG} +# When using a TPM, adopt the TPM's hash algorithm for +# measurements through the Event Log mechanism, ensuring +# the TPM uses the same algorithm for measurements and +# extends the PCR accordingly, allowing for comparison +# between PCR value and Event Log measurements required +# for attestation. +ifdef MBOOT_TPM_HASH_ALG + MBOOT_EL_HASH_ALG := ${MBOOT_TPM_HASH_ALG} else MBOOT_EL_HASH_ALG := sha256 endif +# Measured Boot hash algorithm. +# SHA-256 (or stronger) is required for all devices that are TPM 2.0 compliant. ifeq (${MBOOT_EL_HASH_ALG}, sha512) TPM_ALG_ID := TPM_ALG_SHA512 TCG_DIGEST_SIZE := 64U diff --git a/drivers/tpm/tpm2.mk b/drivers/tpm/tpm2.mk new file mode 100644 index 000000000..4418b97a9 --- /dev/null +++ b/drivers/tpm/tpm2.mk @@ -0,0 +1,26 @@ +# +# Copyright (c) 2025, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +TPM2_SRC_DIR := drivers/tpm/ + +TPM2_SOURCES := ${TPM2_SRC_DIR}tpm2_cmds.c \ + ${TPM2_SRC_DIR}tpm2_chip.c + +# TPM Hash algorithm, used during Measured Boot +# currently only accepts SHA-256 +ifeq (${MBOOT_TPM_HASH_ALG}, sha256) + TPM_ALG_ID := TPM_ALG_SHA256 + TCG_DIGEST_SIZE := 32U +else + $(error "The selected MBOOT_TPM_HASH_ALG is invalid.") +endif #MBOOT_TPM_HASH_ALG + +ifeq (${TPM_INTERFACE}, FIFO_SPI) + TPM2_SOURCES += ${TPM2_SRC_DIR}tpm2_fifo.c \ + ${TPM2_SRC_DIR}tpm2_fifo_spi.c +else + $(error "The selected TPM_INTERFACE is invalid.") +endif #TPM_INTERFACE diff --git a/drivers/tpm/tpm2_chip.c b/drivers/tpm/tpm2_chip.c new file mode 100644 index 000000000..537ce92a9 --- /dev/null +++ b/drivers/tpm/tpm2_chip.c @@ -0,0 +1,21 @@ + +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +/* + * TPM timeout values + * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05 + */ +struct tpm_chip_data tpm_chip_data = { + .locality = -1, + .timeout_msec_a = 750, + .timeout_msec_b = 2000, + .timeout_msec_c = 200, + .timeout_msec_d = 30, + .address = 0, +}; diff --git a/drivers/tpm/tpm2_cmds.c b/drivers/tpm/tpm2_cmds.c new file mode 100644 index 000000000..b6422a8db --- /dev/null +++ b/drivers/tpm/tpm2_cmds.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#include +#include +#include +#include + +#define CMD_SIZE_OFFSET 6 + +#define SINGLE_BYTE 1 +#define TWO_BYTES 2 +#define FOUR_BYTES 4 + +static struct interface_ops *interface; + +static int tpm_xfer(struct tpm_chip_data *chip_data, const tpm_cmd *send, tpm_cmd *receive) +{ + int ret; + + ret = interface->send(chip_data, send); + if (ret < 0) { + return ret; + } + + ret = interface->receive(chip_data, receive); + if (ret < 0) { + return ret; + } + + return TPM_SUCCESS; +} + +int tpm_interface_init(struct tpm_chip_data *chip_data, uint8_t locality) +{ + int err; + + interface = tpm_interface_getops(chip_data, locality); + + err = interface->request_access(chip_data, locality); + if (err != 0) { + return err; + } + + return interface->get_info(chip_data, locality); +} + +int tpm_interface_close(struct tpm_chip_data *chip_data, uint8_t locality) +{ + return interface->release_locality(chip_data, locality); +} + +static int tpm_update_buffer(tpm_cmd *buf, uint32_t new_data, size_t new_len) +{ + int i, j, start; + uint32_t command_size; + + union { + uint8_t var8; + uint16_t var16; + uint32_t var32; + uint8_t array[4]; + } tpm_new_data; + + command_size = be32toh(buf->header.cmd_size); + + if (command_size + new_len > MAX_SIZE_CMDBUF) { + ERROR("%s: buf size exceeded, increase MAX_SIZE_CMDBUF\n", + __func__); + return TPM_INVALID_PARAM; + } + /* + * Subtract the cmd header size from the current command size + * so the data buffer is written to starting at index 0. + */ + start = command_size - TPM_HEADER_SIZE; + + /* + * The TPM, according to the TCG spec, processes data in BE byte order, + * in the case where the Host is LE, htobe correctly handles the byte order. + * When updating the buffer, keep in mind to only pass sizeof(new_data) or + * the variable type size for the new_len function parameter. This ensures + * there is only the possiblility of writing 1, 2, or 4 bytes to the buffer, + * and that the correct number of bytes are written to data[i]. + */ + if (new_len == SINGLE_BYTE) { + tpm_new_data.var8 = new_data & 0xFF; + } else if (new_len == TWO_BYTES) { + tpm_new_data.var16 = htobe16(new_data & 0xFFFF); + } else if (new_len == FOUR_BYTES) { + tpm_new_data.var32 = htobe32(new_data); + } else { + ERROR("%s: Invalid data length\n", __func__); + return TPM_INVALID_PARAM; + } + + for (i = start, j = 0; i < start + new_len; i++, j++) { + buf->data[i] = tpm_new_data.array[j]; + } + buf->header.cmd_size = htobe32(command_size + new_len); + + return TPM_SUCCESS; +} + + +int tpm_startup(struct tpm_chip_data *chip_data, uint16_t mode) +{ + tpm_cmd startup_cmd, startup_response; + uint32_t tpm_rc; + int ret; + + memset(&startup_cmd, 0, sizeof(startup_cmd)); + memset(&startup_response, 0, sizeof(startup_response)); + + startup_cmd.header.tag = htobe16(TPM_ST_NO_SESSIONS); + startup_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr)); + startup_cmd.header.cmd_code = htobe32(TPM_CMD_STARTUP); + + ret = tpm_update_buffer(&startup_cmd, mode, sizeof(mode)); + if (ret < 0) { + return ret; + } + + ret = tpm_xfer(chip_data, &startup_cmd, &startup_response); + if (ret < 0) { + return ret; + } + + tpm_rc = be32toh(startup_response.header.cmd_code); + if (tpm_rc != TPM_RESPONSE_SUCCESS) { + ERROR("%s: response code contains error = %X\n", __func__, tpm_rc); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} + +int tpm_pcr_extend(struct tpm_chip_data *chip_data, uint32_t index, + uint16_t algorithm, const uint8_t *digest, + uint32_t digest_len) +{ + tpm_cmd pcr_extend_cmd, pcr_extend_response; + uint32_t tpm_rc; + int ret; + + memset(&pcr_extend_cmd, 0, sizeof(pcr_extend_cmd)); + memset(&pcr_extend_response, 0, sizeof(pcr_extend_response)); + + if (digest == NULL) { + return TPM_INVALID_PARAM; + } + pcr_extend_cmd.header.tag = htobe16(TPM_ST_SESSIONS); + pcr_extend_cmd.header.cmd_size = htobe32(sizeof(tpm_cmd_hdr)); + pcr_extend_cmd.header.cmd_code = htobe32(TPM_CMD_PCR_EXTEND); + + /* handle (PCR Index)*/ + ret = tpm_update_buffer(&pcr_extend_cmd, index, sizeof(index)); + if (ret < 0) { + return ret; + } + + /* authorization size , session handle, nonce size, attributes*/ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_MIN_AUTH_SIZE, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_RS_PW, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_NONCE_SIZE, sizeof(uint16_t)); + if (ret < 0) { + return ret; + } + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ATTRIBUTES_DISABLE, sizeof(uint8_t)); + if (ret < 0) { + return ret; + } + + /* hmac/password size */ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_ZERO_HMAC_SIZE, sizeof(uint16_t)); + if (ret < 0) { + return ret; + } + + /* hashes count */ + ret = tpm_update_buffer(&pcr_extend_cmd, TPM_SINGLE_HASH_COUNT, sizeof(uint32_t)); + if (ret < 0) { + return ret; + } + + /* hash algorithm */ + ret = tpm_update_buffer(&pcr_extend_cmd, algorithm, sizeof(algorithm)); + if (ret < 0) { + return ret; + } + + /* digest */ + for (int i = 0; i < digest_len; i++) { + ret = tpm_update_buffer(&pcr_extend_cmd, digest[i], sizeof(uint8_t)); + if (ret < 0) { + return ret; + } + } + + ret = tpm_xfer(chip_data, &pcr_extend_cmd, &pcr_extend_response); + if (ret < 0) { + return ret; + } + + tpm_rc = be32toh(pcr_extend_response.header.cmd_code); + if (tpm_rc != TPM_RESPONSE_SUCCESS) { + ERROR("%s: response code contains error = %X\n", __func__, tpm_rc); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} diff --git a/drivers/tpm/tpm2_fifo.c b/drivers/tpm/tpm2_fifo.c new file mode 100644 index 000000000..7c4b9d8b4 --- /dev/null +++ b/drivers/tpm/tpm2_fifo.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include + +#include +#include +#include +#include + +#define LOCALITY_START_ADDRESS(x, y) \ + ((uint16_t)(x->address + (0x1000 * y))) + +static int tpm2_get_info(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint32_t vid_did; + uint8_t revision; + int err; + + err = tpm2_fifo_read_chunk(tpm_base_addr + TPM_FIFO_REG_VENDID, DWORD, &vid_did); + if (err < 0) { + return err; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_REVID, &revision); + if (err < 0) { + return err; + } + + INFO("TPM Chip: vendor-id 0x%x, device-id 0x%x, revision-id: 0x%x\n", + 0xFFFF & vid_did, vid_did >> 16, revision); + + return TPM_SUCCESS; +} + +static int tpm2_wait_reg_bits(uint16_t reg, uint8_t set, unsigned long timeout, uint8_t *status) +{ + int err; + uint64_t timeout_delay = timeout_init_us(timeout * 1000); + + do { + err = tpm2_fifo_read_byte(reg, status); + if (err < 0) { + return err; + } + if ((*status & set) == set) { + return TPM_SUCCESS; + } + } while (!timeout_elapsed(timeout_delay)); + + return TPM_ERR_TIMEOUT; +} + +static int tpm2_fifo_request_access(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint8_t status; + int err; + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, TPM_ACCESS_REQUEST_USE); + if (err < 0) { + return err; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_ACCESS, + TPM_ACCESS_ACTIVE_LOCALITY, + chip_data->timeout_msec_a, &status); + if (err == 0) { + chip_data->locality = locality; + return TPM_SUCCESS; + } + + return err; +} + +static int tpm2_fifo_release_locality(struct tpm_chip_data *chip_data, uint8_t locality) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, locality); + uint8_t buf; + int err; + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, &buf); + if (err < 0) { + return err; + } + + if (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { + return tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_ACCESS, + TPM_ACCESS_RELINQUISH_LOCALITY); + } + + ERROR("%s: Unable to release locality\n", __func__); + return TPM_ERR_RESPONSE; +} + +static int tpm2_fifo_prepare(struct tpm_chip_data *chip_data) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint8_t status; + int err; + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_COMMAND_READY); + if (err < 0) { + return err; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_COMMAND_READY, + chip_data->timeout_msec_b, &status); + if (err < 0) { + ERROR("%s: TPM Status Busy\n", __func__); + return err; + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_get_burstcount(struct tpm_chip_data *chip_data, uint16_t *burstcount) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint64_t timeout_delay = timeout_init_us(chip_data->timeout_msec_a * 1000); + int err; + + if (burstcount == NULL) { + return TPM_INVALID_PARAM; + } + + do { + uint8_t byte0, byte1; + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_LO, &byte0); + if (err < 0) { + return err; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_BURST_COUNT_HI, &byte1); + if (err < 0) { + return err; + } + + *burstcount = (uint16_t)((byte1 << 8) + byte0); + if (*burstcount != 0U) { + return TPM_SUCCESS; + } + } while (!timeout_elapsed(timeout_delay)); + + return TPM_ERR_TIMEOUT; +} + +static int tpm2_fifo_send(struct tpm_chip_data *chip_data, const tpm_cmd *buf) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + uint8_t status; + uint16_t burstcnt; + int err; + uint32_t len, index; + + if (sizeof(buf->header) != TPM_HEADER_SIZE) { + ERROR("%s: invalid command header size.\n", __func__); + return TPM_INVALID_PARAM; + } + + err = tpm2_fifo_read_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, &status); + if (err < 0) { + return err; + } + + if (!(status & TPM_STAT_COMMAND_READY)) { + err = tpm2_fifo_prepare(chip_data); + if (err < 0) { + return err; + } + } + + /* write the command header to the TPM first */ + const uint8_t *header_data = (const uint8_t *)&buf->header; + + for (index = 0; index < TPM_HEADER_SIZE; index++) { + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + header_data[index]); + if (err < 0) { + return err; + } + } + + len = be32toh(buf->header.cmd_size); + + while (index < len) { + err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); + if (err < 0) { + return err; + } + + for (; burstcnt > 0U && index < len; burstcnt--) { + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + buf->data[index - TPM_HEADER_SIZE]); + if (err < 0) { + return err; + } + index++; + } + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_VALID, + chip_data->timeout_msec_c, + &status); + if (err < 0) { + return err; + } + + if (status & TPM_STAT_EXPECT) { + ERROR("%s: TPM is still expecting data after command buffer is sent\n", __func__); + return TPM_ERR_TRANSFER; + } + + err = tpm2_fifo_write_byte(tpm_base_addr + TPM_FIFO_REG_STATUS, TPM_STAT_GO); + if (err < 0) { + return err; + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_read_data(struct tpm_chip_data *chip_data, tpm_cmd *buf, + uint16_t tpm_base_addr, uint8_t *status, int *size, int bytes_expected) +{ + int err, read_size, loop_index; + uint16_t burstcnt; + uint8_t *read_data; + + if (bytes_expected == TPM_READ_HEADER) { + /* read the response header from the TPM first */ + read_data = (uint8_t *)&buf->header; + read_size = TPM_HEADER_SIZE; + loop_index = *size; + } else { + /* process the rest of the mssg with bytes_expected */ + read_data = buf->data; + read_size = bytes_expected; + loop_index = *size - TPM_HEADER_SIZE; + } + + err = tpm2_wait_reg_bits(tpm_base_addr + TPM_FIFO_REG_STATUS, + TPM_STAT_AVAIL, + chip_data->timeout_msec_c, + status); + if (err < 0) { + return err; + } + + while (*size < read_size) { + err = tpm2_fifo_get_burstcount(chip_data, &burstcnt); + if (err < 0) { + ERROR("%s: TPM burst count error\n", __func__); + return err; + } + + for (; burstcnt > 0U && loop_index < read_size; + burstcnt--, loop_index++, (*size)++) { + err = tpm2_fifo_read_byte( + tpm_base_addr + TPM_FIFO_REG_DATA_FIFO, + (void *)&read_data[loop_index]); + if (err < 0) { + return err; + } + } + } + + return TPM_SUCCESS; +} + +static int tpm2_fifo_receive(struct tpm_chip_data *chip_data, tpm_cmd *buf) +{ + uint16_t tpm_base_addr = LOCALITY_START_ADDRESS(chip_data, chip_data->locality); + int size = 0, bytes_expected, err; + uint8_t status; + + err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, TPM_READ_HEADER); + if (err < 0) { + return err; + } + + bytes_expected = be32toh(buf->header.cmd_size); + if (bytes_expected > sizeof(*buf)) { + ERROR("%s: tpm response buffer cannot store expected response\n", __func__); + return TPM_INVALID_PARAM; + } + + if (size == bytes_expected) { + return size; + } + + err = tpm2_fifo_read_data(chip_data, buf, tpm_base_addr, &status, &size, bytes_expected); + if (err < 0) { + return err; + } + + if (size < bytes_expected) { + ERROR("%s: response buffer size is less than expected\n", __func__); + return TPM_ERR_RESPONSE; + } + + return TPM_SUCCESS; +} + +static interface_ops_t fifo_ops = { + .get_info = tpm2_get_info, + .send = tpm2_fifo_send, + .receive = tpm2_fifo_receive, + .request_access = tpm2_fifo_request_access, + .release_locality = tpm2_fifo_release_locality, +}; + +struct interface_ops * +tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality) +{ + return &fifo_ops; +} diff --git a/drivers/tpm/tpm2_fifo_spi.c b/drivers/tpm/tpm2_fifo_spi.c new file mode 100644 index 000000000..16d87d918 --- /dev/null +++ b/drivers/tpm/tpm2_fifo_spi.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define ENCODE_LIMIT 128 +#define CS_ASSERT_OFFSET 0xD4 +#define RETRY_COUNT 50 + +#define TPM_READ false +#define TPM_WRITE true + +extern struct spi_plat *spidev; + +static int tpm2_spi_transfer(const void *data_out, void *data_in, uint8_t len) +{ + return spidev->ops->xfer(len, data_out, data_in); +} + +/* + * Reference: TCG PC Client Platform TPM Profile (PTP) Specification v1.05 + */ +static int tpm2_spi_start_transaction(uint16_t tpm_reg, bool write, uint8_t len) +{ + int rc; + uint8_t header[4]; + uint8_t header_response[4]; + uint8_t zero = 0, byte; + int retries; + + /* check to make sure len does not exceed the encoding limit */ + if (len > ENCODE_LIMIT) { + return TPM_INVALID_PARAM; + } + + /* + * 7.4.6 TPM SPI Bit protocol calls for the following header + * to be sent to the TPM at the start of every attempted read/write. + */ + + /* header[0] contains the r/w and the xfer size, if the msb is not + * set, the operation is write, if it is set then it is read. + * The size of the transfer is encoded, and must not overwrite + * the msb, therefore an ENCODE LIMIT of 128 is present. + */ + header[0] = ((write) ? 0x00 : 0x80) | (len - 1); + + /* + * header[1] contains the address offset 0xD4_xxxx as defined + * in the TPM spec, since the CS# is asserted. + */ + header[1] = CS_ASSERT_OFFSET; + + /* + * header[2] and header[3] contain the address of the register + * to be read/written. + */ + header[2] = tpm_reg >> 8; + header[3] = tpm_reg; + + rc = tpm2_spi_transfer(header, header_response, 4); + if (rc != 0) { + return TPM_ERR_TRANSFER; + } + + /* + * 7.4.5 Flow Control defines a wait state in order to accommodate + * the TPM in case it needs to free its buffer. + */ + if ((header_response[3] & 0x01) != 0U) { + return TPM_SUCCESS; + } + + /* + * if the wait state over bit is not set in the initial header_response, + * poll for the wait state over by sending a zeroed byte, if the + * RETRY_COUNT is exceeded the transfer fails. + */ + for (retries = RETRY_COUNT; retries > 0; retries--) { + rc = tpm2_spi_transfer(&zero, &byte, 1); + if (rc != 0) { + return TPM_ERR_TRANSFER; + } + if ((byte & 0x01) != 0U) { + return TPM_SUCCESS; + } + } + + if (retries == 0) { + ERROR("%s: TPM Timeout\n", __func__); + return TPM_ERR_TIMEOUT; + } + + return TPM_SUCCESS; +} + +static void tpm2_spi_end_transaction(void) +{ + spidev->ops->stop(); +} + +static void tpm2_spi_init(void) +{ + spidev->ops->get_access(); + spidev->ops->start(); +} + +static int tpm2_fifo_io(uint16_t tpm_reg, bool is_write, uint8_t len, void *val) +{ + int rc; + + tpm2_spi_init(); + rc = tpm2_spi_start_transaction(tpm_reg, is_write, len); + if (rc != 0) { + tpm2_spi_end_transaction(); + return rc; + } + + rc = tpm2_spi_transfer( + is_write ? val : NULL, + is_write ? NULL : val, + len); + if (rc != 0) { + tpm2_spi_end_transaction(); + return rc; + } + + tpm2_spi_end_transaction(); + + return TPM_SUCCESS; +} + +int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val) +{ + return tpm2_fifo_io(tpm_reg, TPM_WRITE, BYTE, &val); +} + +int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val) +{ + return tpm2_fifo_io(tpm_reg, TPM_READ, BYTE, val); +} + +int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val) +{ + if ((len != BYTE) && (len != WORD) && (len != DWORD)) { + return TPM_INVALID_PARAM; + } + + return tpm2_fifo_io(tpm_reg, TPM_READ, len, val); +} diff --git a/include/drivers/tpm/tpm2.h b/include/drivers/tpm/tpm2.h new file mode 100644 index 000000000..c91acf8e9 --- /dev/null +++ b/include/drivers/tpm/tpm2.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TPM2_H +#define TPM2_H + +#include +#include +#include +#include + +#include + +/* Return values */ +enum tpm_ret_value { + TPM_SUCCESS = 0, + TPM_ERR_RESPONSE = -1, + TPM_INVALID_PARAM = -2, + TPM_ERR_TIMEOUT = -3, + TPM_ERR_TRANSFER = -4, +}; + +/* + * TPM FIFO register space address offsets + */ +#define TPM_FIFO_REG_ACCESS 0x00 +#define TPM_FIFO_REG_INTR_ENABLE 0x08 +#define TPM_FIFO_REG_INTR_VECTOR 0x0C +#define TPM_FIFO_REG_INTR_STS 0x10 +#define TPM_FIFO_REG_INTF_CAPS 0x14 +#define TPM_FIFO_REG_STATUS 0x18 +#define TPM_FIFO_REG_BURST_COUNT_LO 0x19 +#define TPM_FIFO_REG_BURST_COUNT_HI 0x20 +#define TPM_FIFO_REG_DATA_FIFO 0x24 +#define TPM_FIFO_REG_VENDID 0xF00 +#define TPM_FIFO_REG_DEVID 0xF02 +#define TPM_FIFO_REG_REVID 0xF04 + +#define TPM_ST_NO_SESSIONS U(0x8001) +#define TPM_ST_SESSIONS U(0x8002) + +#define TPM_SU_CLEAR U(0x0000) +#define TPM_SU_STATE U(0x0001) + +#define TPM_MIN_AUTH_SIZE 9 +#define TPM_RS_PW 0x40000009 +#define TPM_ZERO_NONCE_SIZE 0 +#define TPM_ATTRIBUTES_DISABLE 0 +#define TPM_ZERO_HMAC_SIZE 0 +#define TPM_SINGLE_HASH_COUNT 1 + + +#define TPM_CMD_STARTUP U(0x0144) +#define TPM_CMD_PCR_READ U(0x017E) +#define TPM_CMD_PCR_EXTEND U(0x0182) + +#define TPM_RESPONSE_SUCCESS U(0x0000) + +#define TPM_ACCESS_ACTIVE_LOCALITY U(1 << 5) +#define TPM_ACCESS_VALID U(1 << 7) +#define TPM_ACCESS_RELINQUISH_LOCALITY U(1 << 5) +#define TPM_ACCESS_REQUEST_USE U(1 << 1) +#define TPM_ACCESS_REQUEST_PENDING U(1 << 2) + +#define TPM_STAT_VALID U(1 << 7) +#define TPM_STAT_COMMAND_READY U(1 << 6) +#define TPM_STAT_GO U(1 << 5) +#define TPM_STAT_AVAIL U(1 << 4) +#define TPM_STAT_EXPECT U(1 << 3) + +#define TPM_READ_HEADER -1 + +#define TPM_HEADER_SIZE 10 +#define MAX_SIZE_CMDBUF 256 +#define MAX_CMD_DATA (MAX_SIZE_CMDBUF - TPM_HEADER_SIZE) + +#pragma pack(1) +typedef struct tpm_cmd_hdr { + uint16_t tag; + uint32_t cmd_size; + uint32_t cmd_code; +} tpm_cmd_hdr; + +typedef struct tpm_cmd { + tpm_cmd_hdr header; + uint8_t data[MAX_CMD_DATA]; +} tpm_cmd; +#pragma pack() + +int tpm_interface_init(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm_interface_close(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm_startup(struct tpm_chip_data *chip_data, uint16_t mode); + +int tpm_pcr_extend(struct tpm_chip_data *chip_data, uint32_t index, + uint16_t algorithm, const uint8_t *digest, + uint32_t digest_len); + +#endif /* TPM2_H */ diff --git a/include/drivers/tpm/tpm2_chip.h b/include/drivers/tpm/tpm2_chip.h new file mode 100644 index 000000000..ce052ad98 --- /dev/null +++ b/include/drivers/tpm/tpm2_chip.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#ifndef TPM2_CHIP_H +#define TPM2_CHIP_H + +#define BYTE U(0x1) +#define WORD U(0x2) +#define DWORD U(0x4) + +struct tpm_chip_data { + uint8_t locality; + unsigned long timeout_msec_a, timeout_msec_b; + unsigned long timeout_msec_c, timeout_msec_d; + uint16_t address; +}; + +#endif /* TPM2_CHIP_H */ diff --git a/include/drivers/tpm/tpm2_interface.h b/include/drivers/tpm/tpm2_interface.h new file mode 100644 index 000000000..6bfbf6ced --- /dev/null +++ b/include/drivers/tpm/tpm2_interface.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TPM2_INTERFACE_H +#define TPM2_INTERFACE_H + +#include "tpm2_chip.h" + +typedef struct interface_ops { + int (*get_info)(struct tpm_chip_data *chip_data, uint8_t locality); + int (*send)(struct tpm_chip_data *chip_data, const tpm_cmd *buf); + int (*receive)(struct tpm_chip_data *chip_data, tpm_cmd *buf); + int (*request_access)(struct tpm_chip_data *chip_data, uint8_t locality); + int (*release_locality)(struct tpm_chip_data *chip_data, uint8_t locality); +} interface_ops_t; + +struct interface_ops *tpm_interface_getops(struct tpm_chip_data *chip_data, uint8_t locality); + +int tpm2_fifo_write_byte(uint16_t tpm_reg, uint8_t val); + +int tpm2_fifo_read_byte(uint16_t tpm_reg, uint8_t *val); + +int tpm2_fifo_read_chunk(uint16_t tpm_reg, uint8_t len, void *val); + +#endif /* TPM2_INTERFACE_H */ diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index ec2aa1bfb..906e5d706 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -192,6 +192,9 @@ endif # Option to build TF with Measured Boot support MEASURED_BOOT := 0 +# Option to build TF with Discrete TPM support +DISCRETE_TPM := 0 + # Option to enable the DICE Protection Environmnet as a Measured Boot backend DICE_PROTECTION_ENVIRONMENT :=0