svn commit: r263035 - in head: lib/libvmmapi sys/amd64/include sys/amd64/vmm sys/amd64/vmm/intel sys/amd64/vmm/io sys/modules/vmm usr.sbin/bhyve

Tycho Nightingale tychon at FreeBSD.org
Tue Mar 11 16:56:03 UTC 2014


Author: tychon
Date: Tue Mar 11 16:56:00 2014
New Revision: 263035
URL: http://svnweb.freebsd.org/changeset/base/263035

Log:
  Replace the userspace atpic stub with a more functional vmm.ko model.
  
  New ioctls VM_ISA_ASSERT_IRQ, VM_ISA_DEASSERT_IRQ and VM_ISA_PULSE_IRQ
  can be used to manipulate the pic, and optionally the ioapic, pin state.
  
  Reviewed by:	jhb, neel
  Approved by:	neel (co-mentor)

Added:
  head/sys/amd64/vmm/io/vatpic.c   (contents, props changed)
  head/sys/amd64/vmm/io/vatpic.h   (contents, props changed)
  head/sys/amd64/vmm/vmm_ioport.c   (contents, props changed)
  head/sys/amd64/vmm/vmm_ioport.h   (contents, props changed)
Deleted:
  head/usr.sbin/bhyve/atpic.c
  head/usr.sbin/bhyve/elcr.c
Modified:
  head/lib/libvmmapi/vmmapi.c
  head/lib/libvmmapi/vmmapi.h
  head/sys/amd64/include/vmm.h
  head/sys/amd64/include/vmm_dev.h
  head/sys/amd64/vmm/intel/vmx.c
  head/sys/amd64/vmm/io/vhpet.c
  head/sys/amd64/vmm/io/vlapic.c
  head/sys/amd64/vmm/io/vlapic_priv.h
  head/sys/amd64/vmm/vmm.c
  head/sys/amd64/vmm/vmm_dev.c
  head/sys/modules/vmm/Makefile
  head/usr.sbin/bhyve/Makefile
  head/usr.sbin/bhyve/pci_lpc.c
  head/usr.sbin/bhyve/pit_8254.c

Modified: head/lib/libvmmapi/vmmapi.c
==============================================================================
--- head/lib/libvmmapi/vmmapi.c	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/lib/libvmmapi/vmmapi.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -458,6 +458,41 @@ vm_ioapic_pincount(struct vmctx *ctx, in
 }
 
 int
+vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
+{
+	struct vm_isa_irq isa_irq;
+
+	bzero(&isa_irq, sizeof(struct vm_isa_irq));
+	isa_irq.atpic_irq = atpic_irq;
+	isa_irq.ioapic_irq = ioapic_irq;
+
+	return (ioctl(ctx->fd, VM_ISA_ASSERT_IRQ, &isa_irq));
+}
+
+int
+vm_isa_deassert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
+{
+	struct vm_isa_irq isa_irq;
+
+	bzero(&isa_irq, sizeof(struct vm_isa_irq));
+	isa_irq.atpic_irq = atpic_irq;
+	isa_irq.ioapic_irq = ioapic_irq;
+
+	return (ioctl(ctx->fd, VM_ISA_DEASSERT_IRQ, &isa_irq));
+}
+
+int
+vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq)
+{
+	struct vm_isa_irq isa_irq;
+	bzero(&isa_irq, sizeof(struct vm_isa_irq));
+	isa_irq.atpic_irq = atpic_irq;
+	isa_irq.ioapic_irq = ioapic_irq;
+
+	return (ioctl(ctx->fd, VM_ISA_PULSE_IRQ, &isa_irq));
+}
+
+int
 vm_inject_nmi(struct vmctx *ctx, int vcpu)
 {
 	struct vm_nmi vmnmi;

Modified: head/lib/libvmmapi/vmmapi.h
==============================================================================
--- head/lib/libvmmapi/vmmapi.h	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/lib/libvmmapi/vmmapi.h	Tue Mar 11 16:56:00 2014	(r263035)
@@ -71,6 +71,9 @@ int	vm_ioapic_assert_irq(struct vmctx *c
 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_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);
+int	vm_isa_pulse_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq);
 int	vm_inject_nmi(struct vmctx *ctx, int vcpu);
 int	vm_capability_name2type(const char *capname);
 const char *vm_capability_type2name(int type);

