The current acpiprt(4) code doesn't handle non-standard polarity and trigger mode correctly. Typically not a problem for real hardware, but some virtualization stuff gets creative. The diff below tries to do a better job. It fixes the qemu "power button". But before I can commit this, it needs testing on more than just my thinkpad laptops.
So please give this a spin on your hardware, especially servers and desktop machines. Index: acpimadt.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpimadt.c,v retrieving revision 1.26 diff -u -p -r1.26 acpimadt.c --- acpimadt.c 7 Jan 2012 20:13:16 -0000 1.26 +++ acpimadt.c 27 Apr 2014 15:57:31 -0000 @@ -216,8 +216,7 @@ acpimadt_attach(struct device *parent, s arg.type = AML_OBJTYPE_INTEGER; arg.v_integer = 1; - if (aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL) != 0) - return; + aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL); mp_busses = acpimadt_busses; mp_nbusses = nitems(acpimadt_busses); Index: acpiprt.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpiprt.c,v retrieving revision 1.44 diff -u -p -r1.44 acpiprt.c --- acpiprt.c 22 Dec 2013 18:55:25 -0000 1.44 +++ acpiprt.c 27 Apr 2014 15:57:31 -0000 @@ -41,6 +41,13 @@ #include "ioapic.h" +struct acpiprt_irq { + int _int; + int _shr; + int _ll; + int _he; +}; + struct acpiprt_map { int bus, dev; int pin; @@ -134,16 +141,29 @@ acpiprt_attach(struct device *parent, st int acpiprt_getirq(union acpi_resource *crs, void *arg) { - int *irq = (int *)arg; - int typ; + struct acpiprt_irq *irq = arg; + int typ, len; + + irq->_shr = 0; + irq->_ll = 0; + irq->_he = 1; typ = AML_CRSTYPE(crs); + len = AML_CRSLEN(crs); switch (typ) { case SR_IRQ: - *irq = ffs(letoh16(crs->sr_irq.irq_mask)) - 1; + irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1; + if (len > 2) { + irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR); + irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY); + irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE); + } break; case LR_EXTIRQ: - *irq = letoh32(crs->lr_extirq.irq[0]); + irq->_int = letoh32(crs->lr_extirq.irq[0]); + irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR); + irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY); + irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE); break; default: printf("unknown interrupt: %x\n", typ); @@ -174,35 +194,48 @@ acpiprt_pri[16] = { int acpiprt_chooseirq(union acpi_resource *crs, void *arg) { - int *irq = (int *)arg; - int typ, i, pri = -1; + struct acpiprt_irq *irq = arg; + int typ, len, i, pri = -1; + + irq->_shr = 0; + irq->_ll = 0; + irq->_he = 1; typ = AML_CRSTYPE(crs); + len = AML_CRSLEN(crs); switch (typ) { case SR_IRQ: for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) { if (crs->sr_irq.irq_mask & (1 << i) && acpiprt_pri[i] > pri) { - *irq = i; - pri = acpiprt_pri[*irq]; + irq->_int = i; + pri = acpiprt_pri[irq->_int]; } } + if (len > 2) { + irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR); + irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY); + irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE); + } break; case LR_EXTIRQ: /* First try non-8259 interrupts. */ for (i = 0; i < crs->lr_extirq.irq_count; i++) { if (crs->lr_extirq.irq[i] > 15) { - *irq = crs->lr_extirq.irq[i]; + irq->_int = crs->lr_extirq.irq[i]; return (0); } } for (i = 0; i < crs->lr_extirq.irq_count; i++) { if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) { - *irq = crs->lr_extirq.irq[i]; - pri = acpiprt_pri[*irq]; + irq->_int = crs->lr_extirq.irq[i]; + pri = acpiprt_pri[irq->_int]; } } + irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR); + irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY); + irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE); break; default: printf("unknown interrupt: %x\n", typ); @@ -215,8 +248,9 @@ acpiprt_prt_add(struct acpiprt_softc *sc { struct aml_node *node; struct aml_value res, *pp; + struct acpiprt_irq irq; u_int64_t addr; - int pin, irq; + int pin; int64_t sta; #if NIOAPIC > 0 struct mp_intr_map *map; @@ -283,7 +317,7 @@ acpiprt_prt_add(struct acpiprt_softc *sc aml_freevalue(&res); /* Pick a new IRQ if necessary. */ - if ((irq == 0 || irq == 2 || irq == 13) && + if ((irq._int == 0 || irq._int == 2 || irq._int == 13) && !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){ aml_parse_resource(&res, acpiprt_chooseirq, &irq); aml_freevalue(&res); @@ -294,24 +328,28 @@ acpiprt_prt_add(struct acpiprt_softc *sc p->bus = sc->sc_bus; p->dev = ACPI_PCI_DEV(addr << 16); p->pin = pin; - p->irq = irq; + p->irq = irq._int; p->sc = sc; p->node = node; SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list); } else { - irq = aml_val2int(v->v_package[3]); + irq._int = aml_val2int(v->v_package[3]); + irq._shr = 1; + irq._ll = 1; + irq._he = 0; } #ifdef ACPI_DEBUG printf("%s: %s addr 0x%llx pin %d irq %d\n", - DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq); + DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int); #endif #if NIOAPIC > 0 if (nioapics > 0) { - apic = ioapic_find_bybase(irq); + apic = ioapic_find_bybase(irq._int); if (apic == NULL) { - printf("%s: no apic found for irq %d\n", DEVNAME(sc), irq); + printf("%s: no apic found for irq %d\n", + DEVNAME(sc), irq._int); return; } @@ -320,10 +358,30 @@ acpiprt_prt_add(struct acpiprt_softc *sc return; map->ioapic = apic; - map->ioapic_pin = irq - apic->sc_apic_vecbase; + map->ioapic_pin = irq._int - apic->sc_apic_vecbase; map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3); - map->redir = IOAPIC_REDLO_ACTLO | IOAPIC_REDLO_LEVEL; - map->redir |= (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); + if (irq._ll) + map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT); + else + map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT); + if (irq._he) + map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT); + else + map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT); + + map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT); + switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) { + case MPS_INTPO_DEF: + case MPS_INTPO_ACTLO: + map->redir |= IOAPIC_REDLO_ACTLO; + break; + } + switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) { + case MPS_INTTR_DEF: + case MPS_INTTR_LEVEL: + map->redir |= IOAPIC_REDLO_LEVEL; + break; + } map->ioapic_ih = APIC_INT_VIA_APIC | ((apic->sc_apicid << APIC_INT_APIC_SHIFT) | @@ -353,7 +411,7 @@ acpiprt_prt_add(struct acpiprt_softc *sc reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG); if (PCI_INTERRUPT_PIN(reg) == pin + 1) { reg &= ~(PCI_INTERRUPT_LINE_MASK << PCI_INTERRUPT_LINE_SHIFT); - reg |= irq << PCI_INTERRUPT_LINE_SHIFT; + reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT; pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg); } } @@ -372,10 +430,11 @@ acpiprt_route_interrupt(int bus, int dev { struct acpiprt_softc *sc; struct acpiprt_map *p; + struct acpiprt_irq irq; struct aml_node *node = NULL; struct aml_value res, res2; union acpi_resource *crs; - int irq, newirq; + int newirq; int64_t sta; SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) { @@ -408,7 +467,7 @@ acpiprt_route_interrupt(int bus, int dev aml_parse_resource(&res, acpiprt_getirq, &irq); /* Only re-route interrupts when necessary. */ - if ((sta & STA_ENABLED) && irq == newirq) { + if ((sta & STA_ENABLED) && irq._int == newirq) { aml_freevalue(&res); return; } Index: dsdt.h =================================================================== RCS file: /cvs/src/sys/dev/acpi/dsdt.h,v retrieving revision 1.61 diff -u -p -r1.61 dsdt.h --- dsdt.h 18 Jan 2013 04:07:06 -0000 1.61 +++ dsdt.h 27 Apr 2014 15:57:31 -0000 @@ -168,6 +168,9 @@ union acpi_resource { uint8_t typecode; uint16_t length; uint8_t flags; +#define LR_EXTIRQ_SHR (1L << 3) +#define LR_EXTIRQ_POLARITY (1L << 2) +#define LR_EXTIRQ_MODE (1L << 1) uint8_t irq_count; uint32_t irq[1]; } __packed lr_extirq;