[SOLVED] Spurious interrupt firing after disabling another
Posted: Sat Jan 27, 2018 11:01 pm
Are there circumstances in which an interrupt can fire even if it is gated? Of course not, right?
I have been writing a driver for the CMOS AIP in x86 and began observing strange interrupt behavior once I started doing CMOS-related things that required me to adjust the state of interrupts during certain blocks of code. Specifically, I wanted to disable _only_ the CMOS interrupt (irq #8) while getting the cached hardware time from a global structure (so the handler would not also try to modify it). It was then that I started getting int15s firing for an unknown reason. You'll notice that I registered a few interrupts in the prints with a PIC vector base of 32.
Since it only began once I started executing my code to read the CMOS registers, I started there. Eventually I narrowed it down to this small bit of offending code:
Note that I strategically placed the printk calls so could see where exactly the int15 was occurring. I've let multiple simultaneous QEMU sessions run my kernel for a while now and the int15 always arrives just after the irq_disable and before the printk (which does not use/adjust interrupts). It is also worth noting that the arrival of the spurious interrupt is non-deterministic. It appears to be a race condition of sorts, as sometimes it can take several minutes to happen or it may take only a few seconds. Here is a screenshot of the QEMU video:
My int15 handler just prints the number of times the interrupt has occurred along with the current mask, ISR, and IRR registers. (Note that the ISR and IRR registers are read from both the master and slave at once and are returned as a 16-bit value). As shown, only IRQs 0, 1, and 2 are enabled and the ISR register shows IRQ2/pin2 (on the master) fired. Of course this indicates an IRQ came from the slave, which is reflected in the IRR register. In this case, the IRR register informs that IRQ 8 fired. Why would the IDT entry for IRQ 15 execute in this case? Am I improperly handling the cause where an interrupt is coming from the slave PIC? There is no instant where int15 is unmasked. I've tried inserting asserts to catch this case. My irq_disable function looks like this:
Should the PIC be cleared of all pending interrupts when changing the mask? Unless I missed something in the 8259 datasheet I did not come across anything relevant to this problem.
I have been writing a driver for the CMOS AIP in x86 and began observing strange interrupt behavior once I started doing CMOS-related things that required me to adjust the state of interrupts during certain blocks of code. Specifically, I wanted to disable _only_ the CMOS interrupt (irq #8) while getting the cached hardware time from a global structure (so the handler would not also try to modify it). It was then that I started getting int15s firing for an unknown reason. You'll notice that I registered a few interrupts in the prints with a PIC vector base of 32.
Since it only began once I started executing my code to read the CMOS registers, I started there. Eventually I narrowed it down to this small bit of offending code:
Code: Select all
void cmos_get_time(struct cmos_rtc_time *time)
{
if(NULL != time){
printk("\r \r@");
plat.irq_disable(8);
printk("@");
memcpy(time, &g_time_hw, sizeof(struct cmos_rtc_time));
plat.irq_enable(8);
printk("@");
...
}
}
Code: Select all
void pic8259_setmask(uint16_t mask)
{
pic_outb(PIC8259_MASTER_DATA, mask & 0xff);
pic_outb(PIC8259_SLAVE_DATA, mask >> 8);
}
uint16_t pic8259_getmask(void)
{
return (pic_inb(PIC8259_SLAVE_DATA) << 8) | pic_inb(PIC8259_MASTER_DATA);
}
void pic8259_disable_irq(uint8_t irq)
{
pic8259_setmask(pic8259_getmask() | (1 << irq));
}