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.nighting...@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.nighting...@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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to