diff --git a/usr.sbin/bhyve/tpm_device.c b/usr.sbin/bhyve/tpm_device.c index e1db0bab1ee9..94e9f1ffdc27 100644 --- a/usr.sbin/bhyve/tpm_device.c +++ b/usr.sbin/bhyve/tpm_device.c @@ -1,144 +1,157 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2023 Beckhoff Automation GmbH & Co. KG * Author: Corvin Köhne */ #include #include #include #include #include #include #include #include "acpi_device.h" #include "config.h" #include "tpm_device.h" #include "tpm_emul.h" #include "tpm_intf.h" #define TPM_ACPI_DEVICE_NAME "TPM" #define TPM_ACPI_HARDWARE_ID "MSFT0101" SET_DECLARE(tpm_emul_set, struct tpm_emul); SET_DECLARE(tpm_intf_set, struct tpm_intf); struct tpm_device { struct vmctx *vm_ctx; struct acpi_device *acpi_dev; struct tpm_emul *emul; void *emul_sc; struct tpm_intf *intf; void *intf_sc; }; +static int +tpm_build_acpi_table(const struct acpi_device *const dev) +{ + const struct tpm_device *const tpm = acpi_device_get_softc(dev); + + if (tpm->intf->build_acpi_table == NULL) { + return (0); + } + + return (tpm->intf->build_acpi_table(tpm->intf_sc, tpm->vm_ctx)); +} + static const struct acpi_device_emul tpm_acpi_device_emul = { .name = TPM_ACPI_DEVICE_NAME, .hid = TPM_ACPI_HARDWARE_ID, + .build_table = tpm_build_acpi_table, }; void tpm_device_destroy(struct tpm_device *const dev) { if (dev == NULL) return; if (dev->intf != NULL && dev->intf->deinit != NULL) dev->intf->deinit(dev->intf_sc); if (dev->emul != NULL && dev->emul->deinit != NULL) dev->emul->deinit(dev->emul_sc); acpi_device_destroy(dev->acpi_dev); free(dev); } int tpm_device_create(struct tpm_device **const new_dev, struct vmctx *const vm_ctx, nvlist_t *const nvl) { struct tpm_device *dev = NULL; struct tpm_emul **ppemul; struct tpm_intf **ppintf; const char *value; int error; if (new_dev == NULL || vm_ctx == NULL) { error = EINVAL; goto err_out; } set_config_value_node_if_unset(nvl, "intf", "crb"); value = get_config_value_node(nvl, "version"); assert(value != NULL); if (strcmp(value, "2.0")) { warnx("%s: unsupported tpm version %s", __func__, value); error = EINVAL; goto err_out; } dev = calloc(1, sizeof(*dev)); if (dev == NULL) { error = ENOMEM; goto err_out; } dev->vm_ctx = vm_ctx; error = acpi_device_create(&dev->acpi_dev, dev, dev->vm_ctx, &tpm_acpi_device_emul); if (error) goto err_out; value = get_config_value_node(nvl, "type"); assert(value != NULL); SET_FOREACH(ppemul, tpm_emul_set) { if (strcmp(value, (*ppemul)->name)) continue; dev->emul = *ppemul; break; } if (dev->emul == NULL) { warnx("TPM emulation \"%s\" not found", value); error = EINVAL; goto err_out; } if (dev->emul->init) { error = dev->emul->init(&dev->emul_sc, nvl); if (error) goto err_out; } value = get_config_value_node(nvl, "intf"); SET_FOREACH(ppintf, tpm_intf_set) { if (strcmp(value, (*ppintf)->name)) { continue; } dev->intf = *ppintf; break; } if (dev->intf == NULL) { warnx("TPM interface \"%s\" not found", value); error = EINVAL; goto err_out; } if (dev->intf->init) { error = dev->intf->init(&dev->intf_sc); if (error) goto err_out; } *new_dev = dev; return (0); err_out: tpm_device_destroy(dev); return (error); } diff --git a/usr.sbin/bhyve/tpm_intf.h b/usr.sbin/bhyve/tpm_intf.h index 7de7bc6d4435..3003d8fbd754 100644 --- a/usr.sbin/bhyve/tpm_intf.h +++ b/usr.sbin/bhyve/tpm_intf.h @@ -1,35 +1,37 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG * Author: Corvin Köhne */ #pragma once +#include + #include "config.h" #include "tpm_device.h" #define TPM_INTF_TYPE_FIFO_PTP 0x0 #define TPM_INTF_TYPE_CRB 0x1 #define TPM_INTF_TYPE_FIFO_TIS 0xF #define TPM_INTF_VERSION_FIFO 0 #define TPM_INTF_VERSION_CRB 1 #define TPM_INTF_CAP_CRB_DATA_XFER_SIZE_4 0 #define TPM_INTF_CAP_CRB_DATA_XFER_SIZE_8 1 #define TPM_INTF_CAP_CRB_DATA_XFER_SIZE_32 2 #define TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64 3 #define TPM_INTF_SELECTOR_FIFO 0 #define TPM_INTF_SELECTOR_CRB 1 struct tpm_intf { const char *name; int (*init)(void **sc); void (*deinit)(void *sc); - int (*build_acpi_table)(void *sc); + int (*build_acpi_table)(void *sc, struct vmctx *vm_ctx); }; #define TPM_INTF_SET(x) DATA_SET(tpm_intf_set, x) diff --git a/usr.sbin/bhyve/tpm_intf_crb.c b/usr.sbin/bhyve/tpm_intf_crb.c index 5fd640b2d5c9..b8ae33c5ec0a 100644 --- a/usr.sbin/bhyve/tpm_intf_crb.c +++ b/usr.sbin/bhyve/tpm_intf_crb.c @@ -1,232 +1,283 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2022 Beckhoff Automation GmbH & Co. KG * Author: Corvin Köhne */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "basl.h" #include "config.h" #include "mem.h" #include "qemu_fwcfg.h" #include "tpm_intf.h" #define TPM_CRB_ADDRESS 0xFED40000 #define TPM_CRB_REGS_SIZE 0x1000 +#define TPM_CRB_CONTROL_AREA_ADDRESS \ + (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, ctrl_req)) +#define TPM_CRB_CONTROL_AREA_SIZE TPM_CRB_REGS_SIZE + #define TPM_CRB_DATA_BUFFER_ADDRESS \ (TPM_CRB_ADDRESS + offsetof(struct tpm_crb_regs, data_buffer)) #define TPM_CRB_DATA_BUFFER_SIZE 0xF80 #define TPM_CRB_LOCALITIES_MAX 5 +#define TPM_CRB_LOG_AREA_MINIMUM_SIZE (64 * 1024) + +#define TPM_CRB_LOG_AREA_FWCFG_NAME "etc/tpm/log" + struct tpm_crb_regs { union tpm_crb_reg_loc_state { struct { uint32_t tpm_established : 1; uint32_t loc_assigned : 1; uint32_t active_locality : 3; uint32_t _reserved : 2; uint32_t tpm_req_valid_sts : 1; }; uint32_t val; } loc_state; /* 0h */ uint8_t _reserved1[4]; /* 4h */ union tpm_crb_reg_loc_ctrl { struct { uint32_t request_access : 1; uint32_t relinquish : 1; uint32_t seize : 1; uint32_t reset_establishment_bit : 1; }; uint32_t val; } loc_ctrl; /* 8h */ union tpm_crb_reg_loc_sts { struct { uint32_t granted : 1; uint32_t been_seized : 1; }; uint32_t val; } loc_sts; /* Ch */ uint8_t _reserved2[0x20]; /* 10h */ union tpm_crb_reg_intf_id { struct { uint64_t interface_type : 4; uint64_t interface_version : 4; uint64_t cap_locality : 1; uint64_t cap_crb_idle_bypass : 1; uint64_t _reserved1 : 1; uint64_t cap_data_xfer_size_support : 2; uint64_t cap_fifo : 1; uint64_t cap_crb : 1; uint64_t _reserved2 : 2; uint64_t interface_selector : 2; uint64_t intf_sel_lock : 1; uint64_t _reserved3 : 4; uint64_t rid : 8; uint64_t vid : 16; uint64_t did : 16; }; uint64_t val; } intf_id; /* 30h */ union tpm_crb_reg_ctrl_ext { struct { uint32_t clear; uint32_t remaining_bytes; }; uint64_t val; } ctrl_ext; /* 38 */ union tpm_crb_reg_ctrl_req { struct { uint32_t cmd_ready : 1; uint32_t go_idle : 1; }; uint32_t val; } ctrl_req; /* 40h */ union tpm_crb_reg_ctrl_sts { struct { uint32_t tpm_sts : 1; uint32_t tpm_idle : 1; }; uint32_t val; } ctrl_sts; /* 44h */ union tpm_crb_reg_ctrl_cancel { struct { uint32_t cancel : 1; }; uint32_t val; } ctrl_cancel; /* 48h */ union tpm_crb_reg_ctrl_start { struct { uint32_t start : 1; }; uint32_t val; } ctrl_start; /* 4Ch*/ uint32_t int_enable; /* 50h */ uint32_t int_sts; /* 54h */ uint32_t cmd_size; /* 58h */ uint32_t cmd_addr_lo; /* 5Ch */ uint32_t cmd_addr_hi; /* 60h */ uint32_t rsp_size; /* 64h */ uint64_t rsp_addr; /* 68h */ uint8_t _reserved3[0x10]; /* 70h */ uint8_t data_buffer[TPM_CRB_DATA_BUFFER_SIZE]; /* 80h */ } __packed; static_assert(sizeof(struct tpm_crb_regs) == TPM_CRB_REGS_SIZE, "Invalid size of tpm_crb"); #define CRB_CMD_SIZE_READ(regs) (regs.cmd_size) #define CRB_CMD_SIZE_WRITE(regs, val) \ do { \ regs.cmd_size = val; \ } while (0) #define CRB_CMD_ADDR_READ(regs) \ (((uint64_t)regs.cmd_addr_hi << 32) | regs.cmd_addr_lo) #define CRB_CMD_ADDR_WRITE(regs, val) \ do { \ regs.cmd_addr_lo = val & 0xFFFFFFFF; \ regs.cmd_addr_hi = val >> 32; \ } while (0) #define CRB_RSP_SIZE_READ(regs) (regs.rsp_size) #define CRB_RSP_SIZE_WRITE(regs, val) \ do { \ regs.rsp_size = val; \ } while (0) #define CRB_RSP_ADDR_READ(regs) (regs.rsp_addr) #define CRB_RSP_ADDR_WRITE(regs, val) \ do { \ regs.rsp_addr = val; \ } while (0) struct tpm_crb { + uint8_t tpm_log_area[TPM_CRB_LOG_AREA_MINIMUM_SIZE]; struct tpm_crb_regs regs; }; static int tpm_crb_init(void **sc) { struct tpm_crb *crb = NULL; int error; assert(sc != NULL); crb = calloc(1, sizeof(struct tpm_crb)); if (crb == NULL) { warnx("%s: failed to allocate tpm crb", __func__); error = ENOMEM; goto err_out; } memset(crb, 0, sizeof(*crb)); crb->regs.loc_state.tpm_req_valid_sts = true; crb->regs.loc_state.tpm_established = true; crb->regs.intf_id.interface_type = TPM_INTF_TYPE_CRB; crb->regs.intf_id.interface_version = TPM_INTF_VERSION_CRB; crb->regs.intf_id.cap_locality = false; crb->regs.intf_id.cap_crb_idle_bypass = false; crb->regs.intf_id.cap_data_xfer_size_support = TPM_INTF_CAP_CRB_DATA_XFER_SIZE_64; crb->regs.intf_id.cap_fifo = false; crb->regs.intf_id.cap_crb = true; crb->regs.intf_id.interface_selector = TPM_INTF_SELECTOR_CRB; crb->regs.intf_id.intf_sel_lock = false; crb->regs.intf_id.rid = 0; crb->regs.intf_id.vid = 0x1014; /* IBM */ crb->regs.intf_id.did = 0x1014; /* IBM */ crb->regs.ctrl_sts.tpm_idle = true; CRB_CMD_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); CRB_CMD_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); CRB_RSP_SIZE_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_SIZE); CRB_RSP_ADDR_WRITE(crb->regs, TPM_CRB_DATA_BUFFER_ADDRESS); + error = qemu_fwcfg_add_file(TPM_CRB_LOG_AREA_FWCFG_NAME, + TPM_CRB_LOG_AREA_MINIMUM_SIZE, crb->tpm_log_area); + if (error) { + warnx("%s: failed to add fwcfg file", __func__); + goto err_out; + } + *sc = crb; return (0); err_out: free(crb); return (error); } static void tpm_crb_deinit(void *sc) { struct tpm_crb *crb; if (sc == NULL) { return; } crb = sc; free(crb); } +static int +tpm_crb_build_acpi_table(void *sc __unused, struct vmctx *vm_ctx) +{ + struct basl_table *table; + + BASL_EXEC(basl_table_create(&table, vm_ctx, ACPI_SIG_TPM2, + BASL_TABLE_ALIGNMENT)); + + /* Header */ + BASL_EXEC(basl_table_append_header(table, ACPI_SIG_TPM2, 4, 1)); + /* Platform Class */ + BASL_EXEC(basl_table_append_int(table, 0, 2)); + /* Reserved */ + BASL_EXEC(basl_table_append_int(table, 0, 2)); + /* Control Address */ + BASL_EXEC( + basl_table_append_int(table, TPM_CRB_CONTROL_AREA_ADDRESS, 8)); + /* Start Method == (7) Command Response Buffer */ + BASL_EXEC(basl_table_append_int(table, 7, 4)); + /* Start Method Specific Parameters */ + uint8_t parameters[12] = { 0 }; + BASL_EXEC(basl_table_append_bytes(table, parameters, 12)); + /* Log Area Minimum Length */ + BASL_EXEC( + basl_table_append_int(table, TPM_CRB_LOG_AREA_MINIMUM_SIZE, 4)); + /* Log Area Start Address */ + BASL_EXEC( + basl_table_append_fwcfg(table, TPM_CRB_LOG_AREA_FWCFG_NAME, 1, 8)); + + BASL_EXEC(basl_table_register_to_rsdt(table)); + + return (0); +} + static struct tpm_intf tpm_intf_crb = { .name = "crb", .init = tpm_crb_init, .deinit = tpm_crb_deinit, + .build_acpi_table = tpm_crb_build_acpi_table, }; TPM_INTF_SET(tpm_intf_crb);