intrinfo

Information about the interrupt system.

The intrinfo area is used to store information about the interrupt system. It also contains the kernel callouts that manipulate the interrupt controller hardware. For 64-bit systems, the system page's intrinfo area is defined by an array of new_intrinfo data structures; 32-bit systems use the old_intrinfo data structure.

On a multicore system, each interrupt is directed to one (and only one) CPU, although it doesn't matter which. How this happens is controled by the board's programmable interrupt controller chip (PIC) or chips. When you initialize the PICs at startup, you can program them to deliver the interrupts to whichever CPU you want; on some PICs you can even get an interrupt to rotate between the CPUs each time it is asserted.

For the startups we write, we typically program things so that all interrupts (except those used for interprocessor interrupts) are sent to CPU 0. This configuration lets us use the same startup for both procnto and procnto-smp. In addition, directing all interrupts to one CPU is efficient because it allows for better cache utilization.

The startup library init_intrinfo() function automatically fills in the intrinfo area. If you need to override some of the defaults provided by init_intrinfo() (which is particularly likely for non-x86 systems) or if the function isn't appropriate for your custom environment, you can call add_interrupt_array() directly with a table of the following format:

vector_base
The base number of the logical interrupt numbers that programs will use (e.g., the interrupt vector passed to InterruptAttach()).
num_vectors
The number of vectors starting at vector_base described by this entry.
cascade_vector
If this interrupt entry describes a set of interrupts that are cascaded into another interrupt controller, then this member contains the logical interrupt number of the interrupt that this controller cascades into.
cpu_intr_base
The association between this set of interrupts and the CPU's view of the source of the interrupt.
The interpretation of the cpu_intr_base member varies with the processor. For ARM processors, this value should be 0, since all ARM interrupts are handled via the IRQ exception. For x86, this is the Interrupt Descriptor Table (IDT) entry, typically 0x30.
cpu_intr_stride
The spacing between interrupt vector entries for interrupt systems that do autovectoring. Default is 0; there is no autovectoring.
On an x86 platform with the standard 8259 controller setup, this is the value 1, meaning that the interrupt vector corresponding to the hardware interrupt sources is offset by 1 (e.g., interrupt vector 0 goes to interrupt 0x30, interrupt vector 1 goes to interrupt 0x31, and so on).
On non-x86 systems it's usually 0, because their interrupt systems generally don't do autovectoring.
CAUTION:
Prior to QNX Neutrino RTOS 7.0, for non-x86 platforms the system didn't check the value of cpu_intr_stride because these systems generally don't support autovectoring. Starting with QNX Neutrino, however, the system checks the value of cpu_intr_stride on all platforms, and fails if this value isn't valid for the current hardware (0 for non-x86 architectures).
flags
Used by the startup code when generating the kernel's interrupt service routine entry points (see INTR_* flags below).
id
A code snippet that gets copied into the kernel's interrupt service routine. It is used to identify the source of the interrupt, in the event of multiple hardware events being able to trigger one CPU-visible interrupt. Further modified by the INTR_GENFLAG_* flags, defined below.
eoi
A code snippet that gets copied into the kernel's interrupt service routine that provides the End Of Interrupt (EOI) functionality. This code snippet is responsible for telling the controller that the interrupt is done, and for unmasking the interrupt level. For CPU fault-as-an-interrupt handling, eoi identifies the cause of the fault.
mask
An outcall to mask an interrupt source at the hardware controller level. The numbers passed to this function are the interrupt vector numbers (starting at 0 to num_vectors - 1).
unmask
An outcall to unmask an interrupt source at the hardware controller level. Same vector numbers as mask, above.
config
Provides configuration information on individual interrupt levels. Passed the system page pointer (1st argument), a pointer to this interrupt info entry (2nd argument), and the zero-based interrupt level. Returns a bitmask (see INTR_CONFIG_FLAG* below).
patch_data
Provides information about patched data. The patched data is passed to the patcher() routine that gets called once for each kernel callout in a startup_intrinfo() structure (see Patching the kernel callout code).
Note:

Each group of kernel callouts (i.e., id, eoi, mask, unmask) for each level of interrupt controller deals with a set of interrupt vectors that start at 0 (zero-based). Set the kernel callouts for each level of interruption accordingly.

Interrupt vector numbers are passed without offset to the kernel callout routines. The association between the zero-based interrupt vectors the callouts use and the system-wide interrupt vectors is configured in the startup-intrinfo structures. These structures are found in the init_intrinfo() routine of startup.

INTR_* flags

The flags member takes two sets of flags. One set of flags describes interrupt characteristics, the other set affects interrupt code generation. Both sets of flags are system independent.

Interrupt characteristics

The first set of flags deals with the characteristics of the interrupts:

