Index: sys/amd64/vmm/io/vatpic.c =================================================================== --- sys/amd64/vmm/io/vatpic.c (revision 276096) +++ sys/amd64/vmm/io/vatpic.c (working copy) @@ -73,9 +73,10 @@ uint8_t request; /* Interrupt Request Register (IIR) */ uint8_t service; /* Interrupt Service (ISR) */ uint8_t mask; /* Interrupt Mask Register (IMR) */ + uint8_t smm; /* special mask mode */ + uint8_t lowprio; /* lowest priority irq */ int acnt[8]; /* sum of pin asserts and deasserts */ - int lowprio; /* lowest priority irq */ bool intr_raised; }; @@ -122,6 +123,14 @@ return (false); } +static __inline void +atpic_set_lowprio(struct atpic *atpic, int prio) +{ + + KASSERT(prio >= 0 && prio <= 7, ("invalid vatpic priority %d", prio)); + atpic->lowprio = prio; +} + static __inline int vatpic_get_highest_isrpin(struct atpic *atpic) { @@ -131,8 +140,16 @@ ATPIC_PIN_FOREACH(pin, atpic, i) { bit = (1 << pin); - if (atpic->service & bit) - return (pin); + if (atpic->service & bit) { + /* + * An IS bit that is masked by an IMR bit will not be + * cleared by a non-specific EOI in Special Mask Mode. + */ + if (atpic->smm && (atpic->mask & bit) != 0) + continue; + else + return (pin); + } } return (-1); @@ -153,6 +170,15 @@ if (atpic->sfn) serviced &= ~(1 << 2); + /* + * In 'Special Mask Mode' when a mask bit it set in OCW1, it inhibits + * further interrupts at that level and enables interrupts from all + * other levels that are not masked. In other words the ISR has no + * bearing the levels that can generate interrupts. + */ + if (atpic->smm) + serviced = 0; + ATPIC_PIN_FOREACH(pin, atpic, tmp) { bit = 1 << pin; @@ -258,9 +284,10 @@ atpic->icw_num = 1; atpic->mask = 0; - atpic->lowprio = 7; atpic->rd_cmd_reg = 0; atpic->poll = 0; + atpic->smm = 0; + atpic_set_lowprio(atpic, 7); if ((val & ICW1_SNGL) != 0) { VATPIC_CTR0(vatpic, "vatpic cascade mode required"); @@ -359,11 +386,11 @@ atpic->service &= ~(1 << isr_bit); if (atpic->rotate) - atpic->lowprio = isr_bit; + atpic_set_lowprio(atpic, isr_bit); } } else if ((val & OCW2_SL) != 0 && atpic->rotate == true) { /* specific priority */ - atpic->lowprio = val & 0x7; + atpic_set_lowprio(atpic, val & 0x7); } return (0); @@ -375,8 +402,10 @@ VATPIC_CTR1(vatpic, "atpic ocw3 0x%x", val); if (val & OCW3_ESMM) { - VATPIC_CTR0(vatpic, "atpic special mask mode not implemented"); - return (-1); + atpic->smm = val & OCW3_SMM; + VATPIC_CTR2(vatpic, "%s atpic special mask mode %s", + master_atpic(vatpic, atpic) ? "master" : "slave", + atpic->smm ? "enabled" : "disabled"); } if (val & OCW3_RR) { @@ -566,7 +595,7 @@ if (atpic->aeoi == true) { if (atpic->rotate == true) - atpic->lowprio = pin; + atpic_set_lowprio(atpic, pin); } else { atpic->service |= (1 << pin); }