From b96be57a2d1892e7ce46b2259453ef91665f6822 Mon Sep 17 00:00:00 2001 From: Tycho Nightingale Date: Mon, 14 Apr 2014 19:00:20 +0000 Subject: [PATCH] Add support for emulating the slave PIC. Reviewed by: grehan, jhb Approved by: grehan (co-mentor) --- lib/libvmmapi/vmmapi.h | 2 +- sys/amd64/vmm/io/vatpic.c | 203 +++++++++++++++++++++++++------------- usr.sbin/bhyve/pm.c | 4 +- 3 files changed, 138 insertions(+), 71 deletions(-) diff --git a/lib/libvmmapi/vmmapi.h b/lib/libvmmapi/vmmapi.h index ce150d8c3d5e..ed63fb20259e 100644 --- a/lib/libvmmapi/vmmapi.h +++ b/lib/libvmmapi/vmmapi.h @@ -72,9 +72,9 @@ 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_ioapic_pincount(struct vmctx *ctx, int *pincount); +int vm_isa_assert_irq(struct vmctx *ctx, int atpic_irq, int ioapic_irq); 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); diff --git a/sys/amd64/vmm/io/vatpic.c b/sys/amd64/vmm/io/vatpic.c index bc057225aef9..8e582fad417e 100644 --- a/sys/amd64/vmm/io/vatpic.c +++ b/sys/amd64/vmm/io/vatpic.c @@ -67,6 +67,7 @@ struct atpic { bool aeoi; bool poll; bool rotate; + bool sfn; /* special fully-nested mode */ int irq_base; uint8_t request; /* Interrupt Request Register (IIR) */ @@ -75,6 +76,8 @@ struct atpic { int acnt[8]; /* sum of pin asserts and deasserts */ int priority; /* current pin priority */ + + bool intr_raised; }; struct vatpic { @@ -82,8 +85,6 @@ struct vatpic { struct mtx mtx; struct atpic atpic[2]; uint8_t elc[2]; - - bool intr_raised; }; #define VATPIC_CTR0(vatpic, fmt) \ @@ -101,6 +102,7 @@ struct vatpic { #define VATPIC_CTR4(vatpic, fmt, a1, a2, a3, a4) \ VM_CTR4((vatpic)->vm, fmt, a1, a2, a3, a4) +static void vatpic_set_pinstate(struct vatpic *vatpic, int pin, bool newstate); static __inline int vatpic_get_highest_isrpin(struct atpic *atpic) @@ -122,13 +124,23 @@ vatpic_get_highest_isrpin(struct atpic *atpic) static __inline int vatpic_get_highest_irrpin(struct atpic *atpic) { + int serviced; int bit, pin; int i, j; + /* + * In 'Special Fully-Nested Mode' when an interrupt request from + * a slave is in service, the slave is not locked out from the + * master's priority logic. + */ + serviced = atpic->service; + if (atpic->sfn) + serviced &= ~(1 << 2); + for (i = 0; i <= 7; i++) { pin = ((i + 7 - atpic->priority) & 0x7); bit = (1 << pin); - if (atpic->service & bit) + if (serviced & bit) break; } @@ -150,14 +162,35 @@ vatpic_notify_intr(struct vatpic *vatpic) KASSERT(VATPIC_LOCKED(vatpic), ("vatpic_notify_intr not locked")); - if (vatpic->intr_raised == true) - return; + /* + * First check the slave. + */ + atpic = &vatpic->atpic[1]; + if (!atpic->intr_raised && + (pin = vatpic_get_highest_irrpin(atpic)) != -1) { + VATPIC_CTR4(vatpic, "atpic slave notify pin = %d " + "(imr 0x%x irr 0x%x isr 0x%x)", pin, + atpic->mask, atpic->request, atpic->service); - /* XXX master only */ + /* + * Cascade the request from the slave to the master. + */ + atpic->intr_raised = true; + vatpic_set_pinstate(vatpic, 2, true); + vatpic_set_pinstate(vatpic, 2, false); + } else { + VATPIC_CTR3(vatpic, "atpic slave no eligible interrupts " + "(imr 0x%x irr 0x%x isr 0x%x)", + atpic->mask, atpic->request, atpic->service); + } + + /* + * Then check the master. + */ atpic = &vatpic->atpic[0]; - - if ((pin = vatpic_get_highest_irrpin(atpic)) != -1) { - VATPIC_CTR4(vatpic, "atpic notify pin = %d " + if (!atpic->intr_raised && + (pin = vatpic_get_highest_irrpin(atpic)) != -1) { + VATPIC_CTR4(vatpic, "atpic master notify pin = %d " "(imr 0x%x irr 0x%x isr 0x%x)", pin, atpic->mask, atpic->request, atpic->service); @@ -183,11 +216,11 @@ vatpic_notify_intr(struct vatpic *vatpic) * programmed as ExtINT to indicate that the PIC is * the source of the interrupt. */ + atpic->intr_raised = true; lapic_set_local_intr(vatpic->vm, -1, APIC_LVT_LINT0); vioapic_pulse_irq(vatpic->vm, 0); - vatpic->intr_raised = true; } else { - VATPIC_CTR3(vatpic, "atpic no eligible interrupts " + VATPIC_CTR3(vatpic, "atpic master no eligible interrupts " "(imr 0x%x irr 0x%x isr 0x%x)", atpic->mask, atpic->request, atpic->service); } @@ -422,7 +455,6 @@ vatpic_pending_intr(struct vm *vm, int *vecptr) vatpic = vm_atpic(vm); - /* XXX master only */ atpic = &vatpic->atpic[0]; VATPIC_LOCK(vatpic); @@ -430,28 +462,20 @@ vatpic_pending_intr(struct vm *vm, int *vecptr) pin = vatpic_get_highest_irrpin(atpic); if (pin == -1) pin = 7; + if (pin == 2) { + atpic = &vatpic->atpic[1]; + pin = vatpic_get_highest_irrpin(atpic); + } *vecptr = atpic->irq_base + pin; VATPIC_UNLOCK(vatpic); } -void -vatpic_intr_accepted(struct vm *vm, int vector) +static void +vatpic_pin_accepted(struct atpic *atpic, int pin) { - struct vatpic *vatpic; - struct atpic *atpic; - int pin; - - vatpic = vm_atpic(vm); - - /* XXX master only */ - atpic = &vatpic->atpic[0]; - - VATPIC_LOCK(vatpic); - vatpic->intr_raised = false; - - pin = vector & 0x7; + atpic->intr_raised = false; if (atpic->acnt[pin] == 0) atpic->request &= ~(1 << pin); @@ -462,53 +486,73 @@ vatpic_intr_accepted(struct vm *vm, int vector) } else { atpic->service |= (1 << pin); } +} + +void +vatpic_intr_accepted(struct vm *vm, int vector) +{ + struct vatpic *vatpic; + int pin; + + vatpic = vm_atpic(vm); + + VATPIC_LOCK(vatpic); + + pin = vector & 0x7; + + if ((vector & ~0x7) == vatpic->atpic[1].irq_base) { + vatpic_pin_accepted(&vatpic->atpic[1], pin); + /* + * If this vector originated from the slave, + * accept the cascaded interrupt too. + */ + vatpic_pin_accepted(&vatpic->atpic[0], 2); + } else { + vatpic_pin_accepted(&vatpic->atpic[0], pin); + } vatpic_notify_intr(vatpic); VATPIC_UNLOCK(vatpic); } -int -vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit) +static int +vatpic_read(struct vatpic *vatpic, struct atpic *atpic, struct vm_exit *vmexit) { - struct vatpic *vatpic; - struct atpic *atpic; - int error; - uint8_t val; + VATPIC_LOCK(vatpic); - error = 0; - vatpic = vm_atpic(vm); - atpic = &vatpic->atpic[0]; - - if (vmexit->u.inout.bytes != 1) + if (atpic->poll) { + VATPIC_CTR0(vatpic, "vatpic polled mode not supported"); + VATPIC_UNLOCK(vatpic); 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 (vmexit->u.inout.port & ICU_IMR_OFFSET) { - /* read interrrupt mask register */ - vmexit->u.inout.eax = atpic->mask; + if (atpic->rd_cmd_reg == OCW3_RIS) { + /* read interrupt service register */ + vmexit->u.inout.eax = atpic->service; } 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; - } + /* read interrupt request register */ + vmexit->u.inout.eax = atpic->request; } } - VATPIC_UNLOCK(vatpic); - - return (0); } + VATPIC_UNLOCK(vatpic); + + return (0); + +} + +static int +vatpic_write(struct vatpic *vatpic, struct atpic *atpic, + struct vm_exit *vmexit) +{ + int error; + uint8_t val; + val = vmexit->u.inout.eax; VATPIC_LOCK(vatpic); @@ -550,22 +594,41 @@ vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit) } int -vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit) +vatpic_master_handler(void *vm, int vcpuid, struct vm_exit *vmexit) { + struct vatpic *vatpic; + struct atpic *atpic; + + vatpic = vm_atpic(vm); + atpic = &vatpic->atpic[0]; + 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; - } + return (vatpic_read(vatpic, atpic, vmexit)); } - /* Pretend all accesses to the slave 8259 are alright */ - return (0); + return (vatpic_write(vatpic, atpic, vmexit)); +} + +int +vatpic_slave_handler(void *vm, int vcpuid, struct vm_exit *vmexit) +{ + struct vatpic *vatpic; + struct atpic *atpic; + + vatpic = vm_atpic(vm); + atpic = &vatpic->atpic[1]; + + if (vmexit->u.inout.bytes != 1) + return (-1); + + if (vmexit->u.inout.in) { + return (vatpic_read(vatpic, atpic, vmexit)); + } + + return (vatpic_write(vatpic, atpic, vmexit)); } int @@ -580,6 +643,8 @@ vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit) if (vmexit->u.inout.bytes != 1) return (-1); + VATPIC_LOCK(vatpic); + if (vmexit->u.inout.in) { if (is_master) vmexit->u.inout.eax = vatpic->elc[0]; @@ -602,6 +667,8 @@ vatpic_elc_handler(void *vm, int vcpuid, struct vm_exit *vmexit) vatpic->elc[1] = (vmexit->u.inout.eax & 0xde); } + VATPIC_UNLOCK(vatpic); + return (0); } diff --git a/usr.sbin/bhyve/pm.c b/usr.sbin/bhyve/pm.c index 627f527a6af4..99087e44d8c6 100644 --- a/usr.sbin/bhyve/pm.c +++ b/usr.sbin/bhyve/pm.c @@ -83,7 +83,7 @@ sci_assert(struct vmctx *ctx) if (sci_active) return; - vm_ioapic_assert_irq(ctx, SCI_INT); + vm_isa_assert_irq(ctx, SCI_INT, SCI_INT); sci_active = 1; } @@ -93,7 +93,7 @@ sci_deassert(struct vmctx *ctx) if (!sci_active) return; - vm_ioapic_deassert_irq(ctx, SCI_INT); + vm_isa_deassert_irq(ctx, SCI_INT, SCI_INT); sci_active = 0; }