Balint Dobszay | 943c6b5 | 2024-11-21 13:52:27 +0100 | [diff] [blame^] | 1 | // SPDX-License-Identifier: BSD-3-Clause |
| 2 | /* |
| 3 | * Copyright (c) 2024, Arm Limited and Contributors. All rights reserved. |
| 4 | */ |
| 5 | |
| 6 | #include <stdint.h> |
| 7 | #include <string.h> |
| 8 | #include "rpc/tpm_crb_ffa/common/tpm_crb_ffa.h" |
| 9 | #include "tpm_crb_provider.h" |
| 10 | #include "trace.h" |
| 11 | #include "util.h" |
| 12 | |
| 13 | #define CRB_DATA_BUF_SIZE (size_t)0x800 |
| 14 | |
| 15 | /* CRB register definitions based on TCG PC Client Platform TPM Profile Specification for TPM 2.0 */ |
| 16 | |
| 17 | /* TPM_LOC_STATE_x: Locality State Register */ |
| 18 | #define LOC_STATE_TPM_ESTABLISHED BIT32(0) |
| 19 | #define LOC_STATE_LOC_ASSIGNED BIT32(1) |
| 20 | #define LOC_STATE_ACTIVE_LOCALITY_SHIFT UINT32_C(2) |
| 21 | #define LOC_STATE_ACTIVE_LOCALITY_MASK GENMASK_32(2, 0) |
| 22 | #define LOC_STATE_TPM_REG_VALID_STATUS BIT32(7) |
| 23 | |
| 24 | /* TPM_LOC_CTRL_x: Locality Control Register for Localities 0 - 3 */ |
| 25 | #define LOC_CTRL_REQUEST_ACCESS BIT32(0) |
| 26 | #define LOC_CTRL_RELINQUISH BIT32(1) |
| 27 | #define LOC_CTRL_SEIZE BIT32(2) |
| 28 | #define LOC_CTRL_RESET_ESTABLISHMENT_BIT BIT32(3) |
| 29 | |
| 30 | /* TPM_LOC_CTRL_4: Locality Control Register for Locality 4 */ |
| 31 | #define LOC_CTRL_4_HASH_START BIT32(0) |
| 32 | #define LOC_CTRL_4_HASH_DATA BIT32(1) |
| 33 | #define LOC_CTRL_4_HASH_END BIT32(2) |
| 34 | #define LOC_CTRL_4_RESET_ESTABLISHMENT_BIT BIT32(3) |
| 35 | |
| 36 | /* TPM_LOC_STS_x: Locality Status Register */ |
| 37 | #define LOC_STS_GRANTED BIT32(0) |
| 38 | #define LOC_STS_BEEN_SEIZED BIT32(1) |
| 39 | |
| 40 | /* TPM_CRB_CTRL_REQ_x: Control Area Request Register */ |
| 41 | #define CRB_CTRL_REQ_CMD_READY BIT32(0) |
| 42 | #define CRB_CTRL_REQ_GO_IDLE BIT32(1) |
| 43 | |
| 44 | /* TPM_CRB_CTRL_STS_x: Control Area Status Register */ |
| 45 | #define CRB_CTRL_STS_TPM_STATUS BIT32(0) |
| 46 | #define CRB_CTRL_STS_TPM_IDLE BIT32(1) |
| 47 | |
| 48 | /* TPM_CRB_CTRL_CANCEL_x: Control Area Status Register */ |
| 49 | #define CRB_CTRL_CANCEL_COMMAND BIT32(0) |
| 50 | |
| 51 | /* TPM_CRB_CTRL_START_x: Control Area Status Register */ |
| 52 | #define CRB_CTRL_START_COMMAND BIT32(0) |
| 53 | |
| 54 | struct loc_and_crb_ctrl { |
| 55 | // Offset 0x00 - 0x03: Used to determine current state of locality of the TPM. This register |
| 56 | // is aliased across all localities. Read-only. |
| 57 | uint32_t loc_state; |
| 58 | // Offset 0x04 - 0x07: Reserved |
| 59 | uint8_t _res4[4]; |
| 60 | // Offset 0x08 - 0x0b: Used to gain control of the TPM by this locality. |
| 61 | uint32_t loc_ctrl; |
| 62 | // Offset 0x0c - 0x0f: Used to determine whether locality has been granted or Seized. |
| 63 | // Read-only. This register SHALL NOT be aliased. |
| 64 | uint32_t loc_status; |
| 65 | // Offset 0x10 - 0x2f: Reserved |
| 66 | uint8_t _res10[32]; |
| 67 | // Offset 0x30 - 0x37: Used to identify the Interface types supported by the TPM as well as |
| 68 | // the Vendor ID, Device ID and Revision ID. |
| 69 | uint64_t interface_id; |
| 70 | // Offset 0x38 - 0x3f: Optional Register used in low memory environments prior to |
| 71 | // CRB_DATA_BUFFER availability. This field is not implemented in hardware TPMs. This field |
| 72 | // is only available in Locality 0. |
| 73 | uint64_t ctrl_ext; |
| 74 | // Offset 0x40 - 0x43: Register used to initiate transactions for the CRB interface. This |
| 75 | // register may be aliased across localities. |
| 76 | uint32_t ctrl_request; |
| 77 | // Offset 0x44 - 0x47: Register used by the TPM to provide status of the CRB interface. This |
| 78 | // register may be aliased across localities. |
| 79 | uint32_t ctrl_status; |
| 80 | // Offset 0x48 - 0x4b: Register used by Software to cancel command processing. This register |
| 81 | // may be aliased across localities. |
| 82 | uint32_t ctrl_cancel; |
| 83 | // Offset 0x4c - 0x4f: Register used to indicate presence of command or response data in the |
| 84 | // CRB buffer. This register may be aliased across localities. |
| 85 | uint32_t ctrl_start; |
| 86 | // Offset 0x50 - 0x53: Register used to configure interrupts. This register may be aliased |
| 87 | // across localities. |
| 88 | uint32_t ctrl_int_enable; |
| 89 | // Offset 0x54 - 0x57: Register used to respond to interrupts. This register may be aliased |
| 90 | // across localities. |
| 91 | uint32_t ctrl_int_status; |
| 92 | // Offset 0x58 - 0x5b: Size of the Command buffer. This register may be aliased across |
| 93 | // localities. |
| 94 | uint32_t ctrl_cmd_size; |
| 95 | // Offset 0x5c - 0x5f: Lower 32bits of the Command buffer start address for the locality. |
| 96 | // This register may be aliased across localities. |
| 97 | uint32_t ctrl_cmd_addr_lo; |
| 98 | // Offset 0x60 - 0x63: Upper 32bits of the Command buffer start address for the locality. |
| 99 | // This register may be aliased across localities. |
| 100 | uint32_t ctrl_cmd_addr_hi; |
| 101 | // Offset 0x64 - 0x67: Size of the Response buffer. Note: If command and response buffers |
| 102 | // are implemented as a single buffer, this field SHALL be identical to the value in the |
| 103 | // TPM_CRB_CTRL_CMD_SIZE_x buffer. This register may be aliased across localities. |
| 104 | uint32_t ctrl_resp_size; |
| 105 | // Offset 0x68 - 0x6f: Address of the start of the Response buffer. Note: If command and |
| 106 | // response buffers are implemented as a single buffer, this field SHALL contain the same |
| 107 | // address contained in TPM_CRB_CTRL_CMD_HADDR_x and TPM_CRB_CMD_LADDR_x. This register may |
| 108 | // be aliased across localities. |
| 109 | uint64_t ctrl_resp_addr; |
| 110 | // Offset 0x70 - 0x7f: Reserved |
| 111 | uint8_t _res70[16]; |
| 112 | // Offset 0x80 - 0x880: Command/Response Data may be defined as large as 3968. This is |
| 113 | // implementation-specific. However, the full address space has been reserved. This buffer |
| 114 | // may be aliased across localities. This field accepts data transfers from 1B up to the |
| 115 | // size indicated by TPM_CRB_INTF_ID_x.CapDataXferSizeSupport (see section 6.4.2.2 CRB |
| 116 | // Interface Identifier Register). |
| 117 | uint8_t data_buffer[CRB_DATA_BUF_SIZE]; |
| 118 | } __packed __aligned(__alignof(uint32_t)); |
| 119 | |
| 120 | void set_loc_state_all(struct loc_and_crb_ctrl *loc_ptr[5], uint32_t loc_state) |
| 121 | { |
| 122 | /* The TPM_LOC_STATE_x register is aliased across all localities, set in one step */ |
| 123 | for (unsigned i = 0; i < 5; i++) |
| 124 | loc_ptr[i]->loc_state = loc_state; |
| 125 | } |
| 126 | |
| 127 | /* Service request handlers */ |
| 128 | static rpc_status_t command_handler(void *context, struct rpc_request *req); |
| 129 | static rpc_status_t locality_req_handler(void *context, struct rpc_request *req); |
| 130 | |
| 131 | /* Handler mapping table for service */ |
| 132 | static const struct service_handler handler_table[] = { |
| 133 | { TPM_START_QUALIFIER_COMMAND, command_handler }, |
| 134 | { TPM_START_QUALIFIER_LOCALITY_REQ, locality_req_handler }, |
| 135 | }; |
| 136 | |
| 137 | static inline uint32_t tpm_get_request_length(uint8_t *buf) |
| 138 | { |
| 139 | if (!buf) |
| 140 | return 0; |
| 141 | |
| 142 | return (uint32_t)((buf[2] << 24) + (buf[3] << 16) + (buf[4] << 8 ) + buf[5]); |
| 143 | } |
| 144 | |
| 145 | static rpc_status_t command_handler(void *context, struct rpc_request *req) |
| 146 | { |
| 147 | struct tpm_crb_provider *this_instance = (struct tpm_crb_provider *)context; |
| 148 | struct loc_and_crb_ctrl *req_loc = NULL; |
| 149 | uint8_t locality = req->client_id; |
| 150 | uint8_t *resp_data = NULL; |
| 151 | uint8_t *req_data = NULL; |
| 152 | size_t resp_max_size = 0; |
| 153 | size_t resp_len = 0; |
| 154 | size_t req_len = 0; |
| 155 | size_t crb_size = 4096; /* TODO: this config should come from the build system */ |
| 156 | |
| 157 | DMSG("Processing TPM service command at locality %d", locality); |
| 158 | |
| 159 | /* The locality which made the request */ |
| 160 | req_loc = this_instance->loc_ptr[locality]; |
| 161 | |
| 162 | if (!(req_loc->ctrl_start & CRB_CTRL_START_COMMAND)) { |
| 163 | req->service_status = TPM_ERROR_INV_CRB_CTRL_DATA; |
| 164 | return RPC_ERROR_INTERNAL; |
| 165 | } |
| 166 | |
| 167 | req_len = tpm_get_request_length(req_loc->data_buffer); |
| 168 | if (req_len == 0 || crb_size < req_len) { |
| 169 | req->service_status = TPM_ERROR_INV_CRB_CTRL_DATA; |
| 170 | return RPC_ERROR_INTERNAL; |
| 171 | } |
| 172 | |
| 173 | req_data = req_loc->data_buffer; |
| 174 | resp_data = req_loc->data_buffer; |
| 175 | resp_max_size = crb_size; |
| 176 | |
| 177 | ms_tpm_backend_execute_command(req_data, req_len, &resp_data, &resp_len, resp_max_size); |
| 178 | |
| 179 | /* All operations done, clear the pending request */ |
| 180 | req_loc->ctrl_start &= ~CRB_CTRL_START_COMMAND; |
| 181 | req->service_status = TPM_STATUS_OK; |
| 182 | |
| 183 | return RPC_SUCCESS; |
| 184 | } |
| 185 | |
| 186 | static rpc_status_t locality_req_handler(void *context, struct rpc_request *req) |
| 187 | { |
| 188 | struct tpm_crb_provider *this_instance = (struct tpm_crb_provider *)context; |
| 189 | struct loc_and_crb_ctrl *req_loc = NULL; |
| 190 | uint8_t locality = req->client_id; |
| 191 | |
| 192 | if (locality == 4) { |
| 193 | EMSG("Locality 4 handling is currently not supported"); |
| 194 | req->service_status = TPM_ERROR_NOTSUP; |
| 195 | return RPC_ERROR_INTERNAL; |
| 196 | } |
| 197 | |
| 198 | DMSG("Processing TPM service locality request at locality %d", locality); |
| 199 | |
| 200 | /* The locality which made the request */ |
| 201 | req_loc = this_instance->loc_ptr[locality]; |
| 202 | |
| 203 | if (req_loc->loc_ctrl & LOC_CTRL_REQUEST_ACCESS) { |
| 204 | uint32_t loc_state = req_loc->loc_state; |
| 205 | |
| 206 | DMSG("Locality access request"); |
| 207 | |
| 208 | /* TODO: execute locality arbitration algorithm */ |
| 209 | |
| 210 | /* Clear the valid status bit before manipulating register contents */ |
| 211 | loc_state &= ~LOC_STATE_TPM_REG_VALID_STATUS; |
| 212 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 213 | |
| 214 | /* Clear current active locality and set the new */ |
| 215 | loc_state &= ~SHIFT_U32(LOC_STATE_ACTIVE_LOCALITY_MASK, |
| 216 | LOC_STATE_ACTIVE_LOCALITY_SHIFT); |
| 217 | loc_state |= SHIFT_U32(locality & LOC_STATE_ACTIVE_LOCALITY_MASK, |
| 218 | LOC_STATE_ACTIVE_LOCALITY_SHIFT); |
| 219 | loc_state |= LOC_STATE_LOC_ASSIGNED; |
| 220 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 221 | |
| 222 | /* Set the valid status bit */ |
| 223 | loc_state |= LOC_STATE_TPM_REG_VALID_STATUS; |
| 224 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 225 | |
| 226 | /* Set the locality status to granted */ |
| 227 | req_loc->loc_status |= LOC_STS_GRANTED; |
| 228 | |
| 229 | /* All operations done, clear the pending request */ |
| 230 | req_loc->loc_ctrl &= ~LOC_CTRL_REQUEST_ACCESS; |
| 231 | } |
| 232 | |
| 233 | if (req_loc->loc_ctrl & LOC_CTRL_RELINQUISH) { |
| 234 | uint32_t loc_state = req_loc->loc_state; |
| 235 | |
| 236 | DMSG("Locality relinquish"); |
| 237 | |
| 238 | /* Clear the valid status bit before manipulating register contents */ |
| 239 | loc_state &= ~LOC_STATE_TPM_REG_VALID_STATUS; |
| 240 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 241 | |
| 242 | /* Clear current active locality and the locality assigned bit */ |
| 243 | loc_state &= ~SHIFT_U32(LOC_STATE_ACTIVE_LOCALITY_MASK, |
| 244 | LOC_STATE_ACTIVE_LOCALITY_SHIFT); |
| 245 | loc_state &= ~LOC_STATE_LOC_ASSIGNED; |
| 246 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 247 | |
| 248 | /* Set the valid status bit */ |
| 249 | loc_state |= LOC_STATE_TPM_REG_VALID_STATUS; |
| 250 | set_loc_state_all(this_instance->loc_ptr, loc_state); |
| 251 | |
| 252 | /* Clear the locality granted bit */ |
| 253 | req_loc->loc_status &= ~LOC_STS_GRANTED; |
| 254 | |
| 255 | /* All operations done, clear the pending request */ |
| 256 | req_loc->loc_ctrl &= ~LOC_CTRL_RELINQUISH; |
| 257 | } |
| 258 | |
| 259 | req->service_status = TPM_STATUS_OK; |
| 260 | |
| 261 | return RPC_SUCCESS; |
| 262 | } |
| 263 | |
| 264 | struct rpc_service_interface *tpm_provider_init(struct tpm_crb_provider *context, |
| 265 | uint8_t *ns_crb, size_t ns_crb_size, |
| 266 | uint8_t* s_crb, size_t s_crb_size) |
| 267 | { |
| 268 | const struct rpc_uuid tpm_crb_service_uuid = { |
| 269 | .uuid = TPM_CRB_FFA_UUID |
| 270 | }; |
| 271 | |
| 272 | if (!context || !ns_crb || ns_crb_size == 0 || !s_crb || s_crb_size == 0) |
| 273 | return NULL; |
| 274 | |
| 275 | /* All of the locality and CRB control registers should be cleared an power on */ |
| 276 | memset(ns_crb, 0, ns_crb_size); |
| 277 | memset(s_crb, 0, s_crb_size); |
| 278 | |
| 279 | /* Note: these are the CRB VAs that we got from the SP boot info */ |
| 280 | context->loc_ptr[0] = (struct loc_and_crb_ctrl *)ns_crb; |
| 281 | context->loc_ptr[1] = (struct loc_and_crb_ctrl *)(ns_crb + 0x1000); |
| 282 | context->loc_ptr[2] = (struct loc_and_crb_ctrl *)(ns_crb + 0x2000); |
| 283 | context->loc_ptr[3] = (struct loc_and_crb_ctrl *)(ns_crb + 0x3000); |
| 284 | context->loc_ptr[4] = (struct loc_and_crb_ctrl *)s_crb; |
| 285 | |
| 286 | for (unsigned i = 0; i <= 4; i++) { |
| 287 | uint64_t data_buf_addr = 0; |
| 288 | |
| 289 | /* Note: here we have to use the CRB PAs that's fixed at compile time */ |
| 290 | if (i < 4) { |
| 291 | data_buf_addr = TPM_CRB_NS_PA + i * 0x1000 + |
| 292 | offsetof(struct loc_and_crb_ctrl, data_buffer); |
| 293 | } else { |
| 294 | data_buf_addr = TPM_CRB_S_PA + |
| 295 | offsetof(struct loc_and_crb_ctrl, data_buffer); |
| 296 | } |
| 297 | |
| 298 | context->loc_ptr[i]->ctrl_cmd_addr_lo = data_buf_addr & UINT32_MAX; |
| 299 | context->loc_ptr[i]->ctrl_cmd_addr_hi = data_buf_addr >> 32; |
| 300 | context->loc_ptr[i]->ctrl_cmd_size = CRB_DATA_BUF_SIZE; |
| 301 | context->loc_ptr[i]->ctrl_resp_addr = data_buf_addr; |
| 302 | context->loc_ptr[i]->ctrl_resp_size = CRB_DATA_BUF_SIZE; |
| 303 | } |
| 304 | |
| 305 | /* Set the valid bit in Locality State Register for all localities */ |
| 306 | set_loc_state_all(context->loc_ptr, LOC_STATE_TPM_REG_VALID_STATUS); |
| 307 | |
| 308 | service_provider_init(&context->base_provider, context, &tpm_crb_service_uuid, |
| 309 | handler_table, ARRAY_SIZE(handler_table)); |
| 310 | |
| 311 | return service_provider_get_rpc_interface(&context->base_provider); |
| 312 | } |