Modified: head/sys/amd64/include/vmm.h
==============================================================================
--- head/sys/amd64/include/vmm.h	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/include/vmm.h	Tue Mar 11 16:56:00 2014	(r263035)
@@ -187,6 +187,7 @@ void vcpu_notify_event(struct vm *vm, in
 struct vmspace *vm_get_vmspace(struct vm *vm);
 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);
+struct vatpic *vm_atpic(struct vm *vm);
 
 /*
  * Inject exception 'vme' into the guest vcpu. This function returns 0 on

Modified: head/sys/amd64/include/vmm_dev.h
==============================================================================
--- head/sys/amd64/include/vmm_dev.h	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/include/vmm_dev.h	Tue Mar 11 16:56:00 2014	(r263035)
@@ -79,6 +79,11 @@ struct vm_ioapic_irq {
 	int		irq;
 };
 
+struct vm_isa_irq {
+	int		atpic_irq;
+	int		ioapic_irq;
+};
+
 struct vm_capability {
 	int		cpuid;
 	enum vm_cap_type captype;
@@ -198,6 +203,11 @@ enum {
 	IOCNUM_SET_X2APIC_STATE = 60,
 	IOCNUM_GET_X2APIC_STATE = 61,
 	IOCNUM_GET_HPET_CAPABILITIES = 62,
+
+	/* legacy interrupt injection */
+	IOCNUM_ISA_ASSERT_IRQ = 80,
+	IOCNUM_ISA_DEASSERT_IRQ = 81,
+	IOCNUM_ISA_PULSE_IRQ = 82,
 };
 
 #define	VM_RUN		\
@@ -230,6 +240,12 @@ enum {
 	_IOW('v', IOCNUM_IOAPIC_PULSE_IRQ, struct vm_ioapic_irq)
 #define	VM_IOAPIC_PINCOUNT	\
 	_IOR('v', IOCNUM_IOAPIC_PINCOUNT, int)
+#define	VM_ISA_ASSERT_IRQ	\
+	_IOW('v', IOCNUM_ISA_ASSERT_IRQ, struct vm_isa_irq)
+#define	VM_ISA_DEASSERT_IRQ	\
+	_IOW('v', IOCNUM_ISA_DEASSERT_IRQ, struct vm_isa_irq)
+#define	VM_ISA_PULSE_IRQ	\
+	_IOW('v', IOCNUM_ISA_PULSE_IRQ, struct vm_isa_irq)
 #define	VM_SET_CAPABILITY \
 	_IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability)
 #define	VM_GET_CAPABILITY \

Modified: head/sys/amd64/vmm/intel/vmx.c
==============================================================================
--- head/sys/amd64/vmm/intel/vmx.c	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/vmm/intel/vmx.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/vmm.h>
 #include <machine/vmm_dev.h>
 #include "vmm_host.h"
+#include "vmm_ioport.h"
 #include "vmm_ipi.h"
 #include "vmm_msr.h"
 #include "vmm_ktr.h"
