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/types.h> 9 #include <sys/param.h> 10 #include <sys/linker_set.h> 11 12 #include <machine/vmm.h> 13 14 #include <assert.h> 15 #include <err.h> 16 #include <errno.h> 17 #include <pthread.h> 18 #include <pthread_np.h> 19 #include <stddef.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <vmmapi.h> 23 24 #include "basl.h" 25 #include "config.h" 26 #include "mem.h" 27 #include "qemu_fwcfg.h" 28 #include "tpm_device.h" 29 #include "tpm_intf.h" 30 31 #define TPM_CRB_ADDRESS 0xFED40000 32 #define TPM_CRB_REGS_SIZE 0x1000 33 34 #define TPM_CRB_CONTROL_AREA_ADDRESS \ 35 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req)) 36 #define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE 37 38 #define TPM_CRB_DATA_BUFFER_ADDRESS \ 39 (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer)) 40 #define TPM_CRB_DATA_BUFFER_SIZE 0xF80 41 42 #define TPM_CRB_LOCALITIES_MAX 5 43 44 #define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024) 45 46 #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log" 47 48 #define TPM_CRB_INTF_NAME "crb" 49 50 struct tpm_crb_regs { 51 union tpm_crb_reg_loc_state { 52 struct { 53 uint32_t tpm_established : 1; 54 uint32_t loc_assigned : 1; 55 uint32_t active_locality : 3; 56 uint32_t _reserved : 2; 57 uint32_t tpm_req_valid_sts : 1; 58 }; 59 uint32_t val; 60 } loc_state; /* 0h */ 61 uint8_t _reserved1[4]; /* 4h */ 62 union tpm_crb_reg_loc_ctrl { 63 struct { 64 uint32_t request_access : 1; 65 uint32_t relinquish : 1; 66 uint32_t seize : 1; 67 uint32_t reset_establishment_bit : 1; 68 }; 69 uint32_t val; 70 } loc_ctrl; /* 8h */ 71 union tpm_crb_reg_loc_sts { 72 struct { 73 uint32_t granted : 1; 74 uint32_t been_seized : 1; 75 }; 76 uint32_t val; 77 } loc_sts; /* Ch */ 78 uint8_t _reserved2[0x20]; /* 10h */ 79 union tpm_crb_reg_intf_id { 80 struct { 81 uint64_t interface_type : 4; 82 uint64_t interface_version : 4; 83 uint64_t cap_locality : 1; 84 uint64_t cap_crb_idle_bypass : 1; 85 uint64_t _reserved1 : 1; 86 uint64_t cap_data_xfer_size_support : 2; 87 uint64_t cap_fifo : 1; 88 uint64_t cap_crb : 1; 89 uint64_t _reserved2 : 2; 90 uint64_t interface_selector : 2; 91 uint64_t intf_sel_lock : 1; 92 uint64_t _reserved3 : 4; 93 uint64_t rid : 8; 94 uint64_t vid : 16; 95 uint64_t did : 16; 96 }; 97 uint64_t val; 98 } intf_id; /* 30h */ 99 union tpm_crb_reg_ctrl_ext { 100 struct { 101 uint32_t clear; 102 uint32_t remaining_bytes; 103 }; 104 uint64_t val; 105 } ctrl_ext; /* 38 */ 106 union tpm_crb_reg_ctrl_req { 107 struct { 108 uint32_t cmd_ready : 1; 109 uint32_t go_idle : 1; 110 }; 111 uint32_t val; 112 } ctrl_req; /* 40h */ 113 union tpm_crb_reg_ctrl_sts { 114 struct { 115 uint32_t tpm_sts : 1; 116 uint32_t tpm_idle : 1; 117 }; 118 uint32_t val; 119 } ctrl_sts; /* 44h */ 120 union tpm_crb_reg_ctrl_cancel { 121 struct { 122 uint32_t cancel : 1; 123 }; 124 uint32_t val; 125 } ctrl_cancel; /* 48h */ 126 union tpm_crb_reg_ctrl_start { 127 struct { 128 uint32_t start : 1; 129 }; 130 uint32_t val; 131 } ctrl_start; /* 4Ch*/ 132 uint32_t int_enable; /* 50h */ 133 uint32_t int_sts; /* 54h */ 134 uint32_t cmd_size; /* 58h */ 135 uint32_t cmd_addr_lo; /* 5Ch */ 136 uint32_t cmd_addr_hi; /* 60h */ 137 uint32_t rsp_size; /* 64h */ 138 uint64_t rsp_addr; /* 68h */ 139 uint8_t _reserved3[0x10]; /* 70h */ 140 uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */ 141 } __packed; 142 static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE, 143 "Invalid size of tpm_crb"); 144 145 #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size) 146 #define CRB_CMD_SIZE_WRITE(regs, val) \ 147 do { \ 148 regs.cmd_size = val; \ 149 } while (0) 150 #define CRB_CMD_ADDR_READ(regs) \ 151 (((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo) 152 #define CRB_CMD_ADDR_WRITE(regs, val) \ 153 do { \ 154 regs.cmd_addr_lo = val & 0xFFFFFFFF; \ 155 regs.cmd_addr_hi = val >> 32; \ 156 } while (0) 157 #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size) 158 #define CRB_RSP_SIZE_WRITE(regs, val) \ 159 do { \ 160 regs.rsp_size = val; \ 161 } while (0) 162 #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr) 163 #define CRB_RSP_ADDR_WRITE(regs, val) \ 164 do { \ 165 regs.rsp_addr = val; \ 166 } while (0) 167 168 struct tpm_crb { 169 struct tpm_emul *emul; 170 void *emul_sc; 171 uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE]; 172 struct tpm_crb_regs regs; 173 pthread_t thread; 174 pthread_mutex_t mutex; 175 pthread_cond_t cond; 176 bool closing; 177 }; 178 179 static void * 180 tpm_crb_thread(void *const arg) 181 { 182 struct tpm_crb *const crb = arg; 183 184 pthread_mutex_lock(&crb->mutex); 185 for (;;) { 186 /* 187 * We're releasing the lock after wake up. Therefore, we have to 188 * check the closing condition before and after going to sleep. 189 */ 190 if (crb->closing) 191 break; 192 193 pthread_cond_wait(&crb->cond, &crb->mutex); 194 195 if (crb->closing) 196 break; 197 198 const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs); 199 const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs); 200 const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs); 201 const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs); 202 203 const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 204 const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS; 205 206 if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE || 207 cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE || 208 rsp_off > TPM_CRB_DATA_BUFFER_SIZE || 209 rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) { 210 warnx( 211 "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r", 212 __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr, 213 rsp_addr + rsp_size); 214 break; 215 } 216 217 uint8_t cmd[TPM_CRB_DATA_BUFFER_SIZE]; 218 memcpy(cmd, crb->regs.data_buffer, TPM_CRB_DATA_BUFFER_SIZE); 219 220 /* 221 * A TPM command can take multiple seconds to execute. As we've 222 * copied all required values and buffers at this point, we can 223 * release the mutex. 224 */ 225 pthread_mutex_unlock(&crb->mutex); 226 227 /* 228 * The command response buffer interface uses a single buffer 229 * for sending a command to and receiving a response from the 230 * tpm. To avoid reading old data from the command buffer which 231 * might be a security issue, we zero out the command buffer 232 * before writing the response into it. The rsp_size parameter 233 * is controlled by the guest and it's not guaranteed that the 234 * response has a size of rsp_size (e.g. if the tpm returned an 235 * error, the response would have a different size than 236 * expected). For that reason, use a second buffer for the 237 * response. 238 */ 239 uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 }; 240 crb->emul->execute_cmd(crb->emul_sc, &cmd[cmd_off], cmd_size, 241 &rsp[rsp_off], rsp_size); 242 243 pthread_mutex_lock(&crb->mutex); 244 memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE); 245 memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size); 246 247 crb->regs.ctrl_start.start = false; 248 } 249 pthread_mutex_unlock(&crb->mutex); 250 251 return (NULL); 252 } 253 254 static int 255 tpm_crb_mmiocpy(void *const dst, void *const src, const int size) 256 { 257 if (!(size == 1 || size == 2 || size == 4 || size == 8)) 258 return (EINVAL); 259 memcpy(dst, src, size); 260 261 return (0); 262 } 263 264 static int 265 tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir, 266 const uint64_t addr, const int size, uint64_t *const val, void *const arg1, 267 const long arg2 __unused) 268 { 269 struct tpm_crb *crb; 270 uint8_t *ptr; 271 uint64_t off, shift; 272 int error = 0; 273 274 if ((addr & (size - 1)) != 0) { 275 warnx("%s: unaligned %s access @ %16lx [size = %x]", __func__, 276 (dir == MEM_F_READ) ? "read" : "write", addr, size); 277 return (EINVAL); 278 } 279 280 crb = arg1; 281 282 off = addr - TPM_CRB_ADDRESS; 283 if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) { 284 return (EINVAL); 285 } 286 287 shift = 8 * (off & 3); 288 ptr = (uint8_t *)&crb->regs + off; 289 290 if (dir == MEM_F_READ) { 291 error = tpm_crb_mmiocpy(val, ptr, size); 292 if (error) 293 goto err_out; 294 } else { 295 switch (off & ~0x3) { 296 case offsetof(struct tpm_crb_regs, loc_ctrl): { 297 union tpm_crb_reg_loc_ctrl loc_ctrl; 298 299 if ((size_t)size > sizeof(loc_ctrl)) 300 goto err_out; 301 302 *val = *val << shift; 303 tpm_crb_mmiocpy(&loc_ctrl, val, size); 304 305 if (loc_ctrl.relinquish) { 306 crb->regs.loc_sts.granted = false; 307 crb->regs.loc_state.loc_assigned = false; 308 } else if (loc_ctrl.request_access) { 309 crb->regs.loc_sts.granted = true; 310 crb->regs.loc_state.loc_assigned = true; 311 } 312 313 break; 314 } 315 case offsetof(struct tpm_crb_regs, ctrl_req): { 316 union tpm_crb_reg_ctrl_req req; 317 318 if ((size_t)size > sizeof(req)) 319 goto err_out; 320 321 *val = *val << shift; 322 tpm_crb_mmiocpy(&req, val, size); 323 324 if (req.cmd_ready && !req.go_idle) { 325 crb->regs.ctrl_sts.tpm_idle = false; 326 } else if (!req.cmd_ready && req.go_idle) { 327 crb->regs.ctrl_sts.tpm_idle = true; 328 } 329 330 break; 331 } 332 case offsetof(struct tpm_crb_regs, ctrl_cancel): { 333 /* TODO: cancel the tpm command */ 334 warnx( 335 "%s: cancelling a TPM command is not implemented yet", 336 __func__); 337 338 break; 339 } 340 case offsetof(struct tpm_crb_regs, ctrl_start): { 341 union tpm_crb_reg_ctrl_start start; 342 343 if ((size_t)size > sizeof(start)) 344 goto err_out; 345 346 *val = *val << shift; 347 348 pthread_mutex_lock(&crb->mutex); 349 tpm_crb_mmiocpy(&start, val, size); 350 351 if (!start.start || crb->regs.ctrl_start.start) { 352 pthread_mutex_unlock(&crb->mutex); 353 break; 354 } 355 356 crb->regs.ctrl_start.start = true; 357 358 pthread_cond_signal(&crb->cond); 359 pthread_mutex_unlock(&crb->mutex); 360 361 break; 362 } 363 case offsetof(struct tpm_crb_regs, cmd_size): 364 case offsetof(struct tpm_crb_regs, cmd_addr_lo): 365 case offsetof(struct tpm_crb_regs, cmd_addr_hi): 366 case offsetof(struct tpm_crb_regs, rsp_size): 367 case offsetof(struct tpm_crb_regs, 368 rsp_addr) ... offsetof(struct tpm_crb_regs, rsp_addr) + 369 4: 370 case offsetof(struct tpm_crb_regs, 371 data_buffer) ... offsetof(struct tpm_crb_regs, data_buffer) + 372 TPM_CRB_DATA_BUFFER_SIZE / 4: 373 /* 374 * Those fields are used to execute a TPM command. The 375 * crb_thread will access them. For that reason, we have 376 * to acquire the crb mutex in order to write them. 377 */ 378 pthread_mutex_lock(&crb->mutex); 379 error = tpm_crb_mmiocpy(ptr, val, size); 380 pthread_mutex_unlock(&crb->mutex); 381 if (error) 382 goto err_out; 383 break; 384 default: 385 /* 386 * The other fields are either readonly or we do not 387 * support writing them. 388 */ 389 error = EINVAL; 390 goto err_out; 391 } 392 } 393 394 return (0); 395 396 err_out: 397 warnx("%s: invalid %s @ %16lx [size = %d]", __func__, 398 dir == MEM_F_READ ? "read" : "write", addr, size); 399 400 return (error); 401 } 402 403 static int 404 tpm_crb_modify_mmio_registration(const bool registration, void *const arg1) 405 { 406 struct mem_range crb_mmio = { 407 .name = "crb-mmio", 408 .base = TPM_CRB_ADDRESS, 409 .size = TPM_CRB_LOCALITIES_MAX * TPM_CRB_CONTROL_AREA_SIZE, 410 .flags = MEM_F_RW, 411 .arg1 = arg1, 412 .handler = tpm_crb_mem_handler, 413 }; 414 415 if (registration) 416 return (register_mem(&crb_mmio)); 417 else 418 return (unregister_mem(&crb_mmio)); 419 } 420 421 static int 422 tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc, 423 struct acpi_device *acpi_dev) 424 { 425 struct tpm_crb *crb = NULL; 426 int error; 427 428 assert(sc != NULL); 429 assert(emul != NULL); 430 431 crb = calloc(1, sizeof(struct tpm_crb)); 432 if (crb == NULL) { 433 warnx("%s: failed to allocate tpm crb", __func__); 434 error = ENOMEM; 435 goto err_out; 436 } 437 438 memset(crb, 0, sizeof(*crb)); 439 440 crb->emul = emul; 441 crb->emul_sc = emul_sc; 442 443 crb->regs.loc_state.tpm_req_valid_sts = true; 444 crb->regs.loc_state.tpm_established = true; 445 446 crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB; 447 crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB; 448 crb->regs.intf_id.cap_locality = false; 449 crb->regs.intf_id.cap_crb_idle_bypass = false; 450 crb->regs.intf_id.cap_data_xfer_size_support = 451 TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64; 452 crb->regs.intf_id.cap_fifo = false; 453 crb->regs.intf_id.cap_crb = true; 454 crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB; 455 crb->regs.intf_id.intf_sel_lock = false; 456 crb->regs.intf_id.rid = 0; 457 crb->regs.intf_id.vid = 0x1014; /* IBM */ 458 crb->regs.intf_id.did = 0x1014; /* IBM */ 459 460 crb->regs.ctrl_sts.tpm_idle = true; 461 462 CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 463 CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 464 CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); 465 CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); 466 467 error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME, 468 TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area); 469 if (error) { 470 warnx("%s: failed to add fwcfg file", __func__); 471 goto err_out; 472 } 473 474 error = acpi_device_add_res_fixed_memory32(acpi_dev, false, 475 TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE); 476 if (error) { 477 warnx("%s: failed to add acpi resources\n", __func__); 478 goto err_out; 479 } 480 481 error = tpm_crb_modify_mmio_registration(true, crb); 482 if (error) { 483 warnx("%s: failed to register crb mmio", __func__); 484 goto err_out; 485 } 486 487 error = pthread_mutex_init(&crb->mutex, NULL); 488 if (error) { 489 warnc(error, "%s: failed to init mutex", __func__); 490 goto err_out; 491 } 492 493 error = pthread_cond_init(&crb->cond, NULL); 494 if (error) { 495 warnc(error, "%s: failed to init cond", __func__); 496 goto err_out; 497 } 498 499 error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb); 500 if (error) { 501 warnx("%s: failed to create thread\n", __func__); 502 goto err_out; 503 } 504 505 pthread_set_name_np(crb->thread, "tpm_intf_crb"); 506 507 *sc = crb; 508 509 return (0); 510 511 err_out: 512 free(crb); 513 514 return (error); 515 } 516 517 static void 518 tpm_crb_deinit(void *sc) 519 { 520 struct tpm_crb *crb; 521 int error; 522 523 if (sc == NULL) { 524 return; 525 } 526 527 crb = sc; 528 529 crb->closing = true; 530 pthread_cond_signal(&crb->cond); 531 pthread_join(crb->thread, NULL); 532 533 pthread_cond_destroy(&crb->cond); 534 pthread_mutex_destroy(&crb->mutex); 535 536 error = tpm_crb_modify_mmio_registration(false, NULL); 537 assert(error == 0); 538 539 free(crb); 540 } 541 542 static int 543 tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx) 544 { 545 struct basl_table *table; 546 547 BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2, 548 BASL_TABLE_ALIGNMENT)); 549 550 /* Header */ 551 BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1)); 552 /* Platform Class */ 553 BASL_EXEC(basl_table_append_int(table, 0, 2)); 554 /* Reserved */ 555 BASL_EXEC(basl_table_append_int(table, 0, 2)); 556 /* Control Address */ 557 BASL_EXEC( 558 basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8)); 559 /* Start Method == (7) Command Response Buffer */ 560 BASL_EXEC(basl_table_append_int(table, 7, 4)); 561 /* Start Method Specific Parameters */ 562 uint8_t parameters[12] = { 0 }; 563 BASL_EXEC(basl_table_append_bytes(table, parameters, 12)); 564 /* Log Area Minimum Length */ 565 BASL_EXEC( 566 basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4)); 567 /* Log Area Start Address */ 568 #ifdef __FreeBSD__ 569 BASL_EXEC( 570 basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); 571 #else 572 BASL_EXEC( 573 basl_table_append_fwcfg(table, 574 (const uint8_t *)TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); 575 #endif 576 577 BASL_EXEC(basl_table_register_to_rsdt(table)); 578 579 return (0); 580 } 581 582 static struct tpm_intf tpm_intf_crb = { 583 .name = TPM_CRB_INTF_NAME, 584 .init = tpm_crb_init, 585 .deinit = tpm_crb_deinit, 586 .build_acpi_table = tpm_crb_build_acpi_table, 587 }; 588 TPM_INTF_SET(tpm_intf_crb); 589