Managing interrupt processing (irq.h)

The API is defined in the header file sysroot-*-kos/include/coresrv/io/irq.h from the KasperskyOS SDK.

The API manages the handling of hardware interrupts. A hardware interrupt is a signal sent from a device to direct the processor to immediately pause execution of the current program and instead handle an event related to this device. For example, pressing a key on the keyboard invokes a hardware interrupt that ensures the required response to this pressed key (for example, input of a character).

A hardware interrupt occurs when the device queries the interrupt controller. This query can be transmitted through a hardware interrupt line between the device and the interrupt controller or through MMIO memory. In the second case, the device writes to MMIO memory by calling the Message Signaled Interrupt (MSI).

Each hardware interrupt line corresponds to one interrupt with a unique number.

Two types of message signaled interrupts are supported: MSI and MSI-X (extended version).

Information about API functions is provided in the table below.

Using the API

To attach an interrupt to its handler, complete the following steps:

  1. Registering an interrupt.

    To register an interrupt that occurs when a device queries the interrupt controller through the hardware interrupt line, call the KnRegisterIrq() function. If such an interrupt needs to be registered multiple times, the MMIO_U_SHARED flag must be specified in the flags parameter during each registration. If the registered interrupt is used by the kernel code, the MMIO_K_SHARED flag must be specified. If these flags are not specified, only the code that requests this access first (this can be either the kernel code or the code that is executed in user mode) will have access to the interrupt. All subsequent registration attempts by calling the KnRegisterIrq() function will end with the rcAlreadyExists error, and messages regarding the interrupt sharing problem will be added to the diagnostic output. If the kernel fails to access the interrupt because this interrupt is already registered by the code being executed in user mode, a message regarding the interrupt sharing problem will also be added to the diagnostic output.

    To register a message signaled interrupt, call the KnIoRegisterMsiIrq() function after first creating a message signaled interrupt object by calling the KnIoAllocMsi() function. The MSI object handle can be transferred to another process via IPC. One object can be associated with only one message signaled interrupt at a time. A message signaled interrupt registration object can be reused only after closing the handle of the previously registered interrupt. After registering the message signaled interrupt, the object handle can be closed.

    The data received through the addr and data output parameters of the KnIoAllocMsi() function must be passed to the PCIe bus controller to configure the message signaled interrupts (such as the MSI or MSI-X type) for the specified device. Moreover, one interrupt of the MSI type or one or more interrupts of the MSI-X type can be configured for one device. (The KnIoAllocMsi() function does not allow allocation of a sequence of interrupt vectors, thereby making it impossible to support multiple interrupts of the MSI type for one device.) If you need to configure one interrupt of the MSI or MSI-X type for a device, you must create one message signaled interrupt object via a single call of the KnIoAllocMsi() function. If you need to configure multiple MSI-X interrupts for a device, you must create the same number of message signaled interrupt objects via several calls of the KnIoAllocMsi() function. If the maximum possible number of message signaled interrupt objects have been created in the system, the KnIoAllocMsi() function returns rcResourceNotFound. If the maximum possible number of message signaled interrupt objects with the same interrupt priority have been created in the system, the KnIoAllocMsi() function returns rcFail. If the maximum number of message signaled interrupt objects have been created for the device, the KnIoAllocMsi() function returns rcFail.

    An interrupt handle obtained by calling the KnRegisterIrq() or KnIoRegisterMsiIrq() function can be passed to another process via IPC.

  2. Attaching a thread to an interrupt by calling the KnIoAttachIrq() function.

    This step is performed by the thread in whose context the interrupt will be handled.

    When using the interrupt handle obtained by calling the KnRegisterIrq() or KnIoRegisterMsiIrq() function, you can attach only one thread to an interrupt. In contrast to a message signaled interrupt, the interrupt that occurs when the device queries the interrupt controller via the hardware interrupt line can be attached to multiple threads in one or more processes. To do so, you must use different handles of the same interrupt obtained from separate calls of the KnRegisterIrq() function with the same flags in the flags parameter.

    A handle obtained by calling the KnIoAttachIrq() function cannot be transferred to another process via IPC.

To deny (mask) an interrupt, call the KnIoDisableIrq() or KnIoDisableIrqNoSync() function. The KnIoDisableIrq() function returns control after completion of the current iteration in the maskable interrupt handler except for a case in which the function is called from this handler. In this case, the KnIoDisableIrq() function behaves like the KnIoDisableIrqNoSync() function, which always returns control without waiting for completion of the current iteration in the maskable interrupt handler. In other words, the current iteration in the interrupt handler may be completed after the KnIoDisableIrqNoSync() function has returned control. The KnIoDisableIrq() function cannot be called from a critical section that is synchronized with a critical section in the maskable interrupt handler or in the handler of another interrupt.

To allow (unmask) the interrupt, call the KnIoEnableIrq() function. (After an interrupt is registered and a thread is attached to it, this interrupt does not require unmasking.)

Although the KnIoDisableIrq(), KnIoDisableIrqNoSync(), and KnIoEnableIrq() functions receive an interrupt handle through which only one thread is attached to the interrupt, their effect applies to all threads attached to this interrupt. (This does not apply to message signaled interrupts to which only one thread can be attached.)

To initiate detachment of a thread from an interrupt, call the KnIoDetachIrq() function outside of the thread that is attached to the interrupt. Detachment is performed by the thread attached to the interrupt by calling the KnThreadDetachIrq() function from the thread_api.h API.

Handling an interrupt