INTR_FLAG_NMI
Indicates that this is a NonMaskable Interrupt (NMI). An NMI is an interrupt which, unlike most normal interrupts, can't be disabled by clearing the CPU's interrupt enable flag. NMIs are typically used to signal events that require immediate action, such as a parity error, a hardware failure, or an imminent loss of power.
Regular interrupts that are normally used and referred to by number are called maskable interrupts. Maskable interrupts are interrupts that can be masked, or ignored, to allow the processor to complete a task.
With an NMI, the kernel needs to know that it can't protect (mask) the interrupt. The INTR_FLAG_NMI value in the flags member allows the kernel to differentiate between normal (maskable) interrupts and NMIs.
We strongly discourage the use of the NMI vector in x86 designs; we don't support it on any non-x86 platforms.
INTR_FLAG_CASCADE_IMPLICIT_EOI
Indicates that an EOI to the primary interrupt controller is not required when handling a cascaded interrupt, because the EOI is issued automatically. Only used if this entry describes a cascaded controller.
INTR_FLAG_CPU_FAULT
Indicates that one or more of the vectors described by this entry is not connected to a hardware interrupt source, but is generated as a result of a CPU fault (e.g., bus fault, parity error).
We strongly discourage designing your hardware this way. The implication is that a check needs to be inserted for an exception into the generated code stream; after the interrupt has been identified, an EOI needs to be sent to the controller. The EOI code burst has the additional responsibility of detecting what address caused the fault, retrieving the fault type, and then passing the fault on. The primary disadvantage of this approach is that it causes extra code to be inserted into the code path.

Interrupt code generation

The second set of flags deals with code generation:

INTR_GENFLAG_LOAD_SYSPAGE
Before the interrupt identification or EOI code sequence is generated, a piece of code needs to be inserted to fetch the system page pointer into a register so that it's usable in the identification code sequence.
Note: If you use the interrupt_id_dec(), interrupt_id_dec_smp(), or interrupt_eoi_dec() kernel callouts, you must specify the INTR_GENFLAG_LOAD_SYSPAGE flag in the genflags field of the intrinfo_entry structure in the board-specific code.
INTR_GENFLAG_LOAD_INTRINFO
Same as INTR_GENFLAG_LOAD_SYSPAGE, except that it loads a pointer to this structure.
INTR_GENFLAG_LOAD_INTRMASK
Used only by EOI routines for hardware that doesn't automatically mask at the chip level. When the EOI routine is about to re-enable interrupts, it should re-enable only those interrupts that are actually enabled at the user level (e.g., managed by the functions InterruptMask() and InterruptUnmask()).
When this flag is set, the existing interrupt mask is stored in a register for access by the EOI routine. A zero in the register indicates that the interrupt should be unmasked; a non-zero indicates it should remain masked.
INTR_GENFLAG_NOGLITCH
Used by the interrupt ID code to cause a check to be made to see if the interrupt was due to a glitch or to a different controller. If this flag is set, the check is omitted — you're indicating that there's no reason (other than the fact that the hardware actually did generate an interrupt) to be in the interrupt service routine. If this flag is not set, the check is made to verify that the suspected hardware really is the source of the interrupt.
INTR_GENFLAG_LOAD_CPUNUM
Same as INTR_GENFLAG_LOAD_SYSPAGE, except that it loads a pointer to the number of the CPU this structure uses.
INTR_GENFLAG_ID_LOOP
Some interrupt controllers have read-and-clear registers indicating the active interrupts. That is, the first read returns a bitset with the pending interrupts, and then immediately zeroes the register.
Since the interrupt ID kernel callout can return only one interrupt number at a time, if there's more than one bit on in the status register we might fail to process all the interrupts. When INTR_GENFLAG_ID_LOOP is on, the kernel generates code to jump back to the ID kernel callout after the EOI has finished.

In the ID kernel callout, you need to allocate read-write storage as per the usual procedures. This storage is initially set to zero (by default). When the callout runs, the first thing it does is check the storage area:

  • If the storage is non-zero, the callout uses it to identify another interrupt to process, knocks that bit down, writes the new value back into the storage location and returns the identified interrupt number.
  • If the storage location is zero, the callout reads the hardware status register (clearing it) and identifies the interrupt number from it. It then knocks that bit off, writes the value to the storage location, and then returns the appropriate interrupt number.
  • If both the storage and hardware register are zero, the routine returns -1 to indicate no interrupt is present as per usual.

config() return values

The interrupt interface's config() kernel callout (see Interrupt controller in the Kernel Callouts chapter) may return zero or more of the following flags:

INTR_CONFIG_FLAG_PREATTACH
Normally, an interrupt is masked off until a routine uses attaches to it via InterruptAttach() or InterruptAttachEvent(). If CPU fault indications are routed through to a hardware interrupt (not recommended!), the interrupt would, by default, be disabled. Setting this flag causes a “dummy” connection to be made to this source, causing this level to become unmasked.
INTR_CONFIG_FLAG_DISALLOWED
Prevents user code from attaching to this interrupt level. Generally used with INTR_CONFIG_FLAG_PREATTACH, but could be used to prevent user code from attaching to any interrupt in general.
INTR_CONFIG_FLAG_IPI
Identifies the vector that's used as the target of an inter-processor interrupt in an SMP system.