KasperskyOS Community Edition

Using DMA (dma.h)

May 21, 2024

ID libkos_dma_api

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() function call.

    The KnIoDmaBegin() 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, an object contains virtual addresses of blocks. Otherwise, an object contains physical addresses of blocks.

    A handle received when calling the KnIoDmaBegin() 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() 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 (if IOMMU is enabled, a device on the PCIe bus must be attached to the IOMMU domain).

    To complete this step, you need to call the KnIoDmaGetInfo() or KnIoDmaContinuousGetDmaAddr() function. The KnIoDmaGetInfo() function gets 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.) The KnIoDmaContinuousGetDmaAddr() function can be used if the DMA buffer consists of one block. This function gets the block address. (The accepted block size should be the DMA buffer size that was defined when this buffer was created.)

Closing access to the DMA buffer for a device

If you delete the kernel object that was created when the KnIoDmaBegin() function was called and IOMMU is enabled, the device will be denied access to the DMA buffer. To delete this object, call the KnHandleClose() function and specify the handle that was received when the KnIoDmaBegin() function was called. (The KnHandleClose() function is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h from the KasperskyOS SDK.)

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, use the KnHandleClose() function and specify the handles that were received from KnIoDmaMap() function calls. (KnHandleClose() function is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h from the KasperskyOS SDK.)

    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() function call.

    To complete this step, call the KnHandleClose() function and specify the handle that was received when the KnIoDmaBegin() function was called.

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

    To complete this step, use the KnHandleClose() and/or KnHandleRevoke() functions that are declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h from the KasperskyOS SDK.

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 – 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 information about the DMA buffer. The type of object 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.

KnIoDmaContinuousGetDmaAddr()

Purpose

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

Parameters

  • [in] rid – DMA buffer handle.
  • [out] addr – block address.

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.

Did you find this article helpful?
What can we do better?
Thank you for your feedback! You're helping us improve.
Thank you for your feedback! You're helping us improve.