diff --git a/usr.sbin/bhyve/tpm_device.c b/usr.sbin/bhyve/tpm_device.c --- a/usr.sbin/bhyve/tpm_device.c +++ b/usr.sbin/bhyve/tpm_device.c @@ -142,7 +142,7 @@ } if (dev->intf->init) { - error = dev->intf->init(&dev->intf_sc); + error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc); if (error) goto err_out; } diff --git a/usr.sbin/bhyve/tpm_emul.h b/usr.sbin/bhyve/tpm_emul.h --- a/usr.sbin/bhyve/tpm_emul.h +++ b/usr.sbin/bhyve/tpm_emul.h @@ -18,5 +18,7 @@ int (*init)(void **sc, nvlist_t *nvl); void (*deinit)(void *sc); + int (*execute_cmd)(void *sc, void *cmd, uint32_t cmd_size, void *rsp, + uint32_t rsp_size); }; #define TPM_EMUL_SET(x) DATA_SET(tpm_emul_set, x) diff --git a/usr.sbin/bhyve/tpm_intf.h b/usr.sbin/bhyve/tpm_intf.h --- a/usr.sbin/bhyve/tpm_intf.h +++ b/usr.sbin/bhyve/tpm_intf.h @@ -11,6 +11,7 @@ #include "config.h" #include "tpm_device.h" +#include "tpm_emul.h" #define TPM_INTF_TYPE_FIFO_PTP 0x0 #define TPM_INTF_TYPE_CRB 0x1 @@ -30,7 +31,7 @@ struct tpm_intf { const char *name; - int (*init)(void **sc); + int (*init)(void **sc, struct tpm_emul *emul, void *emul_sc); void (*deinit)(void *sc); int (*build_acpi_table)(void *sc, struct vmctx *vm_ctx); }; diff --git a/usr.sbin/bhyve/tpm_intf_crb.c b/usr.sbin/bhyve/tpm_intf_crb.c --- a/usr.sbin/bhyve/tpm_intf_crb.c +++ b/usr.sbin/bhyve/tpm_intf_crb.c @@ -26,6 +26,7 @@ #include "config.h" #include "mem.h" #include "qemu_fwcfg.h" +#include "tpm_device.h" #include "tpm_intf.h" #define TPM_CRB_ADDRESS 0xFED40000 @@ -45,6 +46,8 @@ #define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log" +#define TPM_CRB_INTF_NAME "crb" + struct tpm_crb_regs { union tpm_crb_reg_loc_state { struct { @@ -164,17 +167,82 @@ } while (0) struct tpm_crb { + struct tpm_emul *emul; + void *emul_sc; uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE]; struct tpm_crb_regs regs; + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + bool closing; }; +static void * +tpm_crb_thread(void *const arg) +{ + struct tpm_crb *const crb = arg; + + pthread_mutex_lock(&crb->mutex); + for (;;) { + pthread_cond_wait(&crb->cond, &crb->mutex); + + if (crb->closing) + break; + + const uint64_t cmd_addr = CRB_CMD_ADDR_READ(crb->regs); + const uint64_t rsp_addr = CRB_RSP_ADDR_READ(crb->regs); + const uint32_t cmd_size = CRB_CMD_SIZE_READ(crb->regs); + const uint32_t rsp_size = CRB_RSP_SIZE_READ(crb->regs); + + const uint64_t cmd_off = cmd_addr - TPM_CRB_DATA_BUFFER_ADDRESS; + const uint64_t rsp_off = rsp_addr - TPM_CRB_DATA_BUFFER_ADDRESS; + + if (cmd_off > TPM_CRB_DATA_BUFFER_SIZE || + cmd_off + cmd_size > TPM_CRB_DATA_BUFFER_SIZE || + rsp_off > TPM_CRB_DATA_BUFFER_SIZE || + rsp_off + rsp_size > TPM_CRB_DATA_BUFFER_SIZE) { + warnx( + "%s: invalid cmd [%16lx, %16lx] --> [%16lx, %16lx]\n\r", + __func__, cmd_addr, cmd_addr + cmd_size, rsp_addr, + rsp_addr + rsp_size); + break; + } + + /* + * The command response buffer interface uses a single buffer + * for sending a command to and receiving a response from the + * tpm. To avoid reading old data from the command buffer which + * might be a security issue, we zero out the command buffer + * before writing the response into it. The rsp_size parameter + * is controlled by the guest and it's not guaranteed that the + * response has a size of rsp_size (e.g. if the tpm returned an + * error, the response would have a different size than + * expected). For that reason, use a second buffer for the + * response. + */ + uint8_t rsp[TPM_CRB_DATA_BUFFER_SIZE] = { 0 }; + crb->emul->execute_cmd(crb->emul_sc, + &crb->regs.data_buffer[cmd_off], cmd_size, &rsp[rsp_off], + rsp_size); + + memset(crb->regs.data_buffer, 0, TPM_CRB_DATA_BUFFER_SIZE); + memcpy(&crb->regs.data_buffer[rsp_off], &rsp[rsp_off], rsp_size); + + crb->regs.ctrl_start.start = false; + } + pthread_mutex_unlock(&crb->mutex); + + return (NULL); +} + static int -tpm_crb_init(void **sc) +tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc) { struct tpm_crb *crb = NULL; int error; assert(sc != NULL); + assert(emul != NULL); crb = calloc(1, sizeof(struct tpm_crb)); if (crb == NULL) { @@ -185,6 +253,9 @@ memset(crb, 0, sizeof(*crb)); + crb->emul = emul; + crb->emul_sc = emul_sc; + crb->regs.loc_state.tpm_req_valid_sts = true; crb->regs.loc_state.tpm_established = true; @@ -216,6 +287,29 @@ goto err_out; } + error = pthread_mutex_init(&crb->mutex, NULL); + if (error) { + warnx("%s: failed to init mutex\n", __func__); + goto err_out; + } + + error = pthread_cond_init(&crb->cond, NULL); + if (error) { + warnx("%s: failed to init cond\n", __func__); + goto err_out; + } + + error = pthread_create(&crb->thread, NULL, tpm_crb_thread, crb); + if (error) { + warnx("%s: failed to create thread\n", __func__); + goto err_out; + } + + char thread_name[NAME_MAX]; + snprintf(thread_name, sizeof(thread_name), "tpm_intf_%s", + TPM_CRB_INTF_NAME); + pthread_set_name_np(crb->thread, thread_name); + *sc = crb; return (0); @@ -237,6 +331,9 @@ crb = sc; + crb->closing = true; + pthread_join(crb->thread, NULL); + free(crb); } @@ -277,7 +374,7 @@ } static struct tpm_intf tpm_intf_crb = { - .name = "crb", + .name = TPM_CRB_INTF_NAME, .init = tpm_crb_init, .deinit = tpm_crb_deinit, .build_acpi_table = tpm_crb_build_acpi_table,