After attaching to an interrupt, the thread calls the Call() function from the syscalls.h API. The thread is locked as a result of this call. When an interrupt occurs or the KnIoDetachIrq() function is called, the KasperskyOS kernel sends an IPC message to the process that contains this thread. This IPC message contains a request to handle the interrupt or a request to detach the thread from the interrupt. When a process receives an IPC message, the Call() function in the thread attached to the interrupt returns control and provides the contents of the IPC message to the thread. The thread extracts the request from the IPC message and either processes the interrupt or detaches from the interrupt. If the interrupt is processed, information about its failure or success upon completion is added to the response IPC message that is sent to the kernel by the next Call() function call in the loop.

When processing an interrupt, use the IoGetIrqRequest() and IoSetIrqAnswer() functions that are declared in the header file sysroot-*-kos/include/io/io_irq.h from the KasperskyOS SDK. These functions let you extract data from IPC messages and add data to IPC messages for data exchange between the kernel and the thread attached to the interrupt.

The standard interrupt processing loop includes the following steps:

  1. Adding information about the failure or success of interrupt processing to an IPC message by calling the IoSetIrqAnswer() function.
  2. Sending the IPC message to the kernel and receiving an IPC message from the kernel.

    To complete this step, call the Call() functions. In the handle parameter, you must specify the handle that was received when the KnIoAttachIrq() function was called. You must use the msgOut parameter to define the IPC message that will be sent to the kernel, and use the msgIn parameter to define the IPC message that will be received from the kernel.

  3. Extracting a request from the IPC message received from the kernel by calling the IoGetIrqRequest() function.
  4. Processing the interrupt or detaching from the interrupt depending on the request.

    If the request requires detachment from the interrupt, exit the interrupt processing loop and call the KnThreadDetachIrq() function from the API thread_api.h.

Deregistering an interrupt

To deregister an interrupt, complete the following steps:

  1. Detach the threads from the interrupt.

    To complete this step, use the KnThreadDetachIrq() function from the API thread_api.h.

  2. Close the handles that were received when the KnIoAttachIrq() function was called.
  3. Close or revoke the interrupt handles in all processes that own these handles.

    In the case of a message signaled interrupt, you must additionally close or revoke the message signaled interrupt object handles in all processes that own these handles.

Information about API functions

irq.h functions

Function

Information about the function

KnRegisterIrq()

Purpose

Registers an interrupt that occurs when a device queries an interrupt controller through a hardware interrupt line.

Parameters

  • [in] irq – interrupt number.
  • [in] flags – flags defining the interrupt parameters. Flags are defined in the header files sysroot-*-kos/include/io/io_irq.h and sysroot-*-kos/include/hal/irqmode.h from the KasperskyOS SDK.
  • [out] outRid – pointer to the interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

Additional information

In the flags parameter, you can specify the following flags:

  • IRQ_LEVEL_LOW – the interrupt occurs when the signal level is low.
  • IRQ_LEVEL_HIGH – the interrupt occurs when the signal level is high.
  • IRQ_EDGE_RAISE – the interrupt occurs when the signal level increases.
  • IRQ_EDGE_FALL – the interrupt occurs when the signal level decreases.
  • IRQ_PRIO_LOW – the interrupt has low priority.
  • IRQ_PRIO_NORMAL – the interrupt has medium priority.
  • IRQ_PRIO_HIGH – the interrupt has high priority.
  • IRQ_PRIO_RT – the interrupt has the highest priority.
  • IRQ_K_SHARED – shared access with the kernel.
  • IRQ_U_SHARED – shared access with multiple registrations.

KnIoAttachIrq()

Purpose

Attaches the calling thread to an interrupt.

Parameters

  • [in] rid – interrupt handle.
  • [out] handle – pointer to the client IPC handle that is used by the interrupt handler.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

KnIoDetachIrq()

Purpose

Sends a request to a thread. When this request is fulfilled, the thread must detach from the interrupt.

Parameters

  • [in] rid – interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

KnIoEnableIrq()

Purpose

Enables (unmasks) an interrupt.

Parameters

  • [in] rid – interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

KnIoDisableIrq()

Purpose

Denies (masks) an interrupt, optionally waiting for completion of the current iteration in the maskable interrupt handler.

Parameters

  • [in] rid – interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

Additional information

The function does not wait for completion of the current iteration in the maskable interrupt handler if it is called from this handler.

KnIoDisableIrqNoSync()

Purpose

Denies (masks) an interrupt without waiting for completion of the current iteration in the maskable interrupt handler.

Parameters

  • [in] rid – interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

KnIoAllocMsi()

Purpose

Creates a message signaled interrupt object.

Parameters

  • [in] order – fictitious parameter (reserved for possible future use). You must specify 0.
  • [in] deviceId – address of the device on the PCIe bus in BDF format.
  • [in] priority – interrupt priority. Constants with possible priority values are defined in the header file sysroot-*-kos/include/io/io_irq.h from the KasperskyOS SDK.
  • [out] addr – pointer to the MMIO memory address to which the device needs to write to trigger an interrupt (Message Address according to the PCIe specification).
  • [out] data – pointer to the data that the device needs to write to the MMIO memory at the address obtained through the addr parameter to trigger an interrupt (Message Data according to the PCIe specification).
  • [out] msi – pointer to the handle of the message signaled interrupt object.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

Additional information

In the priority parameter, you can specify the following flags:

  • IRQ_PRIO_LOW – the interrupt has low priority.
  • IRQ_PRIO_NORMAL – the interrupt has medium priority.
  • IRQ_PRIO_HIGH – the interrupt has high priority.
  • IRQ_PRIO_RT – the interrupt has the highest priority.

KnIoRegisterMsiIrq()

Purpose

Registers a message signaled interrupt.

Parameters

  • [in] msi – handle of the message signaled interrupt object.
  • [in] index – fictitious parameter (reserved for possible future use). You must specify 0.
  • [out] irq – pointer to the interrupt handle.

Returned values

If successful, the function returns rcOk, otherwise it returns an error code.

Page top