@@ -1861,6 +1862,11 @@ vmx_exit_process(struct vmx *vmx, int vc
 		vmexit->u.inout.rep = (qual & 0x20) ? 1 : 0;
 		vmexit->u.inout.port = (uint16_t)(qual >> 16);
 		vmexit->u.inout.eax = (uint32_t)(vmxctx->guest_rax);
+		error = emulate_ioport(vmx->vm, vcpu, vmexit);
+		if (error == 0)  {
+			handled = 1;
+			vmxctx->guest_rax = vmexit->u.inout.eax;
+		}
 		break;
 	case EXIT_REASON_CPUID:
 		vmm_stat_incr(vmx->vm, vcpu, VMEXIT_CPUID, 1);

Added: head/sys/amd64/vmm/io/vatpic.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/amd64/vmm/io/vatpic.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -0,0 +1,595 @@
+/*-
+ * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale at pluribusnetworks.com>
+ * 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 ``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/types.h>
+#include <sys/queue.h>
+#include <sys/cpuset.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/systm.h>
+
+#include <x86/apicreg.h>
+#include <dev/ic/i8259.h>
+
+#include <machine/vmm.h>
+
+#include "vmm_ktr.h"
+#include "vmm_lapic.h"
+#include "vioapic.h"
+#include "vatpic.h"
+
+static MALLOC_DEFINE(M_VATPIC, "atpic", "bhyve virtual atpic (8259)");
+
+#define	VATPIC_LOCK(vatpic)		mtx_lock_spin(&((vatpic)->mtx))
+#define	VATPIC_UNLOCK(vatpic)		mtx_unlock_spin(&((vatpic)->mtx))
+#define	VATPIC_LOCKED(vatpic)		mtx_owned(&((vatpic)->mtx))
+
+enum irqstate {
+	IRQSTATE_ASSERT,
+	IRQSTATE_DEASSERT,
+	IRQSTATE_PULSE
+};
+
+struct atpic {
+	bool		ready;
+	int		icw_num;
+	int		rd_cmd_reg;
+
+	bool		aeoi;
+	bool		poll;
+	bool		rotate;
+
+	int		irq_base;
+	uint8_t		request;	/* Interrupt Request Register (IIR) */
+	uint8_t		service;	/* Interrupt Service (ISR) */
+	uint8_t		mask;		/* Interrupt Mask Register (IMR) */
+
+	int		acnt[8];	/* sum of pin asserts and deasserts */
+	int		priority;	/* current pin priority */
+};
+
+struct vatpic {
+	struct vm	*vm;
+	struct mtx	mtx;
+	struct atpic	atpic[2];
+	uint8_t		elc[2];
+};
+
+#define	VATPIC_CTR0(vatpic, fmt)					\
+	VM_CTR0((vatpic)->vm, fmt)
+
+#define	VATPIC_CTR1(vatpic, fmt, a1)					\
+	VM_CTR1((vatpic)->vm, fmt, a1)
+
+#define	VATPIC_CTR2(vatpic, fmt, a1, a2)				\
+	VM_CTR2((vatpic)->vm, fmt, a1, a2)
+
+#define	VATPIC_CTR3(vatpic, fmt, a1, a2, a3)				\
+	VM_CTR3((vatpic)->vm, fmt, a1, a2, a3)
+
+#define	VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4)			\
+	VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4)
+
+
+static __inline int
+vatpic_get_highest_isrpin(struct atpic *atpic)
+{
+	int bit, pin;
+	int i;
+
+	for (i = 0; i <= 7; i++) {
+		pin = ((i + 7 - atpic->priority) & 0x7);
+                bit = (1 << pin);
+
+		if (atpic->service & bit)
+			return (pin);
+	}
+
+	return (-1);
+}
+
+static __inline int
+vatpic_get_highest_irrpin(struct atpic *atpic)
+{
+	int bit, pin;
+	int i, j;
+
+	for (i = 0; i <= 7; i++) {
+		pin = ((i + 7 - atpic->priority) & 0x7);
+		bit = (1 << pin);
+		if (atpic->service & bit)
+			break;
+	}
+
+	for (j = 0; j < i; j++) {
+		pin = ((j + 7 - atpic->priority) & 0x7);
+		bit = (1 << pin);
+		if (atpic->request & bit && (~atpic->mask & bit))
+			return (pin);
+	}
+
+	return (-1);
+}
+
+static void
+vatpic_notify_intr(struct vatpic *vatpic)
+{
+	struct atpic *atpic;
+	int pin;
+
+	KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked"));
+
+	/* XXX master only */
+	atpic = &vatpic->atpic[0];
+
+	if ((pin = vatpic_get_highest_irrpin(atpic)) != -1) {
+		VATPIC_CTR4(vatpic, "atpic notify pin = %d "
+		    "(imr 0x%x irr 0x%x isr 0x%x)", pin,
+		    atpic->mask, atpic->request, atpic->service);
+		lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0);
+		vioapic_pulse_irq(vatpic->vm, 0);
+	} else {
+		VATPIC_CTR3(vatpic, "atpic no eligible interrupts "
+		    "(imr 0x%x irr 0x%x isr 0x%x)",
+		    atpic->mask, atpic->request, atpic->service);
+	}
+}
+
+static int
+vatpic_icw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic icw1 0x%x", val);
+
+	atpic->ready = false;
+
+	atpic->icw_num = 1;
+	atpic->mask = 0;
+	atpic->priority = 0;
+	atpic->rd_cmd_reg = 0;
+
+	if ((val & ICW1_SNGL) != 0) {
+		VATPIC_CTR0(vatpic, "vatpic cascade mode required");
+		return (-1);
+	}
+
+	if ((val & ICW1_IC4) == 0) {
+		VATPIC_CTR0(vatpic, "vatpic icw4 required");
+		return (-1);
+	}
+
+	atpic->icw_num++;
+
+	return (0);
+}
+
+static int
+vatpic_icw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic icw2 0x%x", val);
+
+	atpic->irq_base = val & 0xf8;
+
+	atpic->icw_num++;
+
+	return (0);
+}
+
+static int
+vatpic_icw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic icw3 0x%x", val);
+
+	atpic->icw_num++;
+
+	return (0);
+}
+
+static int
+vatpic_icw4(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic icw4 0x%x", val);
+
+	if ((val & ICW4_8086) == 0) {
+		VATPIC_CTR0(vatpic, "vatpic microprocessor mode required");
+		return (-1);
+	}
+
+	if ((val & ICW4_AEOI) != 0)
+		atpic->aeoi = true;
+
+	atpic->icw_num = 0;
+	atpic->ready = true;
+
+	return (0);
+}
+
+static int
+vatpic_ocw1(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic ocw1 0x%x", val);
+
+	atpic->mask = val & 0xff;
+
+	return (0);
+}
+
+static int
+vatpic_ocw2(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic ocw2 0x%x", val);
+
+	atpic->rotate = ((val & OCW2_R) != 0);
+
+	if ((val & OCW2_EOI) != 0) {
+		int isr_bit;
+
+		if ((val & OCW2_SL) != 0) {
+			/* specific EOI */
+			isr_bit = val & 0x7;
+		} else {
+			/* non-specific EOI */
+			isr_bit = vatpic_get_highest_isrpin(atpic);
+		}
+
+		if (isr_bit != -1) {
+			atpic->service &= ~(1 << isr_bit);
+
+			if (atpic->rotate)
+				atpic->priority = isr_bit;
+		}
+	} else if ((val & OCW2_SL) != 0 && atpic->rotate == true) {
+		/* specific priority */
+		atpic->priority = val & 0x7;
+	}
+
+	return (0);
+}
+
+static int
+vatpic_ocw3(struct vatpic *vatpic, struct atpic *atpic, uint8_t val)
+{
+	VATPIC_CTR1(vatpic, "atpic ocw3 0x%x", val);
+
+	atpic->poll = ((val & OCW3_P) != 0);
+
+	if (val & OCW3_RR) {
+		/* read register command */
+		atpic->rd_cmd_reg = val & OCW3_RIS;
+	}
+
+	return (0);
+}
+
+static void
+vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate)
+{
+	struct atpic *atpic;
+	int oldcnt, newcnt;
+	bool level;
+
+	KASSERT(pin >= 0 && pin < 16,
+	    ("vatpic_set_pinstate: invalid pin number %d", pin));
+	KASSERT(VATPIC_LOCKED(vatpic),
+	    ("vatpic_set_pinstate: vatpic is not locked"));
+
+	atpic = &vatpic->atpic[pin >> 3];
+
+	oldcnt = atpic->acnt[pin & 0x7];
+	if (newstate)
+		atpic->acnt[pin & 0x7]++;
+	else
+		atpic->acnt[pin & 0x7]--;
+	newcnt = atpic->acnt[pin & 0x7];
+
+	if (newcnt < 0) {
+		VATPIC_CTR2(vatpic, "atpic pin%d: bad acnt %d", pin, newcnt);
+	}
+
+	level = ((vatpic->elc[pin >> 3] & (1 << (pin & 0x7))) != 0);
+
+	if ((oldcnt == 0 && newcnt == 1) || (newcnt > 0 && level == true)) {
+		/* rising edge or level */
+		VATPIC_CTR1(vatpic, "atpic pin%d: asserted", pin);
+		atpic->request |= (1 << (pin & 0x7));
+	} else if (oldcnt == 1 && newcnt == 0) {
+		/* falling edge */
+		VATPIC_CTR1(vatpic, "atpic pin%d: deasserted", pin);
+	} else {
+		VATPIC_CTR3(vatpic, "atpic pin%d: %s, ignored, acnt %d",
+		    pin, newstate ? "asserted" : "deasserted", newcnt);
+	}
+
+	vatpic_notify_intr(vatpic);
+}
+
+static int
+vatpic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
+{
+	struct vatpic *vatpic;
+	struct atpic *atpic;
+
+	if (irq < 0 || irq > 15)
+		return (EINVAL);
+
+	vatpic = vm_atpic(vm);
+	atpic = &vatpic->atpic[irq >> 3];
+
+	if (atpic->ready == false)
+		return (0);
+
+	VATPIC_LOCK(vatpic);
+	switch (irqstate) {
+	case IRQSTATE_ASSERT:
+		vatpic_set_pinstate(vatpic, irq, true);
+		break;
+	case IRQSTATE_DEASSERT:
+		vatpic_set_pinstate(vatpic, irq, false);
+		break;
+	case IRQSTATE_PULSE:
+		vatpic_set_pinstate(vatpic, irq, true);
+		vatpic_set_pinstate(vatpic, irq, false);
+		break;
+	default:
+		panic("vatpic_set_irqstate: invalid irqstate %d", irqstate);
+	}
+	VATPIC_UNLOCK(vatpic);
+
+	return (0);
+}
+
+int
+vatpic_assert_irq(struct vm *vm, int irq)
+{
+	return (vatpic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
+}
+
+int
+vatpic_deassert_irq(struct vm *vm, int irq)
+{
+	return (vatpic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
+}
+
+int
+vatpic_pulse_irq(struct vm *vm, int irq)
+{
+	return (vatpic_set_irqstate(vm, irq, IRQSTATE_PULSE));
+}
+
+int
+vatpic_pending_intr(struct vm *vm, int *vecptr)
+{
+	struct vatpic *vatpic;
+	struct atpic *atpic;
+	int pin;
+
+	vatpic = vm_atpic(vm);
+
+	/* XXX master only */
+	atpic = &vatpic->atpic[0];
+
+	VATPIC_LOCK(vatpic);
+
+	pin = vatpic_get_highest_irrpin(atpic);
+	if (pin == -1)
+		pin = 7;
+
+	*vecptr = atpic->irq_base + pin;
+
+	VATPIC_UNLOCK(vatpic);
+
+	return (1);
+}
+
+void
+vatpic_intr_accepted(struct vm *vm, int vector)
+{
+	struct vatpic *vatpic;
+	struct atpic *atpic;
+	int pin;
+
+	vatpic = vm_atpic(vm);
+
+	/* XXX master only */
+	atpic = &vatpic->atpic[0];
+
+	VATPIC_LOCK(vatpic);
+	pin = vector & 0x7;
+
+	if (atpic->acnt[pin] == 0)
+		atpic->request &= ~(1 << pin);
+
+	if (atpic->aeoi == true) {
+		if (atpic->rotate == true)
+			atpic->priority = pin;
+	} else {
+		atpic->service |= (1 << pin);
+	}
+
+	vatpic_notify_intr(vatpic);
+
+	VATPIC_UNLOCK(vatpic);
+}
+
+int
+vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
+{
+	struct vatpic *vatpic;
+	struct atpic *atpic;
+	int error;
+	uint8_t val;
+
+	vatpic = vm_atpic(vm);
+	atpic = &vatpic->atpic[0];
+
+	if (vmexit->u.inout.bytes != 1)
+		return (-1);
+
+	if (vmexit->u.inout.in) {
+		VATPIC_LOCK(vatpic);
+		if (atpic->poll) {
+			VATPIC_CTR0(vatpic, "vatpic polled mode not "
+			    "supported");
+			VATPIC_UNLOCK(vatpic);
+			return (-1);
+		} else {
+			if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
+				/* read interrrupt mask register */
+				vmexit->u.inout.eax = atpic->mask;
+			} else {
+				if (atpic->rd_cmd_reg == OCW3_RIS) {
+					/* read interrupt service register */
+					vmexit->u.inout.eax = atpic->service;
+				} else {
+					/* read interrupt request register */
+					vmexit->u.inout.eax = atpic->request;
+				}
+			}
+		}
+		VATPIC_UNLOCK(vatpic);
+
+		return (0);
+	}
+
+	val = vmexit->u.inout.eax;
+
+	VATPIC_LOCK(vatpic);
+
+	if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
+		if (atpic->ready) {
+			error = vatpic_ocw1(vatpic, atpic, val);
+		} else {
+			switch (atpic->icw_num) {
+			case 2:
+				error = vatpic_icw2(vatpic, atpic, val);
+				break;
+			case 3:
+				error = vatpic_icw3(vatpic, atpic, val);
+				break;
+			case 4:
+				error = vatpic_icw4(vatpic, atpic, val);
+				break;
+			}
+		}
+	} else {
+		if (val & (1 << 4))
+			error = vatpic_icw1(vatpic, atpic, val);
+
+		if (atpic->ready) {
+			if (val & (1 << 3))
+				error = vatpic_ocw3(vatpic, atpic, val);
+			else
+				error = vatpic_ocw2(vatpic, atpic, val);
+		}
+	}
+
+	if (atpic->ready)
+		vatpic_notify_intr(vatpic);
+
+	VATPIC_UNLOCK(vatpic);
+
+	return (error);
+}
+
+int
+vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
+{
+	if (vmexit->u.inout.bytes != 1)
+		return (-1);
+ 
+	if (vmexit->u.inout.in) {
+		if (vmexit->u.inout.port & ICU_IMR_OFFSET) {
+			/* all interrupts masked */
+			vmexit->u.inout.eax = 0xff;
+		} else {
+			vmexit->u.inout.eax = 0x00;
+		}
+	}
+ 
+	/* Pretend all accesses to the slave 8259 are alright */
+	return (0);
+}
+
+int
+vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit)
+{
+	struct vatpic *vatpic;
+	bool is_master;
+
+	vatpic = vm_atpic(vm);
+	is_master = (vmexit->u.inout.port == IO_ELCR1);
+
+	if (vmexit->u.inout.bytes != 1)
+		return (-1);
+
+	if (vmexit->u.inout.in) {
+		if (is_master)
+			vmexit->u.inout.eax = vatpic->elc[0];
+		else
+			vmexit->u.inout.eax = vatpic->elc[1];
+	} else {
+		/*
+		 * For the master PIC the cascade channel (IRQ2), the
+		 * heart beat timer (IRQ0), and the keyboard
+		 * controller (IRQ1) cannot be programmed for level
+		 * mode.
+		 *
+		 * For the slave PIC the real time clock (IRQ8) and
+		 * the floating point error interrupt (IRQ13) cannot
+		 * be programmed for level mode.
+		 */
+		if (is_master)
+			vatpic->elc[0] = (vmexit->u.inout.eax & 0xf8);
+		else
+			vatpic->elc[1] = (vmexit->u.inout.eax & 0xde);
+	}
+
+	return (0);
+}
+
+struct vatpic *
+vatpic_init(struct vm *vm)
+{
+	struct vatpic *vatpic;
+
+	vatpic = malloc(sizeof(struct vatpic), M_VATPIC, M_WAITOK | M_ZERO);
+	vatpic->vm = vm;
+
+	mtx_init(&vatpic->mtx, "vatpic lock", NULL, MTX_SPIN);
+
+	return (vatpic);
+}
+
+void
+vatpic_cleanup(struct vatpic *vatpic)
+{
+	free(vatpic, M_VATPIC);
+}

