Page 1 of 1

properly disable PIT and HPET IRQ routing?

Posted: Sat Jan 23, 2021 5:05 pm
by xeyes
(this is all on qemu)

Was trying to get HPET working, and ran into some strange behaviors with both itself and PIT and wasn't able to find much about these by searching or reading documentations/wiki.

Question 1:

How to properly disable PIT? As in stop it from generating more interrupt(s), not just masking its interrupt in PIC.

There's no disable/enable bit in PIT registers, nor a disabled mode, so I tried to abuse mode 0 + hi/lo bytes mode. As the manual says "writing the first byte disables counting, OUT is set to low immediately".

Well this doesn't happen in qemu, PIT continues to send periodical interrupts (probably at the setting left in it by BIOS).

The only way I made it to stop is by changing to mode 0 and write out both of the bytes, in this case an interrupt will be fired in an unpredictable time in the future (since I can't tell the counter's value when the write happens, 0000 behaves the same as FFFF). I have to wait for this interrupt, and only continue after getting it, to ensure that PIT won't interrupt again.

This seems like a quite ugly sequence and also risky as it could hang if this mode 0 interrupt is somehow missed.

Question 2:

If you are wondering why I even bother to disable PIT, it is because of it sharing interrupt vector with HPET in qemu.

The HPET comparators in qemu (only 2, which seems to be also against the spec) all advertise that their interrupt routing capability (TN_INT_ROUTE_CAP field) is 0x4, without FSB/MSI support. While the MDAT table advertises: PIT uses GSI 2 :!:

But even if I 'disable' PIT as above, the 1 shot (or periodical) interrupts from HPET doesn't happen unless I keep reading IOAPIC registers in a loop, almost like that somehow pumps out the interrupt from HPET.

This doesn't matter if their are set up as edge or level and polarity setting on IOAPIC doesn't affect it much either. I can also observe the HPET main counter value goes up, goes past the threshold, and the int status bit go high in level mode in a polling loop, yet the interrupt doesn't happen (unless I "pump it out" as mentioned above).

The only way for HPET interrupts to happen reliably seems to be enabling legacy routing of HPET (Timer 0 again uses PIT's interrupt vector). Timer1 also works in legacy routing on vector 8, and does not work otherwise.

So it seems that unless legacy routing is used to force some sort of IRQ routing override, PIT is affecting HPET's interaction with IOAPIC despite stuck in mode 0 :?

Maybe I misread the spec in some way or missed something obvious?

Or could this be some of the expected quirks of qemu? Seems unlikely though.