mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-11 07:04:22 +00:00

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 <tushar.khandelwal@arm.com> Signed-off-by: Abhi Singh <abhi.singh@arm.com> Change-Id: Ie1a189f45c80f26f4dea16c3bd71b1503709e0ea
322 lines
7.6 KiB
C
322 lines
7.6 KiB
C
/*
|
|
* Copyright (c) 2025, Arm Limited. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#include <lib/libc/endian.h>
|
|
|
|
#include <drivers/delay_timer.h>
|
|
#include <drivers/tpm/tpm2.h>
|
|
#include <drivers/tpm/tpm2_chip.h>
|
|
#include <drivers/tpm/tpm2_interface.h>
|
|
|
|
#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;
|
|
}
|