KasperskyOS Community Edition 1.3

Using DMA (dma.h)

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

The API is designed to set up data exchange between devices and RAM in direct memory access (DMA) mode in which the processor is not used.

Information about API functions is provided in the table below.

Using the API

The standard scenario for API usage includes the following steps:

  1. Creating a DMA buffer.

    DMA buffer consists of one or more physical memory regions (blocks) that are used for DMA. A DMA buffer consisting of multiple blocks can be used if the device supports "scatter/gather DMA" mode. A DMA buffer consisting of one block can be used only if the device supports "scatter/gather DMA" or "continuous DMA" mode. The likelihood of creating a DMA buffer consisting of one large block is lower than the likelihood of creating a DMA buffer consisting of multiple small blocks. This is especially relevant when physical memory is highly fragmented.

    If the device supports only "continuous DMA" mode, you must use a DMA buffer consisting of one block even if IOMMU is enabled.

    To complete this step, call the KnIoDmaCreate() or KnIoDmaCreateContinuous() function. The KnIoDmaCreateContinuous() function creates a DMA buffer consisting of one block. The KnIoDmaCreate() function creates a DMA buffer consisting of one block if the 2^order value is equal to the memory page size value, or if the 2^order value is the next largest value of the memory page size in the ascending ordered set {2^(order-1);memory page size;2^order}. If the value of the memory page size is greater than the 2^order value, the KnIoDmaCreate() function can create a DMA buffer consisting of multiple blocks.

    The DMA buffer handle can be transferred to another process via IPC.

  2. Mapping the DMA buffer to the memory of processes.

    One DMA buffer can be mapped to multiple virtual memory regions of one or more processes that own the handle of this DMA buffer. Mapping allows processes to receive read-and/or-write access to the DMA buffer.

    To reserve a virtual memory region and map the DMA buffer to it, call the KnIoDmaMap() function.

    A handle received when calling the KnIoDmaMap() function cannot be transferred to another process via IPC.

  3. Opening access to the DMA buffer for a device via the KnIoDmaBegin() or KnIoDmaBeginEx() function call.

    The KnIoDmaBegin() or KnIoDmaBeginEx() function must be called to create a kernel object containing the addresses and sizes of blocks comprising the DMA buffer. A device needs this information to use the DMA buffer. A device can work with physical addresses and/or virtual addresses depending on whether IOMMU is enabled. If IOMMU is enabled, you must use the KnIoDmaBegin() or KnIoDmaBeginEx() function, and the object will contain virtual and physical addresses of blocks. If IOMMU is not enabled, you must use the KnIoDmaBegin() function, and the object will contain physical addresses of blocks only.

    When IOMMU is enabled, the device must be attached to an IOMMU domain.

    A handle received when calling the KnIoDmaBegin() or KnIoDmaBeginEx() function cannot be transferred to another process via IPC.

  4. Information about the DMA buffer is received.

    At this step, get the addresses and sizes of blocks from the kernel object that was created by calling the KnIoDmaBegin() or KnIoDmaBeginEx() function. The received addresses and sizes will need to be passed to the device by using MMIO, for example. After receiving this information, the device can write to the DMA buffer and/or read from it.

    To complete this step, call one of the following functions:

    • KnIoDmaGetInfo()
    • KnIoDmaGetPhysInfo()
    • KnIoDmaContinuousGetDmaAddr()
    • KnIoDmaContinuousGetPhysAddr()

    The KnIoDmaGetInfo() and KnIoDmaGetPhysInfo() functions get the memory page number (frame) and the order for each block. (The memory page number multiplied by the memory page size results in the block address. The 2^order value is the block size in memory pages.) These functions use the output parameter outInfo to pass the KosObject that contains the structure with information about the DMA buffer (for details about KosObjects, see Using KosObjects (objects.h)). The type of structure is defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK. The KnIoDmaGetInfo() function inserts data into all fields of the structure, while the KnIoDmaGetPhysInfo() function inserts data only into the count and descriptors fields while writing zeros to all other fields. When using the KnIoDmaGetInfo() function, the descriptors array contains virtual memory page numbers if IOMMU is enabled, and it contains physical memory page numbers if IOMMU is not enabled. When using the KnIoDmaGetPhysInfo() function, the descriptors array contains physical memory page numbers regardless of whether or not IOMMU is enabled.

    The KnIoDmaContinuousGetDmaAddr() and KnIoDmaContinuousGetPhysAddr() functions can be used if the DMA buffer consists of one block. These functions let you get the block address. (The accepted block size should be the DMA buffer size that was defined when it was created.) The KnIoDmaContinuousGetDmaAddr() function uses the output parameter addr to pass the virtual address if IOMMU is enabled or the physical address if IOMMU is not enabled. The KnIoDmaContinuousGetPhysAddr() function uses the output parameter addr to pass the physical address, regardless of whether or not IOMMU is enabled.

    On Raspberry Pi 4 B, the KnIoDmaGetPhysInfo() function lets you get the actual physical page numbers, while the KnIoDmaGetInfo() function lets you get the offset physical page numbers because some devices use Visual Processing Unit (VPU) translation for DMA. Similarly, the KnIoDmaContinuousGetPhysAddr() and KnIoDmaContinuousGetDmaAddr() functions let you get the actual and offset physical address, respectively.