Added: head/sys/amd64/vmm/io/vatpic.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/amd64/vmm/io/vatpic.h	Tue Mar 11 16:56:00 2014	(r263035)
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale at pluribusnetworks.com>
+ * 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 ``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$
+ */
+
+#ifndef _VATPIC_H_
+#define	_VATPIC_H_
+
+#include <isa/isareg.h>
+
+#define	ICU_IMR_OFFSET	1
+
+#define	IO_ELCR1	0x4d0
+#define	IO_ELCR2	0x4d1
+
+struct vatpic *vatpic_init(struct vm *vm);
+void vatpic_cleanup(struct vatpic *vatpic);
+
+int vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
+int vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
+int vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit);
+
+int vatpic_assert_irq(struct vm *vm, int irq);
+int vatpic_deassert_irq(struct vm *vm, int irq);
+int vatpic_pulse_irq(struct vm *vm, int irq);
+
+int vatpic_pending_intr(struct vm *vm, int *vecptr);
+void vatpic_intr_accepted(struct vm *vm, int vector);
+
+#endif	/* _VATPIC_H_ */

Modified: head/sys/amd64/vmm/io/vhpet.c
==============================================================================
--- head/sys/amd64/vmm/io/vhpet.c	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/vmm/io/vhpet.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/vmm_dev.h>
 
 #include "vmm_lapic.h"
