Information about the interrupt system
The intrinfo area stores information about the interrupt system,
and contains the kernel callouts that manipulate the interrupt controller hardware.
For 64-bit systems, this area is defined by an array of
new_intrinfo data structures; 32-bit systems use the
old_intrinfo data structures.
On a multicore system, each interrupt is directed to only one CPU, although
it doesn't matter which. How this happens is controlled by the board's Programmable
Interrupt Controller chips (PICs). 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 CPUs each time it is
asserted.
Often, QNX startup programs are written so that all interrupts (except
those used for interprocessor interrupts) are sent to CPU 0. This
allows for the same startup for procnto and
procnto-smp. Also, directing all interrupts to one CPU
is efficient because it supports better cache usage.
The startup library's
init_intrinfo() function
fills in the
intrinfo area. If you need to override some
defaults set by this function (which is common for non-x86 systems)
or if the function isn't suitable for your custom environment,
you can call
add_interrupt_array(), passing in a table
with these fields:
- 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
interrupt source.
- The interpretation of the cpu_intr_base member varies
with the processor. For ARM processors, this value should be 0, because
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. The default is 0 (no autovectoring).
- On an x86 platform with the standard 8259 controller setup, this value is
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, etc).
- On non-x86 systems, this value is usually 0, because their interrupt
systems generally don't do autovectoring.
-
CAUTION:
Prior to QNX Neutrino 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 7.0, 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 (e.g., 0 for
non-x86 architectures).
- flags
- Used by the startup code when generating the kernel's interrupt service
routine (ISR) entry points (see INTR_* flags below).
- id
- A code snippet that gets copied into the kernel's ISR. This code is used
to identify the source of the interrupt, for the case when multiple hardware
events can 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 ISR and
that provides the End Of Interrupt (EOI) functionality. This code 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
- A callout to mask an interrupt source at the hardware controller level. The
numbers passed to this function are the interrupt vector numbers (from 0 to
num_vectors - 1).
- unmask
- A callout to unmask an interrupt source at the hardware controller level.
Same vector numbers as mask, above.
- config
- Provides configuration information on individual interrupt levels.
The function get passed the system page pointer,
a pointer to this interrupt info entry, and the zero-based interrupt level.
Returns a bitmask (see INTR_CONFIG_FLAG* below).
- patch_data
- Provides information about patched data. This data is passed to the
patcher() routine that gets called once for each
kernel callout in a startup_intrinfo() structure.
- local_stride
- When local_stride is non-zero, the interrupt is processor-specific.
- The processor-specific interrupt ID must be passed to functions like
InterruptAttach() for a processor-specific
interrupt. The processor-specific interrupt ID is calculated as follows:
processor_intid = common_intid + (processor_number * local_stride)
The common interrupt ID is architecture-specific and must be known by the programmer.
The processor number is the processor targeted by the interrupt.
- A local_stride of 0 indicates that the interrupt is not
processor-specific.
Note:
Each group of kernel callouts (id,
eoi, mask, unmask)
for each interrupt controller level deals with a set of zero-based interrupt
vectors. You must set the kernel callouts for each interrupt level
accordingly.
Interrupt vector numbers are passed without offset to the callout routines.
The mapping between the zero-based interrupt vectors used by
the callouts and the system-wide interrupt vectors is configured in the
intrinfo structures, which are found in the
init_intrinfo() routine.
INTR_* flags
The flags member takes two sets of flags. One set
describes interrupt characteristics, the other set affects interrupt code
generation. Both sets 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 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.
- Interrupts that are normally used and referred to by number are
called maskable interrupts. These interrupts
can be ignored (masked) to let the processor 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 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 (i.e., 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 trigger a check 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 there's no reason
(other than the fact that the hardware did generate an interrupt)
to be in the ISR. 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. The first read returns a bitset of the pending
interrupts, 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 set in the status register, we might fail to process
all interrupts. When INTR_GENFLAG_ID_LOOP is set, the
kernel generates code to jump back to the ID kernel callout after the EOI
has finished.
In the ID kernel callout, you can allocate read-write storage in the usual way.
The storage is initially set to zero. 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, unsets that bit, 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 unsets that bit, writes the value to the
storage location, and 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 (ignored) until a routine 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 used as the target of an inter-processor
interrupt in an SMP system.