Custom

(QNX Neutrino 7.0.4 or later) You can use the custom() callout to define a callout function to be invoked synchronously when a kernel call with the value __KER_SYS_CUSTOM (defined in <sys/kercalls.h>) is invoked by a process.

The function is called in privileged mode (EL1/Ring0) and using the kernel stack, but without entering the kernel. Interrupts are disabled. In general, the same rules and restrictions apply as for an ISR: keep it short, don't enable interrupts, and don't make any kernel calls.

The signature for the callout is

uintptr_t (*custom_callout)( struct syspage_entry *syspageptr,
                             uint32_t type_id,
                             uintptr_t *regs )

The arguments are as follows:

syspageptr
A pointer to the system page; see the System Page chapter.
type_id
The calling process's type (which can be used by the callout for permission checking).
regs
An array holding the register values of the calling thread. The layout of the thread registers is defined by the arch_CPU_REGISTERS structure, which can be found in the arch/context.h header. For example, the layout for x86_64 is defined by the X86_64_CPU_REGISTERS structure in x86_64/context.h, and for AArch64 by AARCH64_CPU_REGISTERS in aarch64/context.h.

Note that there's no C library function wrapping the kernel call, as the C signature of the function depends on the implementation of the callout and is at the discretion of the author of the callout. The kernel only takes care to propagate the registers of the calling thread to and from the callout. The standard ABI should be followed when writing C wrappers for the kernel call.

Example

Here's an example of a custom callout for AArch64:

#include "callout.ah"

/*
 * -----------------------------------------------------------------------
 * Routine to patch callout code
 *
 * On entry:
 *  r0 - physical address of syspage
 *  r1 - virtual  address of syspage
 *  r2 - offset from start of syspage to start of the callout routine
 *  r3 - offset from start of syspage to read/write data used by callout
 * -----------------------------------------------------------------------
 */
patch_custom:
    ret

/*
 * -----------------------------------------------------------------------
 * Sample implementation of a custom kernel call callout.
 *
 * On entry:
 *  r0 - pointer to the system page
 *  r1 - type ID for the calling process
 *  r2 - pointer to the first position in an array of 64-bit values holding
 *           the register context of the calling thread
 * -----------------------------------------------------------------------
 */
CALLOUT_START(custom, 0, patch_custom)
    mov     x5, #0xabcd
    movk    x5, #0xabcd, lsl #16
    movk    x5, #0xabcd, lsl #32
    movk    x5, #0xabcd, lsl #48

    /*
     * Reject processes with a type ID other than 47.
     */
    cmp      x1, #47
    beq      1f
    mov      x0, #-1
    ret

1:
    /*
     * Return the sum of the values held in the caller's x0 and x1 registers.
     */
    ldp      x2, x3, [x2]
    add      x0, x2, x3
    ret
CALLOUT_END(custom)

Invoking the callout

Here's a program that uses the callout by invoking a kernel call:

#include <stdio.h>

extern unsigned long SysCustom(unsigned long a, unsigned long b);

int
main(int argc, char **argv)
{
    unsigned long   rc = SysCustom(3, 4);
    if (rc == -1) {
        perror("SysCustom");
    } else {
        printf("rc=%ld\n", rc);
    }

    return 0;
}