Programmable Interrupt Chaos, preface
On many older mainboards, especially those with x86 family CPU(s), you might come across Intel 8259 Programmable Interrupt Controller or some similar device.
As the name suggest, the purpose of this little device is to allow the CPU to control what kinds of interrupts are being risen from other devices to the CPU. This allows, for example, the CPU to only select some interrupts to be sent through and not waste time responding to interrupts there’s no handlers yet.
Fairly simple, and seemingly easy to program. Something I had done countless times in past for different OS projects, etc. Easy. Straightforward. Or so I thought…
Qemu
Yes, I know, using an emulator for bios/firmare development might seem a bit silly and I’m asking for trouble. Anyway, I’m not quite yet at a stage where testing and development is convenient on actual hardware, and at this point I was still scared of bochs.
Generally, it seems, qemu does emulate 8259 correctly. And I still assume I’m just doing something wrong myself since things just work with Coreboot/SeaBIOS etc.
Programming PIC
Programming PIC is fairly straightforward, allegedly, there’s a handful of command- and data registers to set up, basically configuring the type of signal the CPU expects, which interrupts are enabled, and so forth.
In my usual fashion, I over-engineered what could be just a matter of a few I/O operations. Regardless, after an afternoon of coding, I had implemented PIC initialisation code that did work just right with a silly hobby-kernel I occasionally use for testing things. Interrupts were coming through correctly, and everything worked. So, happily, I threw it all to the bios project, and never bothered to test it properly there beyond the following snippet
So, software interrupts did work, cool. Time to forget all about this for a few weeks and move on to write some drivers!
Interrupt the development
Eventually, the moment of confusion arose. There’s now a bunch of drivers, many of the devices associated to them should raise interrupts. Some should even raise non-maskable interrupts. Yet, none were triggered.
Confused, I moved on to try and raise NMIs from qemu debug/control interface. Nothing.
What followed, was weeks of rewrites, confusion, and frustration. I eventually gave up for some amount of time, until GPN23. With help of some folks from osdev IRC channel as well as from some event attenders, I retried, and went over the whole design, only to come to the sad conclusion that the code, according to documentation for 8259, is correct.
I must be missing something else from the early system initialisation that’d lead to interrupts just not working.. However, before trying to figure that out, I was suggested to give afore-mentioned bochs a try. Figuring I’d have nothing but a bit of learning-time to lose, I gave it a go.
And everything just worked, immediately. I still to this day don’t know what I was missing with qemu, I’m assuming my system initialisation is just still too barebones and lacking. No surprises there. Anyway, things are at least seemingly working fine for now with bochs, so I can happily forget about the PIC issues again, and leave fixing them for future me.
Conclusions
Firmware development with emulators is a bit silly, maybe? And sometimes your code might actually be at least somewhat correct, with the bug being somewhere else altogether. Should probably try and not waste weeks on rewrites again.
Anyway, thanks for reading this ramble :)