aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Meyer <cem@FreeBSD.org>2020-05-15 15:54:22 +0000
committerConrad Meyer <cem@FreeBSD.org>2020-05-15 15:54:22 +0000
commit8a68ae80f67adcb35e6f9712384a3da6dca26de2 (patch)
tree759861464c206768f72c1a5c998d28a4a180bfb2
parente240ce42bf1fe705bbd2ba78a4761eee675fb786 (diff)
downloadsrc-8a68ae80f67adcb35e6f9712384a3da6dca26de2.tar.gz
src-8a68ae80f67adcb35e6f9712384a3da6dca26de2.zip
vmm(4), bhyve(8): Expose kernel-emulated special devices to userspace
Expose the special kernel LAPIC, IOAPIC, and HPET devices to userspace for use in, e.g., fallback instruction emulation (when userspace has a newer instruction decode/emulation layer than the kernel vmm(4)). Plumb the ioctl through libvmmapi and register the memory ranges in bhyve(8). Reviewed by: grehan Differential Revision: https://reviews.freebsd.org/D24525
Notes
Notes: svn path=/head/; revision=361082
-rw-r--r--lib/libvmmapi/vmmapi.c20
-rw-r--r--lib/libvmmapi/vmmapi.h4
-rw-r--r--sys/amd64/include/vmm_dev.h17
-rw-r--r--sys/amd64/vmm/vmm_dev.c37
-rw-r--r--usr.sbin/bhyve/Makefile3
-rw-r--r--usr.sbin/bhyve/bhyverun.c2
-rw-r--r--usr.sbin/bhyve/kernemu_dev.c98
-rw-r--r--usr.sbin/bhyve/kernemu_dev.h32
8 files changed, 213 insertions, 0 deletions
diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c
index 7347c41dd311..3a791908427d 100644
--- a/lib/libvmmapi/vmmapi.c
+++ b/lib/libvmmapi/vmmapi.c
@@ -799,6 +799,25 @@ vm_ioapic_pincount(struct vmctx *ctx, int *pincount)
}
int
+vm_readwrite_kernemu_device(struct vmctx *ctx, int vcpu, vm_paddr_t gpa,
+ bool write, int size, uint64_t *value)
+{
+ struct vm_readwrite_kernemu_device irp = {
+ .vcpuid = vcpu,
+ .access_width = fls(size) - 1,
+ .gpa = gpa,
+ .value = write ? *value : ~0ul,
+ };
+ long cmd = (write ? VM_SET_KERNEMU_DEV : VM_GET_KERNEMU_DEV);
+ int rc;
+
+ rc = ioctl(ctx->fd, cmd, &irp);
+ if (rc == 0 && !write)
+ *value = irp.value;
+ return (rc);
+}
+
+int
vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
{
struct vm_isa_irq isa_irq;
@@ -1615,6 +1634,7 @@ vm_get_ioctls(size_t *len)
VM_MMAP_GETNEXT, VM_SET_REGISTER, VM_GET_REGISTER,
VM_SET_SEGMENT_DESCRIPTOR, VM_GET_SEGMENT_DESCRIPTOR,
VM_SET_REGISTER_SET, VM_GET_REGISTER_SET,
+ VM_SET_KERNEMU_DEV, VM_GET_KERNEMU_DEV,
VM_INJECT_EXCEPTION, VM_LAPIC_IRQ, VM_LAPIC_LOCAL_IRQ,
VM_LAPIC_MSI, VM_IOAPIC_ASSERT_IRQ, VM_IOAPIC_DEASSERT_IRQ,
VM_IOAPIC_PULSE_IRQ, VM_IOAPIC_PINCOUNT, VM_ISA_ASSERT_IRQ,
diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h
index 2b026031b50f..86a58280f34a 100644
--- a/lib/libvmmapi/vmmapi.h
+++ b/lib/libvmmapi/vmmapi.h
@@ -35,6 +35,8 @@
#include <sys/cpuset.h>
#include <machine/vmm_dev.h>
+#include <stdbool.h>
+
/*
* API version for out-of-tree consumers like grub-bhyve for making compile
* time decisions.
@@ -156,6 +158,8 @@ int vm_ioapic_assert_irq(struct vmctx *ctx, int irq);
int vm_ioapic_deassert_irq(struct vmctx *ctx, int irq);
int vm_ioapic_pulse_irq(struct vmctx *ctx, int irq);
int vm_ioapic_pincount(struct vmctx *ctx, int *pincount);
+int vm_readwrite_kernemu_device(struct vmctx *ctx, int vcpu,
+ vm_paddr_t gpa, bool write, int size, uint64_t *value);
int vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
int vm_isa_deassert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
int vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
index 21775b70718e..250eef9e7afc 100644
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -235,6 +235,15 @@ struct vm_cpu_topology {
uint16_t maxcpus;
};
+struct vm_readwrite_kernemu_device {
+ int vcpuid;
+ unsigned access_width : 3;
+ unsigned _unused : 29;
+ uint64_t gpa;
+ uint64_t value;
+};
+_Static_assert(sizeof(struct vm_readwrite_kernemu_device) == 24, "ABI");
+
enum {
/* general routines */
IOCNUM_ABIVERS = 0,
@@ -262,6 +271,8 @@ enum {
IOCNUM_GET_SEGMENT_DESCRIPTOR = 23,
IOCNUM_SET_REGISTER_SET = 24,
IOCNUM_GET_REGISTER_SET = 25,
+ IOCNUM_GET_KERNEMU_DEV = 26,
+ IOCNUM_SET_KERNEMU_DEV = 27,
/* interrupt injection */
IOCNUM_GET_INTINFO = 28,
@@ -347,6 +358,12 @@ enum {
_IOW('v', IOCNUM_SET_REGISTER_SET, struct vm_register_set)
#define VM_GET_REGISTER_SET \
_IOWR('v', IOCNUM_GET_REGISTER_SET, struct vm_register_set)
+#define VM_SET_KERNEMU_DEV \
+ _IOW('v', IOCNUM_SET_KERNEMU_DEV, \
+ struct vm_readwrite_kernemu_device)
+#define VM_GET_KERNEMU_DEV \
+ _IOWR('v', IOCNUM_GET_KERNEMU_DEV, \
+ struct vm_readwrite_kernemu_device)
#define VM_INJECT_EXCEPTION \
_IOW('v', IOCNUM_INJECT_EXCEPTION, struct vm_exception)
#define VM_LAPIC_IRQ \
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
index e47b7081b795..27849d883527 100644
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
#include <machine/vmm_dev.h>
#include <machine/vmm_instruction_emul.h>
#include <machine/vmm_snapshot.h>
+#include <x86/apicreg.h>
#include "vmm_lapic.h"
#include "vmm_stat.h"
@@ -382,6 +383,7 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
struct vm_rtc_data *rtcdata;
struct vm_memmap *mm;
struct vm_cpu_topology *topology;
+ struct vm_readwrite_kernemu_device *kernemu;
uint64_t *regvals;
int *regnums;
#ifdef BHYVE_SNAPSHOT
@@ -561,6 +563,41 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
case VM_IOAPIC_PINCOUNT:
*(int *)data = vioapic_pincount(sc->vm);
break;
+ case VM_SET_KERNEMU_DEV:
+ case VM_GET_KERNEMU_DEV: {
+ mem_region_write_t mwrite;
+ mem_region_read_t mread;
+ bool arg;
+
+ kernemu = (void *)data;
+
+ if (kernemu->access_width > 0)
+ size = (1u << kernemu->access_width);
+ else
+ size = 1;
+
+ if (kernemu->gpa >= DEFAULT_APIC_BASE && kernemu->gpa < DEFAULT_APIC_BASE + PAGE_SIZE) {
+ mread = lapic_mmio_read;
+ mwrite = lapic_mmio_write;
+ } else if (kernemu->gpa >= VIOAPIC_BASE && kernemu->gpa < VIOAPIC_BASE + VIOAPIC_SIZE) {
+ mread = vioapic_mmio_read;
+ mwrite = vioapic_mmio_write;
+ } else if (kernemu->gpa >= VHPET_BASE && kernemu->gpa < VHPET_BASE + VHPET_SIZE) {
+ mread = vhpet_mmio_read;
+ mwrite = vhpet_mmio_write;
+ } else {
+ error = EINVAL;
+ break;
+ }
+
+ if (cmd == VM_SET_KERNEMU_DEV)
+ error = mwrite(sc->vm, kernemu->vcpuid, kernemu->gpa,
+ kernemu->value, size, &arg);
+ else
+ error = mread(sc->vm, kernemu->vcpuid, kernemu->gpa,
+ &kernemu->value, size, &arg);
+ break;
+ }
case VM_ISA_ASSERT_IRQ:
isa_irq = (struct vm_isa_irq *)data;
error = vatpic_assert_irq(sc->vm, isa_irq->atpic_irq);
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index 81a5f9282a3f..8898468c38cf 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -31,6 +31,7 @@ SRCS= \
hda_codec.c \
inout.c \
ioapic.c \
+ kernemu_dev.c \
mem.c \
mevent.c \
mptbl.c \
@@ -76,6 +77,8 @@ SRCS= \
SRCS+= snapshot.c
.endif
+CFLAGS.kernemu_dev.c+= -I${SRCTOP}/sys/amd64
+
.PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm
SRCS+= vmm_instruction_emul.c
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 8d73bd38cae4..ef98ef80c4ab 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -92,6 +92,7 @@ __FBSDID("$FreeBSD$");
#include "fwctl.h"
#include "gdb.h"
#include "ioapic.h"
+#include "kernemu_dev.h"
#include "mem.h"
#include "mevent.h"
#include "mptbl.h"
@@ -1268,6 +1269,7 @@ main(int argc, char *argv[])
init_mem();
init_inout();
+ kernemu_dev_init();
init_bootrom(ctx);
atkbdc_init(ctx);
pci_irq_init(ctx);
diff --git a/usr.sbin/bhyve/kernemu_dev.c b/usr.sbin/bhyve/kernemu_dev.c
new file mode 100644
index 000000000000..2fa0c3dc1f35
--- /dev/null
+++ b/usr.sbin/bhyve/kernemu_dev.c
@@ -0,0 +1,98 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/tree.h>
+
+#include <amd64/include/vmm.h>
+#include <x86/include/apicreg.h>
+struct vm;
+struct vm_hpet_cap;
+#include <vmm/io/vioapic.h>
+#include <vmm/io/vhpet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <vmmapi.h>
+
+#include "kernemu_dev.h"
+#include "mem.h"
+
+static int
+apic_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, int size,
+ uint64_t *val, void *arg1 __unused, long arg2 __unused)
+{
+ if (vm_readwrite_kernemu_device(ctx, vcpu, addr, (dir == MEM_F_WRITE),
+ size, val) != 0)
+ return (errno);
+ return (0);
+}
+
+static struct mem_range lapic_mmio = {
+ .name = "kern-lapic-mmio",
+ .base = DEFAULT_APIC_BASE,
+ .size = PAGE_SIZE,
+ .flags = MEM_F_RW | MEM_F_IMMUTABLE,
+ .handler = apic_handler,
+
+};
+static struct mem_range ioapic_mmio = {
+ .name = "kern-ioapic-mmio",
+ .base = VIOAPIC_BASE,
+ .size = VIOAPIC_SIZE,
+ .flags = MEM_F_RW | MEM_F_IMMUTABLE,
+ .handler = apic_handler,
+};
+static struct mem_range hpet_mmio = {
+ .name = "kern-hpet-mmio",
+ .base = VHPET_BASE,
+ .size = VHPET_SIZE,
+ .flags = MEM_F_RW | MEM_F_IMMUTABLE,
+ .handler = apic_handler,
+};
+
+void
+kernemu_dev_init(void)
+{
+ int rc;
+
+ rc = register_mem(&lapic_mmio);
+ if (rc != 0)
+ errc(4, rc, "register_mem: LAPIC (0x%08x)",
+ (unsigned)lapic_mmio.base);
+ rc = register_mem(&ioapic_mmio);
+ if (rc != 0)
+ errc(4, rc, "register_mem: IOAPIC (0x%08x)",
+ (unsigned)ioapic_mmio.base);
+ rc = register_mem(&hpet_mmio);
+ if (rc != 0)
+ errc(4, rc, "register_mem: HPET (0x%08x)",
+ (unsigned)hpet_mmio.base);
+}
diff --git a/usr.sbin/bhyve/kernemu_dev.h b/usr.sbin/bhyve/kernemu_dev.h
new file mode 100644
index 000000000000..7927855da0cd
--- /dev/null
+++ b/usr.sbin/bhyve/kernemu_dev.h
@@ -0,0 +1,32 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2020 Conrad Meyer <cem@FreeBSD.org>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#pragma once
+
+void kernemu_dev_init(void);