In the beginning, the x86 CPU had two pins that could be used to notify the incoming of an interrupt: INTR (Interrupt Request) and NMI (Non Maskable Interrupt)
While the picture depicts an 8086 chip, these pins stuck around until the introduction of what is known as the LAPIC (more on this below) during the Pentium era.
INTR when asserted caused the CPU to read an interrupt vector (or interrupt number) from the bus, interrupt the current program and start executing the interrupt handler associated with the interrupt vector just read.
Clearing the IF
(Interrupt Flag) in (E)FLAGS
would mask the interrupts, preventing the CPU from interrupting the current program.
As an informative note, between the INTR and a device there usually was a chip called 8259A also known as PIC (Programmable Interrupt Controller).
NMI when asserted caused the CPU to interrupt the current program and start executing the interrupt handler for the interrupt vector 2. Simply put, asserting NMI generates the interrupt 2.
Clearing the IF
won't mask an NMI, it was possible to mask any signal going to the NMI pin in hardware though: setting the MSb of port 70h would do the trick.
The idea behind NMIs is to partition the interrupts into two categories: one made of the common interrupts, interrupt that are used by device under a normal functioning of the system and that an OS could want to mask; the other category made by interrupt much more serious that an OS wouldn't want to mask (except for transitory states).
The NMI generates the interrupt 2, so NMI uses the IVT (Interrupt Vector Table, used in real mode) or the IDT (Interrupt Descriptor Table, used in protected mode and related).
In the DOS days the interrupt 2 handler was set by the BIOS, this vendor specific handler read a proprietary set of hardware registers, detected the error (like a memory ECC error, a bus error due to a suddend unplug), and attempted a recovery (like a reset or a memory scrubbing).
This mechanism made sense at the time because the OS worked in conjunction with the BIOS, being able to perform a cli
without affecting the NMI was great simplification.
With the advent of protected mode, the BIOS interrupt 2 handler, made for real mode, became useless.
Some kernels used the NMI for implementing a kernel watchdog timer since NMI interrupts would be generated even after a cli
.
Intel recognised that using interrupts to give control to the firmware was no longer a solution as new execution modes could be added to the CPU, a more transparent solution was needed.
So they created the SMM (System Management Mode).
Contrary to a normal interrupt the SMM is a special mode of the CPU, quoting chapter 34 of manual 2 from Intel:
When SMM is invoked through a system management interrupt (SMI), the processor saves the current state of the
processor (the processor’s context), then switches to a separate operating environment defined by a new address
space. The system management software executive (SMI handler) starts execution in that environment, and the
critical code and data of the SMI handler reside in a physical memory region (SMRAM) within that address space.
While in SMM, the processor executes SMI handler code to perform operations such as powering down unused disk
drives or monitors, executing proprietary code, or placing the whole system in a suspended state. When the SMI
handler has completed its operations, it executes a resume (RSM) instruction.
To enter SMM a SMI (System Management Interrupt) must be generated
The only way to enter SMM is by signaling an SMI through the SMI# pin on the processor or through an SMI message received through the APIC bus.
While the I in SMI stand for Interrupt asserting the SMI# won't generate an interrupt in the sense shown above.
SMI was introduced with a later version of the 386, right when protected mode was becoming popular.
It has been used to translate hardware interface transparently, for example, Intel chipsets can be programmed to generate an SMI when ports 60h-64h are accessed in order to give legacy support for USB devices.
Those are the standard PS2 ports for dealing with mouse and keyboard; the SMM code transparently read the value of the ports 60h-64h and appropriately translates those accesses to USB accesses.
With the advent of Pentium, Intel introduced the LAPIC (Local Advanced Programmable Interrupt Controller), the INTR and NMI are gone, substituted by the LINT0 and LINT1 (Local Interrupt).
LINT0 and LINT1 are programmable pins, from Chapter 10 of manual 2 from Intel:
At reset LINT0 is configured as INTR and LINT1 as NMI for backwards compatibility.
The complexity of routing interrupts and giving them a priority has been moved to hardware external to the CPU.
Every device or platform used a custom wiring, for example, PCI devices had their pins for interrupts crossed to minimise pollution.
The advent of the IOAPIC with its hierarchical layout and of the [MSI]s(https://en.wikipedia.org/wiki/Message_Signaled_Interrupts) added more functional layers in the handling of interrupts.
Meanwhile, it was recognised that leaving the OS out of management decision by using the SMI was not a good idea:
an OS can disable or throttle a processor or a device, can report, log, shutdown.
The complexity of dealing with hardware without a standardised informative support from the firmware and the need to make the OS part of the management procedures culminated in the ACPI specifications.
Instead of relying on OS invisible modes like SMM, ACPI defines an interpretable byte-code (the AML).
The OS is responsible for executing, firmware developers are responsible for writing it.
The ACPI designers decided to use a normal interrupt, the SCI (System Control Interrupt), to request the attention of the OS, this interrupt can be signalled in any way but must be handled by the OS and not by the firmware.
Quoting the ACPI specifications:
The device can signal an SCI
to inform the OS of changes in power status. For example, a device can trigger an interrupt to inform
the OS that the battery has reached low power level
The SCI is a normal interrupt, its vector (read: number) is reported in one of the ACPI table/structure.
The SCI vector can be configured by programming the chipset, a common value used the last time I read some datasheet/table was 9.
Upon an SCI the OS typically determines the source of such interrupt and perform the appropriate action, usually this involves executing some AML code in the process.
An hardware interrupt can be "generated" by software in two ways, depending on the meaning of "generated":
The software can "stimulate" the hardware.
For example, a timer can be programmed to expire immediately to trigger its interrupt.
This can be used with any of the interruption sources mentioned above, the NMI/SMI are a bit tricky: the LINT0 need to be programmed appropriately.
The software can execute the interrupt handler.
This can be done with all the interruption sources but SMI, SMI can only be entered upon a hardware event as the memory where the SMM code resides is not mapped when not in SMM.
Note that the OS usually expect an hardware interrupt to be generated under specific hardware status, if no cause can be found the interrupt is usually classified as spurious, not all OS/handler deal well with spurious interrupts.
Priorities
Chapter 6.9 of the 3rd Intel manual reports the interrupts priorities
1 (Highest) Hardware Reset and Machine Checks
- RESET
- Machine Check
2 Trap on Task Switch
- T flag in TSS is set
3 External Hardware Interventions
- FLUSH
- STOPCLK
- SMI
- INIT
4 Traps on the Previous Instruction
- Breakpoints
- Debug Trap Exceptions (TF flag set or data/I-O breakpoint)
5 Nonmaskable Interrupts (NMI)
6 Maskable Hardware Interrupts
7 Code Breakpoint Fault
8 Faults from Fetching Next Instruction
- Code-Segment Limit Violation
- Code Page Fault
9 Faults from Decoding the Next Instruction
- Instruction length > 15 bytes
- Invalid Opcode
- Coprocessor Not Available
10 (Lowest) Faults on Executing an Instruction
- Overflow
- Bound error
- Invalid TSS
- Segment Not Present
- Stack fault
- General Protection
- Data Page Fault
- Alignment Check
- x87 FPU Floating-point exception
- SIMD floating-point exception
- Virtualization exception
Thus, considering that a SCI is just a normal interrupt the ordering is
Normal interrupt/SCI < NMI < SMI
Note that this order is used when multiple interrupts occur at the "same" time.
While serving an NMI or while in SMI is possible to enable and serve normal / SCI interrupts.