Kernel Callouts

Updated: April 19, 2023

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.

  • 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.