Add TPM CRB common components
Add a new RPC component which implements the "TPM Service Command
Response Buffer Interface Over FF-A (DEN0138)" [1] specification, and a
TPM provider/backend component that uses the ms-tpm-20-ref backend. The
implemented functionality is limited, the CRB and locality control
registers are only partially handled. Locality 4 is not supported yet.
[1] https://developer.arm.com/documentation/den0138/
Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: Iaab186c6a8f45ab4d19b2110055acf34c64d14a1
diff --git a/components/rpc/tpm_crb_ffa/common/component.cmake b/components/rpc/tpm_crb_ffa/common/component.cmake
new file mode 100644
index 0000000..03d21dd
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/common/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if(NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/tpm_crb_ffa.c"
+)
diff --git a/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.c b/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.c
new file mode 100644
index 0000000..2c97cee
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include "tpm_crb_ffa.h"
+
+uint64_t tpm_crb_ffa_get_function_id(const uint64_t regs[5])
+{
+ return regs[1];
+}
+
+uint8_t tpm_crb_ffa_get_start_qualifier(const uint64_t regs[5])
+{
+ return regs[2];
+}
+
+uint8_t tpm_crb_ffa_get_start_loc_qualifier(const uint64_t regs[5])
+{
+ return regs[3];
+}
+
+void tpm_crb_ffa_set_status(uint64_t regs[5], uint64_t status)
+{
+ regs[1] = status;
+}
+
+void tpm_crb_ffa_set_interface_version(uint64_t regs[5], uint64_t version)
+{
+ regs[2] = version;
+}
diff --git a/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.h b/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.h
new file mode 100644
index 0000000..147acfe
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2025, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef TPM_CRB_FFA_H
+#define TPM_CRB_FFA_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* TPM CRB over FF-A definitions based on DEN0138 v1.0 BET */
+
+#define TPM_CRB_FFA_UUID \
+{ 0x17, 0xb8, 0x62, 0xa4, 0x18, 0x06, 0x4f, 0xaf, 0x86, 0xb3, 0x08, 0x9a, 0x58, 0x35, 0x38, 0x61 }
+
+/* The interface in this version of the specification is version (1, 0) */
+#define TPM_INTERFACE_VERSION 0x00010000u
+
+/* TPM service function IDs */
+#define TPM_FUNC_GET_INTERFACE_VERSION 0x0f000001u
+#define TPM_FUNC_GET_FEATURE_INFO 0x0f000101u
+#define TPM_FUNC_START 0x0f000201u
+#define TPM_FUNC_REGISTER_FOR_NOTIFICATION 0x0f000301u
+#define TPM_FUNC_UNREGISTER_FROM_NOTIFICATION 0x0f000401u
+#define TPM_FUNC_FINISH_NOTIFIED 0x0f000501u
+
+/* TPM service function status codes */
+/* Function succeeded */
+#define TPM_STATUS_OK 0x05000001u
+/* Function succeeded and results have been returned */
+#define TPM_STATUS_OK_RESULTS_RETURNED 0x05000002u
+/* No such function */
+#define TPM_ERROR_NOFUNC 0x8e000001u
+/* Function not supported */
+#define TPM_ERROR_NOTSUP 0x8e000002u
+/* Invalid argument */
+#define TPM_ERROR_INVARG 0x8e000005u
+/* Invalid Command-Response Buffer control data */
+#define TPM_ERROR_INV_CRB_CTRL_DATA 0x8e000006u
+/* This request has already been carried out */
+#define TPM_ERROR_ALREADY 0x8e000009u
+/* Operation not allowed in the current state */
+#define TPM_ERROR_DENIED 0x8e00000au
+/* Not enough available memory */
+#define TPM_ERROR_NOMEM 0x8e00000bu
+
+/* TPM service features */
+/* Client notification of TPM service events through FF-A Notification */
+#define TPM_SERVICE_FEATURE_NOTIFICATION 0xfea70000u
+
+/* A command is ready to be processed */
+#define TPM_START_QUALIFIER_COMMAND 0
+/* A locality request is ready to be processed */
+#define TPM_START_QUALIFIER_LOCALITY_REQ 1
+
+uint64_t tpm_crb_ffa_get_function_id(const uint64_t regs[5]);
+uint8_t tpm_crb_ffa_get_start_qualifier(const uint64_t regs[5]);
+uint8_t tpm_crb_ffa_get_start_loc_qualifier(const uint64_t regs[5]);
+void tpm_crb_ffa_set_status(uint64_t regs[5], uint64_t status);
+void tpm_crb_ffa_set_interface_version(uint64_t regs[5], uint64_t version);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TPM_CRB_FFA_H */
diff --git a/components/rpc/tpm_crb_ffa/endpoint/sp/component.cmake b/components/rpc/tpm_crb_ffa/endpoint/sp/component.cmake
new file mode 100644
index 0000000..32a16e1
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/endpoint/sp/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if(NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/tpm_crb_ffa_endpoint.c"
+)
diff --git a/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.c b/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.c
new file mode 100644
index 0000000..dadf6db
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <string.h>
+#include "components/rpc/tpm_crb_ffa/common/tpm_crb_ffa.h"
+#include "tpm_crb_ffa_endpoint.h"
+#include "trace.h"
+
+bool tpm_crb_ffa_endpoint_init(struct tpm_crb_ffa_ep *endpoint)
+{
+ if (!endpoint)
+ return false;
+
+ endpoint->service = NULL;
+
+ return true;
+}
+
+bool tpm_crb_ffa_endpoint_add_service(struct tpm_crb_ffa_ep *endpoint,
+ struct rpc_service_interface *service)
+{
+ if (!endpoint || !service || endpoint->service != NULL)
+ return false;
+
+ endpoint->service = service;
+
+ return true;
+}
+
+static void handle_management_command(struct tpm_crb_ffa_ep *endpoint, uint16_t source_id,
+ const uint64_t request[5], uint64_t response[5])
+{
+ switch (tpm_crb_ffa_get_function_id(request)) {
+ case TPM_FUNC_GET_INTERFACE_VERSION:
+ DMSG("Received TPM get interface version function");
+ tpm_crb_ffa_set_interface_version(response, TPM_INTERFACE_VERSION);
+ tpm_crb_ffa_set_status(response, TPM_STATUS_OK_RESULTS_RETURNED);
+ break;
+ case TPM_FUNC_GET_FEATURE_INFO:
+ DMSG("Received TPM get feature info function");
+ tpm_crb_ffa_set_status(response, TPM_ERROR_NOTSUP);
+ break;
+ case TPM_FUNC_REGISTER_FOR_NOTIFICATION:
+ DMSG("Received TPM register for notification function");
+ tpm_crb_ffa_set_status(response, TPM_ERROR_NOTSUP);
+ break;
+ case TPM_FUNC_UNREGISTER_FROM_NOTIFICATION:
+ DMSG("Received TPM unregister from notification function");
+ tpm_crb_ffa_set_status(response, TPM_ERROR_NOTSUP);
+ break;
+ case TPM_FUNC_FINISH_NOTIFIED:
+ DMSG("Received TPM finish notified function");
+ tpm_crb_ffa_set_status(response, TPM_ERROR_NOTSUP);
+ break;
+ default:
+ EMSG("Received unknown TPM function: 0x%lx", tpm_crb_ffa_get_function_id(request));
+ tpm_crb_ffa_set_status(response, TPM_ERROR_NOFUNC);
+ break;
+ }
+}
+
+static void handle_start_command(struct tpm_crb_ffa_ep *endpoint, uint16_t source_id,
+ const uint64_t request[5], uint64_t response[5])
+{
+ rpc_status_t status = RPC_ERROR_INTERNAL;
+ struct rpc_request rpc_request = { 0 };
+ uint8_t command_type = 0;
+ uint8_t locality = 0;
+
+ if (tpm_crb_ffa_get_function_id(request) != TPM_FUNC_START) {
+ EMSG("Expected TPM start command");
+ tpm_crb_ffa_set_status(response, TPM_ERROR_INVARG);
+ return;
+ }
+
+ command_type = tpm_crb_ffa_get_start_qualifier(request);
+ if (command_type != TPM_START_QUALIFIER_COMMAND &&
+ command_type != TPM_START_QUALIFIER_LOCALITY_REQ) {
+ EMSG("Invalid TPM service start function qualifier: %d", command_type);
+ tpm_crb_ffa_set_status(response, TPM_ERROR_INVARG);
+ return;
+ }
+
+ locality = tpm_crb_ffa_get_start_loc_qualifier(request);
+ if (locality > 4) {
+ EMSG("Invalid TPM service start function locality qualifier.: %d", locality);
+ tpm_crb_ffa_set_status(response, TPM_ERROR_INVARG);
+ return;
+ }
+
+ DMSG("Received TPM start command. Type: %d. Locality: %d", command_type, locality);
+
+ /*
+ * The common RPC request type can be reused for TPM CRB too. Some of the fields are not
+ * used, since the TPM CRB provider uses static carveouts for the payload and status codes.
+ */
+ rpc_request.source_id = source_id;
+ rpc_request.opcode = command_type;
+ rpc_request.client_id = locality;
+
+ status = rpc_service_receive(endpoint->service, &rpc_request);
+ if (status != RPC_SUCCESS) {
+ tpm_crb_ffa_set_status(response, rpc_request.service_status);
+ }
+
+ tpm_crb_ffa_set_status(response, TPM_STATUS_OK);
+}
+
+void tpm_crb_ffa_endpoint_receive(struct tpm_crb_ffa_ep *endpoint, const struct sp_msg *request,
+ struct sp_msg *response)
+{
+ uint64_t req_copy[SP_MSG_ARG_COUNT] = { 0 };
+ uint64_t resp_copy[SP_MSG_ARG_COUNT] = { 0 };
+
+ response->source_id = request->destination_id;
+ response->destination_id = request->source_id;
+ response->is_64bit_message = request->is_64bit_message;
+ memset(&response->args, 0x00, sizeof(response->args));
+
+ /*
+ * We have to accept both 32-bit and 64-bit messages, but the spec hasn't been updated yet
+ * to cover this. For now let's just treat them the same way, zero extending the 32-bit
+ * arguments and only use 64-bit from this point.
+ */
+ if (request->is_64bit_message) {
+ memcpy(req_copy, request->args.args64, sizeof(request->args.args64));
+ } else {
+ for (unsigned i = 0; i < SP_MSG_ARG_COUNT; i++)
+ req_copy[i] = request->args.args32[i];
+ }
+
+ /* Sanity check */
+ if (req_copy[0] != 0) {
+ EMSG("The TPM CRB services requires x3 MBZ");
+ return;
+ }
+
+ if (tpm_crb_ffa_get_function_id(req_copy) == TPM_FUNC_START)
+ handle_start_command(endpoint, request->source_id, req_copy, resp_copy);
+ else
+ handle_management_command(endpoint, request->source_id, req_copy, resp_copy);
+
+ /* Truncate the response arguments to 32 bits if necessary */
+ if (response->is_64bit_message) {
+ memcpy(response->args.args64, resp_copy, sizeof(request->args.args64));
+ } else {
+ for (unsigned i = 0; i < SP_MSG_ARG_COUNT; i++)
+ response->args.args32[i] = (uint32_t)resp_copy[i];
+ }
+}
diff --git a/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.h b/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.h
new file mode 100644
index 0000000..c641c98
--- /dev/null
+++ b/components/rpc/tpm_crb_ffa/endpoint/sp/tpm_crb_ffa_endpoint.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef TPM_CRB_FFA_ENDPOINT_H
+#define TPM_CRB_FFA_ENDPOINT_H
+
+#include "components/rpc/common/endpoint/rpc_service_interface.h"
+#include "sp_messaging.h"
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tpm_crb_ffa_ep {
+ struct rpc_service_interface *service;
+};
+
+bool tpm_crb_ffa_endpoint_init(struct tpm_crb_ffa_ep *endpoint);
+bool tpm_crb_ffa_endpoint_add_service(struct tpm_crb_ffa_ep *endpoint,
+ struct rpc_service_interface *service);
+void tpm_crb_ffa_endpoint_receive(struct tpm_crb_ffa_ep *endpoint, const struct sp_msg *request,
+ struct sp_msg *response);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TPM_CRB_FFA_ENDPOINT_H */
diff --git a/components/service/tpm/backend/ms_tpm/component.cmake b/components/service/tpm/backend/ms_tpm/component.cmake
new file mode 100644
index 0000000..a35a27c
--- /dev/null
+++ b/components/service/tpm/backend/ms_tpm/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/ms_tpm_backend.c"
+)
diff --git a/components/service/tpm/backend/ms_tpm/ms_tpm_backend.c b/components/service/tpm/backend/ms_tpm/ms_tpm_backend.c
new file mode 100644
index 0000000..8264c5a
--- /dev/null
+++ b/components/service/tpm/backend/ms_tpm/ms_tpm_backend.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "TpmBuildSwitches.h"
+#include "BaseTypes.h"
+#include "Platform_fp.h"
+#include "ExecCommand_fp.h"
+#include "Manufacture_fp.h"
+#include "_TPM_Init_fp.h"
+#include "_TPM_Hash_Start_fp.h"
+#include "_TPM_Hash_Data_fp.h"
+#include "_TPM_Hash_End_fp.h"
+#include "TpmFail_fp.h"
+
+#include "ms_tpm_backend.h"
+#include "trace.h"
+
+static inline uint32_t tpm_get_response_code(uint8_t *buf)
+{
+ if (!buf)
+ return 0x101; // TPM_RC_FAILURE
+
+ return (uint32_t)((buf[6] << 24) + (buf[7] << 16) + (buf[8] << 8 ) + buf[9]);
+}
+
+/*
+* Hardcoded buffer of the startup command with "clear" argument
+*
+* tag: 0x8001 (TPM_ST_NO_SESSIONS),
+* commandSize: 0x0c,
+* commandCode: 0x0144 (TPM_CC_Startup),
+* startupType: 0x0 (TPM_SU_CLEAR)
+*/
+static uint8_t tpm_startup_clear_cmd[] = { 0x80, 0x01,
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x01, 0x44,
+ 0x00, 0x00 };
+
+
+static uint32_t tpm_startup(void)
+{
+ uint8_t out_buf[128] = { 0 };
+ uint8_t *out_ptr = out_buf;
+ uint32_t out_size = sizeof(out_buf);
+
+ ExecuteCommand(sizeof(tpm_startup_clear_cmd), tpm_startup_clear_cmd, &out_size, &out_ptr);
+
+ return tpm_get_response_code(out_ptr);
+}
+
+void ms_tpm_backend_execute_command(const uint8_t *req, size_t req_len, uint8_t **resp,
+ size_t *resp_len, size_t resp_max_size)
+{
+ /* Set response buffer to same as request, discard const */
+ uint8_t *response_buf = (uint8_t *)req;
+
+ /* ms_tpm expects the maximum response size as input in this variable */
+ uint32_t response_len = resp_max_size;
+
+ ExecuteCommand(req_len, (uint8_t *)req, &response_len, &response_buf);
+
+ *resp = response_buf;
+ *resp_len = response_len;
+}
+
+bool ms_tpm_backend_init(void)
+{
+ uint32_t tpm_result = 0;
+ int rc = 0;
+
+ _plat__SetNvAvail();
+ rc = _plat__NVEnable(NULL);
+ if (rc) {
+ EMSG("NV enable error: %d", rc);
+ return false;
+ }
+
+ /* The parameter indicates if it's the first time we call this function */
+ rc = TPM_Manufacture(true);
+ if (rc) {
+ EMSG("TPM manufacture error: %d", rc);
+ return false;
+ }
+
+ rc = _plat__Signal_PowerOn();
+ if (rc) {
+ EMSG("Power on signal error: %d", rc);
+ return false;
+ }
+
+ _TPM_Init();
+ IMSG("TPM init done");
+
+ tpm_result = tpm_startup();
+ if (tpm_result != 0) {
+ EMSG("TPM startup failed with error: 0x%x", tpm_result);
+ return false;
+ }
+
+ IMSG("TPM startup done");
+
+ return true;
+}
diff --git a/components/service/tpm/backend/ms_tpm/ms_tpm_backend.h b/components/service/tpm/backend/ms_tpm/ms_tpm_backend.h
new file mode 100644
index 0000000..205e455
--- /dev/null
+++ b/components/service/tpm/backend/ms_tpm/ms_tpm_backend.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef MS_TPM_BACKEND_H
+#define MS_TPM_BACKEND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void ms_tpm_backend_execute_command(const uint8_t *req, size_t req_len, uint8_t **resp,
+ size_t *resp_len, size_t resp_max_size);
+bool ms_tpm_backend_init(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* MS_TPM_BACKEND_H */
diff --git a/components/service/tpm/provider/component.cmake b/components/service/tpm/provider/component.cmake
new file mode 100644
index 0000000..8d1ea74
--- /dev/null
+++ b/components/service/tpm/provider/component.cmake
@@ -0,0 +1,13 @@
+#-------------------------------------------------------------------------------
+# Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+#-------------------------------------------------------------------------------
+if (NOT DEFINED TGT)
+ message(FATAL_ERROR "mandatory parameter TGT is not defined.")
+endif()
+
+target_sources(${TGT} PRIVATE
+ "${CMAKE_CURRENT_LIST_DIR}/tpm_crb_provider.c"
+)
diff --git a/components/service/tpm/provider/tpm_crb_provider.c b/components/service/tpm/provider/tpm_crb_provider.c
new file mode 100644
index 0000000..a0c8c4b
--- /dev/null
+++ b/components/service/tpm/provider/tpm_crb_provider.c
@@ -0,0 +1,312 @@
+// 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);
+}
diff --git a/components/service/tpm/provider/tpm_crb_provider.h b/components/service/tpm/provider/tpm_crb_provider.h
new file mode 100644
index 0000000..74faf34
--- /dev/null
+++ b/components/service/tpm/provider/tpm_crb_provider.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
+ */
+
+#ifndef TPM_CRB_PROVIDER_H
+#define TPM_CRB_PROVIDER_H
+
+#include "components/rpc/common/endpoint/rpc_service_interface.h"
+#include "service/common/provider/service_provider.h"
+#include "service/tpm/backend/ms_tpm/ms_tpm_backend.h"
+#include "compiler.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct loc_and_crb_ctrl;
+
+struct tpm_crb_provider {
+ struct service_provider base_provider;
+ struct loc_and_crb_ctrl *loc_ptr[5];
+};
+
+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);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* TPM_CRB_PROVIDER_H */