blob: a0c8c4bf5afc0c449049ea1348cd984cea676983 [file] [log] [blame]
// SPDX-License-Identifier: BSD-3-Clause
/*
* Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
*/
#include <stdint.h>
#include <string.h>
#include "rpc/tpm_crb_ffa/common/tpm_crb_ffa.h"
#include "tpm_crb_provider.h"
#include "trace.h"
#include "util.h"
#define CRB_DATA_BUF_SIZE (size_t)0x800
/* CRB register definitions based on TCG PC Client Platform TPM Profile Specification for TPM 2.0 */
/* TPM_LOC_STATE_x: Locality State Register */
#define LOC_STATE_TPM_ESTABLISHED BIT32(0)
#define LOC_STATE_LOC_ASSIGNED BIT32(1)
#define LOC_STATE_ACTIVE_LOCALITY_SHIFT UINT32_C(2)
#define LOC_STATE_ACTIVE_LOCALITY_MASK GENMASK_32(2, 0)
#define LOC_STATE_TPM_REG_VALID_STATUS BIT32(7)
/* TPM_LOC_CTRL_x: Locality Control Register for Localities 0 - 3 */
#define LOC_CTRL_REQUEST_ACCESS BIT32(0)
#define LOC_CTRL_RELINQUISH BIT32(1)
#define LOC_CTRL_SEIZE BIT32(2)
#define LOC_CTRL_RESET_ESTABLISHMENT_BIT BIT32(3)
/* TPM_LOC_CTRL_4: Locality Control Register for Locality 4 */
#define LOC_CTRL_4_HASH_START BIT32(0)
#define LOC_CTRL_4_HASH_DATA BIT32(1)
#define LOC_CTRL_4_HASH_END BIT32(2)
#define LOC_CTRL_4_RESET_ESTABLISHMENT_BIT BIT32(3)
/* TPM_LOC_STS_x: Locality Status Register */
#define LOC_STS_GRANTED BIT32(0)
#define LOC_STS_BEEN_SEIZED BIT32(1)
/* TPM_CRB_CTRL_REQ_x: Control Area Request Register */
#define CRB_CTRL_REQ_CMD_READY BIT32(0)
#define CRB_CTRL_REQ_GO_IDLE BIT32(1)
/* TPM_CRB_CTRL_STS_x: Control Area Status Register */
#define CRB_CTRL_STS_TPM_STATUS BIT32(0)
#define CRB_CTRL_STS_TPM_IDLE BIT32(1)
/* TPM_CRB_CTRL_CANCEL_x: Control Area Status Register */
#define CRB_CTRL_CANCEL_COMMAND BIT32(0)
/* TPM_CRB_CTRL_START_x: Control Area Status Register */
#define CRB_CTRL_START_COMMAND BIT32(0)
struct loc_and_crb_ctrl {
// Offset 0x00 - 0x03: Used to determine current state of locality of the TPM. This register
// is aliased across all localities. Read-only.
uint32_t loc_state;
// Offset 0x04 - 0x07: Reserved
uint8_t _res4[4];
// Offset 0x08 - 0x0b: Used to gain control of the TPM by this locality.
uint32_t loc_ctrl;
// Offset 0x0c - 0x0f: Used to determine whether locality has been granted or Seized.
// Read-only. This register SHALL NOT be aliased.
uint32_t loc_status;
// Offset 0x10 - 0x2f: Reserved
uint8_t _res10[32];
// Offset 0x30 - 0x37: Used to identify the Interface types supported by the TPM as well as
// the Vendor ID, Device ID and Revision ID.
uint64_t interface_id;
// Offset 0x38 - 0x3f: Optional Register used in low memory environments prior to
// CRB_DATA_BUFFER availability. This field is not implemented in hardware TPMs. This field
// is only available in Locality 0.
uint64_t ctrl_ext;
// Offset 0x40 - 0x43: Register used to initiate transactions for the CRB interface. This
// register may be aliased across localities.
uint32_t ctrl_request;
// Offset 0x44 - 0x47: Register used by the TPM to provide status of the CRB interface. This
// register may be aliased across localities.
uint32_t ctrl_status;
// Offset 0x48 - 0x4b: Register used by Software to cancel command processing. This register
// may be aliased across localities.
uint32_t ctrl_cancel;
// Offset 0x4c - 0x4f: Register used to indicate presence of command or response data in the
// CRB buffer. This register may be aliased across localities.
uint32_t ctrl_start;
// Offset 0x50 - 0x53: Register used to configure interrupts. This register may be aliased
// across localities.
uint32_t ctrl_int_enable;
// Offset 0x54 - 0x57: Register used to respond to interrupts. This register may be aliased
// across localities.
uint32_t ctrl_int_status;
// Offset 0x58 - 0x5b: Size of the Command buffer. This register may be aliased across
// localities.
uint32_t ctrl_cmd_size;
// Offset 0x5c - 0x5f: Lower 32bits of the Command buffer start address for the locality.
// This register may be aliased across localities.
uint32_t ctrl_cmd_addr_lo;
// Offset 0x60 - 0x63: Upper 32bits of the Command buffer start address for the locality.
// This register may be aliased across localities.
uint32_t ctrl_cmd_addr_hi;
// Offset 0x64 - 0x67: Size of the Response buffer. Note: If command and response buffers
// are implemented as a single buffer, this field SHALL be identical to the value in the
// TPM_CRB_CTRL_CMD_SIZE_x buffer. This register may be aliased across localities.
uint32_t ctrl_resp_size;
// Offset 0x68 - 0x6f: Address of the start of the Response buffer. Note: If command and
// response buffers are implemented as a single buffer, this field SHALL contain the same
// address contained in TPM_CRB_CTRL_CMD_HADDR_x and TPM_CRB_CMD_LADDR_x. This register may
// be aliased across localities.
uint64_t ctrl_resp_addr;
// Offset 0x70 - 0x7f: Reserved
uint8_t _res70[16];
// Offset 0x80 - 0x880: Command/Response Data may be defined as large as 3968. This is
// implementation-specific. However, the full address space has been reserved. This buffer
// may be aliased across localities. This field accepts data transfers from 1B up to the
// size indicated by TPM_CRB_INTF_ID_x.CapDataXferSizeSupport (see section 6.4.2.2 CRB
// Interface Identifier Register).
uint8_t data_buffer[CRB_DATA_BUF_SIZE];
} __packed __aligned(__alignof(uint32_t));
void set_loc_state_all(struct loc_and_crb_ctrl *loc_ptr[5], uint32_t loc_state)
{
/* The TPM_LOC_STATE_x register is aliased across all localities, set in one step */
for (unsigned i = 0; i < 5; i++)
loc_ptr[i]->loc_state = loc_state;
}
/* Service request handlers */
static rpc_status_t command_handler(void *context, struct rpc_request *req);
static rpc_status_t locality_req_handler(void *context, struct rpc_request *req);
/* Handler mapping table for service */
static const struct service_handler handler_table[] = {
{ TPM_START_QUALIFIER_COMMAND, command_handler },
{ TPM_START_QUALIFIER_LOCALITY_REQ, locality_req_handler },
};
static inline uint32_t tpm_get_request_length(uint8_t *buf)
{
if (!buf)
return 0;
return (uint32_t)((buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8 ) + buf[5]);
}
static rpc_status_t command_handler(void *context, struct rpc_request *req)
{
struct tpm_crb_provider *this_instance = (struct tpm_crb_provider *)context;
struct loc_and_crb_ctrl *req_loc = NULL;
uint8_t locality = req->client_id;
uint8_t *resp_data = NULL;
uint8_t *req_data = NULL;
size_t resp_max_size = 0;
size_t resp_len = 0;
size_t req_len = 0;
size_t crb_size = 4096; /* TODO: this config should come from the build system */
DMSG("Processing TPM service command at locality %d", locality);
/* The locality which made the request */
req_loc = this_instance->loc_ptr[locality];
if (!(req_loc->ctrl_start & CRB_CTRL_START_COMMAND)) {
req->service_status = TPM_ERROR_INV_CRB_CTRL_DATA;
return RPC_ERROR_INTERNAL;
}
req_len = tpm_get_request_length(req_loc->data_buffer);
if (req_len == 0 || crb_size < req_len) {
req->service_status = TPM_ERROR_INV_CRB_CTRL_DATA;
return RPC_ERROR_INTERNAL;
}
req_data = req_loc->data_buffer;
resp_data = req_loc->data_buffer;
resp_max_size = crb_size;
ms_tpm_backend_execute_command(req_data, req_len, &resp_data, &resp_len, resp_max_size);
/* All operations done, clear the pending request */
req_loc->ctrl_start &= ~CRB_CTRL_START_COMMAND;
req->service_status = TPM_STATUS_OK;
return RPC_SUCCESS;
}
static rpc_status_t locality_req_handler(void *context, struct rpc_request *req)
{
struct tpm_crb_provider *this_instance = (struct tpm_crb_provider *)context;
struct loc_and_crb_ctrl *req_loc = NULL;
uint8_t locality = req->client_id;
if (locality == 4) {
EMSG("Locality 4 handling is currently not supported");
req->service_status = TPM_ERROR_NOTSUP;
return RPC_ERROR_INTERNAL;
}
DMSG("Processing TPM service locality request at locality %d", locality);
/* The locality which made the request */
req_loc = this_instance->loc_ptr[locality];
if (req_loc->loc_ctrl & LOC_CTRL_REQUEST_ACCESS) {
uint32_t loc_state = req_loc->loc_state;
DMSG("Locality access request");
/* TODO: execute locality arbitration algorithm */
/* Clear the valid status bit before manipulating register contents */
loc_state &= ~LOC_STATE_TPM_REG_VALID_STATUS;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Clear current active locality and set the new */
loc_state &= ~SHIFT_U32(LOC_STATE_ACTIVE_LOCALITY_MASK,
LOC_STATE_ACTIVE_LOCALITY_SHIFT);
loc_state |= SHIFT_U32(locality & LOC_STATE_ACTIVE_LOCALITY_MASK,
LOC_STATE_ACTIVE_LOCALITY_SHIFT);
loc_state |= LOC_STATE_LOC_ASSIGNED;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Set the valid status bit */
loc_state |= LOC_STATE_TPM_REG_VALID_STATUS;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Set the locality status to granted */
req_loc->loc_status |= LOC_STS_GRANTED;
/* All operations done, clear the pending request */
req_loc->loc_ctrl &= ~LOC_CTRL_REQUEST_ACCESS;
}
if (req_loc->loc_ctrl & LOC_CTRL_RELINQUISH) {
uint32_t loc_state = req_loc->loc_state;
DMSG("Locality relinquish");
/* Clear the valid status bit before manipulating register contents */
loc_state &= ~LOC_STATE_TPM_REG_VALID_STATUS;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Clear current active locality and the locality assigned bit */
loc_state &= ~SHIFT_U32(LOC_STATE_ACTIVE_LOCALITY_MASK,
LOC_STATE_ACTIVE_LOCALITY_SHIFT);
loc_state &= ~LOC_STATE_LOC_ASSIGNED;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Set the valid status bit */
loc_state |= LOC_STATE_TPM_REG_VALID_STATUS;
set_loc_state_all(this_instance->loc_ptr, loc_state);
/* Clear the locality granted bit */
req_loc->loc_status &= ~LOC_STS_GRANTED;
/* All operations done, clear the pending request */
req_loc->loc_ctrl &= ~LOC_CTRL_RELINQUISH;
}
req->service_status = TPM_STATUS_OK;
return RPC_SUCCESS;
}
struct rpc_service_interface *tpm_provider_init(struct tpm_crb_provider *context,
uint8_t *ns_crb, size_t ns_crb_size,
uint8_t* s_crb, size_t s_crb_size)
{
const struct rpc_uuid tpm_crb_service_uuid = {
.uuid = TPM_CRB_FFA_UUID
};
if (!context || !ns_crb || ns_crb_size == 0 || !s_crb || s_crb_size == 0)
return NULL;
/* All of the locality and CRB control registers should be cleared an power on */
memset(ns_crb, 0, ns_crb_size);
memset(s_crb, 0, s_crb_size);
/* Note: these are the CRB VAs that we got from the SP boot info */
context->loc_ptr[0] = (struct loc_and_crb_ctrl *)ns_crb;
context->loc_ptr[1] = (struct loc_and_crb_ctrl *)(ns_crb + 0x1000);
context->loc_ptr[2] = (struct loc_and_crb_ctrl *)(ns_crb + 0x2000);
context->loc_ptr[3] = (struct loc_and_crb_ctrl *)(ns_crb + 0x3000);
context->loc_ptr[4] = (struct loc_and_crb_ctrl *)s_crb;
for (unsigned i = 0; i <= 4; i++) {
uint64_t data_buf_addr = 0;
/* Note: here we have to use the CRB PAs that's fixed at compile time */
if (i < 4) {
data_buf_addr = TPM_CRB_NS_PA + i * 0x1000 +
offsetof(struct loc_and_crb_ctrl, data_buffer);
} else {
data_buf_addr = TPM_CRB_S_PA +
offsetof(struct loc_and_crb_ctrl, data_buffer);
}
context->loc_ptr[i]->ctrl_cmd_addr_lo = data_buf_addr & UINT32_MAX;
context->loc_ptr[i]->ctrl_cmd_addr_hi = data_buf_addr >> 32;
context->loc_ptr[i]->ctrl_cmd_size = CRB_DATA_BUF_SIZE;
context->loc_ptr[i]->ctrl_resp_addr = data_buf_addr;
context->loc_ptr[i]->ctrl_resp_size = CRB_DATA_BUF_SIZE;
}
/* Set the valid bit in Locality State Register for all localities */
set_loc_state_all(context->loc_ptr, LOC_STATE_TPM_REG_VALID_STATUS);
service_provider_init(&context->base_provider, context, &tpm_crb_service_uuid,
handler_table, ARRAY_SIZE(handler_table));
return service_provider_get_rpc_interface(&context->base_provider);
}