Closing access to the DMA buffer for a device

Access to the DMA buffer can be closed only if IOMMU is enabled. If you delete the kernel object that was created when the KnIoDmaBegin() or KnIoDmaBeginEx() function was called, device access to the DMA buffer will be closed. To delete this object, you must close its handle.

Deleting a DMA buffer

To delete a DMA buffer, complete the following steps:

  1. Free the virtual memory regions that were reserved during KnIoDmaMap() function calls.

    To complete this step, you must close the handles that were received from KnIoDmaMap() function calls.

    This step must be completed for all processes whose memory is mapped to the DMA buffer.

  2. Delete the kernel object that was created by the KnIoDmaBegin() or KnIoDmaBeginEx() function call.

    To complete this step, you must close the handle that was received when the KnIoDmaBegin() or KnIoDmaBeginEx() function was called.

  3. Close or revoke each DMA buffer handle in all processes that own these handles.

Information about API functions

dma.h functions

Function

Information about the function

KnIoDmaCreate()

Purpose

Creates a DMA buffer.

Parameters

  • [in] order – parameter defining the minimum number of memory pages (2^order) in a block.
  • [in] size – size (in bytes) of the DMA buffer. It must be a multiple of the memory page size.
  • [in] flags – flags defining the DMA buffer parameters. The parameter type and flags are defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK.
  • [out] outRid – pointer to the DMA buffer 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:

  • DMA_DIR_TO_DEVICE – the device has read-access to the DMA buffer.
  • DMA_DIR_FROM_DEVICE – the device has write-access to the DMA buffer.
  • DMA_DIR_BIDIR – the device has read-and-write access to the DMA buffer.
  • DMA_ZONE_DMA32 – only the first four gigabytes of physical memory can be used to create a DMA buffer.
  • DMA_ATTR_WRITE_BACK, DMA_ATTR_WRITE_THROUGH, DMA_ATTR_CACHE_DISABLE, DMA_ATTR_WRITE_COMBINE, DMA_RULE_CACHE_VOLATILE, DMA_RULE_CACHE_FIXED – cache management.

KnIoDmaCreateContinuous()

Purpose

Creates a DMA buffer consisting of one block.

Parameters

  • [in] size – size (in bytes) of the DMA buffer. It must be a multiple of the memory page size.
  • [in] flags – flags defining the DMA buffer parameters. The parameter type and flags are defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK.
  • [out] outRid – pointer to the DMA buffer 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:

  • DMA_DIR_TO_DEVICE – the device has read-access to the DMA buffer.
  • DMA_DIR_FROM_DEVICE – the device has write-access to the DMA buffer.
  • DMA_DIR_BIDIR – the device has read-and-write access to the DMA buffer.
  • DMA_ZONE_DMA32 – only the first four gigabytes of physical memory can be used to create a DMA buffer.
  • DMA_ATTR_WRITE_BACK, DMA_ATTR_WRITE_THROUGH, DMA_ATTR_CACHE_DISABLE, DMA_ATTR_WRITE_COMBINE, DMA_RULE_CACHE_VOLATILE, DMA_RULE_CACHE_FIXED – cache management.

