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_mmiocpy(void *const dst, void *const src, const int size) 257 { 258 if (!(size == 1 || size == 2 || size == 4 || size == 8)) 259 return (EINVAL); 260 memcpy(dst, src, size); 261 262 return (0); 263 } 264 265 static int 266 tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir, 267 const uint64_t addr, const int size, uint64_t *const val, void *const arg1, 268 const long arg2 __unused) 269 { 270 struct tpm_crb *crb; 271 uint8_t *ptr; 272 uint64_t off, shift; 273 int error = 0; 274 275 if ((addr & (size - 1)) != 0) { 276 warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__, 277 (dir == MEM_F_READ) ? "read" : "write", addr, size); 278 return (EINVAL); 279 } 280 281 crb = arg1; 282 283 off = addr - TPM_CRB_ADDRESS; 284 if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) { 285 return (EINVAL); 286 } 287 288 shift = 8 * (off & 3); 289 ptr = (uint8_t *)&crb->regs + off; 290 291 if (dir == MEM_F_READ) { 292 error = tpm_crb_mmiocpy(val, ptr, size); 293 if (error) 294 goto err_out; 295 } else { 296 switch (off & ~0x3) { 297 case offsetof(struct tpm_crb_regs, loc_ctrl): { 298 union tpm_crb_reg_loc_ctrl loc_ctrl; 299 300 if ((size_t)size > sizeof(loc_ctrl)) 301 goto err_out; 302 303 *val = *val << shift; 304 tpm_crb_mmiocpy(&loc_ctrl, val, size); 305 306 if (loc_ctrl.relinquish) { 307 crb->regs.loc_sts.granted = false; 308 crb->regs.loc_state.loc_assigned = false; 309 } else if (loc_ctrl.request_access) { 310 crb->regs.loc_sts.granted = true; 311 crb->regs.loc_state.loc_assigned = true; 312 } 313 314 break; 315 } 316 case offsetof(struct tpm_crb_regs, ctrl_req): { 317 union tpm_crb_reg_ctrl_req req; 318 319 if ((size_t)size > sizeof(req)) 320 goto err_out; 321 322 *val = *val << shift; 323 tpm_crb_mmiocpy(&req, val, size); 324 325 if (req.cmd_ready && !req.go_idle) { 326 crb->regs.ctrl_sts.tpm_idle = false; 327 } else if (!req.cmd_ready && req.go_idle) { 328 crb->regs.ctrl_sts.tpm_idle = true; 329 } 330 331 break; 332 } 333 case offsetof(struct tpm_crb_regs, ctrl_cancel): { 334 /* TODO: cancel the tpm command */ 335 warnx( 336 "%s: cancelling a TPM command is not implemented yet", 337 __func__); 338 339 break; 340 } 341 case offsetof(struct tpm_crb_regs, ctrl_start): { 342 union tpm_crb_reg_ctrl_start start; 343 344 if ((size_t)size > sizeof(start)) 345 goto err_out; 346 347 *val = *val << shift; 348 349 pthread_mutex_lock(&crb->mutex); 350 tpm_crb_mmiocpy(&start, val, size); 351 352 if (!start.start || crb->regs.ctrl_start.start) 353 break; 354 355 crb->regs.ctrl_start.start = true; 356 357 pthread_cond_signal(&crb->cond); 358 pthread_mutex_unlock(&crb->mutex); 359 360 break; 361 } 362 case offsetof(struct tpm_crb_regs, cmd_size): 363 case offsetof(struct tpm_crb_regs, cmd_addr_lo): 364 case offsetof(struct tpm_crb_regs, cmd_addr_hi): 365 case offsetof(struct tpm_crb_regs, rsp_size): 366 case offsetof(struct tpm_crb_regs, 367 rsp_addr) ... offsetof(struct tpm_crb_regs, rsp_addr) + 368 4: 369 case offsetof(struct tpm_crb_regs, 370 data_buffer) ... offsetof(struct tpm_crb_regs, data_buffer) + 371 TPM_CRB_DATA_BUFFER_SIZE / 4: 372 /* 373 * Those fields are used to execute a TPM command. The 374 * crb_thread will access them. For that reason, we have 375 * to acquire the crb mutex in order to write them. 376 */ 377 pthread_mutex_lock(&crb->mutex); 378 error = tpm_crb_mmiocpy(ptr, val, size); 379 pthread_mutex_unlock(&crb->mutex); 380 if (error) 381 goto err_out; 382 break; 383 default: 384 /* 385 * The other fields are either readonly or we do not 386 * support writing them. 387 */ 388 error = EINVAL; 389 goto err_out; 390 } 391 } 392 393 return (0); 394 395 err_out: 396 warnx("%s: invalid %s @ %16lx [size = %d]", __func__, 397 dir == MEM_F_READ ? "read" : "write", addr, size); 398 399 return (error); 400 } 401 402 static int 403 tpm_crb_modify_mmio_registration(const bool registration, void *const arg1) 404 { 405 struct mem_range crb_mmio = { 406 .name = "crb-mmio", 407 .base = TPM_CRB_ADDRESS, 408 .size = TPM_CRB_LOCALITIES_MAX * TPM_CRB_CONTROL_AREA_SIZE, 409 .flags = MEM_F_RW, 410 .arg1 = arg1, 411 .handler = tpm_crb_mem_handler, 412 }; 413 414 if (registration) 415 return (register_mem(&crb_mmio)); 416 else 417 return (unregister_mem(&crb_mmio)); 418 } 419 420 static int 421 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc, 422 struct acpi_device *acpi_dev) 423 { 424 struct tpm_crb *crb = NULL; 425 int error; 426 427 assert(sc != NULL); 428 assert(emul != NULL); 429 430 crb = calloc(1, sizeof(struct tpm_crb)); 431 if (crb == NULL) { 432 warnx("%s: failed to allocate tpm crb", __func__); 433 error = ENOMEM; 434 goto err_out; 435 } 436 437 memset(crb, 0, sizeof(*crb)); 438 439 crb->emul = emul; 440 crb->emul_sc = emul_sc; 441 442 crb->regs.loc_state.tpm_req_valid_sts = true; 443 crb->regs.loc_state.tpm_established = true; 444 445 crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB; 446 crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB; 447 crb->regs.intf_id.cap_locality = false; 448 crb->regs.intf_id.cap_crb_idle_bypass = false; 449 crb->regs.intf_id.cap_data_xfer_size_support = 450 TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64; 451 crb->regs.intf_id.cap_fifo = false; 452 crb->regs.intf_id.cap_crb = true; 453 crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB; 454 crb->regs.intf_id.intf_sel_lock = false; 455 crb->regs.intf_id.rid = 0; 456 crb->regs.intf_id.vid = 0x1014; /* IBM */ 457 crb->regs.intf_id.did = 0x1014; /* IBM */ 458 459 crb->regs.ctrl_sts.tpm_idle = true; 460 461 CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 462 CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 463 CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 464 CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 465 466 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME, 467 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area); 468 if (error) { 469 warnx("%s: failed to add fwcfg file", __func__); 470 goto err_out; 471 } 472 473 error = acpi_device_add_res_fixed_memory32(acpi_dev, false, 474 TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE); 475 if (error) { 476 warnx("%s: failed to add acpi resources\n", __func__); 477 goto err_out; 478 } 479 480 error = tpm_crb_modify_mmio_registration(true, crb); 481 if (error) { 482 warnx("%s: failed to register crb mmio", __func__); 483 goto err_out; 484 } 485 486 error = pthread_mutex_init(&crb->mutex, NULL); 487 if (error) { 488 warnc(error, "%s: failed to init mutex", __func__); 489 goto err_out; 490 } 491 492 error = pthread_cond_init(&crb->cond, NULL); 493 if (error) { 494 warnc(error, "%s: failed to init cond", __func__); 495 goto err_out; 496 } 497 498 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb); 499 if (error) { 500 warnx("%s: failed to create thread\n", __func__); 501 goto err_out; 502 } 503 504 pthread_set_name_np(crb->thread, "tpm_intf_crb"); 505 506 *sc = crb; 507 508 return (0); 509 510 err_out: 511 free(crb); 512 513 return (error); 514 } 515 516 static void 517 tpm_crb_deinit(void *sc) 518 { 519 struct tpm_crb *crb; 520 int error; 521 522 if (sc == NULL) { 523 return; 524 } 525 526 crb = sc; 527 528 crb->closing = true; 529 pthread_cond_signal(&crb->cond); 530 pthread_join(crb->thread, NULL); 531 532 pthread_cond_destroy(&crb->cond); 533 pthread_mutex_destroy(&crb->mutex); 534 535 error = tpm_crb_modify_mmio_registration(false, NULL); 536 assert(error == 0); 537 538 free(crb); 539 } 540 541 static int 542 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx) 543 { 544 struct basl_table *table; 545 546 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2, 547 BASL_TABLE_ALIGNMENT)); 548 549 /* Header */ 550 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1)); 551 /* Platform Class */ 552 BASL_EXEC(basl_table_append_int(table, 0, 2)); 553 /* Reserved */ 554 BASL_EXEC(basl_table_append_int(table, 0, 2)); 555 /* Control Address */ 556 BASL_EXEC( 557 basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8)); 558 /* Start Method == (7) Command Response Buffer */ 559 BASL_EXEC(basl_table_append_int(table, 7, 4)); 560 /* Start Method Specific Parameters */ 561 uint8_t parameters[12] = { 0 }; 562 BASL_EXEC(basl_table_append_bytes(table, parameters, 12)); 563 /* Log Area Minimum Length */ 564 BASL_EXEC( 565 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4)); 566 /* Log Area Start Address */ 567 BASL_EXEC( 568 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); 569 570 BASL_EXEC(basl_table_register_to_rsdt(table)); 571 572 return (0); 573 } 574 575 static struct tpm_intf tpm_intf_crb = { 576 .name = TPM_CRB_INTF_NAME, 577 .init = tpm_crb_init, 578 .deinit = tpm_crb_deinit, 579 .build_acpi_table = tpm_crb_build_acpi_table, 580 }; 581 TPM_INTF_SET(tpm_intf_crb); 582