1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG 5 * Author: Corvin Köhne <c.koehne@beckhoff.com> 6 */ 7 8 #include <sys/cdefs.h> 9 #include <sys/types.h> 10 #include <sys/param.h> 11 #include <sys/linker_set.h> 12 13 #include <machine/vmm.h> 14 15 #include <assert.h> 16 #include <err.h> 17 #include <errno.h> 18 #include <pthread.h> 19 #include <pthread_np.h> 20 #include <stddef.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <vmmapi.h> 24 25 #include "basl.h" 26 #include "config.h" 27 #include "mem.h" 28 #include "qemu_fwcfg.h" 29 #include "tpm_device.h" 30 #include "tpm_intf.h" 31 32 #define TPM_CRB_ADDRESS 0xFED40000 33 #define TPM_CRB_REGS_SIZE 0x1000 34 35 #define TPM_CRB_CONTROL_AREA_ADDRESS \ 36 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req)) 37 #define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE 38 39 #define TPM_CRB_DATA_BUFFER_ADDRESS \ 40 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer)) 41 #define TPM_CRB_DATA_BUFFER_SIZE 0xF80 42 43 #define TPM_CRB_LOCALITIES_MAX 5 44 45 #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024) 46 47 #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log" 48 49 #define TPM_CRB_INTF_NAME "crb" 50 51 struct tpm_crb_regs { 52 union tpm_crb_reg_loc_state { 53 struct { 54 uint32_t tpm_established : 1; 55 uint32_t loc_assigned : 1; 56 uint32_t active_locality : 3; 57 uint32_t _reserved : 2; 58 uint32_t tpm_req_valid_sts : 1; 59 }; 60 uint32_t val; 61 } loc_state; /* 0h */ 62 uint8_t _reserved1[4]; /* 4h */ 63 union tpm_crb_reg_loc_ctrl { 64 struct { 65 uint32_t request_access : 1; 66 uint32_t relinquish : 1; 67 uint32_t seize : 1; 68 uint32_t reset_establishment_bit : 1; 69 }; 70 uint32_t val; 71 } loc_ctrl; /* 8h */ 72 union tpm_crb_reg_loc_sts { 73 struct { 74 uint32_t granted : 1; 75 uint32_t been_seized : 1; 76 }; 77 uint32_t val; 78 } loc_sts; /* Ch */ 79 uint8_t _reserved2[0x20]; /* 10h */ 80 union tpm_crb_reg_intf_id { 81 struct { 82 uint64_t interface_type : 4; 83 uint64_t interface_version : 4; 84 uint64_t cap_locality : 1; 85 uint64_t cap_crb_idle_bypass : 1; 86 uint64_t _reserved1 : 1; 87 uint64_t cap_data_xfer_size_support : 2; 88 uint64_t cap_fifo : 1; 89 uint64_t cap_crb : 1; 90 uint64_t _reserved2 : 2; 91 uint64_t interface_selector : 2; 92 uint64_t intf_sel_lock : 1; 93 uint64_t _reserved3 : 4; 94 uint64_t rid : 8; 95 uint64_t vid : 16; 96 uint64_t did : 16; 97 }; 98 uint64_t val; 99 } intf_id; /* 30h */ 100 union tpm_crb_reg_ctrl_ext { 101 struct { 102 uint32_t clear; 103 uint32_t remaining_bytes; 104 }; 105 uint64_t val; 106 } ctrl_ext; /* 38 */ 107 union tpm_crb_reg_ctrl_req { 108 struct { 109 uint32_t cmd_ready : 1; 110 uint32_t go_idle : 1; 111 }; 112 uint32_t val; 113 } ctrl_req; /* 40h */ 114 union tpm_crb_reg_ctrl_sts { 115 struct { 116 uint32_t tpm_sts : 1; 117 uint32_t tpm_idle : 1; 118 }; 119 uint32_t val; 120 } ctrl_sts; /* 44h */ 121 union tpm_crb_reg_ctrl_cancel { 122 struct { 123 uint32_t cancel : 1; 124 }; 125 uint32_t val; 126 } ctrl_cancel; /* 48h */ 127 union tpm_crb_reg_ctrl_start { 128 struct { 129 uint32_t start : 1; 130 }; 131 uint32_t val; 132 } ctrl_start; /* 4Ch*/ 133 uint32_t int_enable; /* 50h */ 134 uint32_t int_sts; /* 54h */ 135 uint32_t cmd_size; /* 58h */ 136 uint32_t cmd_addr_lo; /* 5Ch */ 137 uint32_t cmd_addr_hi; /* 60h */ 138 uint32_t rsp_size; /* 64h */ 139 uint64_t rsp_addr; /* 68h */ 140 uint8_t _reserved3[0x10]; /* 70h */ 141 uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */ 142 } __packed; 143 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE, 144 "Invalid size of tpm_crb"); 145 146 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size) 147 #define CRB_CMD_SIZE_WRITE(regs, val) \ 148 do { \ 149 regs.cmd_size = val; \ 150 } while (0) 151 #define CRB_CMD_ADDR_READ(regs) \ 152 (((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo) 153 #define CRB_CMD_ADDR_WRITE(regs, val) \ 154 do { \ 155 regs.cmd_addr_lo = val & 0xFFFFFFFF; \ 156 regs.cmd_addr_hi = val >> 32; \ 157 } while (0) 158 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size) 159 #define CRB_RSP_SIZE_WRITE(regs, val) \ 160 do { \ 161 regs.rsp_size = val; \ 162 } while (0) 163 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr) 164 #define CRB_RSP_ADDR_WRITE(regs, val) \ 165 do { \ 166 regs.rsp_addr = val; \ 167 } while (0) 168 169 struct tpm_crb { 170 struct tpm_emul *emul; 171 void *emul_sc; 172 uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE]; 173 struct tpm_crb_regs regs; 174 pthread_t thread; 175 pthread_mutex_t mutex; 176 pthread_cond_t cond; 177 bool closing; 178 }; 179 180 static void * 181 tpm_crb_thread(void *const arg) 182 { 183 struct tpm_crb *const crb = arg; 184 185 pthread_mutex_lock(&crb->mutex); 186 for (;;) { 187 /* 188 * We're releasing the lock after wake up. Therefore, we have to 189 * check the closing condition before and after going to sleep. 190 */ 191 if (crb->closing) 192 break; 193 194 pthread_cond_wait(&crb->cond, &crb->mutex); 195 196 if (crb->closing) 197 break; 198 199 const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs); 200 const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs); 201 const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs); 202 const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs); 203 204 const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 205 const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 206 207 if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE || 208 cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE || 209 rsp_off > TPM_CRB_DATA_BUFFER_SIZE || 210 rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) { 211 warnx( 212 "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r", 213 __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr, 214 rsp_addr + rsp_size); 215 break; 216 } 217 218 uint8_t cmd[TPM_CRB_DATA_BUFFER_SIZE]; 219 memcpy(cmd, crb->regs.data_buffer, TPM_CRB_DATA_BUFFER_SIZE); 220 221 /* 222 * A TPM command can take multiple seconds to execute. As we've 223 * copied all required values and buffers at this point, we can 224 * release the mutex. 225 */ 226 pthread_mutex_unlock(&crb->mutex); 227 228 /* 229 * The command response buffer interface uses a single buffer 230 * for sending a command to and receiving a response from the 231 * tpm. To avoid reading old data from the command buffer which 232 * might be a security issue, we zero out the command buffer 233 * before writing the response into it. The rsp_size parameter 234 * is controlled by the guest and it's not guaranteed that the 235 * response has a size of rsp_size (e.g. if the tpm returned an 236 * error, the response would have a different size than 237 * expected). For that reason, use a second buffer for the 238 * response. 239 */ 240 uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 }; 241 crb->emul->execute_cmd(crb->emul_sc, &cmd[cmd_off], cmd_size, 242 &rsp[rsp_off], rsp_size); 243 244 pthread_mutex_lock(&crb->mutex); 245 memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE); 246 memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size); 247 248 crb->regs.ctrl_start.start = false; 249 } 250 pthread_mutex_unlock(&crb->mutex); 251 252 return (NULL); 253 } 254 255 static int 256 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc) 257 { 258 struct tpm_crb *crb = NULL; 259 int error; 260 261 assert(sc != NULL); 262 assert(emul != NULL); 263 264 crb = calloc(1, sizeof(struct tpm_crb)); 265 if (crb == NULL) { 266 warnx("%s: failed to allocate tpm crb", __func__); 267 error = ENOMEM; 268 goto err_out; 269 } 270 271 memset(crb, 0, sizeof(*crb)); 272 273 crb->emul = emul; 274 crb->emul_sc = emul_sc; 275 276 crb->regs.loc_state.tpm_req_valid_sts = true; 277 crb->regs.loc_state.tpm_established = true; 278 279 crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB; 280 crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB; 281 crb->regs.intf_id.cap_locality = false; 282 crb->regs.intf_id.cap_crb_idle_bypass = false; 283 crb->regs.intf_id.cap_data_xfer_size_support = 284 TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64; 285 crb->regs.intf_id.cap_fifo = false; 286 crb->regs.intf_id.cap_crb = true; 287 crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB; 288 crb->regs.intf_id.intf_sel_lock = false; 289 crb->regs.intf_id.rid = 0; 290 crb->regs.intf_id.vid = 0x1014; /* IBM */ 291 crb->regs.intf_id.did = 0x1014; /* IBM */ 292 293 crb->regs.ctrl_sts.tpm_idle = true; 294 295 CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 296 CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 297 CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 298 CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 299 300 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME, 301 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area); 302 if (error) { 303 warnx("%s: failed to add fwcfg file", __func__); 304 goto err_out; 305 } 306 307 error = pthread_mutex_init(&crb->mutex, NULL); 308 if (error) { 309 warnc(error, "%s: failed to init mutex", __func__); 310 goto err_out; 311 } 312 313 error = pthread_cond_init(&crb->cond, NULL); 314 if (error) { 315 warnc(error, "%s: failed to init cond", __func__); 316 goto err_out; 317 } 318 319 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb); 320 if (error) { 321 warnx("%s: failed to create thread\n", __func__); 322 goto err_out; 323 } 324 325 pthread_set_name_np(crb->thread, "tpm_intf_crb"); 326 327 *sc = crb; 328 329 return (0); 330 331 err_out: 332 free(crb); 333 334 return (error); 335 } 336 337 static void 338 tpm_crb_deinit(void *sc) 339 { 340 struct tpm_crb *crb; 341 342 if (sc == NULL) { 343 return; 344 } 345 346 crb = sc; 347 348 crb->closing = true; 349 pthread_cond_signal(&crb->cond); 350 pthread_join(crb->thread, NULL); 351 352 pthread_cond_destroy(&crb->cond); 353 pthread_mutex_destroy(&crb->mutex); 354 355 free(crb); 356 } 357 358 static int 359 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx) 360 { 361 struct basl_table *table; 362 363 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2, 364 BASL_TABLE_ALIGNMENT)); 365 366 /* Header */ 367 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1)); 368 /* Platform Class */ 369 BASL_EXEC(basl_table_append_int(table, 0, 2)); 370 /* Reserved */ 371 BASL_EXEC(basl_table_append_int(table, 0, 2)); 372 /* Control Address */ 373 BASL_EXEC( 374 basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8)); 375 /* Start Method == (7) Command Response Buffer */ 376 BASL_EXEC(basl_table_append_int(table, 7, 4)); 377 /* Start Method Specific Parameters */ 378 uint8_t parameters[12] = { 0 }; 379 BASL_EXEC(basl_table_append_bytes(table, parameters, 12)); 380 /* Log Area Minimum Length */ 381 BASL_EXEC( 382 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4)); 383 /* Log Area Start Address */ 384 BASL_EXEC( 385 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); 386 387 BASL_EXEC(basl_table_register_to_rsdt(table)); 388 389 return (0); 390 } 391 392 static struct tpm_intf tpm_intf_crb = { 393 .name = TPM_CRB_INTF_NAME, 394 .init = tpm_crb_init, 395 .deinit = tpm_crb_deinit, 396 .build_acpi_table = tpm_crb_build_acpi_table, 397 }; 398 TPM_INTF_SET(tpm_intf_crb); 399