KnIoDmaMap()

Purpose

Reserves a virtual memory region and maps the DMA buffer to it.

Parameters

  • [in] rid – DMA buffer handle.
  • [in] offset – offset (in bytes) in the DMA buffer where mapping should start. It must be a multiple of the memory page size.
  • [in] length – size (in bytes) of the part of the DMA buffer that needs to be mapped. It must be a multiple of the memory page size. The following condition must also be fulfilled: length<=size of DMA buffer-offset.
  • [in,optional] hint – page-aligned, preferred base address of the virtual memory region, or 0 to select this address automatically.
  • [in] vmflags – flags defining the access rights to the virtual memory region. The flags are defined in the header file sysroot-*-kos/include/vmm/flags.h from the KasperskyOS SDK.
  • [out] addr – pointer to the base address of the virtual memory region.
  • [out] handle – pointer to the handle that is used to free the virtual memory region.

Returned values

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

Additional information

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

  • VMM_FLAG_READ – read access.
  • VMM_FLAG_WRITE – write access.

KnIoDmaModify()

Purpose

Modifies the DMA buffer cache settings.

Parameters

  • [in] rid – DMA buffer handle.
  • [in] newAttr – flags defining the DMA buffer caching parameters. The flags are defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK.

Returned values

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

Additional information

This function can be used if the following conditions are fulfilled:

  1. The DMA_RULE_CACHE_VOLATILE flag was specified when the DMA buffer was created.
  2. The DMA buffer is not mapped to virtual memory.
  3. The DMA_RULE_CACHE_VOLATILE flag was specified during the previous function call (if completed).

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

  • DMA_ATTR_WRITE_BACK, DMA_ATTR_WRITE_THROUGH, DMA_ATTR_CACHE_DISABLE, DMA_ATTR_WRITE_COMBINE, DMA_RULE_CACHE_VOLATILE – cache management.

KnIoDmaGetInfo()

Purpose

Gets information about a DMA buffer.

This information includes the addresses and sizes of blocks.

Parameters

  • [in] rid – DMA buffer handle.
  • [out] outInfo – pointer to the address of the object containing the structure with information about the DMA buffer. The type of structure is defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK.

Returned values

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

Additional information

To delete an object that was received via the outInfo parameter, you must use the KosPutObject() function from the objects.h API.

KnIoDmaGetPhysInfo()

Purpose

Gets the physical addresses and sizes of DMA buffer blocks.

Parameters

  • [in] rid – DMA buffer handle.
  • [out] outInfo – pointer to the address of the object containing the structure with information about the DMA buffer. The type of structure is defined in the header file sysroot-*-kos/include/io/io_dma.h from the KasperskyOS SDK.

Returned values

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

Additional information

To delete an object that was received via the outInfo parameter, you must use the KosPutObject() function from the objects.h API.

KnIoDmaContinuousGetDmaAddr()

Purpose

Gets the block address for a DMA buffer consisting of one block.

Parameters

  • [in] rid – DMA buffer handle.
  • [out] addr – pointer to the block address. The type of address is defined in the header file sysroot-*-kos/include/hal/mtypes.h from the KasperskyOS SDK.

Returned values

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

KnIoDmaContinuousGetPhysAddr()

Purpose

Gets the block physical address for a DMA buffer consisting of one block.

Parameters

  • [in] rid – DMA buffer handle.
  • [out] addr – pointer to the physical block address. The type of address is defined in the header file sysroot-*-kos/include/hal/mtypes.h from the KasperskyOS SDK.

Returned values

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

KnIoDmaBegin()

Purpose

Opens access to a DMA buffer for a device.

Parameters

  • [in] rid – DMA buffer handle.
  • [out] handle – pointer to the handle of the kernel object containing the addresses and sizes of blocks that were required for the device to use the DMA buffer.

Returned values

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

KnIoDmaBeginEx()

Purpose

Opens access to a DMA buffer for a device.

Parameters

  • [in] rid – DMA buffer handle.
  • [in] domain – IOMMU domain handle.
  • [out] handle – pointer to the handle of the kernel object containing the addresses and sizes of blocks that were required for the device to use the DMA buffer.

Returned values

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