Date: Tue, 11 Mar 2014 16:56:00 +0000 (UTC) From: Tycho Nightingale <tychon@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: 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 Message-ID: <201403111656.s2BGu1fp082907@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@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@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 ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201403111656.s2BGu1fp082907>