diff --git a/lib/libvmmapi/Makefile b/lib/libvmmapi/Makefile --- a/lib/libvmmapi/Makefile +++ b/lib/libvmmapi/Makefile @@ -10,5 +10,6 @@ LIBADD= util CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${SRCTOP}/sys .include diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -37,6 +37,8 @@ #include +#include + /* * API version for out-of-tree consumers like grub-bhyve for making compile * time decisions. @@ -180,6 +182,16 @@ vm_paddr_t gpa, size_t len, vm_paddr_t hpa); int vm_unmap_pptdev_mmio(struct vmctx *ctx, int bus, int slot, int func, vm_paddr_t gpa, size_t len); +int vm_get_memory_region_info(struct vmctx *ctx, vm_paddr_t *base, + vm_paddr_t *size, enum vm_memory_region_type type); +int vm_mmap_mmio(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len, const vm_paddr_t hpa); +int vm_munmap_mmio(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len); +int vm_mwire_gpa(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len); +int vm_munwire_gpa(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len); int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec); int vm_setup_pptdev_msix(struct vmctx *ctx, int vcpu, int bus, int slot, @@ -190,6 +202,11 @@ int vm_get_intinfo(struct vmctx *ctx, int vcpu, uint64_t *i1, uint64_t *i2); int vm_set_intinfo(struct vmctx *ctx, int vcpu, uint64_t exit_intinfo); +/* + * Return current resources (CRS) used by an ACPI device. + */ +int vm_acpi_device_get_crs(struct vmctx *const ctx, const char *const name, ACPI_BUFFER *const crs); + const cap_ioctl_t *vm_get_ioctls(size_t *len); /* diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -1009,6 +1009,71 @@ return (ioctl(ctx->fd, VM_UNMAP_PPTDEV_MMIO, &pptmmio)); } +int +vm_get_memory_region_info(struct vmctx *ctx, vm_paddr_t *base, vm_paddr_t *size, + enum vm_memory_region_type type) +{ + struct vm_memory_region_info memory_region_info; + + bzero(&memory_region_info, sizeof(memory_region_info)); + memory_region_info.type = type; + + const int error = ioctl(ctx->fd, VM_GET_MEMORY_REGION_INFO, &memory_region_info); + + if (base) + *base = memory_region_info.base; + if (size) + *size = memory_region_info.size; + + return (error); +} + +/* + * Modifies the second level address translation (SLAT) of the guest memory + * space. + */ +static int +vm_mmodify_slat(struct vmctx * const ctx, const vm_paddr_t gpa, const vm_paddr_t len, + const vm_paddr_t hpa, const enum slat_op_type type) +{ + struct vm_slat_op slat_op = { + .gpa = gpa, + .len = len, + .hpa = hpa, + .type = type, + }; + + return ioctl(ctx->fd, VM_MODIFY_SLAT, &slat_op); +} + +int +vm_mmap_mmio(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len, const vm_paddr_t hpa) +{ + return vm_mmodify_slat(ctx, gpa, len, hpa, VM_MAP_MMIO); +} + +int +vm_munmap_mmio(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len) +{ + return vm_mmodify_slat(ctx, gpa, len, 0, VM_UNMAP_MMIO); +} + +int +vm_mwire_gpa(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len) +{ + return vm_mmodify_slat(ctx, gpa, len, 0, VM_WIRE_GPA); +} + +int +vm_munwire_gpa(struct vmctx *const ctx, const vm_paddr_t gpa, + const vm_paddr_t len) +{ + return vm_mmodify_slat(ctx, gpa, len, 0, VM_UNWIRE_GPA); +} + int vm_setup_pptdev_msi(struct vmctx *ctx, int vcpu, int bus, int slot, int func, uint64_t addr, uint64_t msg, int numvec) @@ -1059,6 +1124,46 @@ return ioctl(ctx->fd, VM_PPTDEV_DISABLE_MSIX, &ppt); } +int +vm_acpi_device_get_crs(struct vmctx *const ctx, const char *const name, + ACPI_BUFFER *const crs) +{ + if (crs == NULL) { + return (EINVAL); + } + + char path[NAME_MAX]; + snprintf(path, NAME_MAX, "\\_SB.%s", name); + + /* get required size to hold CRS data */ + struct vm_acpi_device_info acpi_device_info = { + .type = VM_ACPI_DEVICE_INFO_CRS, + .path = path, + }; + int error = ioctl(ctx->fd, VM_GET_ACPI_DEVICE_INFO, &acpi_device_info); + if (error) { + return (error); + } + + /* allocate buffer for CRS */ + crs->Pointer = malloc(acpi_device_info.buffer_length); + if (crs->Pointer == NULL) { + return (ENOMEM); + } + crs->Length = acpi_device_info.buffer_length; + + /* get CRS data */ + acpi_device_info.buffer = crs->Pointer; + acpi_device_info.buffer_length = crs->Length; + error = ioctl(ctx->fd, VM_GET_ACPI_DEVICE_INFO, &acpi_device_info); + if (error) { + free(crs->Pointer); + crs->Length = 0; + } + + return (error); +} + uint64_t * vm_get_stats(struct vmctx *ctx, int vcpu, struct timeval *ret_tv, int *ret_entries) @@ -1684,14 +1789,15 @@ VM_SET_CAPABILITY, VM_GET_CAPABILITY, VM_BIND_PPTDEV, VM_UNBIND_PPTDEV, VM_MAP_PPTDEV_MMIO, VM_PPTDEV_MSI, VM_PPTDEV_MSIX, VM_UNMAP_PPTDEV_MMIO, VM_PPTDEV_DISABLE_MSIX, - VM_INJECT_NMI, VM_STATS, VM_STAT_DESC, - VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, + VM_GET_MEMORY_REGION_INFO, VM_MODIFY_SLAT, VM_INJECT_NMI, VM_STATS, + VM_STAT_DESC, VM_SET_X2APIC_STATE, VM_GET_X2APIC_STATE, VM_GET_HPET_CAPABILITIES, VM_GET_GPA_PMAP, VM_GLA2GPA, VM_GLA2GPA_NOFAULT, VM_ACTIVATE_CPU, VM_GET_CPUS, VM_SUSPEND_CPU, VM_RESUME_CPU, VM_SET_INTINFO, VM_GET_INTINFO, VM_RTC_WRITE, VM_RTC_READ, VM_RTC_SETTIME, VM_RTC_GETTIME, - VM_RESTART_INSTRUCTION, VM_SET_TOPOLOGY, VM_GET_TOPOLOGY }; + VM_RESTART_INSTRUCTION, VM_SET_TOPOLOGY, VM_GET_TOPOLOGY, + VM_GET_ACPI_DEVICE_INFO }; if (len == NULL) { cmds = malloc(sizeof(vm_ioctl_cmds)); diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -236,6 +236,10 @@ void vm_free_memseg(struct vm *vm, int ident); int vm_map_mmio(struct vm *vm, vm_paddr_t gpa, size_t len, vm_paddr_t hpa); int vm_unmap_mmio(struct vm *vm, vm_paddr_t gpa, size_t len); +int vm_wire_gpa(struct vm *const vm, const vm_paddr_t gpa, + const vm_paddr_t len); +int vm_unwire_gpa(struct vm *const vm, const vm_paddr_t gpa, + const vm_paddr_t len); int vm_assign_pptdev(struct vm *vm, int bus, int slot, int func); int vm_unassign_pptdev(struct vm *vm, int bus, int slot, int func); @@ -741,6 +745,16 @@ } u; }; +enum vm_acpi_device_info_type { + VM_ACPI_DEVICE_INFO_CRS, +}; + +enum vm_memory_region_type { + MEMORY_REGION_INTEL_GSM, + MEMORY_REGION_INTEL_OPREGION, + MEMORY_REGION_TPM_CONTROL_ADDRESS, +}; + /* APIs to inject faults into the guest */ void vm_inject_fault(void *vm, int vcpuid, int vector, int errcode_valid, int errcode); diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -146,6 +146,32 @@ size_t len; }; +struct vm_memory_region_info { + vm_paddr_t base; + vm_paddr_t size; + enum vm_memory_region_type type; +}; + +#ifdef _KERNEL +extern vm_paddr_t intel_graphics_stolen_base; +extern vm_paddr_t intel_graphics_stolen_size; +#endif + +/* second level address translation */ +enum slat_op_type { + VM_MAP_MMIO, + VM_UNMAP_MMIO, + VM_WIRE_GPA, + VM_UNWIRE_GPA +}; + +struct vm_slat_op { + vm_paddr_t gpa; + vm_paddr_t len; + vm_paddr_t hpa; + enum slat_op_type type; +}; + struct vm_pptdev_msi { int vcpu; int bus; @@ -256,6 +282,13 @@ }; _Static_assert(sizeof(struct vm_readwrite_kernemu_device) == 24, "ABI"); +struct vm_acpi_device_info { + const char *path; + void *buffer; + size_t buffer_length; + enum vm_acpi_device_info_type type; +}; + enum { /* general routines */ IOCNUM_ABIVERS = 0, @@ -309,6 +342,8 @@ IOCNUM_PPTDEV_MSIX = 44, IOCNUM_PPTDEV_DISABLE_MSIX = 45, IOCNUM_UNMAP_PPTDEV_MMIO = 46, + IOCNUM_GET_MEMORY_REGION_INFO = 47, + IOCNUM_MODIFY_SLAT = 48, /* statistics */ IOCNUM_VM_STATS = 50, @@ -341,6 +376,9 @@ IOCNUM_RTC_SETTIME = 102, IOCNUM_RTC_GETTIME = 103, + /* ACPI */ + IOCNUM_GET_ACPI_DEVICE_INFO = 110, + /* checkpoint */ IOCNUM_SNAPSHOT_REQ = 113, @@ -427,6 +465,10 @@ _IOW('v', IOCNUM_PPTDEV_DISABLE_MSIX, struct vm_pptdev) #define VM_UNMAP_PPTDEV_MMIO \ _IOW('v', IOCNUM_UNMAP_PPTDEV_MMIO, struct vm_pptdev_mmio) +#define VM_GET_MEMORY_REGION_INFO \ + _IOWR('v', IOCNUM_GET_MEMORY_REGION_INFO, struct vm_memory_region_info) +#define VM_MODIFY_SLAT \ + _IOW('v', IOCNUM_MODIFY_SLAT, struct vm_slat_op) #define VM_INJECT_NMI \ _IOW('v', IOCNUM_INJECT_NMI, struct vm_nmi) #define VM_STATS \ @@ -471,6 +513,8 @@ _IOR('v', IOCNUM_RTC_GETTIME, struct vm_rtc_time) #define VM_RESTART_INSTRUCTION \ _IOW('v', IOCNUM_RESTART_INSTRUCTION, int) +#define VM_GET_ACPI_DEVICE_INFO \ + _IOWR('v', IOCNUM_GET_ACPI_DEVICE_INFO, struct vm_acpi_device_info) #define VM_SNAPSHOT_REQ \ _IOWR('v', IOCNUM_SNAPSHOT_REQ, struct vm_snapshot_meta) #define VM_RESTORE_TIME \ diff --git a/sys/amd64/vmm/io/acpi.h b/sys/amd64/vmm/io/acpi.h new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/io/acpi.h @@ -0,0 +1,19 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include +#include + +/* + * Executes the CRS (Current Resources) method of an ACPI device. + */ +int vmm_acpi_get_crs(const char *const device_path, void *const buffer, + size_t *const buffer_length); +int vmm_tpm2_get_control_address(vm_paddr_t *const base, + vm_paddr_t *const size); diff --git a/sys/amd64/vmm/io/acpi.c b/sys/amd64/vmm/io/acpi.c new file mode 100644 --- /dev/null +++ b/sys/amd64/vmm/io/acpi.c @@ -0,0 +1,81 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include "acpi.h" + +int +vmm_acpi_get_crs(const char *const device_path, void *const buffer, + size_t *const buffer_length) +{ + if (device_path == NULL || buffer_length == NULL) { + return (EINVAL); + } + + /* get device handle */ + ACPI_HANDLE acpi_device; + ACPI_STRING const path = strdup(device_path, M_TEMP); + ACPI_STATUS status = AcpiGetHandle(NULL, path, &acpi_device); + if (!ACPI_SUCCESS(status)) { + printf("%s: failed to get handle for ACPI device \"%s\" (%x)\n", + __func__, device_path, status); + return (ENOENT); + } + + /* get current resources */ + ACPI_BUFFER resources = { ACPI_ALLOCATE_BUFFER }; + status = AcpiGetCurrentResources(acpi_device, &resources); + if (!ACPI_SUCCESS(status)) { + printf( + "%s: failed to get current resources of ACPI device \"%s\" (%x)\n", + __func__, device_path, status); + return (ENODEV); + } + + /* copy data to user space buffer */ + int error = 0; + if (buffer == NULL) { + *buffer_length = resources.Length; + } else { + size_t bytes_to_write = MIN(*buffer_length, resources.Length); + error = copyout(resources.Pointer, buffer, bytes_to_write); + } + + AcpiOsFree(resources.Pointer); + + return (error); +} + +int +vmm_tpm2_get_control_address(vm_paddr_t *const base, vm_paddr_t *const size) +{ + ACPI_TABLE_HEADER *tpm_header; + if (!ACPI_SUCCESS(AcpiGetTable("TPM2", 1, &tpm_header))) { + return (ENOENT); + } + + if (base) { + const ACPI_TABLE_TPM2 *const tpm_table = (ACPI_TABLE_TPM2 *) + tpm_header; + *base = tpm_table->ControlAddress; + } + if (size) { + *size = 0; + } + + return (0); +} diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -635,6 +635,20 @@ return (0); } +int +vm_wire_gpa(struct vm *const vm, const vm_paddr_t gpa, const vm_paddr_t len) +{ + return vm_map_wire(&vm->vmspace->vm_map, gpa, gpa + len, + VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); +} + +int +vm_unwire_gpa(struct vm *const vm, const vm_paddr_t gpa, const vm_paddr_t len) +{ + return vm_map_unwire(&vm->vmspace->vm_map, gpa, gpa + len, + VM_MAP_WIRE_USER | VM_MAP_WIRE_NOHOLES); +} + /* * Return 'true' if 'gpa' is allocated in the guest address space. * diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -63,6 +63,7 @@ #include "vmm_lapic.h" #include "vmm_stat.h" #include "vmm_mem.h" +#include "io/acpi.h" #include "io/ppt.h" #include "io/vatpic.h" #include "io/vioapic.h" @@ -366,6 +367,8 @@ struct vm_capability *vmcap; struct vm_pptdev *pptdev; struct vm_pptdev_mmio *pptmmio; + struct vm_memory_region_info *memory_region_info; + struct vm_slat_op *slat_op; struct vm_pptdev_msi *pptmsi; struct vm_pptdev_msix *pptmsix; struct vm_nmi *vmnmi; @@ -383,6 +386,8 @@ struct vm_memmap *mm; struct vm_munmap *mu; struct vm_cpu_topology *topology; + struct vm_acpi_device_info *acpi_device_info; + char acpi_device_path[NAME_MAX]; struct vm_readwrite_kernemu_device *kernemu; uint64_t *regvals; int *regnums; @@ -446,6 +451,7 @@ case VM_MMAP_MEMSEG: case VM_MUNMAP_MEMSEG: case VM_REINIT: + case VM_MODIFY_SLAT: /* * ioctls that operate on the entire virtual machine must * prevent all vcpus from running. @@ -533,6 +539,39 @@ error = ppt_unmap_mmio(sc->vm, pptmmio->bus, pptmmio->slot, pptmmio->func, pptmmio->gpa, pptmmio->len); break; + case VM_GET_MEMORY_REGION_INFO: + memory_region_info = (struct vm_memory_region_info *)data; + switch (memory_region_info->type) { + case MEMORY_REGION_TPM_CONTROL_ADDRESS: + error = vmm_tpm2_get_control_address( + &memory_region_info->base, + &memory_region_info->size); + break; + default: + error = EINVAL; + break; + } + break; + case VM_MODIFY_SLAT: + slat_op = (struct vm_slat_op *)data; + switch (slat_op->type) { + case VM_MAP_MMIO: + error = vm_map_mmio(sc->vm, slat_op->gpa, slat_op->len, + slat_op->hpa); + break; + case VM_UNMAP_MMIO: + error = vm_unmap_mmio(sc->vm, slat_op->gpa, + slat_op->len); + break; + case VM_WIRE_GPA: + error = vm_wire_gpa(sc->vm, slat_op->gpa, slat_op->len); + break; + case VM_UNWIRE_GPA: + error = vm_unwire_gpa(sc->vm, slat_op->gpa, + slat_op->len); + break; + } + break; case VM_BIND_PPTDEV: pptdev = (struct vm_pptdev *)data; error = vm_assign_pptdev(sc->vm, pptdev->bus, pptdev->slot, @@ -863,6 +902,23 @@ &topology->threads, &topology->maxcpus); error = 0; break; + case VM_GET_ACPI_DEVICE_INFO: + acpi_device_info = (struct vm_acpi_device_info *)data; + /* copy device name into kernel space */ + error = copyinstr(acpi_device_info->path, acpi_device_path, + sizeof(acpi_device_path), NULL); + if (error) { + break; + } + /* handle request */ + switch (acpi_device_info->type) { + case VM_ACPI_DEVICE_INFO_CRS: + error = vmm_acpi_get_crs(acpi_device_path, + acpi_device_info->buffer, + &acpi_device_info->buffer_length); + break; + } + break; #ifdef BHYVE_SNAPSHOT case VM_SNAPSHOT_REQ: snapshot_meta = (struct vm_snapshot_meta *)data; diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile --- a/sys/modules/vmm/Makefile +++ b/sys/modules/vmm/Makefile @@ -30,6 +30,7 @@ .PATH: ${SRCTOP}/sys/amd64/vmm/io SRCS+= iommu.c \ + acpi.c \ ppt.c \ vatpic.c \ vatpit.c \ diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -15,6 +15,7 @@ BHYVE_SYSDIR?=${SRCTOP} SRCS= \ + acpi_device.c \ atkbdc.c \ acpi.c \ audio.c \ @@ -64,6 +65,8 @@ smbiostbl.c \ sockstream.c \ task_switch.c \ + tpm2_device.c \ + tpm2_passthru.c \ uart_emul.c \ usb_emul.c \ usb_mouse.c \ diff --git a/usr.sbin/bhyve/acpi.h b/usr.sbin/bhyve/acpi.h --- a/usr.sbin/bhyve/acpi.h +++ b/usr.sbin/bhyve/acpi.h @@ -31,6 +31,8 @@ #ifndef _ACPI_H_ #define _ACPI_H_ +#include "acpi_device.h" + #define SCI_INT 9 #define SMI_CMD 0xb2 @@ -55,6 +57,7 @@ int acpi_build(struct vmctx *ctx, int ncpu); void acpi_raise_gpe(struct vmctx *ctx, unsigned bit); +int acpi_tables_add_device(const struct acpi_device *const dev); void dsdt_line(const char *fmt, ...); void dsdt_fixed_ioport(uint16_t iobase, uint16_t length); void dsdt_fixed_irq(uint8_t irq); diff --git a/usr.sbin/bhyve/acpi.c b/usr.sbin/bhyve/acpi.c --- a/usr.sbin/bhyve/acpi.c +++ b/usr.sbin/bhyve/acpi.c @@ -74,6 +74,7 @@ #include "bhyverun.h" #include "acpi.h" #include "pci_emul.h" +#include "pci_lpc.h" #include "vmgenc.h" /* @@ -101,7 +102,9 @@ #define MCFG_SIZE 0x40 #define FACS_OFFSET (MCFG_OFFSET + MCFG_SIZE) #define FACS_SIZE 0x40 -#define DSDT_OFFSET (FACS_OFFSET + FACS_SIZE) +#define TPM2_OFFSET (FACS_OFFSET + FACS_SIZE) +#define TPM2_SIZE 0x80 +#define DSDT_OFFSET (TPM2_OFFSET + TPM2_SIZE) #define BHYVE_ASL_TEMPLATE "bhyve.XXXXXXX" #define BHYVE_ASL_SUFFIX ".aml" @@ -139,6 +142,30 @@ #define EFFLUSH(x) \ if (fflush(x) != 0) goto err_exit; +/* + * A list for additional ACPI devices like a TPM. + */ +struct acpi_device_list_entry { + SLIST_ENTRY(acpi_device_list_entry) chain; + const struct acpi_device *dev; +}; +SLIST_HEAD(acpi_device_list, + acpi_device_list_entry) acpi_devices = SLIST_HEAD_INITIALIZER(acpi_devices); + +int +acpi_tables_add_device(const struct acpi_device *const dev) +{ + struct acpi_device_list_entry *const entry = calloc(1, sizeof(*entry)); + if (entry == NULL) { + return (ENOMEM); + } + + entry->dev = dev; + SLIST_INSERT_HEAD(&acpi_devices, entry, chain); + + return (0); +} + static int basl_fwrite_rsdp(FILE *fp) { @@ -184,15 +211,22 @@ EFPRINTF(fp, "\n"); /* Add in pointers to the MADT, FADT and HPET */ - EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : %08X\n", + uint32_t table = 0; + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : %08X\n", table++, basl_acpi_base + MADT_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : %08X\n", + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : %08X\n", table++, basl_acpi_base + FADT_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : %08X\n", + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : %08X\n", table++, basl_acpi_base + HPET_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : %08X\n", + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : %08X\n", table++, basl_acpi_base + MCFG_OFFSET); + /* Add pointer for miscellaneous tables */ + if (lpc_tpm2_in_use()) { + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : %08X\n", + table++, basl_acpi_base + TPM2_OFFSET); + } + EFFLUSH(fp); return (0); @@ -220,14 +254,21 @@ EFPRINTF(fp, "\n"); /* Add in pointers to the MADT, FADT and HPET */ - EFPRINTF(fp, "[0004]\t\tACPI Table Address 0 : 00000000%08X\n", - basl_acpi_base + MADT_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 1 : 00000000%08X\n", - basl_acpi_base + FADT_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 2 : 00000000%08X\n", - basl_acpi_base + HPET_OFFSET); - EFPRINTF(fp, "[0004]\t\tACPI Table Address 3 : 00000000%08X\n", - basl_acpi_base + MCFG_OFFSET); + uint32_t table = 0; + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : 00000000%08X\n", + table++, basl_acpi_base + MADT_OFFSET); + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : 00000000%08X\n", + table++, basl_acpi_base + FADT_OFFSET); + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : 00000000%08X\n", + table++, basl_acpi_base + HPET_OFFSET); + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : 00000000%08X\n", + table++, basl_acpi_base + MCFG_OFFSET); + + /* Add pointer for miscellaneous tables */ + if (lpc_tpm2_in_use()) { + EFPRINTF(fp, "[0004]\t\tACPI Table Address %u : 00000000%08X\n", + table++, basl_acpi_base + TPM2_OFFSET); + } EFFLUSH(fp); @@ -647,6 +688,46 @@ return (errno); } +static int +basl_fwrite_tpm2(FILE *const fp) +{ + if (!lpc_tpm2_in_use()) { + return (0); + } + + EFPRINTF(fp, "/*\n"); + EFPRINTF(fp, " * bhyve TPM2 template\n"); + EFPRINTF(fp, " */\n"); + EFPRINTF(fp, "[0004]\t\tSignature : \"TPM2\"\n"); + EFPRINTF(fp, "[0004]\t\tTable Length : 0000004C\n"); + EFPRINTF(fp, "[0001]\t\tRevision : 00\n"); + EFPRINTF(fp, "[0001]\t\tChecksum : 00\n"); + EFPRINTF(fp, "[0006]\t\tOem ID : \"BHYVE \"\n"); + EFPRINTF(fp, "[0008]\t\tOem Table ID : \"BVTPM2 \"\n"); + EFPRINTF(fp, "[0004]\t\tOem Revision : 00000000\n"); + + /* iasl will fill in the compiler ID/revision fields */ + EFPRINTF(fp, "[0004]\t\tAsl Compiler ID : \"xxxx\"\n"); + EFPRINTF(fp, "[0004]\t\tAsl Compiler Revision : 00000000\n"); + + EFPRINTF(fp, "[0002]\t\tPlatform Class : 0000\n"); + EFPRINTF(fp, "[0002]\t\tReserved : 0000\n"); + EFPRINTF(fp, "[0008]\t\tControl Address : %016lX\n", + lpc_tpm2_get_control_address()); + EFPRINTF(fp, "[0004]\t\tStart Method : 00000007\n"); + EFPRINTF(fp, + "[0012]\t\tMethod Parameters : 00 00 00 00 00 00 00 00 00 00 00 00\n"); + EFPRINTF(fp, "[0004]\t\tMinimum Log Length : 00000000\n"); + EFPRINTF(fp, "[0008]\t\tLog Address : 0000000000000000\n"); + + EFFLUSH(fp); + + return (0); + +err_exit: + return (errno); +} + /* * Helper routines for writing to the DSDT from other modules. */ @@ -760,6 +841,11 @@ vmgenc_write_dsdt(); + const struct acpi_device_list_entry *entry; + SLIST_FOREACH(entry, &acpi_devices, chain) { + acpi_device_write_dsdt(entry->dev); + } + dsdt_line("}"); if (dsdt_error != 0) @@ -956,6 +1042,7 @@ { basl_fwrite_hpet, HPET_OFFSET }, { basl_fwrite_mcfg, MCFG_OFFSET }, { basl_fwrite_facs, FACS_OFFSET }, + { basl_fwrite_tpm2, TPM2_OFFSET }, { basl_fwrite_dsdt, DSDT_OFFSET }, { NULL } }; diff --git a/usr.sbin/bhyve/acpi_device.h b/usr.sbin/bhyve/acpi_device.h new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/acpi_device.h @@ -0,0 +1,51 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include + +struct vmctx; + +struct acpi_device; + +/** + * Creates an ACPI device. + * + * @param[out] new_dev Returns the newly create ACPI device. + * @param[in] vm_ctx VM context the ACPI device is created in. + * @param[in] name Name of the ACPI device. Should always be a NULL + * terminated string. + * @param[in] hid Hardware ID of the ACPI device. Should always be a NULL + * terminated string. + */ +int acpi_device_create(struct acpi_device **const new_dev, + struct vmctx *const vm_ctx, const char *const name, const char *const hid); +void acpi_device_destroy(struct acpi_device *const dev); + +/** + * @note: acpi_device_add_res_acpi_buffer doesn't ensure that no resources are + * added on an error condition. On error the caller should assume that + * the ACPI_BUFFER is partially added to the ACPI device. + */ +int acpi_device_add_res_acpi_buffer(struct acpi_device *const dev, + const ACPI_BUFFER resources); +int acpi_device_add_res_fixed_ioport(struct acpi_device *const dev, + const UINT16 port, UINT8 length); +int acpi_device_add_res_fixed_memory32(struct acpi_device *const dev, + const UINT8 write_protected, const UINT32 address, const UINT32 length); + +int acpi_device_get_physical_crs(const struct acpi_device *const dev, + ACPI_BUFFER *const crs); + +/** + * Maps the current resources (CRS) occupied by the ACPI device to the guest + * memory space. + */ +int acpi_device_map_crs(const struct acpi_device *const dev); + +void acpi_device_write_dsdt(const struct acpi_device *const dev); diff --git a/usr.sbin/bhyve/acpi_device.c b/usr.sbin/bhyve/acpi_device.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/acpi_device.c @@ -0,0 +1,297 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include +#include + +#include "acpi.h" +#include "acpi_device.h" + +/** + * List entry to enumerate all resources used by an ACPI device. + * + * @param chain Used to chain multiple elements together. + * @param type Type of the ACPI resource. + * @param data Data of the ACPI resource. + */ +struct acpi_resource_list_entry { + SLIST_ENTRY(acpi_resource_list_entry) chain; + UINT32 type; + ACPI_RESOURCE_DATA data; +}; + +/** + * Holds information about an ACPI device. + * + * @param vm_ctx VM context the ACPI device was created in. + * @param name Name of the ACPI device. + * @param hid Hardware ID of the ACPI device. + * @param crs Current resources used by the ACPI device. + */ +struct acpi_device { + struct vmctx *vm_ctx; + const char *name; + const char *hid; + SLIST_HEAD(acpi_resource_list, acpi_resource_list_entry) crs; +}; + +int +acpi_device_create(struct acpi_device **const new_dev, + struct vmctx *const vm_ctx, const char *const name, const char *const hid) +{ + if (new_dev == NULL || vm_ctx == NULL || name == NULL || hid == NULL) { + return (EINVAL); + } + + struct acpi_device *const dev = calloc(1, sizeof(*dev)); + if (dev == NULL) { + return (ENOMEM); + } + + dev->vm_ctx = vm_ctx; + dev->name = name; + dev->hid = hid; + SLIST_INIT(&dev->crs); + + /* current resources always contain an end tag */ + struct acpi_resource_list_entry *const crs_end_tag = calloc(1, + sizeof(*crs_end_tag)); + if (crs_end_tag == NULL) { + acpi_device_destroy(dev); + return (ENOMEM); + } + crs_end_tag->type = ACPI_RESOURCE_TYPE_END_TAG; + SLIST_INSERT_HEAD(&dev->crs, crs_end_tag, chain); + + const int error = acpi_tables_add_device(dev); + if (error) { + acpi_device_destroy(dev); + return (error); + } + + *new_dev = dev; + + return (0); +} + +void +acpi_device_destroy(struct acpi_device *const dev) +{ + if (dev == NULL) { + return; + } + + struct acpi_resource_list_entry *res; + while (!SLIST_EMPTY(&dev->crs)) { + res = SLIST_FIRST(&dev->crs); + SLIST_REMOVE_HEAD(&dev->crs, chain); + free(res); + } +} + +int +acpi_device_add_res_acpi_buffer(struct acpi_device *const dev, + const ACPI_BUFFER resources) +{ + if (dev == NULL) { + return (EINVAL); + } + + int error = 0; + size_t offset = 0; + while (offset < resources.Length) { + const ACPI_RESOURCE *const res = + (const ACPI_RESOURCE *)((UINT8 *)resources.Pointer + + offset); + switch (res->Type) { + case ACPI_RESOURCE_TYPE_FIXED_IO: + error = acpi_device_add_res_fixed_ioport(dev, + res->Data.FixedIo.Address, + res->Data.FixedIo.AddressLength); + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: + error = acpi_device_add_res_fixed_memory32(dev, + res->Data.FixedMemory32.WriteProtect, + res->Data.FixedMemory32.Address, + res->Data.FixedMemory32.AddressLength); + break; + case ACPI_RESOURCE_TYPE_END_TAG: + break; + default: + warnx("%s: unknown resource type %d", __func__, + res->Type); + return (ENODEV); + } + if (error) { + break; + } + offset += res->Length; + } + + return (error); +} + +int +acpi_device_add_res_fixed_ioport(struct acpi_device *const dev, + const UINT16 port, const UINT8 length) +{ + if (dev == NULL) { + return (EINVAL); + } + + struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res)); + if (res == NULL) { + return (ENOMEM); + } + + res->type = ACPI_RESOURCE_TYPE_FIXED_IO; + res->data.FixedIo.Address = port; + res->data.FixedIo.AddressLength = length; + + SLIST_INSERT_HEAD(&dev->crs, res, chain); + + return (0); +} + +int +acpi_device_add_res_fixed_memory32(struct acpi_device *const dev, + const UINT8 write_protected, const UINT32 address, const UINT32 length) +{ + if (dev == NULL) { + return (EINVAL); + } + + struct acpi_resource_list_entry *const res = calloc(1, sizeof(*res)); + if (res == NULL) { + return (ENOMEM); + } + + res->type = ACPI_RESOURCE_TYPE_FIXED_MEMORY32; + res->data.FixedMemory32.WriteProtect = write_protected; + res->data.FixedMemory32.Address = address; + res->data.FixedMemory32.AddressLength = length; + + SLIST_INSERT_HEAD(&dev->crs, res, chain); + + return (0); +} + +int +acpi_device_get_physical_crs(const struct acpi_device *const dev, + ACPI_BUFFER *const crs) +{ + return vm_acpi_device_get_crs(dev->vm_ctx, dev->name, crs); +} + +static int +acpi_device_map_crs_resource(const struct acpi_device *const dev, + const struct acpi_resource_list_entry *const res) +{ + int error = 0; + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_IO: + warnx("%s: mapping IO is unsupported", __func__); + return (ENOTSUP); + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: { + /* get memory range */ + const UINT32 base = rounddown2(res->data.FixedMemory32.Address, + PAGE_SIZE); + const UINT32 end = roundup2(res->data.FixedMemory32.Address + + res->data.FixedMemory32.AddressLength, + PAGE_SIZE); + /* map memory range */ + error = vm_mmap_mmio(dev->vm_ctx, base, end - base, base); + if (error) { + return (error); + } + /* make memory range always visible by wiring it */ + return vm_mwire_gpa(dev->vm_ctx, base, end - base); + } + case ACPI_RESOURCE_TYPE_END_TAG: + return (0); + default: + warnx("%s: unknown resource type %d", __func__, res->type); + return (ENODEV); + } +} + +int +acpi_device_map_crs(const struct acpi_device *const dev) +{ + if (dev == NULL) { + return (EINVAL); + } + + const struct acpi_resource_list_entry *res; + SLIST_FOREACH (res, &dev->crs, chain) { + const int error = acpi_device_map_crs_resource(dev, res); + if (error) { + return (error); + } + } + + return (0); +} + +static void +acpi_device_write_dsdt_crs(const struct acpi_device *const dev) +{ + const struct acpi_resource_list_entry *res; + SLIST_FOREACH (res, &dev->crs, chain) { + switch (res->type) { + case ACPI_RESOURCE_TYPE_FIXED_IO: + dsdt_fixed_ioport(res->data.FixedIo.Address, + res->data.FixedIo.AddressLength); + break; + case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: { + dsdt_fixed_mem32(res->data.FixedMemory32.Address, + res->data.FixedMemory32.AddressLength); + break; + } + case ACPI_RESOURCE_TYPE_END_TAG: + break; + default: + warnx("%s: unknown resource type %d", __func__, + res->type); + return; + } + } +} + +void +acpi_device_write_dsdt(const struct acpi_device *const dev) +{ + if (dev == NULL) { + return; + } + + dsdt_line(""); + dsdt_line(" Scope (\\_SB)"); + dsdt_line(" {"); + dsdt_line(" Device (%s)", dev->name); + dsdt_line(" {"); + dsdt_line(" Name (_HID, \"%s\")", dev->hid); + dsdt_line(" Name (_STA, 0x0F)"); + dsdt_line(" Name (_CRS, ResourceTemplate ()"); + dsdt_line(" {"); + dsdt_indent(4); + acpi_device_write_dsdt_crs(dev); + dsdt_unindent(4); + dsdt_line(" })"); + dsdt_line(" }"); + dsdt_line(" }"); +} diff --git a/usr.sbin/bhyve/bhyve.8 b/usr.sbin/bhyve/bhyve.8 --- a/usr.sbin/bhyve/bhyve.8 +++ b/usr.sbin/bhyve/bhyve.8 @@ -155,6 +155,8 @@ .Ar com1 through .Ar com4 , +the TPM2 module +.Ar tpm2 , the boot ROM device .Ar bootrom , and the debug/test device @@ -420,6 +422,12 @@ loader variable as described in .Xr vmm 4 . .Pp +TPM2 devices: +.Bl -tag -width 10n +.It Li passthru +Give the guest direct access to the physical TPM2 device of the host. +.El +.Pp Virtio console devices: .Bl -tag -width 10n .It Li port1= Ns Pa /path/to/port1.sock Ns ,anotherport= Ns Pa ... diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -1941,7 +1941,7 @@ } else { x = *eax; cfgenable = (x & CONF1_ENABLE) == CONF1_ENABLE; - cfgoff = x & PCI_REGMAX; + cfgoff = (x & PCI_REGMAX) & ~0x03; cfgfunc = (x >> 8) & PCI_FUNCMAX; cfgslot = (x >> 11) & PCI_SLOTMAX; cfgbus = (x >> 16) & PCI_BUSMAX; diff --git a/usr.sbin/bhyve/pci_lpc.h b/usr.sbin/bhyve/pci_lpc.h --- a/usr.sbin/bhyve/pci_lpc.h +++ b/usr.sbin/bhyve/pci_lpc.h @@ -72,5 +72,7 @@ char *lpc_pirq_name(int pin); void lpc_pirq_routed(void); const char *lpc_bootrom(void); +vm_paddr_t lpc_tpm2_get_control_address(void); +int lpc_tpm2_in_use(void); #endif diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c --- a/usr.sbin/bhyve/pci_lpc.c +++ b/usr.sbin/bhyve/pci_lpc.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,7 @@ #include "pci_irq.h" #include "pci_lpc.h" #include "pctestdev.h" +#include "tpm2_device.h" #include "uart_emul.h" #define IO_ICU1 0x20 @@ -83,6 +85,9 @@ static bool pctestdev_present; +static const char *lpc_tpm2_opts; +static struct tpm2_device *lpc_tpm2; + /* * LPC device configuration is in the following form: * [,] @@ -103,6 +108,21 @@ error = 0; goto done; } + if (strcasecmp(lpcdev, "tpm2") == 0) { + if (lpc_tpm2_opts != NULL) { + warnx("%s: only a single TPM allowed", + __func__); + error = EINVAL; + goto done; + } + lpc_tpm2_opts = str; + if (lpc_tpm2_opts == NULL) { + error = EINVAL; + goto done; + } + error = 0; + goto done; + } for (unit = 0; unit < LPC_UART_NUM; unit++) { if (strcasecmp(lpcdev, lpc_uart_names[unit]) == 0) { lpc_uart_softc[unit].opts = str; @@ -139,6 +159,7 @@ printf("bootrom\n"); for (i = 0; i < LPC_UART_NUM; i++) printf("%s\n", lpc_uart_names[i]); + printf("tpm2\n"); printf("%s\n", pctestdev_getname()); } @@ -213,6 +234,15 @@ if (error) return (error); } + if (lpc_tpm2_opts != NULL) { + error = tpm2_device_create(&lpc_tpm2, ctx, lpc_tpm2_opts); + if (error) { + warnx( + "%s: unable to create a TPM device with opts \"%s\" (%d)", + __func__, lpc_tpm2_opts, error); + return (error); + } + } /* COM1 and COM2 */ for (unit = 0; unit < LPC_UART_NUM; unit++) { @@ -476,6 +506,18 @@ pci_set_cfgdata8(lpc_bridge, 0x68 + pin, pirq_read(pin + 5)); } +vm_paddr_t +lpc_tpm2_get_control_address(void) +{ + return tpm2_device_get_control_address(lpc_tpm2); +} + +int +lpc_tpm2_in_use(void) +{ + return (lpc_tpm2 != NULL); +} + #ifdef BHYVE_SNAPSHOT static int pci_lpc_snapshot(struct vm_snapshot_meta *meta) diff --git a/usr.sbin/bhyve/tpm2_device.h b/usr.sbin/bhyve/tpm2_device.h new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/tpm2_device.h @@ -0,0 +1,23 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include + +#include "acpi_device.h" + +struct tpm2_device; + +/* device creation and destruction */ +int tpm2_device_create(struct tpm2_device **const new_dev, + struct vmctx *const vm_ctx, const char *const opts); +void tpm2_device_destroy(struct tpm2_device *const dev); +/* device methods */ +vm_paddr_t tpm2_device_get_control_address(const struct tpm2_device *const dev); +int tpm2_device_set_control_address(struct tpm2_device *const dev, + const vm_paddr_t control_address); diff --git a/usr.sbin/bhyve/tpm2_device.c b/usr.sbin/bhyve/tpm2_device.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/tpm2_device.c @@ -0,0 +1,134 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include +#include +#include + +#include "acpi.h" +#include "tpm2_device_priv.h" + +#define TPM2_ACPI_DEVICE_NAME "TPM" +#define TPM2_ACPI_HARDWARE_ID "MSFT0101" + +SET_DECLARE(tpm2_device_emul_set, struct tpm2_device_emul); + +int +tpm2_device_create(struct tpm2_device **const new_dev, + struct vmctx *const vm_ctx, const char *const opts) +{ + if (new_dev == NULL || vm_ctx == NULL) { + return (EINVAL); + } + + struct tpm2_device *const dev = calloc(1, sizeof(*dev)); + if (dev == NULL) { + return (ENOMEM); + } + + int error = acpi_device_create(&dev->acpi_dev, vm_ctx, + TPM2_ACPI_DEVICE_NAME, TPM2_ACPI_HARDWARE_ID); + if (error) { + tpm2_device_destroy(dev); + return (error); + } + + dev->control_address = 0; + + struct tpm2_device_emul **ppemul; + SET_FOREACH(ppemul, tpm2_device_emul_set) + { + struct tpm2_device_emul *const pemul = *ppemul; + if (strcmp(opts, pemul->name)) + continue; + dev->emul = pemul; + break; + } + if (dev->emul == NULL) { + tpm2_device_destroy(dev); + return (EINVAL); + } + + if (dev->emul->init) { + error = dev->emul->init(dev, vm_ctx, opts); + if (error) { + tpm2_device_destroy(dev); + return (error); + } + } + + *new_dev = dev; + + return (0); +} + +void +tpm2_device_destroy(struct tpm2_device *const dev) +{ + if (dev == NULL) { + return; + } + if (dev->emul != NULL && dev->emul->deinit != NULL) { + dev->emul->deinit(dev); + } + + acpi_device_destroy((struct acpi_device *)dev); + free(dev); +} + +vm_paddr_t +_tpm2_device_get_control_address(const struct tpm2_device *const dev) +{ + return (dev->control_address); +} + +vm_paddr_t +tpm2_device_get_control_address(const struct tpm2_device *const dev) +{ + if (dev == NULL || dev->emul == NULL) { + return (0); + } + + if (dev->emul->get_control_address) { + return dev->emul->get_control_address(dev); + } + + return _tpm2_device_get_control_address(dev); +} + +int +_tpm2_device_set_control_address(struct tpm2_device *const dev, + const vm_paddr_t control_address) +{ + dev->control_address = control_address; + + return (0); +} + +int +tpm2_device_set_control_address(struct tpm2_device *const dev, + const vm_paddr_t control_address) +{ + if (dev == NULL || dev->emul == NULL) { + return (EINVAL); + } + + if (dev->emul->set_control_address) { + dev->emul->set_control_address(dev, control_address); + } + + return _tpm2_device_set_control_address(dev, control_address); +} diff --git a/usr.sbin/bhyve/tpm2_device_priv.h b/usr.sbin/bhyve/tpm2_device_priv.h new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/tpm2_device_priv.h @@ -0,0 +1,49 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#pragma once + +#include + +#include + +#include "acpi_device.h" +#include "tpm2_device.h" + +struct tpm2_device_emul { + const char *name; + + int (*init)(struct tpm2_device *const dev, struct vmctx *const vm_ctx, + const char *const opts); + void (*deinit)(struct tpm2_device *const dev); + vm_paddr_t (*get_control_address)(const struct tpm2_device *const dev); + int (*set_control_address)(struct tpm2_device *const dev, + const vm_paddr_t control_address); +}; +#define TPM2_DEVICE_EMUL_SET(x) DATA_SET(tpm2_device_emul_set, x) + +/** + * This struct represents a TPM2 device. + * + * @param acpi_dev A TPM2 device is an ACPI device. + * @param emul Emulation functions for different types of TPM2 + * devices. + * @param control_address Control address of the TPM device. + * @param dev_data Device specific data for a specific TPM2 device type. + */ +struct tpm2_device { + struct acpi_device *acpi_dev; + struct tpm2_device_emul *emul; + vm_paddr_t control_address; + void *dev_data; +}; + +/* default emulation functions */ +vm_paddr_t _tpm2_device_get_control_address( + const struct tpm2_device *const dev); +int _tpm2_device_set_control_address(struct tpm2_device *const dev, + const vm_paddr_t control_address); diff --git a/usr.sbin/bhyve/tpm2_passthru.c b/usr.sbin/bhyve/tpm2_passthru.c new file mode 100644 --- /dev/null +++ b/usr.sbin/bhyve/tpm2_passthru.c @@ -0,0 +1,78 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2021 Beckhoff Automation GmbH & Co. KG + * Author: Corvin Köhne + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include + +#include +#include +#include +#include + +#include "acpi.h" +#include "tpm2_device_priv.h" + +static int +tpm2_passthru_device_init(struct tpm2_device *const dev, + struct vmctx *const vm_ctx, const char *const opts) +{ + ACPI_BUFFER crs; + int error = acpi_device_get_physical_crs(dev->acpi_dev, &crs); + if (error) { + warnx("%s: failed to get current resources of TPM2 device", + __func__); + return (error); + } + error = acpi_device_add_res_acpi_buffer(dev->acpi_dev, crs); + if (error) { + warnx("%s: failed to set current resources for TPM2 device", + __func__); + return (error); + } + /* + * TPM2 should use the address 0xFED40000. This address shouldn't + * conflict with any other device, yet. However, it could change in + * future. It may be a good idea to check whether we can dynamically + * allocate the TPM2 mmio address or not. + */ + error = acpi_device_map_crs(dev->acpi_dev); + if (error) { + warnx( + "%s: failed to map current resources into guest memory space", + __func__); + return (error); + } + + vm_paddr_t control_address; + error = vm_get_memory_region_info(vm_ctx, &control_address, NULL, + MEMORY_REGION_TPM_CONTROL_ADDRESS); + if (error) { + warnx("%s: failed to get control address of TPM2 device", + __func__); + return (error); + } + + error = _tpm2_device_set_control_address(dev, control_address); + if (error) { + warnx("%s: unable to set control address of TPM2 device", + __func__); + return (error); + } + + return (0); +} + +struct tpm2_device_emul tpm2_passthru_device_emul = { + .name = "passthru", + .init = tpm2_passthru_device_init, +}; +TPM2_DEVICE_EMUL_SET(tpm2_passthru_device_emul); diff --git a/usr.sbin/bhyvectl/Makefile b/usr.sbin/bhyvectl/Makefile --- a/usr.sbin/bhyvectl/Makefile +++ b/usr.sbin/bhyvectl/Makefile @@ -14,6 +14,7 @@ WARNS?= 3 +CFLAGS+= -I${SRCTOP}/sys CFLAGS+= -I${SRCTOP}/sys/amd64/vmm .if ${MK_BHYVE_SNAPSHOT} != "no" diff --git a/usr.sbin/bhyveload/Makefile b/usr.sbin/bhyveload/Makefile --- a/usr.sbin/bhyveload/Makefile +++ b/usr.sbin/bhyveload/Makefile @@ -10,5 +10,6 @@ WARNS?= 3 CFLAGS+=-I${SRCTOP}/stand/userboot +CFLAGS+= -I${SRCTOP}/sys .include