diff --git a/usr.sbin/bhyve/tpm_device.h b/usr.sbin/bhyve/tpm_device.h --- a/usr.sbin/bhyve/tpm_device.h +++ b/usr.sbin/bhyve/tpm_device.h @@ -9,6 +9,7 @@ #include +#include "acpi_device.h" #include "config.h" struct tpm_device; 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,8 @@ } if (dev->intf->init) { - error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc); + error = dev->intf->init(&dev->intf_sc, dev->emul, dev->emul_sc, + dev->acpi_dev); if (error) goto err_out; } 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 @@ -9,6 +9,7 @@ #include +#include "acpi_device.h" #include "config.h" #include "tpm_device.h" #include "tpm_emul.h" @@ -31,7 +32,8 @@ struct tpm_intf { const char *name; - int (*init)(void **sc, struct tpm_emul *emul, void *emul_sc); + int (*init)(void **sc, struct tpm_emul *emul, void *emul_sc, + struct acpi_device *acpi_dev); 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 @@ -174,6 +174,7 @@ pthread_t thread; pthread_mutex_t mutex; pthread_cond_t cond; + pthread_mutex_t rw_mutex; bool closing; }; @@ -236,9 +237,141 @@ } static int -tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc) +tpm_crb_mem_handler(struct vcpu *vcpu __unused, const int dir, + const uint64_t addr, const int size, uint64_t *const val, void *const arg1, + const long arg2 __unused) +{ + if ((addr & (size - 1)) != 0) { + warnx("%s: unaligned %s access @ %16lx [size = %x]\n", __func__, + (dir == MEM_F_READ) ? "read" : "write", addr, size); + } + + struct tpm_crb *const crb = arg1; + + const uint64_t off = addr - TPM_CRB_ADDRESS; + const uint64_t reg = off & ~3; + const uint64_t reg_off = off & 3; + uint8_t *const ptr = (uint8_t *)&crb->regs + off; + + if (off > TPM_CRB_REGS_SIZE || off + size >= TPM_CRB_REGS_SIZE) { + return (EINVAL); + } + + pthread_mutex_lock(&crb->rw_mutex); + if (dir == MEM_F_READ) { + switch (size) { + case 1: + *val = *(uint8_t *)ptr; + break; + case 2: + *val = *(uint16_t *)ptr; + break; + case 4: + *val = *(uint32_t *)ptr; + break; + case 8: + *val = *(uint64_t *)ptr; + break; + default: + warnx("%s: invalid read size %d @ %16lx\n", __func__, + size, addr); + return (EINVAL); + } + } else { + switch (reg) { + case offsetof(struct tpm_crb_regs, loc_ctrl): { + const union tpm_crb_reg_loc_ctrl loc_ctrl = { + .val = *val << (8 * reg_off), + }; + + if (loc_ctrl.relinquish) { + crb->regs.loc_sts.granted = false; + crb->regs.loc_state.loc_assigned = false; + } else if (loc_ctrl.request_access) { + crb->regs.loc_sts.granted = true; + crb->regs.loc_state.loc_assigned = true; + } + + break; + } + case offsetof(struct tpm_crb_regs, ctrl_req): { + const union tpm_crb_reg_ctrl_req req = { + .val = *val << (8 * reg_off), + }; + + if (req.cmd_ready && !req.go_idle) { + crb->regs.ctrl_sts.tpm_idle = false; + } else if (!req.cmd_ready && req.go_idle) { + crb->regs.ctrl_sts.tpm_idle = true; + } + + break; + } + case offsetof(struct tpm_crb_regs, ctrl_cancel): { + /* TODO: cancel the tpm command */ + warnx( + "%s: cancelling a TPM command is not implemented yet\n", + __func__); + + break; + } + case offsetof(struct tpm_crb_regs, ctrl_start): { + const union tpm_crb_reg_ctrl_start start = { + .val = *val << (8 * reg_off), + }; + + if (!start.start || crb->regs.ctrl_start.start) + break; + + crb->regs.ctrl_start.start = true; + + pthread_cond_signal(&crb->cond); + + break; + } + case offsetof(struct tpm_crb_regs, loc_state): + case offsetof(struct tpm_crb_regs, loc_sts): + case offsetof(struct tpm_crb_regs, intf_id): + case offsetof(struct tpm_crb_regs, ctrl_ext): + case offsetof(struct tpm_crb_regs, ctrl_sts): + case offsetof(struct tpm_crb_regs, int_enable): + case offsetof(struct tpm_crb_regs, int_sts): + /* ignore writes */ + break; + default: + switch (size) { + case 1: + *(uint8_t *)ptr = *val; + break; + case 2: + *(uint16_t *)ptr = *val; + break; + case 4: + *(uint32_t *)ptr = *val; + break; + case 8: + *(uint64_t *)ptr = *val; + break; + default: + warnx("%s: invalid write size %d @ %16lx\n", + __func__, size, addr); + return (EINVAL); + } + break; + } + } + pthread_mutex_unlock(&crb->rw_mutex); + + return (0); +} + +static int +tpm_crb_init(void **sc, struct tpm_emul *emul, void *emul_sc, + struct acpi_device *acpi_dev) { struct tpm_crb *crb = NULL; + struct mem_range mr; + char mr_name[NAME_MAX]; int error; assert(sc != NULL); @@ -287,6 +420,28 @@ goto err_out; } + error = acpi_device_add_res_fixed_memory32(acpi_dev, false, + TPM_CRB_ADDRESS, TPM_CRB_CONTROL_AREA_SIZE); + if (error) { + warnx("%s: failed to add acpi resources\n", __func__); + goto err_out; + } + + /* trap accesses to crb */ + bzero(&mr, sizeof(struct mem_range)); + snprintf(mr_name, sizeof(mr_name), "tpm_intf_%s", TPM_CRB_INTF_NAME); + mr.name = mr_name; + mr.base = TPM_CRB_ADDRESS; + mr.size = TPM_CRB_CONTROL_AREA_SIZE; + mr.flags = MEM_F_RW; + mr.handler = tpm_crb_mem_handler; + mr.arg1 = crb; + error = register_mem(&mr); + if (error) { + warnx("%s: failed to create trap for crb accesses\n", __func__); + return (error); + } + error = pthread_mutex_init(&crb->mutex, NULL); if (error) { warnx("%s: failed to init mutex\n", __func__); @@ -310,6 +465,12 @@ TPM_CRB_INTF_NAME); pthread_set_name_np(crb->thread, thread_name); + error = pthread_mutex_init(&crb->rw_mutex, NULL); + if (error) { + warnx("%s: failed to init rw mutex\n", __func__); + goto err_out; + } + *sc = crb; return (0);