Okay, so here’s a first attempt at creating a portable interrupt model from the previous data :
- There are, at some level, various atomic interrupt sources, which are each labeled in an arch-specific way (naming information can always be stored in a C-style struct which itself can be pointed by an integer, so it doesn’t matter). This is what unprivileged software can see.
- There may or may not be a number of OS-controlled routing/masking layers inbetween, but these interrupt sources see their signals redirected to a number of standard interrupt signals which the processor can discriminate without communicating with another device. We call these IRQs, and they are managed by the kernel.
- Whether the aforementioned routing layers should be managed by the kernel or by an external driver should be decided on a per-device basis, depending on things like its importance on the architecture (e.g. PCI and USB are universal on the x86 architecture), how much performance it needs, etc. However, to be managed within the kernel, an interrupt routing mechanism must be a widely documented standard, with freely available documentation. If two manufacturers or more which each have a significant market share manage the same mechanism in an incompatible way, it is not standard and must be left to external drivers.
- Before starting to talk about IRQ handling, let’s remind a top security rule, which overrides any other concern : No process should ever be able to prevent the kernel from running for an unspecified period of time. We’ve been talking about this in a previous post about real time processes. What this means is that no process may ever get control on the IRQ where the clock is redirected, which belongs to the kernel scheduler, nor run while the clock interrupt is disabled.
- Depending on the considered architecture, it may be possible to mask each IRQ individually, or only to mask groups of IRQs. Our interrupt management system must provide a way for low-level code to learn about this. Processes may mask all interrupts which they “own”, and to mask a group they need to own all interrupts within it.
- Although our microkernel model requires the ability to start pop-up threads within processes each time an interrupt occurs, as if the interrupt directly triggered the execution of that process’ interrupt handling code, it is generally not safe on most architectures to set a user process as the interrupt handler, as it would imply running said process with the clock interrupt disabled and would offer no guarantee whatsoever that the state of the previously running process would be saved. For this reason, a bit of kernel code must be run before any interrupt is handled. This code takes care of saving the processor’s state on a stack or the heap and re-enabling other interrupts, before running the actual interrupt handler. Care must be taken that this code fully runs in kernel mode, with the kernel stack, and not with a potentially corrupt user-mode stack.
- It may also be possible to prioritize some interrupts above others. This isn’t very important in this interrupt model, as we go back to user-mode code with interrupts enabled very quickly. But they say that power shouldn’t be hidden, so we have to find a way to provide low-level code with access to this capability.
What do you think about it ?