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
This commit is contained in:
Conrad Meyer 2020-05-15 15:54:22 +00:00
parent e240ce42bf
commit 8a68ae80f6
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=361082
8 changed files with 213 additions and 0 deletions

View File

@ -798,6 +798,25 @@ vm_ioapic_pincount(struct vmctx *ctx, int *pincount)
return (ioctl(ctx->fd, VM_IOAPIC_PINCOUNT, 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)
{
@ -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,

View File

@ -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);

View File

@ -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 \

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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);
}

View File

@ -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);