Index: head/lib/libvmmapi/vmmapi.h =================================================================== --- head/lib/libvmmapi/vmmapi.h +++ head/lib/libvmmapi/vmmapi.h @@ -35,6 +35,8 @@ #include #include +#include + /* * API version for out-of-tree consumers like grub-bhyve for making compile * time decisions. @@ -156,6 +158,8 @@ 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); Index: head/lib/libvmmapi/vmmapi.c =================================================================== --- head/lib/libvmmapi/vmmapi.c +++ head/lib/libvmmapi/vmmapi.c @@ -799,6 +799,25 @@ } 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_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, Index: head/sys/amd64/include/vmm_dev.h =================================================================== --- head/sys/amd64/include/vmm_dev.h +++ head/sys/amd64/include/vmm_dev.h @@ -235,6 +235,15 @@ 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 @@ 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 @@ _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 \ Index: head/sys/amd64/vmm/vmm_dev.c =================================================================== --- head/sys/amd64/vmm/vmm_dev.c +++ head/sys/amd64/vmm/vmm_dev.c @@ -58,6 +58,7 @@ #include #include #include +#include #include "vmm_lapic.h" #include "vmm_stat.h" @@ -382,6 +383,7 @@ 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 @@ 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); Index: head/usr.sbin/bhyve/Makefile =================================================================== --- head/usr.sbin/bhyve/Makefile +++ head/usr.sbin/bhyve/Makefile @@ -31,6 +31,7 @@ hda_codec.c \ inout.c \ ioapic.c \ + kernemu_dev.c \ mem.c \ mevent.c \ mptbl.c \ @@ -75,6 +76,8 @@ .if ${MK_BHYVE_SNAPSHOT} != "no" SRCS+= snapshot.c .endif + +CFLAGS.kernemu_dev.c+= -I${SRCTOP}/sys/amd64 .PATH: ${BHYVE_SYSDIR}/sys/amd64/vmm SRCS+= vmm_instruction_emul.c Index: head/usr.sbin/bhyve/bhyverun.c =================================================================== --- head/usr.sbin/bhyve/bhyverun.c +++ head/usr.sbin/bhyve/bhyverun.c @@ -92,6 +92,7 @@ #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 @@ init_mem(); init_inout(); + kernemu_dev_init(); init_bootrom(ctx); atkbdc_init(ctx); pci_irq_init(ctx); Index: head/usr.sbin/bhyve/kernemu_dev.h =================================================================== --- head/usr.sbin/bhyve/kernemu_dev.h +++ head/usr.sbin/bhyve/kernemu_dev.h @@ -0,0 +1,32 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Conrad Meyer . 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); Index: head/usr.sbin/bhyve/kernemu_dev.c =================================================================== --- head/usr.sbin/bhyve/kernemu_dev.c +++ head/usr.sbin/bhyve/kernemu_dev.c @@ -0,0 +1,98 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright 2020 Conrad Meyer . 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +struct vm; +struct vm_hpet_cap; +#include +#include + +#include +#include +#include + +#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); +}