+#include "vatpic.h"
 #include "vioapic.h"
 #include "vhpet.h"
 
@@ -167,6 +168,25 @@ vhpet_timer_ioapic_pin(struct vhpet *vhp
 	return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
 }
 
+static __inline int
+vhpet_timer_atpic_pin(struct vhpet *vhpet, int n)
+{
+	if (vhpet->config & HPET_CNF_LEG_RT) {
+		/*
+		 * In "legacy routing" timers 0 and 1 are connected to
+		 * 8259 master pin 0 and slave pin 0 respectively.
+		 */
+		switch (n) {
+		case 0:
+			return (0);
+		case 1:
+			return (8);
+		}
+	}
+
+	return (-1);
+}
+
 static uint32_t
 vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
 {
@@ -196,12 +216,17 @@ vhpet_counter(struct vhpet *vhpet, sbint
 static void
 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
 {
-	int pin;
+	int pin, legacy_pin;
 
 	if (vhpet->isr & (1 << n)) {
 		pin = vhpet_timer_ioapic_pin(vhpet, n);
 		KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
 		vioapic_deassert_irq(vhpet->vm, pin);
+
+		legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
+		if (legacy_pin != -1)
+			vatpic_deassert_irq(vhpet->vm, legacy_pin);
+
 		vhpet->isr &= ~(1 << n);
 	}
 }
@@ -242,7 +267,7 @@ vhpet_timer_edge_trig(struct vhpet *vhpe
 static void
 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
 {
-	int pin;
+	int pin, legacy_pin;
 
 	/* If interrupts are not enabled for this timer then just return. */
 	if (!vhpet_timer_interrupt_enabled(vhpet, n))
@@ -268,11 +293,17 @@ vhpet_timer_interrupt(struct vhpet *vhpe
 		return;
 	}
 
+	legacy_pin = vhpet_timer_atpic_pin(vhpet, n);
+
 	if (vhpet_timer_edge_trig(vhpet, n)) {
 		vioapic_pulse_irq(vhpet->vm, pin);
+		if (legacy_pin != -1)
+			vatpic_pulse_irq(vhpet->vm, legacy_pin);
 	} else {
 		vhpet->isr |= 1 << n;
 		vioapic_assert_irq(vhpet->vm, pin);
+		if (legacy_pin != -1)
+			vatpic_assert_irq(vhpet->vm, legacy_pin);
 	}
 }
 

Modified: head/sys/amd64/vmm/io/vlapic.c
==============================================================================
--- head/sys/amd64/vmm/io/vlapic.c	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/vmm/io/vlapic.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
 
 #include "vlapic.h"
 #include "vlapic_priv.h"
+#include "vatpic.h"
 #include "vioapic.h"
 
 #define	PRIO(x)			((x) >> 4)
@@ -299,6 +300,16 @@ vlapic_set_intr_ready(struct vlapic *vla
 	return (1);
 }
 
+static VMM_STAT(VLAPIC_EXTINT_COUNT, "number of ExtINTs received by vlapic");
+
+static void
+vlapic_deliver_extint(struct vlapic *vlapic)
+{
+	vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_EXTINT_COUNT, 1);
+	vlapic->extint_pending = true;
+	vcpu_notify_event(vlapic->vm, vlapic->vcpuid, false);
+}
+
 static __inline uint32_t *
 vlapic_get_lvtptr(struct vlapic *vlapic, uint32_t offset)
 {
@@ -448,6 +459,9 @@ vlapic_fire_lvt(struct vlapic *vlapic, u
 	case APIC_LVT_DM_NMI:
 		vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
 		break;
+	case APIC_LVT_DM_EXTINT:
+		vlapic_deliver_extint(vlapic);
+		break;
 	default:
 		// Other modes ignored
 		return (0);
@@ -651,6 +665,25 @@ vlapic_trigger_lvt(struct vlapic *vlapic
 {
 	uint32_t lvt;
 
+	if (vlapic_enabled(vlapic) == false) {
+		/*
+		 * When the local APIC is global/hardware disabled,
+		 * LINT[1:0] pins are configured as INTR and NMI pins,
+		 * respectively.
+		*/
+		switch (vector) {
+			case APIC_LVT_LINT0:
+				vlapic_deliver_extint(vlapic);
+				break;
+			case APIC_LVT_LINT1:
+				vm_inject_nmi(vlapic->vm, vlapic->vcpuid);
+				break;
+			default:
+				break;
+		}
+		return (0);
+	}
+
 	switch (vector) {
 	case APIC_LVT_LINT0:
 		lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_LINT0_LVT);
@@ -1020,6 +1053,9 @@ vlapic_pending_intr(struct vlapic *vlapi
 	int	  	 idx, i, bitpos, vector;
 	uint32_t	*irrptr, val;
 
+	if (vlapic->extint_pending)
+		return (vatpic_pending_intr(vlapic->vm, vecptr));
+
 	if (vlapic->ops.pending_intr)
 		return ((*vlapic->ops.pending_intr)(vlapic, vecptr));
 
@@ -1054,6 +1090,12 @@ vlapic_intr_accepted(struct vlapic *vlap
 	uint32_t	*irrptr, *isrptr;
 	int		idx, stk_top;
 
+	if (vlapic->extint_pending) {
+		vlapic->extint_pending = false;
+		vatpic_intr_accepted(vlapic->vm, vector);
+		return;
+	}
+
 	if (vlapic->ops.intr_accepted)
 		return ((*vlapic->ops.intr_accepted)(vlapic, vector));
 
@@ -1474,11 +1516,13 @@ vlapic_deliver_intr(struct vm *vm, bool 
 	int vcpuid;
 	cpuset_t dmask;
 
-	if (delmode != APIC_DELMODE_FIXED && delmode != APIC_DELMODE_LOWPRIO) {
+	if (delmode != IOART_DELFIXED &&
+	    delmode != IOART_DELLOPRI &&
+	    delmode != IOART_DELEXINT) {
 		VM_CTR1(vm, "vlapic intr invalid delmode %#x", delmode);
 		return;
 	}
-	lowprio = (delmode == APIC_DELMODE_LOWPRIO);
+	lowprio = (delmode == IOART_DELLOPRI);
 
 	/*
 	 * We don't provide any virtual interrupt redirection hardware so
@@ -1490,7 +1534,11 @@ vlapic_deliver_intr(struct vm *vm, bool 
 	while ((vcpuid = CPU_FFS(&dmask)) != 0) {
 		vcpuid--;
 		CPU_CLR(vcpuid, &dmask);
-		lapic_set_intr(vm, vcpuid, vec, level);
+		if (delmode == IOART_DELEXINT) {
+			vlapic_deliver_extint(vm_lapic(vm, vcpuid));
+		} else {
+			lapic_set_intr(vm, vcpuid, vec, level);
+		}
 	}
 }
 

Modified: head/sys/amd64/vmm/io/vlapic_priv.h
==============================================================================
--- head/sys/amd64/vmm/io/vlapic_priv.h	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/vmm/io/vlapic_priv.h	Tue Mar 11 16:56:00 2014	(r263035)
@@ -156,6 +156,8 @@ struct vlapic {
 	uint32_t		esr_pending;
 	int			esr_firing;
 
+	bool			extint_pending;
+
 	struct callout	callout;	/* vlapic timer */
 	struct bintime	timer_fire_bt;	/* callout expiry time */
 	struct bintime	timer_freq_bt;	/* timer frequency */

Modified: head/sys/amd64/vmm/vmm.c
==============================================================================
--- head/sys/amd64/vmm/vmm.c	Tue Mar 11 16:53:03 2014	(r263034)
+++ head/sys/amd64/vmm/vmm.c	Tue Mar 11 16:56:00 2014	(r263035)
@@ -67,6 +67,7 @@ __FBSDID("$FreeBSD$");
 #include "vmm_host.h"
 #include "vmm_mem.h"
 #include "vmm_util.h"
+#include "vatpic.h"
 #include "vhpet.h"
 #include "vioapic.h"
 #include "vlapic.h"
@@ -116,6 +117,7 @@ struct vm {

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list