The QNX Neutrino RTOS kernel and process manager
(procnto*) is designed to be as platform-independent as
possible. Each release provides a kernel version for each
supported hardware architecture (see procnto* in the Utilities
Reference).
Kernel callouts are standalone pieces of code (or code fragments) that the kernel
uses to perform hardware-specific functions. These callouts don't need to be
statically linked to the kernel.
The startup code can include multiple versions of the same kernel callout. For
example, it can include a generic kernel callout as well as kernel callouts that are
specific to an architecture and board. During hardware discovery, the startup code can
determine which kernel callout is most appropriate for the current hardware and make
that version of the callout available to the kernel. However, it is simpler and more
efficient to avoid the discovery process and include only one version of
each callout in the startup code and have this code tell the kernel
which callouts to use.
Note:
- The startup program copies the kernel callout code from its own memory
into the system
page. When this is complete, the memory used for the startup
program is freed.
- For all but two routines, the kernel uses normal function-calling
conventions to invoke the callouts. For information about these two
exceptions, see interrupt_id_*() and interrupt_eoi_*().
Common characteristics
For information about what different kernel callouts are used for,
see Kernel callout categories in this chapter.
All kernel callouts share some common characteristics, including:
- Assembly code
-
Kernel callouts are written in assembly because:
- They must be position-independent (see Interrupt controller below).
- There is no portable way of controlling pre- and post-amble
creation, or code generation; a change to an Application Binary
Interface (ABI) or a build configuration problem might create a latent
bug in code that isn't fully self-contained and position-independent.
- Position independent code (PIC)
- Position independence is required because kernel callouts are provided in
the startup code, which is overwritten when the kernel starts up.
- To keep the kernel callouts after the kernel has started, the startup
library copies them to a location where they won't be overwritten.
After this copying, the callouts are in a location
other than where they were loaded, so they must be written to be
position-independent. This position independence requires that they be
written in assembly.
- Inline in kernel source
- Kernel callouts are copied inline to the kernel code and don't use the C
language return keyword. However, they can provide the kernel with
information in CPU registers.
- Interrupt ID and EOI
- The interrupt ID and EOI used for kernel callouts are specific to kernel
callouts.
- No static read/write storage
- Kernel callouts can't specify any static read/write storage space for
themselves. However, in the system page, you can allocate a small amount of storage space
for a specific kernel callout.
Passing data to and from kernel callouts
The kernel uses the SoC's Application Binary Interface (ABI) to pass data to and get
data from kernel callouts. You should become familiar with the ABI documentation for
your board before you attempt to write kernel callouts for it.
In many cases, the kernel puts inputs to kernel callouts in general-purpose
registers. You can also use INTR_GENFLAG_* flags to have the kernel
pass in additional input values. These and other flags are in the
intrinfo structure that is set up by the init_intrinfo() function.
Similarly, you may need to have kernel callouts put their output in
registers.
Note: QNX kernel callout code usually has comments right in the source files,
and they provide details about which registers to use for callout input and output.
If these comments are absent or unclear, refer to the ABI documentation for
your board.