Page MenuHomeFreeBSD

D32961.id98414.diff
No OneTemporary

D32961.id98414.diff

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 <bsd.lib.mk>
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 <stdbool.h>
+#include <contrib/dev/acpica/include/acpi.h>
+
/*
* 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 <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
+
+/*
+ * 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 <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/acpixf.h>
+
+#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 <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <contrib/dev/acpica/include/acpi.h>
+
+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 <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <vmmapi.h>
+
+#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 <machine/vmm.h>
#include <machine/vmm_snapshot.h>
+#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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:
* <lpc_device_name>[,<options>]
@@ -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 <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <vmmapi.h>
+
+#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 <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <vmmapi.h>
+
+#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 <c.koehne@beckhoff.com>
+ */
+
+#pragma once
+
+#include <sys/linker_set.h>
+
+#include <vmmapi.h>
+
+#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 <c.koehne@beckhoff.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <machine/vmm.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <vmmapi.h>
+
+#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 <bsd.prog.mk>

File Metadata

Mime Type
text/plain
Expires
Mon, Apr 20, 8:10 PM (18 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
31861822
Default Alt Text
D32961.id98414.diff (41 KB)

Event Timeline