From 3b87354d1ea49068db130b4218c016fcc20cb9e2 Mon Sep 17 00:00:00 2001 From: neel Date: Sat, 23 Nov 2013 03:56:03 +0000 Subject: [PATCH] Add an ioctl to assert and deassert an ioapic pin atomically. This will be used to inject edge triggered legacy interrupts into the guest. Start using the new API in device models that use edge triggered interrupts: viz. the 8254 timer and the LPC/uart device emulation. Submitted by: Tycho Nightingale (tycho.nightingale@pluribusnetworks.com) --- lib/libvmmapi/vmmapi.c | 11 +++++++++++ lib/libvmmapi/vmmapi.h | 1 + sys/amd64/include/vmm_dev.h | 3 +++ sys/amd64/vmm/io/vioapic.c | 34 ++++++++++++++++++++++++++++++---- sys/amd64/vmm/io/vioapic.h | 1 + sys/amd64/vmm/vmm_dev.c | 4 ++++ usr.sbin/bhyve/pci_emul.c | 12 ++++++++++-- usr.sbin/bhyve/pci_emul.h | 1 + usr.sbin/bhyve/pci_lpc.c | 11 +++++------ usr.sbin/bhyve/pit_8254.c | 3 +-- 10 files changed, 67 insertions(+), 14 deletions(-) diff --git a/lib/libvmmapi/vmmapi.c b/lib/libvmmapi/vmmapi.c index 5ece80b6442d..85b10699edeb 100644 --- a/lib/libvmmapi/vmmapi.c +++ b/lib/libvmmapi/vmmapi.c @@ -418,6 +418,17 @@ vm_ioapic_deassert_irq(struct vmctx *ctx, int irq) return (ioctl(ctx->fd, VM_IOAPIC_DEASSERT_IRQ, &ioapic_irq)); } +int +vm_ioapic_pulse_irq(struct vmctx *ctx, int irq) +{ + struct vm_ioapic_irq ioapic_irq; + + bzero(&ioapic_irq, sizeof(struct vm_ioapic_irq)); + ioapic_irq.irq = irq; + + return (ioctl(ctx->fd, VM_IOAPIC_PULSE_IRQ, &ioapic_irq)); +} + int vm_inject_nmi(struct vmctx *ctx, int vcpu) { diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h index c9a2a096bf66..8eaea293879d 100644 --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -69,6 +69,7 @@ int vm_inject_event2(struct vmctx *ctx, int vcpu, enum vm_event_type type, int vm_lapic_irq(struct vmctx *ctx, int vcpu, int vector); int vm_ioapic_assert_irq(struct vmctx *ctx, int irq); int vm_ioapic_deassert_irq(struct vmctx *ctx, int irq); +int vm_ioapic_pulse_irq(struct vmctx *ctx, int irq); int vm_inject_nmi(struct vmctx *ctx, int vcpu); int vm_capability_name2type(const char *capname); const char *vm_capability_type2name(int type); diff --git a/sys/amd64/include/vmm_dev.h b/sys/amd64/include/vmm_dev.h index 2adf50d80048..cd4332b56a8b 100644 --- a/sys/amd64/include/vmm_dev.h +++ b/sys/amd64/include/vmm_dev.h @@ -170,6 +170,7 @@ enum { IOCNUM_INJECT_NMI = 32, IOCNUM_IOAPIC_ASSERT_IRQ = 33, IOCNUM_IOAPIC_DEASSERT_IRQ = 34, + IOCNUM_IOAPIC_PULSE_IRQ = 35, /* PCI pass-thru */ IOCNUM_BIND_PPTDEV = 40, @@ -209,6 +210,8 @@ enum { _IOW('v', IOCNUM_IOAPIC_ASSERT_IRQ, struct vm_ioapic_irq) #define VM_IOAPIC_DEASSERT_IRQ \ _IOW('v', IOCNUM_IOAPIC_DEASSERT_IRQ, struct vm_ioapic_irq) +#define VM_IOAPIC_PULSE_IRQ \ + _IOW('v', IOCNUM_IOAPIC_PULSE_IRQ, struct vm_ioapic_irq) #define VM_SET_CAPABILITY \ _IOW('v', IOCNUM_SET_CAPABILITY, struct vm_capability) #define VM_GET_CAPABILITY \ diff --git a/sys/amd64/vmm/io/vioapic.c b/sys/amd64/vmm/io/vioapic.c index bc8485eafd26..9ce8a5ec8618 100644 --- a/sys/amd64/vmm/io/vioapic.c +++ b/sys/amd64/vmm/io/vioapic.c @@ -160,8 +160,14 @@ vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate) } } +enum irqstate { + IRQSTATE_ASSERT, + IRQSTATE_DEASSERT, + IRQSTATE_PULSE +}; + static int -vioapic_set_irqstate(struct vm *vm, int irq, bool state) +vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate) { struct vioapic *vioapic; @@ -171,7 +177,20 @@ vioapic_set_irqstate(struct vm *vm, int irq, bool state) vioapic = vm_ioapic(vm); VIOAPIC_LOCK(vioapic); - vioapic_set_pinstate(vioapic, irq, state); + switch (irqstate) { + case IRQSTATE_ASSERT: + vioapic_set_pinstate(vioapic, irq, true); + break; + case IRQSTATE_DEASSERT: + vioapic_set_pinstate(vioapic, irq, false); + break; + case IRQSTATE_PULSE: + vioapic_set_pinstate(vioapic, irq, true); + vioapic_set_pinstate(vioapic, irq, false); + break; + default: + panic("vioapic_set_irqstate: invalid irqstate %d", irqstate); + } VIOAPIC_UNLOCK(vioapic); return (0); @@ -181,14 +200,21 @@ int vioapic_assert_irq(struct vm *vm, int irq) { - return (vioapic_set_irqstate(vm, irq, true)); + return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT)); } int vioapic_deassert_irq(struct vm *vm, int irq) { - return (vioapic_set_irqstate(vm, irq, false)); + return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT)); +} + +int +vioapic_pulse_irq(struct vm *vm, int irq) +{ + + return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE)); } static uint32_t diff --git a/sys/amd64/vmm/io/vioapic.h b/sys/amd64/vmm/io/vioapic.h index 54ad36ad847a..3890250e78d0 100644 --- a/sys/amd64/vmm/io/vioapic.h +++ b/sys/amd64/vmm/io/vioapic.h @@ -41,6 +41,7 @@ void vioapic_cleanup(struct vioapic *vioapic); int vioapic_assert_irq(struct vm *vm, int irq); int vioapic_deassert_irq(struct vm *vm, int irq); +int vioapic_pulse_irq(struct vm *vm, int irq); int vioapic_mmio_write(void *vm, int vcpuid, uint64_t gpa, uint64_t wval, int size, void *arg); diff --git a/sys/amd64/vmm/vmm_dev.c b/sys/amd64/vmm/vmm_dev.c index a639ce9b8573..500ef41e68ce 100644 --- a/sys/amd64/vmm/vmm_dev.c +++ b/sys/amd64/vmm/vmm_dev.c @@ -303,6 +303,10 @@ vmmdev_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag, ioapic_irq = (struct vm_ioapic_irq *)data; error = vioapic_deassert_irq(sc->vm, ioapic_irq->irq); break; + case VM_IOAPIC_PULSE_IRQ: + ioapic_irq = (struct vm_ioapic_irq *)data; + error = vioapic_pulse_irq(sc->vm, ioapic_irq->irq); + break; case VM_MAP_MEMORY: seg = (struct vm_memory_segment *)data; error = vm_malloc(sc->vm, seg->gpa, seg->len); diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c index fd087faf674b..c7086ddcbcf5 100644 --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -1135,7 +1135,11 @@ pci_lintr_assert(struct pci_devinst *pi) { assert(pi->pi_lintr_pin >= 0); - vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + + if (pi->pi_lintr_state == 0) { + pi->pi_lintr_state = 1; + vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + } } void @@ -1143,7 +1147,11 @@ pci_lintr_deassert(struct pci_devinst *pi) { assert(pi->pi_lintr_pin >= 0); - vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + + if (pi->pi_lintr_state == 1) { + pi->pi_lintr_state = 0; + vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr_pin); + } } /* diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h index ebcf8347c7d1..6a1d7575b44c 100644 --- a/usr.sbin/bhyve/pci_emul.h +++ b/usr.sbin/bhyve/pci_emul.h @@ -104,6 +104,7 @@ struct pci_devinst { struct vmctx *pi_vmctx; uint8_t pi_bus, pi_slot, pi_func; int8_t pi_lintr_pin; + int8_t pi_lintr_state; char pi_name[PI_NAMESZ]; int pi_bar_getsize; diff --git a/usr.sbin/bhyve/pci_lpc.c b/usr.sbin/bhyve/pci_lpc.c index 5dc79e02d752..4157c5cb0c7e 100644 --- a/usr.sbin/bhyve/pci_lpc.c +++ b/usr.sbin/bhyve/pci_lpc.c @@ -94,17 +94,16 @@ lpc_uart_intr_assert(void *arg) assert(sc->irq >= 0); - vm_ioapic_assert_irq(lpc_bridge->pi_vmctx, sc->irq); + vm_ioapic_pulse_irq(lpc_bridge->pi_vmctx, sc->irq); } static void lpc_uart_intr_deassert(void *arg) { - struct lpc_uart_softc *sc = arg; - - assert(sc->irq >= 0); - - vm_ioapic_deassert_irq(lpc_bridge->pi_vmctx, sc->irq); + /* + * The COM devices on the LPC bus generate edge triggered interrupts, + * so nothing more to do here. + */ } static int diff --git a/usr.sbin/bhyve/pit_8254.c b/usr.sbin/bhyve/pit_8254.c index fd05b008ff68..9ecb565369f8 100644 --- a/usr.sbin/bhyve/pit_8254.c +++ b/usr.sbin/bhyve/pit_8254.c @@ -105,8 +105,7 @@ pit_mevent_cb(int fd, enum ev_type type, void *param) pit_mev_count++; - vm_ioapic_assert_irq(c->ctx, 2); - vm_ioapic_deassert_irq(c->ctx, 2); + vm_ioapic_pulse_irq(c->ctx, 2); /* * Delete the timer for one-shots