summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2013-11-12 22:51:03 +0000
committerneel <neel@FreeBSD.org>2013-11-12 22:51:03 +0000
commit384d86e8883acf1387f02c9f3ec34798f973dd62 (patch)
treec5c556e061b1f182036d3b8ffc028cc9c7a83e2d
parente41d2fd565cf8d3945a0bbfc72489332deb7ae0d (diff)
downloadFreeBSD-src-384d86e8883acf1387f02c9f3ec34798f973dd62.zip
FreeBSD-src-384d86e8883acf1387f02c9f3ec34798f973dd62.tar.gz
Move the ioapic device model from userspace into vmm.ko. This is needed for
upcoming in-kernel device emulations like the HPET. The ioctls VM_IOAPIC_ASSERT_IRQ and VM_IOAPIC_DEASSERT_IRQ are used to manipulate the ioapic pin state. Discussed with: grehan@ Submitted by: Tycho Nightingale (tycho.nightingale@pluribusnetworks.com)
-rw-r--r--lib/libvmmapi/vmmapi.c22
-rw-r--r--lib/libvmmapi/vmmapi.h2
-rw-r--r--sys/amd64/include/vmm.h3
-rw-r--r--sys/amd64/include/vmm_dev.h10
-rw-r--r--sys/amd64/vmm/io/vioapic.c366
-rw-r--r--sys/amd64/vmm/io/vioapic.h (renamed from usr.sbin/bhyve/ioapic.h)25
-rw-r--r--sys/amd64/vmm/vmm.c37
-rw-r--r--sys/amd64/vmm/vmm_dev.c12
-rw-r--r--sys/modules/vmm/Makefile1
-rw-r--r--usr.sbin/bhyve/Makefile2
-rw-r--r--usr.sbin/bhyve/bhyverun.c3
-rw-r--r--usr.sbin/bhyve/ioapic.c378
-rw-r--r--usr.sbin/bhyve/pci_emul.c5
-rw-r--r--usr.sbin/bhyve/pci_lpc.c9
-rw-r--r--usr.sbin/bhyve/pit_8254.c5
15 files changed, 477 insertions, 403 deletions
diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c
index bb69358..5ece80b 100644
--- a/lib/libvmmapi/vmmapi.c
+++ b/lib/libvmmapi/vmmapi.c
@@ -397,6 +397,28 @@ vm_lapic_irq(struct vmctx *ctx, int vcpu, int vector)
}
int
+vm_ioapic_assert_irq(struct vmctx *ctx, int irq)
+{
+ struct vm_ioapic_irq ioapic_irq;
+
+ bzero(&ioapic_irq, sizeof(struct vm_ioapic_irq));
+ ioapic_irq.irq = irq;
+
+ return (ioctl(ctx->fd, VM_IOAPIC_ASSERT_IRQ, &ioapic_irq));
+}
+
+int
+vm_ioapic_deassert_irq(struct vmctx *ctx, int irq)
+{
+ struct vm_ioapic_irq ioapic_irq;
+
+ bzero(&ioapic_irq, sizeof(struct vm_ioapic_irq));
+ ioapic_irq.irq = irq;
+
+ return (ioctl(ctx->fd, VM_IOAPIC_DEASSERT_IRQ, &ioapic_irq));
+}
+
+int
vm_inject_nmi(struct vmctx *ctx, int vcpu)
{
struct vm_nmi vmnmi;
diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h
index 0720e2e..c9a2a09 100644
--- a/lib/libvmmapi/vmmapi.h
+++ b/lib/libvmmapi/vmmapi.h
@@ -67,6 +67,8 @@ int vm_inject_event(struct vmctx *ctx, int vcpu, enum vm_event_type type,
int vm_inject_event2(struct vmctx *ctx, int vcpu, enum vm_event_type type,
int vector, int error_code);
int vm_lapic_irq(struct vmctx *ctx, int vcpu, int vector);
+int vm_ioapic_assert_irq(struct vmctx *ctx, int irq);
+int vm_ioapic_deassert_irq(struct vmctx *ctx, int irq);
int vm_inject_nmi(struct vmctx *ctx, int vcpu);
int vm_capability_name2type(const char *capname);
const char *vm_capability_type2name(int type);
diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h
index e8bc409..f009461 100644
--- a/sys/amd64/include/vmm.h
+++ b/sys/amd64/include/vmm.h
@@ -38,6 +38,7 @@ struct vm_memory_segment;
struct seg_desc;
struct vm_exit;
struct vm_run;
+struct vioapic;
struct vlapic;
struct vmspace;
struct vm_object;
@@ -116,10 +117,12 @@ int vm_nmi_pending(struct vm *vm, int vcpuid);
void vm_nmi_clear(struct vm *vm, int vcpuid);
uint64_t *vm_guest_msrs(struct vm *vm, int cpu);
struct vlapic *vm_lapic(struct vm *vm, int cpu);
+struct vioapic *vm_ioapic(struct vm *vm);
int vm_get_capability(struct vm *vm, int vcpu, int type, int *val);
int vm_set_capability(struct vm *vm, int vcpu, int type, int val);
int vm_get_x2apic_state(struct vm *vm, int vcpu, enum x2apic_state *state);
int vm_set_x2apic_state(struct vm *vm, int vcpu, enum x2apic_state state);
+int vm_apicid2vcpuid(struct vm *vm, int apicid);
void vm_activate_cpu(struct vm *vm, int vcpu);
cpuset_t vm_active_cpus(struct vm *vm);
struct vm_exit *vm_exitinfo(struct vm *vm, int vcpuid);
diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h
index bf014cc..2adf50d 100644
--- a/sys/amd64/include/vmm_dev.h
+++ b/sys/amd64/include/vmm_dev.h
@@ -71,6 +71,10 @@ struct vm_lapic_irq {
int vector;
};
+struct vm_ioapic_irq {
+ int irq;
+};
+
struct vm_capability {
int cpuid;
enum vm_cap_type captype;
@@ -164,6 +168,8 @@ enum {
IOCNUM_INJECT_EVENT = 30,
IOCNUM_LAPIC_IRQ = 31,
IOCNUM_INJECT_NMI = 32,
+ IOCNUM_IOAPIC_ASSERT_IRQ = 33,
+ IOCNUM_IOAPIC_DEASSERT_IRQ = 34,
/* PCI pass-thru */
IOCNUM_BIND_PPTDEV = 40,
@@ -199,6 +205,10 @@ enum {
_IOW('v', IOCNUM_INJECT_EVENT, struct vm_event)
#define VM_LAPIC_IRQ \
_IOW('v', IOCNUM_LAPIC_IRQ, struct vm_lapic_irq)
+#define VM_IOAPIC_ASSERT_IRQ \
+ _IOW('v', IOCNUM_IOAPIC_ASSERT_IRQ, struct vm_ioapic_irq)
+#define VM_IOAPIC_DEASSERT_IRQ \
+ _IOW('v', IOCNUM_IOAPIC_DEASSERT_IRQ, struct vm_ioapic_irq)
#define VM_SET_CAPABILITY \
_IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
#define VM_GET_CAPABILITY \
diff --git a/sys/amd64/vmm/io/vioapic.c b/sys/amd64/vmm/io/vioapic.c
new file mode 100644
index 0000000..bc8485e
--- /dev/null
+++ b/sys/amd64/vmm/io/vioapic.c
@@ -0,0 +1,366 @@
+/*-
+ * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
+ * Copyright (c) 2013 Neel Natu <neel@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 NETAPP, INC ``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 NETAPP, INC 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$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/cpuset.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+
+#include <x86/apicreg.h>
+#include <machine/vmm.h>
+
+#include "vmm_ktr.h"
+#include "vmm_lapic.h"
+#include "vioapic.h"
+
+#define IOREGSEL 0x00
+#define IOWIN 0x10
+
+#define REDIR_ENTRIES 16
+#define INTR_ASSERTED(vioapic, pin) ((vioapic)->rtbl[(pin)].pinstate == true)
+
+struct vioapic {
+ struct vm *vm;
+ struct mtx mtx;
+ uint32_t id;
+ uint32_t ioregsel;
+ struct {
+ uint64_t reg;
+ bool pinstate;
+ bool pending;
+ } rtbl[REDIR_ENTRIES];
+};
+
+#define VIOAPIC_LOCK(vioapic) mtx_lock(&((vioapic)->mtx))
+#define VIOAPIC_UNLOCK(vioapic) mtx_unlock(&((vioapic)->mtx))
+#define VIOAPIC_LOCKED(vioapic) mtx_owned(&((vioapic)->mtx))
+
+static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
+
+#define VIOAPIC_CTR1(vioapic, fmt, a1) \
+ VM_CTR1((vioapic)->vm, fmt, a1)
+
+#define VIOAPIC_CTR2(vioapic, fmt, a1, a2) \
+ VM_CTR2((vioapic)->vm, fmt, a1, a2)
+
+#define VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3) \
+ VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
+
+#ifdef KTR
+static const char *
+pinstate_str(bool asserted)
+{
+
+ if (asserted)
+ return ("asserted");
+ else
+ return ("deasserted");
+}
+#endif
+
+static void
+vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
+{
+ int vector, apicid, vcpuid;
+ uint32_t low, high;
+ cpuset_t dmask;
+
+ KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
+ ("vioapic_set_pinstate: invalid pin number %d", pin));
+
+ KASSERT(VIOAPIC_LOCKED(vioapic),
+ ("vioapic_set_pinstate: vioapic is not locked"));
+
+ VIOAPIC_CTR2(vioapic, "ioapic pin%d %s", pin, pinstate_str(newstate));
+
+ /* Nothing to do if interrupt pin has not changed state */
+ if (vioapic->rtbl[pin].pinstate == newstate)
+ return;
+
+ vioapic->rtbl[pin].pinstate = newstate; /* record it */
+
+ /* Nothing to do if interrupt pin is deasserted */
+ if (!INTR_ASSERTED(vioapic, pin))
+ return;
+
+ /*
+ * XXX
+ * We only deal with:
+ * - edge triggered interrupts
+ * - fixed delivery mode
+ * Level-triggered sources will work so long as there is no sharing.
+ */
+ low = vioapic->rtbl[pin].reg;
+ high = vioapic->rtbl[pin].reg >> 32;
+ if ((low & IOART_INTMASK) == IOART_INTMCLR &&
+ (low & IOART_DESTMOD) == IOART_DESTPHY &&
+ (low & IOART_DELMOD) == IOART_DELFIXED) {
+ vector = low & IOART_INTVEC;
+ apicid = high >> APIC_ID_SHIFT;
+ if (apicid != 0xff) {
+ /* unicast */
+ vcpuid = vm_apicid2vcpuid(vioapic->vm, apicid);
+ VIOAPIC_CTR3(vioapic, "ioapic pin%d triggering "
+ "intr vector %d on vcpuid %d", pin, vector, vcpuid);
+ lapic_set_intr(vioapic->vm, vcpuid, vector);
+ } else {
+ /* broadcast */
+ VIOAPIC_CTR2(vioapic, "ioapic pin%d triggering intr "
+ "vector %d on all vcpus", pin, vector);
+ dmask = vm_active_cpus(vioapic->vm);
+ while ((vcpuid = CPU_FFS(&dmask)) != 0) {
+ vcpuid--;
+ CPU_CLR(vcpuid, &dmask);
+ lapic_set_intr(vioapic->vm, vcpuid, vector);
+ }
+ }
+ } else if ((low & IOART_INTMASK) != IOART_INTMCLR &&
+ (low & IOART_TRGRLVL) != 0) {
+ /*
+ * For level-triggered interrupts that have been
+ * masked, set the pending bit so that an interrupt
+ * will be generated on unmask and if the level is
+ * still asserted
+ */
+ VIOAPIC_CTR1(vioapic, "ioapic pin%d interrupt pending", pin);
+ vioapic->rtbl[pin].pending = true;
+ }
+}
+
+static int
+vioapic_set_irqstate(struct vm *vm, int irq, bool state)
+{
+ struct vioapic *vioapic;
+
+ if (irq < 0 || irq >= REDIR_ENTRIES)
+ return (EINVAL);
+
+ vioapic = vm_ioapic(vm);
+
+ VIOAPIC_LOCK(vioapic);
+ vioapic_set_pinstate(vioapic, irq, state);
+ VIOAPIC_UNLOCK(vioapic);
+
+ return (0);
+}
+
+int
+vioapic_assert_irq(struct vm *vm, int irq)
+{
+
+ return (vioapic_set_irqstate(vm, irq, true));
+}
+
+int
+vioapic_deassert_irq(struct vm *vm, int irq)
+{
+
+ return (vioapic_set_irqstate(vm, irq, false));
+}
+
+static uint32_t
+vioapic_read(struct vioapic *vioapic, uint32_t addr)
+{
+ int regnum, pin, rshift;
+
+ regnum = addr & 0xff;
+ switch (regnum) {
+ case IOAPIC_ID:
+ return (vioapic->id);
+ break;
+ case IOAPIC_VER:
+ return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
+ break;
+ case IOAPIC_ARB:
+ return (vioapic->id);
+ break;
+ default:
+ break;
+ }
+
+ /* redirection table entries */
+ if (regnum >= IOAPIC_REDTBL &&
+ regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
+ pin = (regnum - IOAPIC_REDTBL) / 2;
+ if ((regnum - IOAPIC_REDTBL) % 2)
+ rshift = 32;
+ else
+ rshift = 0;
+
+ return (vioapic->rtbl[pin].reg >> rshift);
+ }
+
+ return (0);
+}
+
+static void
+vioapic_write(struct vioapic *vioapic, uint32_t addr, uint32_t data)
+{
+ int regnum, pin, lshift;
+
+ regnum = addr & 0xff;
+ switch (regnum) {
+ case IOAPIC_ID:
+ vioapic->id = data & APIC_ID_MASK;
+ break;
+ case IOAPIC_VER:
+ case IOAPIC_ARB:
+ /* readonly */
+ break;
+ default:
+ break;
+ }
+
+ /* redirection table entries */
+ if (regnum >= IOAPIC_REDTBL &&
+ regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
+ pin = (regnum - IOAPIC_REDTBL) / 2;
+ if ((regnum - IOAPIC_REDTBL) % 2)
+ lshift = 32;
+ else
+ lshift = 0;
+
+ vioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
+ vioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
+
+ VIOAPIC_CTR2(vioapic, "ioapic pin%d redir table entry %#lx",
+ pin, vioapic->rtbl[pin].reg);
+
+ if (vioapic->rtbl[pin].pending &&
+ ((vioapic->rtbl[pin].reg & IOART_INTMASK) ==
+ IOART_INTMCLR)) {
+ vioapic->rtbl[pin].pending = false;
+ /*
+ * Inject the deferred level-triggered int if it is
+ * still asserted. Simulate by toggling the pin
+ * off and then on.
+ */
+ if (vioapic->rtbl[pin].pinstate == true) {
+ VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
+ "interrupt delivered", pin);
+ vioapic_set_pinstate(vioapic, pin, false);
+ vioapic_set_pinstate(vioapic, pin, true);
+ } else {
+ VIOAPIC_CTR1(vioapic, "ioapic pin%d pending "
+ "interrupt dismissed", pin);
+ }
+ }
+ }
+}
+
+static int
+vioapic_mmio_rw(struct vioapic *vioapic, uint64_t gpa, uint64_t *data,
+ int size, bool doread)
+{
+ uint64_t offset;
+
+ offset = gpa - VIOAPIC_BASE;
+
+ /*
+ * The IOAPIC specification allows 32-bit wide accesses to the
+ * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
+ */
+ if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
+ if (doread)
+ *data = 0;
+ return (0);
+ }
+
+ VIOAPIC_LOCK(vioapic);
+ if (offset == IOREGSEL) {
+ if (doread)
+ *data = vioapic->ioregsel;
+ else
+ vioapic->ioregsel = *data;
+ } else {
+ if (doread)
+ *data = vioapic_read(vioapic, vioapic->ioregsel);
+ else
+ vioapic_write(vioapic, vioapic->ioregsel, *data);
+ }
+ VIOAPIC_UNLOCK(vioapic);
+
+ return (0);
+}
+
+int
+vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa, uint64_t *rval,
+ int size, void *arg)
+{
+ int error;
+ struct vioapic *vioapic;
+
+ vioapic = vm_ioapic(vm);
+ error = vioapic_mmio_rw(vioapic, gpa, rval, size, true);
+ return (error);
+}
+
+int
+vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval,
+ int size, void *arg)
+{
+ int error;
+ struct vioapic *vioapic;
+
+ vioapic = vm_ioapic(vm);
+ error = vioapic_mmio_rw(vioapic, gpa, &wval, size, false);
+ return (error);
+}
+
+struct vioapic *
+vioapic_init(struct vm *vm)
+{
+ int i;
+ struct vioapic *vioapic;
+
+ vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
+
+ vioapic->vm = vm;
+ mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_DEF);
+
+ /* Initialize all redirection entries to mask all interrupts */
+ for (i = 0; i < REDIR_ENTRIES; i++)
+ vioapic->rtbl[i].reg = 0x0001000000010000UL;
+
+ return (vioapic);
+}
+
+void
+vioapic_cleanup(struct vioapic *vioapic)
+{
+
+ free(vioapic, M_VIOAPIC);
+}
diff --git a/usr.sbin/bhyve/ioapic.h b/sys/amd64/vmm/io/vioapic.h
index 4696f9a..54ad36a 100644
--- a/usr.sbin/bhyve/ioapic.h
+++ b/sys/amd64/vmm/io/vioapic.h
@@ -1,5 +1,6 @@
/*-
- * Copyright (c) 2012 NetApp, Inc.
+ * Copyright (c) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
+ * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,13 +27,23 @@
* $FreeBSD$
*/
-#ifndef _IOAPIC_H_
-#define _IOAPIC_H_
+#ifndef _VIOAPIC_H_
+#define _VIOAPIC_H_
-struct vmctx;
+struct vm;
+struct vioapic;
-void ioapic_init(int num);
-void ioapic_deassert_pin(struct vmctx *ctx, int pin);
-void ioapic_assert_pin(struct vmctx *ctx, int pin);
+#define VIOAPIC_BASE 0xFEC00000
+#define VIOAPIC_SIZE 4096
+struct vioapic *vioapic_init(struct vm *vm);
+void vioapic_cleanup(struct vioapic *vioapic);
+
+int vioapic_assert_irq(struct vm *vm, int irq);
+int vioapic_deassert_irq(struct vm *vm, int irq);
+
+int vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa,
+ uint64_t wval, int size, void *arg);
+int vioapic_mmio_read(void *vm, int vcpuid, uint64_t gpa,
+ uint64_t *rval, int size, void *arg);
#endif
diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c
index b3f56b1..40254c6 100644
--- a/sys/amd64/vmm/vmm.c
+++ b/sys/amd64/vmm/vmm.c
@@ -59,11 +59,13 @@ __FBSDID("$FreeBSD$");
#include <machine/vmparam.h>
#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
+
#include "vmm_ktr.h"
#include "vmm_host.h"
#include "vmm_mem.h"
#include "vmm_util.h"
-#include <machine/vmm_dev.h>
+#include "vioapic.h"
#include "vlapic.h"
#include "vmm_msr.h"
#include "vmm_ipi.h"
@@ -106,6 +108,7 @@ struct mem_seg {
struct vm {
void *cookie; /* processor-specific data */
void *iommu; /* iommu-specific data */
+ struct vioapic *vioapic; /* virtual ioapic */
struct vmspace *vmspace; /* guest's address space */
struct vcpu vcpu[VM_MAXCPU];
int num_mem_segs;
@@ -300,6 +303,7 @@ vm_create(const char *name, struct vm **retvm)
vm = malloc(sizeof(struct vm), M_VM, M_WAITOK | M_ZERO);
strcpy(vm->name, name);
vm->cookie = VMINIT(vm, vmspace_pmap(vmspace));
+ vm->vioapic = vioapic_init(vm);
for (i = 0; i < VM_MAXCPU; i++) {
vcpu_init(vm, i);
@@ -341,6 +345,8 @@ vm_destroy(struct vm *vm)
for (i = 0; i < VM_MAXCPU; i++)
vcpu_cleanup(&vm->vcpu[i]);
+ vioapic_cleanup(vm->vioapic);
+
VMSPACE_FREE(vm->vmspace);
VMCLEANUP(vm->cookie);
@@ -938,6 +944,8 @@ vm_handle_inst_emul(struct vm *vm, int vcpuid, boolean_t *retu)
struct vm_exit *vme;
int error, inst_length;
uint64_t rip, gla, gpa, cr3;
+ mem_region_read_t mread;
+ mem_region_write_t mwrite;
vcpu = &vm->vcpu[vcpuid];
vme = &vcpu->exitinfo;
@@ -960,13 +968,18 @@ vm_handle_inst_emul(struct vm *vm, int vcpuid, boolean_t *retu)
return (EFAULT);
/* return to userland unless this is a local apic access */
- if (gpa < DEFAULT_APIC_BASE || gpa >= DEFAULT_APIC_BASE + PAGE_SIZE) {
+ if (gpa >= DEFAULT_APIC_BASE && gpa < DEFAULT_APIC_BASE + PAGE_SIZE) {
+ mread = lapic_mmio_read;
+ mwrite = lapic_mmio_write;
+ } else if (gpa >= VIOAPIC_BASE && gpa < VIOAPIC_BASE + VIOAPIC_SIZE) {
+ mread = vioapic_mmio_read;
+ mwrite = vioapic_mmio_write;
+ } else {
*retu = TRUE;
return (0);
}
- error = vmm_emulate_instruction(vm, vcpuid, gpa, vie,
- lapic_mmio_read, lapic_mmio_write, 0);
+ error = vmm_emulate_instruction(vm, vcpuid, gpa, vie, mread, mwrite, 0);
/* return to userland to spin up the AP */
if (error == 0 && vme->exitcode == VM_EXITCODE_SPINUP_AP)
@@ -1149,6 +1162,13 @@ vm_lapic(struct vm *vm, int cpu)
return (vm->vcpu[cpu].vlapic);
}
+struct vioapic *
+vm_ioapic(struct vm *vm)
+{
+
+ return (vm->vioapic);
+}
+
boolean_t
vmm_is_pptdev(int bus, int slot, int func)
{
@@ -1313,3 +1333,12 @@ vm_get_vmspace(struct vm *vm)
return (vm->vmspace);
}
+
+int
+vm_apicid2vcpuid(struct vm *vm, int apicid)
+{
+ /*
+ * XXX apic id is assumed to be numerically identical to vcpu id
+ */
+ return (apicid);
+}
diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c
index 939bda3..a639ce9 100644
--- a/sys/amd64/vmm/vmm_dev.c
+++ b/sys/amd64/vmm/vmm_dev.c
@@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include "vmm_stat.h"
#include "vmm_mem.h"
#include "io/ppt.h"
+#include "io/vioapic.h"
#include <machine/vmm_dev.h>
struct vmmdev_softc {
@@ -146,10 +147,11 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
struct vmmdev_softc *sc;
struct vm_memory_segment *seg;
struct vm_register *vmreg;
- struct vm_seg_desc* vmsegdesc;
+ struct vm_seg_desc *vmsegdesc;
struct vm_run *vmrun;
struct vm_event *vmevent;
struct vm_lapic_irq *vmirq;
+ struct vm_ioapic_irq *ioapic_irq;
struct vm_capability *vmcap;
struct vm_pptdev *pptdev;
struct vm_pptdev_mmio *pptmmio;
@@ -293,6 +295,14 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
vmirq = (struct vm_lapic_irq *)data;
error = lapic_set_intr(sc->vm, vmirq->cpuid, vmirq->vector);
break;
+ case VM_IOAPIC_ASSERT_IRQ:
+ ioapic_irq = (struct vm_ioapic_irq *)data;
+ error = vioapic_assert_irq(sc->vm, ioapic_irq->irq);
+ break;
+ case VM_IOAPIC_DEASSERT_IRQ:
+ ioapic_irq = (struct vm_ioapic_irq *)data;
+ error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq);
+ break;
case VM_MAP_MEMORY:
seg = (struct vm_memory_segment *)data;
error = vm_malloc(sc->vm, seg->gpa, seg->len);
diff --git a/sys/modules/vmm/Makefile b/sys/modules/vmm/Makefile
index e1a6e2d..06f8ca9 100644
--- a/sys/modules/vmm/Makefile
+++ b/sys/modules/vmm/Makefile
@@ -27,6 +27,7 @@ SRCS+= vmm.c \
.PATH: ${.CURDIR}/../../amd64/vmm/io
SRCS+= iommu.c \
ppt.c \
+ vioapic.c \
vlapic.c
# intel-specific files
diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index 570182e..a08341f 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -7,7 +7,7 @@ PROG= bhyve
DEBUG_FLAGS= -g -O0
SRCS= acpi.c atpic.c bhyverun.c block_if.c consport.c dbgport.c elcr.c
-SRCS+= inout.c ioapic.c legacy_irq.c mem.c mevent.c mptbl.c pci_ahci.c
+SRCS+= inout.c legacy_irq.c mem.c mevent.c mptbl.c pci_ahci.c
SRCS+= pci_emul.c pci_hostbridge.c pci_lpc.c pci_passthru.c pci_virtio_block.c
SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pmtmr.c post.c rtc.c
SRCS+= uart_emul.c virtio.c xmsr.c spinup_ap.c
diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c
index 8263602..652f402 100644
--- a/usr.sbin/bhyve/bhyverun.c
+++ b/usr.sbin/bhyve/bhyverun.c
@@ -61,7 +61,6 @@ __FBSDID("$FreeBSD$");
#include "pci_emul.h"
#include "pci_lpc.h"
#include "xmsr.h"
-#include "ioapic.h"
#include "spinup_ap.h"
#include "rtc.h"
@@ -663,8 +662,6 @@ main(int argc, char *argv[])
if (init_pci(ctx) != 0)
exit(1);
- ioapic_init(0);
-
if (gdb_port != 0)
init_dbgport(gdb_port);
diff --git a/usr.sbin/bhyve/ioapic.c b/usr.sbin/bhyve/ioapic.c
deleted file mode 100644
index b996214..0000000
--- a/usr.sbin/bhyve/ioapic.c
+++ /dev/null
@@ -1,378 +0,0 @@
-/*-
- * Copyright (c) 2012 NetApp, Inc.
- * 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 NETAPP, INC ``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 NETAPP, INC 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$
- */
-
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include <sys/types.h>
-
-#include <x86/apicreg.h>
-#include <machine/vmm.h>
-
-#include <string.h>
-#include <assert.h>
-#include <stdbool.h>
-#include <pthread.h>
-
-#include <vmmapi.h>
-
-#include "inout.h"
-#include "mem.h"
-#include "bhyverun.h"
-
-#include <stdio.h>
-
-static uint64_t ioapic_clearpend, ioapic_togglepend, ioapic_setpend;
-
-#define IOAPIC_PADDR 0xFEC00000
-
-#define IOREGSEL 0x00
-#define IOWIN 0x10
-
-#define REDIR_ENTRIES 16
-#define INTR_ASSERTED(ioapic, pin) \
- ((ioapic)->rtbl[(pin)].pinstate == true)
-
-struct ioapic {
- int inited;
- uint32_t id;
- struct {
- uint64_t reg;
- bool pinstate;
- bool pending;
- } rtbl[REDIR_ENTRIES];
-
- uintptr_t paddr; /* gpa where the ioapic is mapped */
- uint32_t ioregsel;
- struct memory_region *region;
- pthread_mutex_t mtx;
-};
-
-static struct ioapic ioapics[1]; /* only a single ioapic for now */
-
-static int ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic,
- uintptr_t paddr, int size, uint64_t *data);
-static int ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic,
- uintptr_t paddr, int size, uint64_t data);
-static int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir,
- uintptr_t paddr, int size, uint64_t *val, void *arg1, long arg2);
-
-static void
-ioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate)
-{
- int vector, apicid, vcpu;
- uint32_t low, high;
- struct ioapic *ioapic;
-
- ioapic = &ioapics[0]; /* assume a single ioapic */
-
- /* Nothing to do if interrupt pin has not changed state */
- if (ioapic->rtbl[pin].pinstate == newstate)
- return;
-
- ioapic->rtbl[pin].pinstate = newstate; /* record it */
-
- /* Nothing to do if interrupt pin is deasserted */
- if (!INTR_ASSERTED(ioapic, pin))
- return;
-
- /*
- * XXX
- * We only deal with:
- * - edge triggered interrupts
- * - fixed delivery mode
- * Level-triggered sources will work so long as there is
- * no sharing.
- */
- low = ioapic->rtbl[pin].reg;
- high = ioapic->rtbl[pin].reg >> 32;
- if ((low & IOART_INTMASK) == IOART_INTMCLR &&
- (low & IOART_DESTMOD) == IOART_DESTPHY &&
- (low & IOART_DELMOD) == IOART_DELFIXED) {
- vector = low & IOART_INTVEC;
- apicid = high >> APIC_ID_SHIFT;
- if (apicid != 0xff) {
- /* unicast */
- vcpu = vm_apicid2vcpu(ctx, apicid);
- vm_lapic_irq(ctx, vcpu, vector);
- } else {
- /* broadcast */
- vcpu = 0;
- while (vcpu < guest_ncpus) {
- vm_lapic_irq(ctx, vcpu, vector);
- vcpu++;
- }
- }
- } else if ((low & IOART_INTMASK) != IOART_INTMCLR &&
- low & IOART_TRGRLVL) {
- /*
- * For level-triggered interrupts that have been
- * masked, set the pending bit so that an interrupt
- * will be generated on unmask and if the level is
- * still asserted
- */
- ioapic_setpend++;
- ioapic->rtbl[pin].pending = true;
- }
-}
-
-static void
-ioapic_set_pinstate_locked(struct vmctx *ctx, int pin, bool newstate)
-{
- struct ioapic *ioapic;
-
- if (pin < 0 || pin >= REDIR_ENTRIES)
- return;
-
- ioapic = &ioapics[0];
-
- pthread_mutex_lock(&ioapic->mtx);
- ioapic_set_pinstate(ctx, pin, newstate);
- pthread_mutex_unlock(&ioapic->mtx);
-}
-
-/*
- * External entry points require locking
- */
-void
-ioapic_deassert_pin(struct vmctx *ctx, int pin)
-{
- ioapic_set_pinstate_locked(ctx, pin, false);
-}
-
-void
-ioapic_assert_pin(struct vmctx *ctx, int pin)
-{
- ioapic_set_pinstate_locked(ctx, pin, true);
-}
-
-void
-ioapic_init(int which)
-{
- struct mem_range memp;
- struct ioapic *ioapic;
- int error;
- int i;
-
- assert(which == 0);
-
- ioapic = &ioapics[which];
- assert(ioapic->inited == 0);
-
- bzero(ioapic, sizeof(struct ioapic));
-
- pthread_mutex_init(&ioapic->mtx, NULL);
-
- /* Initialize all redirection entries to mask all interrupts */
- for (i = 0; i < REDIR_ENTRIES; i++)
- ioapic->rtbl[i].reg = 0x0001000000010000UL;
-
- ioapic->paddr = IOAPIC_PADDR;
-
- /* Register emulated memory region */
- memp.name = "ioapic";
- memp.flags = MEM_F_RW;
- memp.handler = ioapic_region_handler;
- memp.arg1 = ioapic;
- memp.arg2 = which;
- memp.base = ioapic->paddr;
- memp.size = sizeof(struct IOAPIC);
- error = register_mem(&memp);
-
- assert (error == 0);
-
- ioapic->inited = 1;
-}
-
-static uint32_t
-ioapic_read(struct ioapic *ioapic, uint32_t addr)
-{
- int regnum, pin, rshift;
-
- assert(ioapic->inited);
-
- regnum = addr & 0xff;
- switch (regnum) {
- case IOAPIC_ID:
- return (ioapic->id);
- break;
- case IOAPIC_VER:
- return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
- break;
- case IOAPIC_ARB:
- return (ioapic->id);
- break;
- default:
- break;
- }
-
- /* redirection table entries */
- if (regnum >= IOAPIC_REDTBL &&
- regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
- pin = (regnum - IOAPIC_REDTBL) / 2;
- if ((regnum - IOAPIC_REDTBL) % 2)
- rshift = 32;
- else
- rshift = 0;
-
- return (ioapic->rtbl[pin].reg >> rshift);
- }
-
- return (0);
-}
-
-static void
-ioapic_write(struct vmctx *vm, struct ioapic *ioapic, uint32_t addr,
- uint32_t data)
-{
- int regnum, pin, lshift;
-
- assert(ioapic->inited);
-
- regnum = addr & 0xff;
- switch (regnum) {
- case IOAPIC_ID:
- ioapic->id = data & APIC_ID_MASK;
- break;
- case IOAPIC_VER:
- case IOAPIC_ARB:
- /* readonly */
- break;
- default:
- break;
- }
-
- /* redirection table entries */
- if (regnum >= IOAPIC_REDTBL &&
- regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
- pin = (regnum - IOAPIC_REDTBL) / 2;
- if ((regnum - IOAPIC_REDTBL) % 2)
- lshift = 32;
- else
- lshift = 0;
-
- ioapic->rtbl[pin].reg &= ~((uint64_t)0xffffffff << lshift);
- ioapic->rtbl[pin].reg |= ((uint64_t)data << lshift);
-
- if (ioapic->rtbl[pin].pending &&
- ((ioapic->rtbl[pin].reg & IOART_INTMASK) ==
- IOART_INTMCLR)) {
- ioapic->rtbl[pin].pending = false;
- ioapic_clearpend++;
- /*
- * Inject the deferred level-triggered int if it is
- * still asserted. Simulate by toggling the pin
- * off and then on.
- */
- if (ioapic->rtbl[pin].pinstate == true) {
- ioapic_togglepend++;
- ioapic_set_pinstate(vm, pin, false);
- ioapic_set_pinstate(vm, pin, true);
- }
- }
- }
-}
-
-static int
-ioapic_region_read(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
- int size, uint64_t *data)
-{
- int offset;
-
- offset = paddr - ioapic->paddr;
-
- /*
- * The IOAPIC specification allows 32-bit wide accesses to the
- * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
- */
- if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
-#if 1
- printf("invalid access to ioapic%d: size %d, offset %d\n",
- (int)(ioapic - ioapics), size, offset);
-#endif
- *data = 0;
- return (0);
- }
-
- if (offset == IOREGSEL)
- *data = ioapic->ioregsel;
- else
- *data = ioapic_read(ioapic, ioapic->ioregsel);
-
- return (0);
-}
-
-static int
-ioapic_region_write(struct vmctx *vm, struct ioapic *ioapic, uintptr_t paddr,
- int size, uint64_t data)
-{
- int offset;
-
- offset = paddr - ioapic->paddr;
-
- /*
- * The ioapic specification allows 32-bit wide accesses to the
- * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
- */
- if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
-#if 1
- printf("invalid access to ioapic%d: size %d, offset %d\n",
- (int)(ioapic - ioapics), size, offset);
-#endif
- return (0);
- }
-
- if (offset == IOREGSEL)
- ioapic->ioregsel = data;
- else
- ioapic_write(vm, ioapic, ioapic->ioregsel, data);
-
- return (0);
-}
-
-static int
-ioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr,
- int size, uint64_t *val, void *arg1, long arg2)
-{
- struct ioapic *ioapic;
- int which;
-
- ioapic = arg1;
- which = arg2;
-
- assert(ioapic == &ioapics[which]);
-
- pthread_mutex_lock(&ioapic->mtx);
- if (dir == MEM_F_READ)
- ioapic_region_read(vm, ioapic, paddr, size, val);
- else
- ioapic_region_write(vm, ioapic, paddr, size, *val);
- pthread_mutex_unlock(&ioapic->mtx);
-
- return (0);
-}
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index 3820b04..fd087fa 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -49,7 +49,6 @@ __FBSDID("$FreeBSD$");
#include "legacy_irq.h"
#include "mem.h"
#include "pci_emul.h"
-#include "ioapic.h"
#define CONF1_ADDR_PORT 0x0cf8
#define CONF1_DATA_PORT 0x0cfc
@@ -1136,7 +1135,7 @@ pci_lintr_assert(struct pci_devinst *pi)
{
assert(pi->pi_lintr_pin >= 0);
- ioapic_assert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
+ vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_pin);
}
void
@@ -1144,7 +1143,7 @@ pci_lintr_deassert(struct pci_devinst *pi)
{
assert(pi->pi_lintr_pin >= 0);
- ioapic_deassert_pin(pi->pi_vmctx, pi->pi_lintr_pin);
+ vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_pin);
}
/*
diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c
index 9fd5f5c..5dc79e0 100644
--- a/usr.sbin/bhyve/pci_lpc.c
+++ b/usr.sbin/bhyve/pci_lpc.c
@@ -31,13 +31,16 @@
__FBSDID("$FreeBSD$");
#include <sys/types.h>
+#include <machine/vmm.h>
+#include <machine/vmm_dev.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <vmmapi.h>
+
#include "inout.h"
-#include "ioapic.h"
#include "pci_emul.h"
#include "uart_emul.h"
@@ -91,7 +94,7 @@ lpc_uart_intr_assert(void *arg)
assert(sc->irq >= 0);
- ioapic_assert_pin(lpc_bridge->pi_vmctx, sc->irq);
+ vm_ioapic_assert_irq(lpc_bridge->pi_vmctx, sc->irq);
}
static void
@@ -101,7 +104,7 @@ lpc_uart_intr_deassert(void *arg)
assert(sc->irq >= 0);
- ioapic_deassert_pin(lpc_bridge->pi_vmctx, sc->irq);
+ vm_ioapic_deassert_irq(lpc_bridge->pi_vmctx, sc->irq);
}
static int
diff --git a/usr.sbin/bhyve/pit_8254.c b/usr.sbin/bhyve/pit_8254.c
index 88c2d22..fd05b00 100644
--- a/usr.sbin/bhyve/pit_8254.c
+++ b/usr.sbin/bhyve/pit_8254.c
@@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$");
#include "bhyverun.h"
#include "inout.h"
-#include "ioapic.h"
#include "mevent.h"
#include "pit_8254.h"
@@ -106,8 +105,8 @@ pit_mevent_cb(int fd, enum ev_type type, void *param)
pit_mev_count++;
- ioapic_assert_pin(c->ctx, 2);
- ioapic_deassert_pin(c->ctx, 2);
+ vm_ioapic_assert_irq(c->ctx, 2);
+ vm_ioapic_deassert_irq(c->ctx, 2);
/*
* Delete the timer for one-shots
OpenPOWER on IntegriCloud