KasperskyOS Community Edition 1.2

Contents

[Topic api]

Return codes

Overview

In a KasperskyOS-based solution, the return codes of functions of various APIs (for example, APIs of the libkos and kdf libraries, drivers, transport code, and application software) are 32-bit signed integers. This type is defined in the sysroot-*-kos/include/rtl/retcode.h header file from the KasperskyOS SDK as follows:

typedef __INT32_TYPE__ Retcode;

The set of return codes consists of a success code with a value of 0 and error codes. An error code is interpreted as a data structure whose format is described in the sysroot-*-kos/include/rtl/retcode.h header file from the KasperskyOS SDK. This format provides for multiple fields that contain not only information about the results of a function call, but also the following additional information:

  • Flag in the Customer field indicating that the error code was defined by the developers of the KasperskyOS-based solution and not by the developers of software from the KasperskyOS SDK.

    Thanks to the flag in the Customer field, developers of a KasperskyOS-based solution and developers of software from the KasperskyOS SDK can define error codes from non-overlapping sets.

  • Global ID of the error code in the Space field.

    Global IDs let you define non-overlapping sets of error codes. Error codes can be generic or specific. Generic error codes can be used in the APIs of any solution components and in the APIs of any constituent parts of solution components (for example, a driver or VFS may be a constituent part of a solution component). Specific error codes are used in the APIs of one or more solution components or in the APIs of one or more constituent parts of solution components.

    For example, the RC_SPACE_GENERAL ID corresponds to generic errors, the RC_SPACE_KERNEL ID corresponds to error codes of the kernel, and the RC_SPACE_DRIVERS ID corresponds to error codes of drivers.

  • Local ID of the error code in the Facility field.

    Local IDs let you define non-overlapping subsets of error codes within the set of error codes corresponding to one global ID. For example, the set of error codes with the global ID RC_SPACE_DRIVERS includes non-overlapping subsets of error codes with the local IDs RC_FACILITY_I2C, RC_FACILITY_USB, and RC_FACILITY_BLKDEV.

The global and local IDs of specific error codes are assigned by the developers of a KasperskyOS-based solution and by the developers of software from the KasperskyOS SDK independently of each other. In other words, two sets of global IDs are generated. Each global ID has a unique meaning within one set. Each local ID has a unique meaning within a set of local IDs related to one global ID. Generic error codes can be used in any API.

This type of centralized approach helps avoid situations in which the same error codes have various meanings within a KasperskyOS-based solution. This is necessary to eliminate a potential problem transmitting error codes through different APIs. For example, this problem occurs when drivers call kdf library functions, receive error codes, and return these codes through their own APIs. If error codes are generated without a centralized approach, the same error code can have different meanings for the kdf library and for the driver. Under these conditions, drivers return correct error codes only if the error codes of the kdf library are converted into error codes of each driver. In other words, error codes in a KasperskyOS-based solution are assigned in such way that does not require conversion of these codes during their transit through various APIs.

The information about return codes provided here does not apply to functions of a POSIX interface or the APIs of third-party software used in KasperskyOS-based solutions.

Generic return codes

Return codes that are generic for APIs of all solution components and their constituent parts are defined in the sysroot-*-kos/include/rtl/retcode.h header file from the KasperskyOS SDK. Descriptions of generic return codes are provided in the table below.

Generic return codes

Return code

Description

rcOk (corresponds to the 0 value)

The function completed successfully.

rcInvalidArgument

Invalid function parameter.

rcNotConnected

No connection between the client and server sides of interaction.

For example, there is no server IPC handle.

rcOutOfMemory

Insufficient memory to perform the operation.

rcBufferTooSmall

Insufficient buffer size.

rcInternalError

The function ended with an internal error related to incorrect logic.

Some examples of internal errors include values outside of the permissible limits, and null indicators and values where they are not permitted.

rcTransferError

Error sending an IPC message.

rcReceiveError

Error receiving an IPC message.

rcSourceFault

IPC message was not transmitted due to the IPC message source.

rcTargetFault

IPC message was not transmitted due to the IPC message recipient.

rcIpcInterrupt

IPC was interrupted by another process thread.

rcRestart

Indicates that the function needs to be called again.

rcFail

The function ended with an error.

rcNoCapability

The operation cannot be performed on the resource.

rcNotReady

Initialization failed.

rcUnimplemented

The function was not implemented.

rcBufferTooLarge

Large buffer size.

rcBusy

Resource temporarily unavailable.

rcResourceNotFound

Resource not found.

rcTimeout

Timed out.

rcSecurityDisallow

The operation was denied by security mechanisms.

rcFutexWouldBlock

The operation will result in a block.

rcAbort

The operation was aborted.

rcInvalidThreadState

Invalid function called in the interrupt handler.

rcAlreadyExists

Set of elements already contains the element being added.

rcInvalidOperation

Operation cannot be completed.

rcHandleRevoked

Resource access rights were revoked.

rcQuotaExceeded

Resource quota exceeded.

rcDeviceNotFound

Device not found.

rcOverflow

An overflow occurred.

rcAlreadyDone

Operation has already been completed.

Defining error codes

To define an error code, the developer of a KasperskyOS-based solution needs to use the MAKE_RETCODE() macro defined in the sysroot-*-kos/include/rtl/retcode.h header file from the KasperskyOS SDK. The developer must also use the customer parameter to pass the symbolic constant RC_CUSTOMER_TRUE.

Example:

#define LV_EBADREQUEST MAKE_RETCODE(RC_CUSTOMER_TRUE, RC_SPACE_APPS, RC_FACILITY_LogViewer, 5, "Bad request")

An error description that is passed via the desc parameter is not used by the MAKE_RETCODE() macro. This description is needed to create a database of error codes when building a KasperskyOS-based solution. At present, a mechanism for creating and using such a database has not been implemented.

Reading error code structure fields

The RC_GET_CUSTOMER(), RC_GET_SPACE(), RC_GET_FACILITY() and RC_GET_CODE() macros defined in the sysroot-*-kos/include/rtl/retcode.h header file from the KasperskyOS SDK let you read error code structure fields.

The RETCODE_HR_PARAMS() and RETCODE_HR_FMT() macros defined in the sysroot-*-kos/include/rtl/retcode_hr.h header file from the KasperskyOS SDK are used for formatted display of error details.

Page top
[Topic return_codes]

libkos library

The libkos library is the basic KasperskyOS library that provides the set of APIs that allow programs and other libraries (for example, libc and kdf) to use core endpoints. The APIs provided by the libkos library enable solution developers to do the following:

  • Manage processes, threads, and virtual memory.
  • Control access to resources.
  • Perform input/output operations.
  • Create IPC channels.
  • Manage power.
  • Obtain statistical data on the system.
  • Use other capabilities supported by core endpoints.

This section contains detailed descriptions for working with some libkos library interfaces. Descriptions of other interfaces can be found in corresponding header files.

The header files that define the libkos library API are located in the following directories:

  • sysroot-*-kos/include/coresrv/
  • sysroot-*-kos/include/kos/

In this section

Managing handles (handle_api.h)

Allocating and freeing memory (alloc.h)

Using DMA (dma.h)

Managing interrupt processing (irq.h)

Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)

Initializing IPC transport for querying the security module (transport-kos-security.h)

Generating random numbers (random_api.h)

Getting and changing time values (time_api.h)

Using notifications (notice_api.h)

Dynamically creating IPC channels (cm_api.h, ns_api.h)

Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)

Managing I/O memory isolation (iommu_api.h)

Using queues (queue.h)

Using memory barriers (barriers.h)

Executing system calls (syscalls.h)

IPC interrupt (ipc_api.h)

Page top
[Topic libkos]

Managing handles (handle_api.h)

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

The API is intended for performing operations with handles. Handles have the Handle type, which is defined in the header file sysroot-*-kos/include/handle/handletype.h from the KasperskyOS SDK.

Locality of handles

Each process receives handles from its own handle space irrespective of other processes. The handle spaces of different processes are absolutely identical in that they consist of the same set of values. Therefore, a handle is unique (has a unique value) only within the handle space of the single process that owns the particular handle. In other words, different processes may have identical handles that identify different resources, or may have different handles that identify the same resource.

In this section

Handle permissions mask

Creating handles

Transferring handles

Duplicating handles

Dereferencing handles

Revoking handles

Closing handles

Getting a security ID (SID)

OCap usage example

Page top
[Topic handles_manage]

Handle permissions mask

A handle permissions mask has a size of 32 bits and consists of a general part and a specialized part. The general part describes the general rights that are not specific to any particular resource (the flags of these rights are defined in the header file sysroot-*-kos/include/services/ocap.h from the KasperskyOS SDK). For example, the general part contains the OCAP_HANDLE_TRANSFER flag, which defines the permission to transfer the handle. The specialized part describes the rights that are specific to the particular user resource or system resource. The flags of the specialized part's permissions for system resources are defined in the ocap.h header file. The structure of the specialized part for user resources is defined by the resource provider by using the OCAP_HANDLE_SPEC() macro that is defined in the ocap.h header file. The resource provider must export the public header files describing the flags of the specialized part.

When the handle of a system resource is created, the permissions mask is defined by the KasperskyOS kernel, which applies permissions masks from the ocap.h header file. It applies permissions masks with names such as OCAP_*_FULL (for example, OCAP_IOPORT_FULL, OCAP_TASK_FULL, OCAP_FILE_FULL) and OCAP_IPC_* (for example, OCAP_IPC_SERVER, OCAP_IPC_LISTENER, OCAP_IPC_CLIENT).

When the handle of a user resource is created, the permissions mask is defined by the user.

When a handle is transferred, the permissions mask is defined by the user but the transferred access rights cannot be elevated above the access rights of the process.

Page top
[Topic libkos_handles_rights]

Creating handles

Creating handles of system resources

Handles of system resources are created when these resources are created. For example, handles are created when an interrupt or MMIO memory region is registered, and when a DMA buffer, thread, or process is created.

Creating handles of user resources

Handles of user resources are created by the providers of these resources by using the KnHandleCreateUserObject() or KnHandleCreateUserObjectEx() function.

The context of a user resource must be defined through the context parameter. The user resource context consists of data that allows the resource provider to identify the resource and its state when access to the resource is requested by other processes. This normally consists of a data set with various types of data (structure). For example, the context of a file may include the name, path, and cursor position. The user resource context is used as the resource transfer context or is used together with multiple resource transfer contexts.

You must use the rights parameter to define the handle permissions mask.

Creating IPC handles

An IPC handle is a handle that identifies an IPC channel. IPC handles are used to execute system calls. A client IPC handle is necessary for executing a Call() system call. A server IPC handle is necessary for executing the Recv() and Reply() system calls. A listener handle is a server IPC handle that has extended rights allowing it to add IPC channels to the set of IPC channels identified by this handle. A callable handle is a client IPC handle that simultaneously identifies the IPC channel to a server and an endpoint of this server.

A server creates a callable handle and passes it to a client so that the client can use the server endpoint. The client initializes IPC transport by using the callable handle that it received. In addition, the client specifies the INVALID_RIID value as the endpoint ID (RIID) in the proxy object initialization function. To create a callable handle, call the KnHandleCreateUserObjectEx() function and specify the server IPC handle and the endpoint ID (RIID) in the ipcChannel and riid parameters, respectively. Use the context parameter to specify the data to be associated with the callable handle. The server will be able to receive the pointer to this data when dereferencing the callable handle. (Even though the callable handle is an IPC handle, the kernel puts it into the base_.self field of the constant part of an IPC request.)

To create the client IPC handle, server IPC handle, and listener IPC handle and associate them with each other, call the KnHandleConnect() or KnHandleConnectEx() function. These functions are used to statically create IPC channels. The KnHandleConnect() function creates IPC handles from the handle space of the calling process. However, the client IPC handle can be transferred to another process. The KnHandleConnectEx() function can create IPC handles from the handle space of the calling process or from the handle spaces of other processes, such as the client and server.

When calling the KnHandleConnect() or KnHandleConnectEx() function with the INVALID_HANDLE value in the parameter that defines the listener handle, a new listener handle is created. However, the server IPC handle and listener IPC handle in the output parameters are the same handle. If a listener handle is specified when calling the KnHandleConnect() or KnHandleConnectEx() function, the created server IPC handle will provide the capability to receive IPC requests over all IPC channels associated with this listener handle. In this case, the server IPC handle and listener IPC handle in the output parameters are different handles. (The first IPC channel associated with the listener handle is created when calling the KnHandleConnect() or KnHandleConnectEx() function with the INVALID_HANDLE value in the parameter that defines the listener handle. The second and subsequent IPC channels associated with the listener handle are created during the second and subsequent calls of the KnHandleConnect() or KnHandleConnectEx() function specifying the listener handle that was obtained during the first call.)

To call a listener handle that is not associated with a client IPC handle and server IPC handle, call the KnHandleCreateListener() function. (The KnHandleConnect() and KnHandleConnectEx() functions create a listener handle associated with a client IPC handle and server IPC handle.) The KnHandleCreateListener() function is convenient for creating a listener handle that will be subsequently bound to callable handles.

To create a client IPC handle for querying the Kaspersky Security Module through the security interface, call the KnHandleSecurityConnect() function. This function is called by the libkos library when initializing IPC transport for querying the security module.

Information about API functions

handle_api.h functions

Function

Information about the function

KnHandleCreateUserObject()

Purpose

Creates a handle.

Parameters

  • [in] type – handle type. Fictitious parameter that must take a value ranging from the HANDLE_TYPE_USER_FIRST constant to the HANDLE_TYPE_USER_LAST constant as defined in the header file sysroot-*-kos/include/handle/handletype.h from the KasperskyOS SDK.
  • [in] rights – handle permissions mask.
  • [in,optional] context – pointer to the data that should be associated with the handle, or RTL_NULL if this association is not required.
  • [out] handle – pointer to the handle.

Returned values

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

KnHandleCreateUserObjectEx()

Purpose

Creates a handle.

Parameters

  • [in] type – handle type. Fictitious parameter that must take a value ranging from the HANDLE_TYPE_USER_FIRST constant to the HANDLE_TYPE_USER_LAST constant as defined in the header file sysroot-*-kos/include/handle/handletype.h from the KasperskyOS SDK.
  • [in] rights – handle permissions mask.
  • [in,optional] context – pointer to the data that should be associated with the handle, or RTL_NULL if this association is not required.
  • [in,optional] ipcChannel – server IPC handle, or INVALID_HANDLE if you do not need to create a callable handle.
  • [in,optional] riid – endpoint ID (RIID), or INVALID_RIID if you do not need to create a callable handle.
  • [out] handle – pointer to the handle.

Returned values

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

KnHandleConnect()

Purpose

Creates and connects the client, server, and listener IPC handles.

Parameters

  • [in,optional] ls – listener handle, or INVALID_HANDLE if you need to create it.
  • [out,optional] outLs – pointer to the listener handle. You can specify RTL_NULL if the ls parameter is used to define the listener handle.
  • [out,optional] outSr – pointer to the server IPC handle, or RTL_NULL to not create a server IPC handle if the ls parameter is used to define the listener handle.
  • [out] outCl – pointer to the client IPC handle.

Returned values

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

KnHandleConnectEx()

Purpose

Creates and connects the client, server, and listener IPC handles.

Parameters

  • [in] server – handle of the server process.
  • [in,optional] srListener – listener handle from the handle space of the server process, or INVALID_HANDLE if you need to create it.
  • [in] client – handle of the client process.
  • [out,optional] outSrListener – pointer to the listener handle from the handle space of the server process. You can specify RTL_NULL if the srListener parameter is used to define the listener handle.
  • [out,optional] outSrEndpoint – pointer to the server IPC handle from the handle space of the server process, or RTL_NULL to not create a server IPC handle if the srListener parameter is used to define the listener handle.
  • [out] outClEndpoint – pointer to the client IPC handle from the handle space of the client process.

Returned values

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

KnHandleSecurityConnect()

Purpose

Creates a client IPC handle for querying the Kaspersky Security Module through the security interface.

Parameters

  • [out] client – pointer to the handle.

Returned values

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

KnHandleCreateListener()

Purpose

Creates a listener handle that is not associated with a client IPC handle and server IPC handle.

Parameters

  • [out] listener – pointer to the listener handle.

Returned values

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

Page top
[Topic libkos_handles_create]

Transferring handles

Overview

Handles are transferred between processes so that resource consumers can gain access to required resources. Due to the specific locality of handles, a handle transfer initiates the creation of a handle from the handle space of the recipient process. This handle is registered as a descendant of the transferred handle and identifies the same resource.

One handle can be transferred multiple times to one or more processes. Each transfer initiates the creation of a new descendant of the transferred handle on the recipient process side. A process can transfer handles that it received from other processes or the KasperskyOS kernel. For this reason, a handle may have multiple generations of descendants. The generation hierarchy of handles for each resource is stored in the KasperskyOS kernel in the form of a handle inheritance tree.

A process can transfer handles for user resources and system resources if the access rights of these handles permit such a transfer (the OCAP_HANDLE_TRANSFER flag is set in the permissions mask). A descendant may have less access rights than an ancestor. For example, a transferring process with read-and-write permissions for a file can transfer read-only permissions. The transferring process can also prohibit the recipient process from further transferring the handle. Access rights are defined in the transferred permissions mask for the handle.

Conditions for transferring handles

To enable processes to transfer handles to other processes, the following conditions must be met:

  1. An IPC channel is created between the processes.
  2. The solution security policy (security.psl) allows interaction between process classes.
  3. Interface methods are implemented for transferring handles.

The API task.h enables a parent process to pass handles to a child process that is not yet running.

In an IDL description, signatures of interface methods for transferring handles have input (in) and/or output (out) parameters of the Handle type or array type with elements of the Handle type. Up to 255 handles can be passed through the input parameters of one method. This same number of handles can be received through output parameters.

Example IDL description that defines the signatures of interface methods for transferring handles:

package IpcTransfer interface { PublishResource1(in Handle handle, out UInt32 result); PublishResource7(in Handle handle1, in Handle handle2, in Handle handle3, in Handle handle4, in Handle handle5, in Handle handle6, in Handle handle7, out UInt32 result); OpenResource(in UInt32 ID, out Handle handle); }

For each parameter of the Handle type, the NK compiler generates a field of the nk_handle_desc_t type (hereinafter also referred to as the transport container of the handle) in the *_req IPC request structure and/or *_res IPC response structure. This type is declared in the header file sysroot-*-kos/include/nk/types.h from the KasperskyOS SDK and comprises a structure consisting of the following three fields: handle field for the handle, rights field for the handle permissions mask, and the badge field for the resource transfer context.

Resource transfer context

The resource transfer context consists of data that allows the server to identify the resource and its state when access to the resource is requested via descendants of the transferred handle. This normally consists of a data set with various types of data (structure). For example, the transfer context of a file may include the name, path, and cursor position. The server receives a pointer to the resource transfer context when dereferencing a handle.

Regardless of whether or not the server is the resource provider, the server can associate each handle transfer with a separate resource transfer context. This resource transfer context is bound only to the handle descendants (handle inheritance subtree) that were generated as a result of a specific transfer of the handle. This lets you define the state of a resource in relation to a separate transfer of the handle of this resource. For example, for cases when one file may be accessed multiple times, the file transfer context lets you define which specific opening of this file corresponds to a received IPC request.

If the server is the resource provider, each transfer of the handle of this resource is associated with the user resource context by default. In other words, the user resource context is used as the resource transfer context for each handle transfer if the particular transfer is not associated with a separate resource transfer context.

A server that is the resource provider can use both the user resource context and the resource transfer context together. For example, the name, path and size of a file is stored in the user resource context while the cursor position can be stored in multiple resource transfer contexts because each client can work with different parts of the file. Technically, joint use of the user resource context and resource transfer contexts is possible because the resource transfer contexts store a pointer to the user resource context.

If the client uses multiple various-type resources of the server, the resource transfer contexts (or contexts of user resources if they are used as resource transfer contexts) must be specialized objects of the KosObject type. This is necessary so that the server can verify that the client using a resource has sent the interface method the handle of the specific resource that corresponds to this method. This verification is required because the client could mistakenly send the interface method a resource handle that does not correspond to this method. For example, a client may have received a file handle and sent it to an interface method for working with volumes.

To associate a handle transfer with a resource transfer context, the server puts the handle of the resource transfer context object into the badge field of the nk_handle_desc_t structure. The resource transfer context object is the kernel object that stores the pointer to the resource transfer context. To create a resource transfer context object, call the KnHandleCreateBadge() function. This function is bound to the notification mechanism because a server needs to know when a resource transfer context object will be closed and deleted. The server needs this information to free up or re-use memory that was allotted for storing the resource transfer context.

The resource transfer context object will be closed upon the closure or revocation of the handle descendants that comprise the handle inheritance subtree whose root node was generated by the transfer of this handle in association with this object. (A transferred handle may be closed intentionally or unintentionally, such as when a recipient client is unexpectedly terminated.) After receiving a notification regarding the closure of a resource transfer context object, the server closes the handle of this object. After this, the resource transfer context object will be deleted. After receiving a notification regarding the deletion of the resource transfer context object, the server frees up or re-uses the memory that was allotted for storing the resource transfer context.

One resource transfer context object can be associated with only one handle transfer.

Packaging data into the transport container of a handle

To package a handle, handle permissions mask, and resource transfer context object handle into a handle transport container, use the nk_handle_desc() macro that is defined in the header file sysroot-*-kos/include/nk/types.h from the KasperskyOS SDK. This macro receives a variable number of parameters.

If no parameter is passed to the macro, the NK_INVALID_HANDLE value will be written to the handle field of the nk_handle_desc_t structure. If one parameter is passed to the macro, this parameter is interpreted as the handle. If two parameters are passed to the macro, the first parameter is interpreted as the handle and the second parameter is interpreted as the handle permissions mask. If three parameters are passed to the macro, the first parameter is interpreted as the handle, the second parameter is interpreted as the handle permissions mask, and the third parameter is interpreted as the resource transfer context object handle.

Extracting data from the transport container of a handle

To extract the handle, handle permissions mask, and pointer to the resource transfer context from the transport container of a handle, use the nk_get_handle(), nk_get_rights() and nk_get_badge_op() (or nk_get_badge()) functions, respectively, which are declared in the header file sysroot-*-kos/include/nk/types.h from the KasperskyOS SDK. The nk_get_badge_op() and nk_get_badge() functions should be used only when dereferencing handles.

Handle transfer scenarios

The scenario for transferring handles from a client to the server includes the following steps:

  1. The client packages the handles and handle permissions masks into fields of the *_req IPC requests structure of the nk_handle_desc_t type.
  2. The client calls the interface method for transferring handles to the server. The Call() system call is executed when this method is called.
  3. The server receives an IPC request by executing the Recv() system call.
  4. The dispatcher on the server side calls the method corresponding to the IPC request. This method extracts the handles and handle permissions masks from fields of the *_req IPC request structure of the nk_handle_desc_t type.

The scenario for transferring handles from the server to a client includes the following steps:

  1. The client calls the interface method for receiving handles from the server. The Call() system call is executed when this method is called.
  2. The server receives an IPC request by executing the Recv() system call.
  3. The dispatcher on the server side calls the method corresponding to the IPC request. This method packages the handles, handle permissions masks and resource transfer context object handles into fields of the *_res IPC response structure of the nk_handle_desc_t type.
  4. The server responds to the IPC request by executing the Reply() system call.
  5. On the client side, the interface method returns control. After this, the client extracts the handles and handle permissions masks from fields of the *_res IPC response structure of the nk_handle_desc_t type.

If the transferring process defines more access rights in the transferred handle permissions mask than the access rights defined for the transferred handle (which it owns), the transfer is not completed. In this case, the Call() system call executed by the transferring or recipient client or the Reply() system call executed by the transferring server ends with the rcSecurityDisallow error.

Information about API functions

handle_api.h functions

Function

Information about the function

KnHandleCreateBadge()

Purpose

Creates a resource transfer context object and configures a notification mechanism for monitoring the life cycle of this object.

Parameters

  • [in] notice – identifier of the notification receiver.
  • [in] eventId – identifier of the "resource–event mask" entry in the notification receiver.
  • [in,optional] context – pointer to the data that should be associated with the handle transfer, or RTL_NULL if this association is not required.
  • [out] handle – pointer to the handle of the resource transfer context object.

Returned values

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

Additional information

The notification receiver is configured to receive notifications about events that match the EVENT_OBJECT_DESTROYED and EVENT_BADGE_CLOSED flags of the event mask.

Page top
[Topic libkos_handles_transfer]

Duplicating handles

Handle duplication is similar to a handle transfer, but duplication is performed within a process. A handle descendant is created in the same process and from the same handle space. The rights of the handle descendant may be less than or equal to the rights of the original handle. Handle duplication can be associated with a resource transfer context object. This lets you use the notification mechanism to track the closure or revocation of all handle descendants that form the handle inheritance subtree whose root node was generated by the duplication operation. It also provides the capability to revoke these descendants.

To duplicate a handle, call the KnHandleCopy() function. To do so, the OCAP_HANDLE_COPY flag must be set in the handle permissions mask.

Information about API functions is provided in the table below.

handle_api.h functions

Function

Information about the function

KnHandleCopy()

Purpose

Duplicates a handle.

As a result of duplication, the calling process receives the handle descendant.

Parameters

  • [in] inHandle – original handle.
  • [in] newRightsMask – permissions mask of the handle descendant.
  • [in,optional] copyBadge – handle of the resource transfer context object, or INVALID_HANDLE if you do not need to associate handle duplication with this object.
  • [out] outHandle – pointer to the handle descendant.

Returned values

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

Page top
[Topic libkos_handle_copy]

Dereferencing handles

When dereferencing a handle, the client sends the handle to the server, and the server receives a pointer to the resource transfer context, the permissions mask of the sent handle, and the ancestor of the handle sent by the client and already owned by the server. Dereferencing occurs when a resource consumer that called methods for working with a resource (such as read/write or access closure) sends the resource provider the handle that was received from this resource provider when access to the resource was opened.

Dereferencing handles requires fulfillment of the same conditions and utilizes the same mechanisms and data types as when transferring handles. A handle dereferencing scenario includes the following steps:

  1. The client packages the handle into a field of the *_req IPC request structure of the nk_handle_desc_t type.
  2. The client calls the interface method for sending the handle to the server for the purpose of performing operations with the resource. The Call() system call is executed when this method is called.
  3. The server receives the IPC request by executing the Recv() system call.
  4. The dispatcher on the server side calls the method corresponding to the IPC request. This method verifies that the dereferencing operation was specifically executed instead of a handle transfer. Then the called method has the option to verify that the access rights of the dereferenced handle (that was sent by the client) permit the requested actions with the resource, and extracts the pointer to the resource transfer context from the field of the *_req request structure of the nk_handle_desc_t type.

To perform verification, the server uses the nk_is_handle_dereferenced() and nk_get_badge_op() functions that are declared in the header file sysroot-*-kos/include/nk/types.h from the KasperskyOS SDK.

types.h (fragment)

/** * Returns a value different from null if * the handle in the transport container of * "desc" is received as a result of dereferencing * the handle. Returns null if the handle * in the transport container of "desc" is received * as a result of a handle transfer. */ static inline nk_bool_t nk_is_handle_dereferenced(const nk_handle_desc_t *desc) /** * Extracts the pointer to the resource transfer context * "badge" from the transport container of "desc" * if the permissions mask that was put in the transport * container of the desc handle has the operation flags set. * If successful, the function returns NK_EOK, otherwise it returns an error code. */ static inline nk_err_t nk_get_badge_op(const nk_handle_desc_t *desc, nk_rights_t operation, nk_badge_t *badge)

Generally, the server does not require the handle that was received from dereferencing because the server normally retains the handles that it owns, for example, within the contexts of user resources. However, the server can extract this handle from the handle transport container if necessary.

Page top
[Topic libkos_handles_dereference]

Revoking handles

A process can revoke descendants of a handle that it owns. Handles are revoked according to the handle inheritance tree.

Revoked handles are not closed. However, you cannot query resources via revoked handles. Any function that receives the handle will end with the rcHandleRevoked error if the function is called with a revoked handle.

To revoke handle descendants, call the KnHandleRevoke() or KnHandleRevokeSubtree() function. The KnHandleRevokeSubtree() function uses the resource transfer context object that is created when transferring handles.

If each handle of a system resource in all processes that own these handles are closed (see "Closing handles") or revoked, this system resource will be deleted.

Information about API functions is provided in the table below.

handle_api.h functions

Function

Information about the function

KnHandleRevoke()

Purpose

Closes a handle and revokes its descendants.

Parameters

  • [in] handle – a handle.

Returned values

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

KnHandleRevokeSubtree()

Purpose

Revokes the handles that make up the inheritance subtree of the specified handle.

Parameters

  • [in] handle – a handle. The handles forming the inheritance subtree of this handle are revoked.
  • [in] badge – handle that identifies the resource transfer context object, which defines the inheritance subtree of the handles to revoke. The root node of this subtree is the handle that was generated by the transfer or duplication of the handle that is defined through the handle parameter and is associated with the resource transfer context object.

Returned values

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

Page top
[Topic libkos_handles_revoke]

Closing handles

A process can close the handles that it owns. Closing a handle terminates the association between an ID and a resource, thereby releasing the ID. Closing a handle does not invalidate its ancestors and descendants (in contrast to revoking a handle, which actually invalidates the descendants of the handle). In other words, the ancestors and descendants of a closed handle can still be used to provide access to the resource that they identify. Also, closing a handle does not disrupt the handle inheritance tree associated with the resource identified by the particular handle. The place of a closed handle is occupied by its ancestor. In other words, the ancestor of a closed handle becomes the direct ancestor of the descendants of the closed handle.

To close the handle, call the KnHandleClose() function.

If each handle of a system resource in all processes that own these handles are revoked (see "Revoking handles") or closed, this system resource will be deleted.

Information about API functions is provided in the table below.

handle_api.h functions

Function

Information about the function

KnHandleClose()

Purpose

Closes a handle.

Parameters

  • [in] handle – a handle.

Returned values

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

Page top
[Topic libkos_handles_delete]

Getting a security ID (SID)

By getting the SID values for different handles, you can determine whether these handles identify different resources or the same resource.

To get an SID for a handle, call the KnHandleGetSidByHandle() function. To do so, the OCAP_HANDLE_GET_SID flag must be set in the handle permissions mask.

Information about API functions is provided in the table below.

handle_api.h functions

Function

Information about the function

KnHandleGetSidByHandle()

Purpose

Receives a security ID (SID) based on a handle.

Parameters

  • [in] handle – a handle.
  • [out] sid – pointer to the security ID (SID).

Returned values

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

Page top
[Topic libkos_handle_get_sid]

OCap usage example

This example describes an OCap usage scenario in which the resource provider provides the following methods for accessing its resources:

  • OpenResource() – opens access to the resource.
  • UseResource() – uses the resource.
  • CloseResource() – closes access to the resource.

The resource consumer uses these methods.

IDL description:

package SimpleOCap interface { OpenResource(in UInt32 ID, out Handle handle); UseResource(in Handle handle, in UInt8 param, out UInt8 result); CloseResource(in Handle handle); }

The scenario includes the following steps:

  1. The resource provider creates the user resource context and calls the KnHandleCreateUserObject() function to create the resource handle. The resource provider saves the resource handle in the user resource context.
  2. The resource consumer calls the OpenResource() method to open access to the resource.
    1. The resource provider creates the resource transfer context and calls the KnHandleCreateBadge() function to create a resource transfer context object and configure the notification receiver to receive notifications regarding the closure and deletion of the resource transfer context object. The resource provider saves the handle of the resource transfer context object and the pointer to the user resource context in the resource transfer context.
    2. The resource provider uses the nk_handle_desc() macro to package the resource handle, permissions mask of the handle, and pointer to the resource transfer context object into the handle transport container.
    3. The handle is transferred from the resource provider to the resource consumer, which means that the resource consumer receives a descendant of the handle owned by the resource provider.
    4. The OpenResource() method call completes successfully. The resource consumer extracts the handle and permissions mask of the handle from the handle transport container by using the nk_get_handle() and nk_get_rights() functions, respectively. The handle permissions mask is not required by the resource consumer to query the resource, but is transferred so that the resource consumer can find out its permissions for accessing the resource.
  3. The resource consumer calls the UseResource() method to utilize the resource.
    1. The handle that was received from the resource provider at step 2 is used as a parameter of the UseResource() method. Before calling this method, the resource consumer uses the nk_handle_desc() macro to package the handle into the handle transport container.
    2. The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
    3. The resource provider uses the nk_is_handle_dereferenced() function to verify that the dereferencing operation was completed instead of a handle transfer.
    4. The resource provider verifies that the access rights of the dereferenced handle (that was sent by the resource consumer) allows the requested operation with the resource, and extracts the pointer to the resource transfer context from the handle transport container. To do so, the resource provider uses the nk_get_badge_op() function, which extracts the pointer to the resource transfer context from the handle transport container if the received permissions mask has the corresponding flags set for the requested operation.
    5. The resource provider uses the resource transfer context and the user resource context to perform the corresponding operation with the resource as requested by the resource consumer. Then the resource provider sends the results of this operation to the resource consumer.
    6. The UseResource() method call completes successfully. The resource consumer receives the results of the operation performed with the resource.
  4. The resource consumer calls the CloseResource() method to close access to the resource.
    1. The handle that was received from the resource provider at step 2 is used as a parameter of the CloseResource() method. Before calling this method, the resource consumer uses the nk_handle_desc() macro to package the handle into the handle transport container. After the CloseResource() method is called, the resource consumer uses the KnHandleClose() function to close the handle.
    2. The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
    3. The resource provider uses the nk_is_handle_dereferenced() function to verify that the dereferencing operation was completed instead of a handle transfer.
    4. The resource provider uses the nk_get_badge() function to extract the pointer to the resource transfer context from the handle transport container.
    5. The resource provider uses the KnHandleRevokeSubtree() function to revoke the handle owned by the resource consumer. The resource handle owned by the resource provider and the handle of the resource transfer context object are used as parameters of this function. The resource provider obtains access to these handles through the pointer to the resource transfer context. (Technically, the handle owned by the resource consumer does not have to be revoked because the resource consumer already closed it. However, the revoke operation is performed in case the resource provider is not sure if the resource consumer actually closed the handle).
    6. The CloseResource() method call completes successfully.
  5. The resource provider frees up the memory that was allocated for the resource transfer context and user resource context.
    1. The resource provider calls the KnNoticeGetEvent() function to receive a notification that the resource transfer context object was closed, and uses the KnHandleClose() function to close the handle of the resource transfer context object.
    2. The resource provider calls the KnNoticeGetEvent() function to receive a notification that the resource transfer context object was deleted, and frees up the memory that was allocated for the resource transfer context.
    3. The resource provider uses the KnHandleClose() function to close the resource handle and free up the memory that was allocated for the user resource context.
Page top
[Topic libkos_handles_simple_scenario]

Allocating and freeing memory (alloc.h)

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

The API is intended for allocating and freeing memory. Allocated memory is a committed virtual memory region that can be accessed for read-and-write operations.

Information about API functions is provided in the table below.

alloc.h functions

Function

Information about the function

KosMemAllocEx()

Purpose

Allocates memory.

Parameters

  • [in] size – size (in bytes) of the allocated memory.
  • [in] align – value defining the alignment of the allocated memory. It must be a power of two. The address of allocated memory can be unaligned (align=1) or aligned (align=2,4,...,2^N) to the boundary of a 2^N-byte sequence (for example, two-byte or four-byte). When an address is aligned, the size of the allocated memory may be rounded up to the nearest multiple of 2^N.
  • [in] zeroed – value defining the initialization of the allocated memory (1 – initialize with zeros, 0 – do not initialize).

Returned values

If successful, the function returns the pointer to the allocated memory, otherwise it returns RTL_NULL.

KosMemAlloc()

Purpose

Allocates memory.

Parameters

  • [in] size – size (in bytes) of the allocated memory.

Returned values

If successful, the function returns the pointer to the allocated memory, otherwise it returns RTL_NULL.

KosMemZalloc()

Purpose

Allocates memory and initializes it with zeros.

Parameters

  • [in] size – size (in bytes) of the allocated memory.

Returned values

If successful, the function returns the pointer to the allocated memory, otherwise it returns RTL_NULL.

KosMemFree()

Purpose

Deallocates memory.

Parameters

  • [in] ptr – pointer to the freed memory.

Returned values

N/A

KosMemGetSize()

Purpose

Gets the actual size of allocated memory.

The actual size of allocated memory exceeds the requested size because it includes the size of service data and also may be increased due to alignment when the KosMemAllocEx() function is called.

Parameters

  • [in] ptr – pointer to the allocated memory.

Returned values

Actual size of allocated memory (in bytes).

KosMemGetOrigSize()

Purpose

Gets the size of memory that was requested when it is allocated.

The actual size of allocated memory exceeds the requested size because it includes the size of service data and also may be increased due to alignment when the KosMemAllocEx() function is called.

Parameters

  • [in] ptr – pointer to the allocated memory.

Returned values

Size (in bytes) of memory that was requested when it is allocated.

Page top
[Topic libkos_alloc_api]

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

Page top
[Topic libkos_dma_api]

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

At present, no functions for managing the handling of MSI interrupts have been implemented.

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

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 by calling the KnRegisterIrq() function.

    One interrupt can be registered multiple times in one or more processes.

    The handle of an interrupt can be transferred 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 handle received from the KnRegisterIrq() function call, you can attach only one thread to an interrupt. To attach multiple threads in one or more processes to an interrupt, use different handles for this interrupt received from separate KnRegisterIrq() function calls. In this case, the KnIoAttachIrq() function must be called with the same flags in the flags parameter.

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

To deny (mask) an interrupt, call the KnIoDisableIrq() function. To allow (unmask) an interrupt, call the KnIoEnableIrq() function. Even though these functions receive an interrupt handle that is used to attach only one thread to the interrupt, their action is applied to all threads that are attached to this interrupt. These functions must be called outside of the threads attached to the interrupt. After an interrupt is registered and a thread is attached to it, this interrupt does not require unmasking.

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 declared in the header file sysroot-*-kos/include/coresrv/thread/thread_api.h from the KasperskyOS SDK.

Handling an interrupt

After attaching to an interrupt, a thread calls the Call() function declared in the header file sysroot-*-kos/include/coresrv/syscalls.h from the KasperskyOS SDK. 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.

Deregistering an interrupt

To deregister an interrupt, complete the following steps:

  1. Detach the thread from the interrupt.

    To complete this step, call the KnThreadDetachIrq() function.

  2. Close the handle that was received when the KnIoAttachIrq() function was called.

    To complete this step, call the KnHandleClose() function. (The KnHandleClose() function is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h from the KasperskyOS SDK.)

  3. Close or revoke each interrupt 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.

One interrupt can be registered multiple times, but completion of these steps cancels only one registration. The other registrations will remain active. Each registration of one interrupt must be canceled separately.

Information about API functions

irq.h functions

Function

Information about the function

KnRegisterIrq()

Purpose

Registers an interrupt.

Parameters

  • [in] irq – interrupt number.
  • [out] outRid – pointer to the interrupt handle.

Returned values

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

KnIoAttachIrq()

Purpose

Attaches the calling thread to an interrupt.

Parameters

  • [in] rid – interrupt handle.
  • [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] 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.

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.

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

Allows (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.

Parameters

  • [in] rid – interrupt handle.

Returned values

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

Page top
[Topic libkos_irq_api]

Initializing IPC transport for interprocess communication and managing IPC request processing (transport-kos.h, transport-kos-dispatch.h)

APIs are defined in the header files transport-kos.h and transport-kos-dispatch.h from the KasperskyOS SDK that are located at the path sysroot-*-kos/include/coresrv/nk.

API capabilities:

Information about API functions is provided in the tables below.

This section contains API usage examples. In these examples, programs acting as servers have the following formal specification:

FsDriver.edl

entity FsDriver components { operationsComp : Operations }

Operations.cdl

component Operations endpoints { fileOperations : FileIface }

FileIface.idl

package FileIface interface { Open(in array<UInt8, 1024> path); Read(out sequence<UInt8, 2048> content); }

Initializing IPC transport for interprocess communication

To initialize IPC transport for interaction with other processes, call the NkKosTransport_Init() or NkKosTransportSync_Init() function declared in the header file transport-kos.h.

Example use of the NkKosTransport_Init() function on the client side:

int main(int argc, const char *argv[]) { /* Declare the structure containing the IPC transport parameters */ NkKosTransport driver_transport; /* Declare the proxy object. (The type of proxy object is automatically * generated transport code.) */ struct FileIface_proxy file_operations_proxy; /* Declare the structures for saving the constant part of an IPC request and * IPC response for the endpoint method. (The types of structures are automatically * generated transport code.) */ struct FileIface_Open_req req; struct FileIface_Open_res res; /* Get the client IPC handle and endpoint ID */ Handle driver_handle; rtl_uint32_t file_operations_riid; if (KnCmConnect("FsDriver", "operationsComp.fileOperations", INFINITE_TIMEOUT, &driver_handle, &file_operations_riid) == rcOk) { /* Initialize the structure containing the IPC transport parameters */ NkKosTransport_Init(&driver_transport, driver_handle, NK_NULL, 0); /* Initialize the proxy object. (The proxy object initialization method is * is automatically generated transport code.) */ FileIface_proxy_init(&file_operations_proxy, &driver_transport.base, (nk_iid_t) file_operations_riid); } ... /* Call the endpoint method. (The method is automatically * generated transport code.) */ strncpy(req.path, "/example/file/path", sizeof(req.path)); if (FileIface_Open(file_operations_proxy.base, &req, NULL, &res, NULL) != NK_EOK) { ... } ... }

If a client needs to use several endpoints, the same number of proxy objects must be initialized. When initializing each proxy object, you need to specify the IPC transport that is associated through the client IPC handle with the relevant server. When initializing multiple proxy objects pertaining to the endpoints of one server, you can specify the same IPC transport that is associated with this server.

Example use of the NkKosTransport_Init() function on the server side:

int main(int argc, const char *argv[]) { ... /* Declare the structure containing the IPC transport parameters */ NkKosTransport transport; /* Get the listener handle. (Endpoint ID * FsDriver_operationsComp_fileOperations_iid is * automatically generated transport code.) */ Handle handle; char client[32]; char endpoint[32]; Retcode rc = KnCmListen(RTL_NULL, INFINITE_TIMEOUT, client, endpoint); if (rc == rcOk) rc = KnCmAccept(client, endpoint, FsDriver_operationsComp_fileOperations_iid, INVALID_HANDLE, &handle); ... /* Initialize the structure containing the IPC transport parameters */ NkKosTransport_Init(&transport, handle, NK_NULL, 0); ... /* IPC request processing loop */ do { ... /* Get the IPC request */ rc = nk_transport_recv(&transport.base, ...); if (rc == NK_EOK) { /* Process the IPC request by calling the dispatcher. (The dispatcher * is automatically generated transport * code.) */ rc = FsDriver_entity_dispatch(...); if (rc == NK_EOK) { /* Send an IPC response */ rc = nk_transport_reply(&transport.base, ...); } } } while (rc == NK_EOK) return EXIT_SUCCESS; }

If a server processes IPC requests received through multiple IPC channels, the following special considerations should be taken into account:

  • If a listener handle is associated with all IPC channels, IPC interaction with all clients can use the same IPC transport associated with this listener handle.
  • If IPC channels are associated with different listener handles, IPC interaction with each group of clients corresponding to the same listener handle must use a separate IPC transport associated with this listener handle. In this case, IPC requests can be processed in parallel threads if you are using a thread-safe implementation of endpoint methods.

The NkKosTransportSync_Init() function initializes IPC transport with support for interrupting the Call() and Recv() locking system calls. (For example, an interrupt of these calls may be required for correct termination of the process that is executing them.) To interrupt the Call() and Recv() system calls, use the API ipc_api.h.

The NkKosSetTransportTimeouts() function declared in the header file transport-kos.h defines the maximum lockout duration for Call() and Recv() system calls used for IPC transport.

Starting the IPC request processing loop

The IPC request processing loop on a server includes the following steps:

  1. Receive an IPC request.
  2. Process the IPC request.
  3. Send an IPC response.

Each step of this loop can be completed separately by sequentially calling the nk_transport_recv(), dispatcher, and nk_transport_reply() functions. (The nk_transport_recv() and nk_transport_reply() functions are declared in the header file sysroot-*-kos/include/nk/transport.h from the KasperskyOS SDK.) You can also call the NkKosTransport_Dispatch() or NkKosDoDispatch() function in which this loop is completed in its entirety. (The NkKosTransport_Dispatch() and NkKosDoDispatch() functions are declared in the header files transport-kos.h and transport-kos-dispatch.h, respectively.) It is more convenient to use the NkKosDoDispatch() function because it requires fewer preparatory operations (for example, you do not need to initialize IPC transport).

You can initialize the structure passed to the NkKosDoDispatch() function through the info parameter by using the macros defined in the header file transport-kos-dispatch.h.

The NkKosTransport_Dispatch() and NkKosDoDispatch() functions can be called from parallel threads if you are using a thread-safe implementation of endpoint methods.

Example use of the NkKosDoDispatch() function:

/* This function implements the endpoint method. */ static nk_err_t Open_impl(...) { ... } /* This function implements the endpoint method. */ static nk_err_t Read_impl(...) { ... } /* This function initializes the pointers to functions implementing the endpoint methods. * (These pointers are used by the dispatcher to call functions implementing the * endpoint methods. The types of structures are automatically generated * transport code.) */ static struct FileIface *CreateFileOperations() { static const struct FileIface_ops ops = { .Open = Open_impl, .Read = Read_impl }; static struct FileIface impl = { .ops = &ops }; return &impl; } int main(int argc, const char *argv[]) { ... /* Declare the structure that is required for the * NkKosDoDispatch() function to use transport code. */ NkKosDispatchInfo info; /* Declare the stubs. (The types of stubs are automatically generated * transport code. */ struct Operations_component component; struct FsDriver_entity entity; /* Get the listener handle */ Handle handle = ServiceLocatorRegister("driver_connection", NULL, 0, &iid); assert(handle != INVALID_HANDLE); /* Initialize the stubs. (Methods for initializing stubs are * automatically generated transport code. Function * CreateFileOperations() is implemented by the developer of the * KasperskyOS-based solution to initialize * pointers to functions implementing the endpoint methods.) */ Operations_component_init(&component, CreateFileOperations()); FsDriver_entity_init(&entity, &component); /* Initialize the structure that is required for the * NkKosDoDispatch() function to use transport code. */ info = NK_TASK_DISPATCH_INFO_INITIALIZER(FsDriver, entity); /* Start the IPC request processing loop */ NkKosDoDispatch(handle, info); return EXIT_SUCCESS; }

Example use of the NkKosTransport_Dispatch() function:

/* This function implements the endpoint method. */ static nk_err_t Open_impl(...) { ... } /* This function implements the endpoint method. */ static nk_err_t Read_impl(...) { ... } /* This function initializes the pointers to functions implementing the endpoint methods. * (These pointers are used by the dispatcher to call functions implementing the * endpoint methods. The types of structures are automatically generated * transport code.) */ static struct FileIface *CreateFileOperations() { static const struct FileIface_ops ops = { .Open = Open_impl, .Read = Read_impl }; static struct FileIface impl = { .ops = &ops }; return &impl; } int main(int argc, const char *argv[]) { ... /* Declare the structure containing the IPC transport parameters */ NkKosTransport transport; /* Declare the stubs. (The types of stubs are automatically generated * transport code. */ struct Operations_component component; struct FsDriver_entity entity; /* Declare the unions of the constant part of IPC requests and * IPC responses. (Types of unions are automatically generated * transport code.) */ union FsDriver_entity_req req; union FsDriver_entity_res res; /* Declare the array for the IPC response arena. (The array size is * automatically generated transport code.) */ char res_buffer[FsDriver_entity_res_arena_size]; /* Declare and initialize the arena descriptor of the IPC response. * (The type of handle and its initialization macro are defined in the header file * sysroot-*-kos/include/nk/arena.h from the KasperskyOS SDK.) */ struct nk_arena res_arena = NK_ARENA_INITIALIZER(res_buffer, res_buffer + sizeof(res_buffer)); /* Get the listener handle */ Handle handle = ServiceLocatorRegister("driver_connection", NULL, 0, &iid); assert(handle != INVALID_HANDLE); /* Initialize the structure containing the IPC transport parameters */ NkKosTransport_Init(&transport, handle, NK_NULL, 0); /* Initialize the stubs. (Methods for initializing stubs are * automatically generated transport code. Function * CreateFileOperations() is implemented by the developer of the * KasperskyOS-based solution to initialize * pointers to functions implementing the endpoint methods.) */ Operations_component_init(&component, CreateFileOperations()); FsDriver_entity_init(&entity, &component); /* Start the IPC request processing loop. (The dispatcher FsDriver_entity_dispatch * is automatically generated transport code.) */ NkKosTransport_Dispatch(&transport.base, FsDriver_entity_dispatch, &entity, &req, sizeof(FsDriver_entity_req), RTL_NULL, &res, &res_arena); return EXIT_SUCCESS; }

Copying data to the IPC message arena

To copy a string to the IPC message arena, call the NkKosCopyStringToArena() function declared in the header file transport-kos.h. This function reserves a segment of the arena and copies a string to this segment.

Example use of the NkKosCopyStringToArena() function:

static nk_err_t Read_impl(struct FileIface *self, const struct FileIface_Read_req *req, const struct nk_arena* req_arena, struct FileIface_Read_res* res, struct nk_arena* res_arena) { /* Copy the string to the IPC response arena */ if (NkKosCopyStringToArena(&res_arena, &res.content, "CONTENT OF THE FILE") != rcOk) { ... } return NK_EOK; }

Information about API functions

transport-kos.h functions

Function

Information about the function

NkKosTransport_Init()

Purpose

Initializes IPC transport.

Parameters

  • [out] transport – pointer to the structure containing the IPC transport parameters.
  • [in] handle – client or server IPC handle.
  • [in] view – parameter that must have the value NK_NULL.
  • [in] size – parameter that must have the value 0.

Returned values

N/A

NkKosTransportSync_Init()

Purpose

Initializes IPC transport with support for interrupting the Call() and/or Recv() system calls.

Parameters

  • [out] transport – pointer to the structure containing the IPC transport parameters.
  • [in] handle – client or server IPC handle.
  • [in,optional] callSyncHandle – handle of the IPC synchronization object for Call() system calls, or INVALID_HANDLE if an interrupt of Call() system calls is not required.
  • [in,optional] recvSyncHandle – handle of the IPC synchronization object for Recv() system calls, or INVALID_HANDLE if an interrupt of Recv() system calls is not required.

Returned values

N/A

NkKosSetTransportTimeouts()

Purpose

Defines the maximum lockout duration for Call() and Recv() system calls used for IPC transport.

Parameters

  • [out] transport – pointer to the structure containing the IPC transport parameters.
  • [in] recvTimeout – maximum lockout duration for Recv() system calls in milliseconds, or INFINITE_TIMEOUT to define an unlimited lockout duration.
  • [in] callTimeout – maximum lockout duration for Call() system calls in milliseconds, or INFINITE_TIMEOUT to define an unlimited lockout duration.

Returned values

N/A

NkKosTransport_Dispatch()

Purpose

Starts the IPC request processing loop.

Parameters

  • [in] transport – pointer to the base field of the structure containing the IPC transport parameters.
  • [in] dispatch – pointer to the dispatcher (dispatch method) from the transport code. The dispatcher is named <process class name>_entity_dispatch.
  • [in] impl – pointer to the stub, which consists of a structure with the type <process class name>_entity from the transport code. The dispatcher uses this structure to get the pointers to functions implementing endpoint methods.
  • [out] req – pointer to the union with the type <process class name>_entity_req from the transport code. This union is intended for storing the constant part of IPC requests for any methods of endpoints provided by the server.
  • [in] req_size – maximum size (in bytes) of the constant part of IPC requests. It is defined as sizeof(<process class name>_entity_req), where <process class name>_entity_req is the type from the transport code.
  • [in,out,optional] req_arena – pointer to the IPC request arena descriptor, or RTL_NULL if an IPC request arena is not in use. The type of handle is defined in the header file sysroot-*-kos/include/nk/arena.h from the KasperskyOS SDK.
  • [out] res – pointer to the union with the type <process class name>_entity_res from the transport code. This union is intended for storing the constant part of IPC responses for any methods of endpoints provided by the server.
  • [in,out,optional] res_arena – pointer to the IPC response arena descriptor, or RTL_NULL if an IPC response arena is not in use. The type of handle is defined in the header file sysroot-*-kos/include/nk/arena.h from the KasperskyOS SDK.

Returned values

If unsuccessful, it returns an error code.

NkKosCopyStringToArena()

Purpose

Reserves a segment of the arena and copies a string to this segment.

Parameters

  • [in,out] arena – pointer to the arena descriptor. The type of handle is defined in the header file sysroot-*-kos/include/nk/arena.h from the KasperskyOS SDK.
  • [out] field – pointer to the arena chunk descriptor where the string is copied. The type of handle is defined in the header file sysroot-*-kos/include/nk/types.h.
  • [in] src – pointer to the string to be copied to the IPC message arena.

Returned values

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

transport-kos-dispatch.h functions

Function

Information about the function

NkKosDoDispatch()

Purpose

Starts the IPC request processing loop.

Parameters

  • [in] h – server IPC handle.
  • [in] info – pointer to the structure containing the data required by the function to use transport code (including the names of types, sizes of the constant part and IPC message arena).

Returned values

N/A

Page top
[Topic libkos_ipc_transport_api]

Initializing IPC transport for querying the security module (transport-kos-security.h)

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

The API initializes IPC transport for querying the Kaspersky Security Module via the security interface. Transport code works on top of IPC transport.

Information about API functions is provided in the table below.

This section contains an API usage example. In this example, the program that queries the security module has the following formal specification:

Verifier.edl

entity Verifier security Approve

Approve.idl

package Approve interface { Check(in UInt32 port); }

Fragment of the policy description in the example:

security.psl

... security src=Verifier, method=Check { assert (message.port > 80) } ...

Using the API

To initialize IPC transport for querying the security module, call the NkKosSecurityTransport_Init() function.

Example use of the NkKosSecurityTransport_Init() function:

int main(void) { /* Declare the structure containing the IPC transport parameters for querying the * security module */ NkKosSecurityTransport security_transport; /* Declare the proxy object. (The type of proxy object is automatically * generated transport code.) */ struct Approve_proxy security_proxy; /* Declare the structures for saving the constant part of an IPC request and IPC response for the * security interface method. (The types of structures are automatically generated * transport code.) */ struct Approve_Check_req security_req; struct Approve_Check_res security_res; /* Initialize the structure containing the IPC transport parameters for querying the * security module */ if (NkKosSecurityTransport_Init(&security_transport, NK_NULL, 0) == NK_EOK) { /* Initialize the proxy object. (The proxy object initialization method and the * security interface ID Verifier_securityIid are * automatically generated transport code.) */ Approve_proxy_init(&security_proxy, &security_transport.base, Verifier_securityIid); } ... /* Call the security interface method. (The method is automatically generated * transport code. The method does not pass any data through the security_res parameter. * This parameter should be specified only if required by the method implementation.) */ security_req.port = 80; nk_err_t result = Approve_Check(&security_proxy.base, &security_req, NULL, &security_res, NULL); if (result == NK_EOK) fprintf(stderr, "Granted"); if (result == NK_EPERM) fprintf(stderr, "Denied"); else fprintf(stderr, "Error"); return EXIT_SUCCESS; }

If a process needs to use several security interfaces, the same number of proxy objects must be initialized by specifying the same IPC transport and the unique IDs of the security interfaces.

Information about API functions

transport-kos-security.h functions

Function

Information about the function

NkKosSecurityTransport_Init()

Purpose

Initializes IPC transport for querying the Kaspersky Security Module through the security interface.

Parameters

  • [out] transport – pointer to the structure containing the IPC transport parameters for querying the security module.
  • [in] view – parameter that must have the value NK_NULL.
  • [in] size – parameter that must have the value 0.

Returned values

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

Page top
[Topic libkos_ipc_security_transport_api]

Generating random numbers (random_api.h)

The API is defined in the header file sysroot-*-kos/include/kos/random/random_api.h from the KasperskyOS SDK.

The API generates random numbers and includes functions that can be used to ensure high entropy (high level of unpredictability) of the seed value of the random number generator. The start number of the random number generator (seed) determines the sequence of the generated random numbers. In other words, if the same seed value is set, the generator creates identical sequences of random numbers. (The entropy of these numbers is fully determined by the entropy of the seed value, which means that these numbers are not entirely random, but pseudorandom.)

The random number generator of one process does not depend on the random number generators of other processes.

Information about API functions is provided in the table below.

Using the API

To generate a sequence of random byte values, call the KosRandomGenerate() or KosRandomGenerateEx() function.

Example use of the KosRandomGenerate() function:

size_t random_number; if (KosRandomGenerate(sizeof random_number, &random_number) == rcOk) { ... }

The KosRandomGenerateEx() function gets the quality level of the generated random values through the output parameter quality. The quality level can be high or low. High-quality random values are random values that are generated while fulfilling all of the following conditions:

  1. When the seed value is changed, at least one entropy source is registered and data is successfully received from all registered entropy sources.

    To register the entropy source, call the KosRandomRegisterSrc() function. This function uses the callback parameter to receive the pointer to a callback function of the following type:

    typedef Retcode (*KosRandomSeedMethod)(void *context, rtl_size_t size, void *output);

    Using the context parameters, this callback function receives data with the specified size of bytes from the entropy source and writes this data to the output buffer. If the function returns rcOk, it is assumed that data was successfully received from the entropy source. An entropy source can be a digitalized signal of a sensor or a hardware-based random number generator, for example.

    To deregister an entropy source, call the KosRandomUnregisterSrc() function.

  2. The random number generator is initialized if the quality level was low before changing the seed value while fulfilling condition 1.

    If the quality level is low, it cannot be changed to high without initializing the random number generator.

    To initialize a random number generator, call the KosRandomInitSeed() function. The entropy of data passed through the seed parameter must be guaranteed by the calling process.

  3. The counter of random byte values that were generated after changing the seed value while fulfilling condition 1 does not exceed the system-defined limit.
  4. The counter of time that has elapsed since changing the seed value while fulfilling condition 1 does not exceed the system-defined limit.

If at least one of these conditions is not fulfilled, the generated random values are deemed low-quality values.

When a process is started, the seed value is automatically defined by the system. Then the seed value is modified when doing the following:

  • Generating a sequence of random values.

    Each successful call of the KosRandomGenerateEx() function with the size parameter greater than zero and each successful call of the KosRandomGenerate() function results in a change of the seed value of the random number generator, but not each seed change results in the receipt of data from registered entropy sources. The registered entropy sources are used only when condition 3 or 4 is not fulfilled. If at least one entropy source is registered, the time counter for the change in the seed value is reset. If data from at least one entropy source is successfully received, the counter of generated random byte values is also reset.

    The quality level may change from high to low.

  • Initializing a random number generator.

    Each successful call of the KosRandomInitSeed() function changes the seed value by using the data that is passed through the seed parameter and received from registered entropy sources. If at least one registered entropy source is available, the counters for generated byte values and the time of a change in the seed value are reset. Otherwise, only the counter of generated random byte values is reset.

    The quality level may change from high to low, and vice versa.

  • Registering an entropy source.

    Each successful call of the KosRandomRegisterSrc() function changes the seed value by using the data only from the registered entropy source. The counter of the generated random byte values is also reset.

    The quality level may not change.

A possible scenario for generating high-quality random values includes the following steps:

  1. Register at least one source of entropy by calling the KosRandomRegisterSrc() function.
  2. Generate a sequence of random values by calling the KosRandomGenerateEx() function.
  3. Check the quality level of the random values.

    If the quality level is low, initialize the random number generator by calling the KosRandomInitSeed() function, and proceed to step 2.

    If the quality level is high, proceed to step 4.

  4. Use random values.

To get the quality level without generating random values, call the KosRandomGenerateEx() function with the values 0 and RTL_NULL in the size and output parameters, respectively.

Information about API functions

random_api.h functions

Function

Information about the function

KosRandomInitSeed()

Purpose

Initializes the random number generator.

Parameters

  • [in] seed – pointer to the byte array that is used to change the seed value. The array must have a size of 32 bytes.

Returned values

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

KosRandomGenerate()

Purpose

Generates a sequence of random byte values.

Parameters

  • [in] size – size (in bytes) of the buffer used to store the sequence.
  • [out] output – pointer to the buffer used to store the sequence.

Returned values

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

KosRandomGenerateEx()

Purpose

Generates a sequence of random byte values.

Parameters

  • [in] size – size (in bytes) of the buffer used to store the sequence.
  • [out] output – pointer to the buffer used to store the sequence.
  • [out] quality – pointer to the boolean value that is true if the generated random values have high quality, and false if the generated random values have low quality.

Returned values

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

KosRandomRegisterSrc()

Purpose

Registers an entropy source.

Parameters

  • [in] callback – pointer to the function that receives data from the entropy source to change the seed value.
  • [in,optional] context – pointer to the parameters passed to the function defined through the callback parameter, or RTL_NULL if there are no parameters.
  • [in] size – size (in bytes) of the data that should be received from the entropy source when calling the function defined through the callback parameter. The size must be at least 32 bytes.
  • [out] handle – address of the pointer used to deregister the entropy source.

Returned values

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

KosRandomUnregisterSrc()

Purpose

Deregisters the source of entropy.

Parameters

  • [in] handle – pointer that is received when registering the source of entropy.

Returned values

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

Page top
[Topic libkos_random_api]

Getting and changing time values (time_api.h)

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

Main capabilities of the API:

  • Get and modify the system time
  • Get the monotonic time that has elapsed since the moment the KasperskyOS kernel was started
  • Get the resolution of the sources of system time and monotonic time

Information about API functions is provided in the table below.

time_api.h functions

Function

Information about the function

KnGetSystemTimeRes()

Purpose

Gets the resolution of the system time source.

Parameters

  • [out] res – pointer to the structure containing the resolution of the system time source (in nanoseconds) in the nsec field. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnSetSystemTime()

Purpose

Sets the system time.

Parameters

  • [in] time – pointer to the structure containing the sec field, which indicates the number of seconds that have elapsed since January 1, 1970, and the nsec field, which indicates the number of nanoseconds that have elapsed since the time defined in the sec field. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnGetSystemTime()

Purpose

Gets the system time.

Parameters

  • [out] time – pointer to the structure containing the sec field, which indicates the number of seconds that have elapsed since January 1, 1970, and the nsec field, which indicates the number of nanoseconds that have elapsed since the time defined in the sec field. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnGetUpTimeRes()

Purpose

Gets the resolution of the source of monotonic time that has elapsed since the KasperskyOS kernel was started.

Parameters

  • [out] res – pointer to the structure containing the nsec field, which indicates the resolution of the source of monotonic time (in nanoseconds) that has elapsed since the KasperskyOS kernel was started. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnGetUpTime()

Purpose

Gets the monotonic time that has elapsed since the moment the KasperskyOS kernel was started.

Parameters

  • [out] time – pointer to the structure containing the sec field, which indicates the number of seconds that have elapsed since the KasperskyOS kernel started, and the nsec field, which indicates the number of nanoseconds that have elapsed since the time defined in the sec field. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnGetRtcTime()

Purpose

Gets the system time.

Parameters

  • [out] rt – pointer to the structure containing the following time data: year, month, day, hours, minutes, seconds, and milliseconds. The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

KnGetMSecSinceStart()

Purpose

Gets the monotonic time that has elapsed since the moment the KasperskyOS kernel was started.

Parameters

N/A

Returned values

Monotonic time (in milliseconds) that has elapsed since the KasperskyOS kernel was started.

KnAdjSystemTime()

Purpose

Starts gradual adjustment of the system time.

Parameters

  • [in] adj – pointer to the structure containing the amount of time by which the system time must be adjusted (sec*10^9+nsec nanoseconds), or RTL_NULL if you do not need to start an adjustment but instead only need information about a previously run adjustment (through the prev parameter). The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.
  • [in] slew – rate of system time adjustment (microseconds per second).
  • [out] prev – pointer to the structure containing the amount of time correction that remained (or remains if RTL_NULL was indicated in the adj parameter) for the already running gradual adjustment to fully complete (sec*10^9+nsec nanoseconds). The type of structure is defined in the header file sysroot-*-kos/include/rtl/rtc.h from the KasperskyOS SDK.

Returned values

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

Additional information

If a new adjustment is started before a previously running adjustment is finished, the previously running adjustment is interrupted.

Page top
[Topic libkos_time_api]

Using notifications (notice_api.h)

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

The API can track events that occur to (both system and user) resources, and inform other processes and threads about events involving user resources.

Information about API functions is provided in the table below.

Using the API

The notification mechanism uses an event mask. An event mask is a value whose bits are interpreted as events that should be tracked or that have already occurred. An event mask has a size of 32 bits and consists of a general part and a specialized part. The common part describes events that are not specific to any resources. The specialized part describes events that are specific to certain resources. Specialized part flags for system resources and common part flags are defined in the sysroot-*-kos/include/handle/event_descr.h header file from KasperskyOS SDK. (For example, the common part flag EVENT_OBJECT_DESTROYED signifies resource termination, and the specialized part flag EVENT_TASK_COMPLETED signifies process termination.) Specialized part flags for a user resource are defined by the resource provider with the help of the OBJECT_EVENT_SPEC() and OBJECT_EVENT_USER() macros, which are defined in the sysroot-*-kos/include/handle/event_descr.h header file from the KasperskyOS SDK. The resource provider must export the public header files describing the flags of the specialized part.

The standard scenario for receiving notifications about events occurring to resources consists of the following steps:

  1. Creating a notification receiver (KasperskyOS kernel object that collects notifications) by calling the KnNoticeCreate() function.
  2. Adding "resource—event mask" entries to the notification receiver to configure it to get notifications about events that occur to relevant resources.

    To add a "resource—event mask" entry to the notification receiver, you need to call the KnNoticeSubscribeToObject() function. (The OCAP_HANDLE_GET_EVENT flag should be set in the handle permissions mask of the resource stated in the object parameter.) Several "resource—event mask" entries can be added for one resource, and the entry identifiers do not need to be unique. Tracked events for each "resource—event mask" entry should be defined with an event mask that may match one or several events.

    "Resource—event mask" entries added to the notification receiver can be fully or partially removed to prevent the receiver from getting notifications that match these entries. To remove all "resource—event mask" entries from the receiver, you need to call the KnNoticeDropAndWake() function. To remove from the receiver "resource—event mask" entries that refer to the same resource, you need to call the KnNoticeUnsubscribeFromObject() function. To remove from the receiver a "resource—event mask" entry with a specific identifier, you need to call the KnNoticeUnsubscribeFromEvent() function.

    "Resource—event mask" entries can be added to, or removed from, the notification receiver throughout its life cycle.

  3. Extracting notifications from the receiver with the KnNoticeGetEvent() function.

    You can set the time-out for notifications to appear in the receiver when calling the KnNoticeGetEvent() function. Threads that are locked while waiting for notifications to appear in the receiver will resume when notifications appear, even if these notifications match "resource—event mask" entries added after wait start.

    Threads that are locked while waiting for notifications to appear in the receiver will resume if all "resource—event mask" entries are removed from the receiver by calling the KnNoticeDropAndWake() function. If you add at least one "resource—event mask" entry to the notification receiver after calling the KnNoticeDropAndWake() function, threads that get notifications from that receiver will be locked again when calling the KnNoticeGetEvent() function for the specified time-out duration as long as there are no notifications. If all "resource—event mask" entries are removed from the notification receiver with the KnNoticeUnsubscribeFromObject() and/or KnNoticeUnsubscribeFromEvent() functions, threads waiting for notifications to appear in the receiver will not resume until the time-out elapses.

  4. Removing a notification receiver by calling the KnNoticeRelease() function.

    Threads that are locked while waiting for notifications to appear in the receiver will resume when the receiver is removed by calling the KnNoticeRelease() function.

To notify other processes and/or threads about events that occurred to the user resource, you need to call the KnNoticeSetObjectEvent() function. Calling the function results in notifications appearing in receivers configured to get events defined with the evMask parameter that occur to the user resource defined with the object parameter. You cannot set flags of the general part of an event mask in the evMask parameter, because only the kernel can signal about events that match the general part of an event mask. If the process calling the KnNoticeSetObjectEvent() function created the user resource handle stated in the object parameter, you can set flags defined by the OBJECT_EVENT_SPEC() and OBJECT_EVENT_USER() macros in the evMask parameter. If the process calling the KnNoticeSetObjectEvent() function received the user resource handle stated in the object parameter from another process, you can set only those flags defined by the OBJECT_EVENT_USER() macro in the evMask parameter, while the permissions mask of the resulting handle must have a OCAP_HANDLE_SET_EVENT flag set.

Information about API functions

notice_api.h functions

Function

Information about the function

KnNoticeCreate()

Purpose

Creates a notification receiver.

Parameters

  • [out] notice – pointer to the identifier of the notification receiver.

Returned values

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

KnNoticeSubscribeToObject()

Purpose

Adds a "resource–event mask" entry to the notification receiver so that it can receive notifications about events that occur with the defined resource and match the defined event mask.

Parameters

  • [in] notice – identifier of the notification receiver.
  • [in] object – resource handle.
  • [in] evMask – event mask.
  • [in] evId – ID of the "resource–event mask" entry.

Returned values

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

KnNoticeGetEvent()

Purpose

Extracts notifications from the receiver.

Parameters

  • [in] notice – identifier of the notification receiver.
  • [in] msec – time-out before notifications appearing in the receiver, in milliseconds, or INFINITE_TIMEOUT to set an unlimited time-out.
  • [in] countMax – maximum number of notifications extracted with one function call.
  • [out] events – pointer to a set of notifications that represent structures containing the identifier of a "resource—event mask" entry and the mask of the events that occurred to the resource.
  • [out] count – number of notifications extracted.

Returned values

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

If the time-out for notifications to appear in the receiver has elapsed, returns rcTimeout.

If the time-out for notifications appear in the receiver is interrupted by a call to the KnNoticeRelease() or KnNoticeDropAndWake() functions, returns rcResourceNotFound.

KnNoticeUnsubscribeFromObject()

Purpose

Removes from the notification receiver "resource—event mask" entries that match the specified resource to prevent the receiver from getting notifications about events that match these entries.

Parameters

  • [in] notice – identifier of the notification receiver.
  • [in] object – resource handle.

Returned values

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

Additional information

Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver.

KnNoticeUnsubscribeFromEvent()

Purpose

Removes from the notification receiver "resource—event mask" entries with the specified identifier to prevent the receiver from getting notifications about events that match these entries.

Parameters

  • [in] notice – identifier of the notification receiver.
  • [in] evId – ID of the "resource–event mask" entry.

Returned values

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

Additional information

Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver.

KnNoticeDropAndWake()

Purpose

Removes all "resource—event mask" entries from the specified notification receiver and resumes all threads that are waiting for notifications to appear in the specified receiver.

Parameters

  • [in] notice – identifier of the notification receiver.

Returned values

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

KnNoticeRelease()

Purpose

Removes the specified notification receiver and resumes all threads that are waiting for notifications to appear in the specified receiver.

Parameters

  • [in] notice – identifier of the notification receiver.

Returned values

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

KnNoticeSetObjectEvent()

Purpose

Signals that events matching the specified event mask occurred to the specified user resource.

Parameters

  • [in] object – user resource handle.
  • [in] evMask – mask of events to be signaled.

Returned values

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

Page top
[Topic libkos_notice_api]

Dynamically creating IPC channels (cm_api.h, ns_api.h)

APIs are defined in the header files from the KasperskyOS SDK:

  • sysroot-*-kos/include/coresrv/cm/cm_api.h
  • sysroot-*-kos/include/coresrv/ns/ns_api.h

The API dynamically creates IPC channels.

Information about API functions is provided in the tables below.

Using the API

To ensure that servers can notify clients about the endpoints that they provide, the solution should include a name server, which is provided by the NameServer system program (executable file sysroot-*-kos/bin/ns from the KasperskyOS SDK). IPC channels from clients and servers to a name server can be statically created (these IPC channels must be named kl.core.NameServer). If this is not done, attempts will be made to dynamically create these IPC channels whenever clients and servers call the NsCreate() function. A name server does not have to be included in a solution if the clients initially already have information about the names of servers and the endpoints provided by these servers.

The names of endpoints and interfaces should be defined according to the formal specifications of solution components. (For information about the qualified name of an endpoint, see "Binding methods of security models to security events") Instead of the qualified name of an endpoint, you can use any conditional name of this endpoint. The names of clients and servers are defined in the init description. You can also get a process name by calling the KnTaskGetName() function from the API task_api.h.

Dynamic creation of an IPC channel on the server side includes the following steps:

  1. Connect to the name server by calling the NsCreate() function.
  2. Publish the provided endpoints on the name server by using the NsPublishService() function.

    To unpublish an endpoint, call the NsUnPublishService() function.

  3. Receive a client request to create an IPC channel by calling the KnCmListen() function.

    The KnCmListen() function gets the first request in the queue without deleting this request but instead putting it at the end of the queue. If there is only one request in the queue, multiple consecutive calls of the KnCmListen() function will provide the same result. A request is deleted from the queue when the KnCmAccept() or KnCmDrop() function is called.

  4. You can accept a client request to create an IPC channel by calling the KnCmAccept() function.

    To decline a client request, call the KnCmDrop() function.

    A listener handle is created when the KnCmAccept() function is called with the INVALID_HANDLE value in the listener parameter. If a listener handle is specified, the created server IPC handle will provide the capability to receive IPC requests over all IPC channels associated with this listener handle. (The first IPC channel associated with the listener handle is created when the KnCmAccept() function is called with the INVALID_HANDLE value in the listener parameter. The second and subsequent IPC channels associated with the listener handle are created during the second and subsequent calls of the KnCmAccept() function specifying the handle that was obtained during the first call.) In the listener parameter of the KnCmAccept() function, you can specify the listener handle received using the KnHandleConnect(), KnHandleConnectEx() and KnHandleCreateListener() functions from the API handle_api.h, and the ServiceLocatorRegister() function declared in the header file sysroot-*-kos/include/coresrv/sl/sl_api.h from the KasperskyOS SDK.

Dynamic creation of an IPC channel on the client side includes the following steps:

  1. Connect to the name server by calling the NsCreate() function.
  2. Find the server providing the required endpoint by using the NsEnumServices() function.

    To get a full list of endpoints with a defined interface, call the function several times while incrementing the index until you receive the rcResourceNotFound error.

  3. Fulfill the request to create an IPC channel with the necessary server by calling the KnCmConnect() function.

You can connect multiple clients and servers to the name server. Each client and server can create multiple connections to the name server. A server can unpublish an endpoint that was published by another server.

Deleting dynamically created IPC channels

A dynamically created IPC channel will be deleted when its client IPC handle and server IPC handle are closed.

Information about API functions

ns_api.h functions

Function

Information about the function

NsCreate()

Purpose

Creates a connection to a name server.

Parameters

  • [in,optional] name – pointer to the name of the name server process, or RTL_NULL to assign the default name (corresponds to the NS_SERVER_NAME macro value).
  • [in] msecs – timeout (in milliseconds) for creating a connection to the name server, or INFINITE_TIMEOUT to define an unlimited timeout.
  • [out] ns – pointer to the ID of the connection to the name server.

Returned values

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

NsPublishService()

Purpose

Publishes an endpoint on a name server.

Parameters

  • [in] ns – ID of the connection to the name server.
  • [in] type – pointer to the name of the endpoint interface.
  • [in,optional] server – pointer to the name of the server providing the endpoint, or RTL_NULL to use the name of the calling process.
  • [in] service – pointer to the qualified name of the endpoint.

Returned values

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

NsUnPublishService()

Purpose

Unpublishes an endpoint on a name server.

Parameters

  • [in] ns – ID of the connection to the name server.
  • [in] type – pointer to the name of the endpoint interface.
  • [in,optional] server – pointer to the name of the server providing the endpoint, or RTL_NULL to use the name of the calling process.
  • [in] service – pointer to the qualified name of the endpoint.

Returned values

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

NsEnumServices()

Purpose

Enumerates the endpoints published on a name server.

Parameters

  • [in] ns – ID of the connection to the name server.
  • [in] type – pointer to the name of the interface of endpoints.
  • [in] index – index for enumerating endpoints. Enumeration starts with zero.
  • [out] server – pointer to the buffer for the name of the server providing the endpoint.
  • [in] serverSize – buffer size (in bytes) for the name of the server providing the endpoint.
  • [out] service – pointer to the buffer for the qualified name of the endpoint.
  • [in] serviceSize – buffer size (in bytes) for the qualified name of the endpoint.

Returned values

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

cm_api.h functions

Function

Information about the function

KnCmConnect()

Purpose

Requests to create an IPC channel with a server for use of the defined endpoint.

Parameters

  • [in] server – pointer to the server name.
  • [in] service – pointer to the qualified name of the endpoint.
  • [in] msecs – timeout (in milliseconds) for fulfilling a request, or INFINITE_TIMEOUT to define an unlimited timeout.
  • [out] handle – pointer to the client IPC handle.
  • [out] rsid – pointer to the endpoint ID (RIID).

Returned values

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

KnCmListen()

Purpose

Receives a client request to create an IPC channel for use of an endpoint.

Parameters

  • [in] filter – fictitious parameter that must have the value RTL_NULL.
  • [in] msecs – timeout (in milliseconds) for the appearance of a client request, or INFINITE_TIMEOUT to define an unlimited timeout.
  • [out] client – pointer to the client name.
  • [out] service – pointer to the qualified name of the endpoint.

Returned values

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

KnCmDrop()

Purpose

Rejects a client request to create an IPC channel for use of the defined endpoint.

Parameters

  • [in] client – pointer to the client name.
  • [in] service – pointer to the qualified name of the endpoint.

Returned values

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

KnCmAccept()

Purpose

Accepts a client request to create an IPC channel for use of the defined endpoint.

Parameters

  • [in] client – pointer to the client name.
  • [in] service – pointer to the qualified name of the endpoint.
  • [in] rsid – endpoint ID (RIID).
  • [in,optional] listener – listener handle, or INVALID_HANDLE if you need to create it.
  • [out] handle – pointer to the server IPC handle.

Returned values

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

Page top
[Topic libkos_cm_ns_api]

Using synchronization primitives (event.h, mutex.h, rwlock.h, semaphore.h, condvar.h)

The libkos library provides APIs that enable use of the following synchronization primitives:

  • Events (event.h)
  • Mutexes (mutex.h)
  • Read-write locks (rwlock.h)
  • Semaphores (semaphore.h)
  • Conditional variables (condvar.h)

The header files are located in the KasperskyOS SDK at sysroot-*-kos/include/kos.

The APIs are intended for synchronizing threads that belong to the same process.

Events

An event is a synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads. The notified thread waits for the event to switch from a non-signaling state to a signaling state, and the notifying thread changes the state of this event.

The standard API usage scenario for working with events includes the following steps:

  1. An event is initialized via the KosEventInit() function call.
  2. The event is used by threads:
    • The notified threads wait for the event to switch from non-signaling state to signaling state via the KosEventWait() or KosEventWaitTimeout() function call.
    • The notifying threads change the state of the event via the KosEventSet() and KosEventReset() function calls.

Information about the API event functions is provided in the table below.

event.h functions

Function

Information about the function

KosEventInit()

Purpose

Initializes an event.

The event is in a non-signaling state after it is initialized.

Parameters

  • [out] event – pointer to the event. The event type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosEventSet()

Purpose

Sets the event state to signaling.

Parameters

  • [out] event – pointer to the event. The event type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosEventReset()

Purpose

Sets the event state to non-signaling.

Parameters

  • [out] event – pointer to the event. The event type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosEventWait()

Purpose

Waits for the event to change its state from non-signaling to signaling.

Parameters

  • [in,out] event – pointer to the event. The event type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] reset – value that defines whether the event state should be changed set to non-signaling after the time-out has elapsed (rtl_true – yes, rtl_false – no). The parameter type is defined in the sysroot-*-kos/include/rtl/stdbool.h header file from the KasperskyOS SDK.

Returned values

N/A

KosEventWaitTimeout()

Purpose

Waits on the event to change its state from non-signaling to signaling for a period that does not exceed the specified time.

Parameters

  • [in,out] event – pointer to the event. The event type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] reset – value that defines whether the event state should be changed set to non-signaling after the time-out has elapsed (rtl_true – yes, rtl_false – no). The parameter type is defined in the sysroot-*-kos/include/rtl/stdbool.h header file from the KasperskyOS SDK.
  • [in] mdelay – time-out (in milliseconds) or INFINITE_TIMEOUT to set an unlimited time-out.

Returned values

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

Returns rcTimeout if the time-out has elapsed.

Mutexes

A mutex is a synchronization primitive that ensures mutually exclusive execution of critical sections (areas of code where resources shared between threads are queried). One thread captures the mutex and executes a critical section. Meanwhile, other threads wait for the mutex to be freed and attempt to capture this mutex to execute other critical sections. A mutex can be freed only by the specific thread that captured it. You can use a recursive mutex, which can be captured by the same thread multiple times.

The standard API usage scenario for working with mutexes includes the following steps:

  1. A mutex is initialized via the KosMutexInit() or KosMutexInitEx() function call.
  2. The mutex is used by threads:
    1. The mutex is captured via the KosMutexTryLock(), KosMutexLock() or KosMutexLockTimeout() function call.
    2. The mutex is freed via the KosMutexUnlock() function call.

Information about the API mutex functions is provided in the table below.

mutex.h functions

Function

Information about the function

KosMutexInit()

Purpose

Initializes a non-recursive mutex.

Parameters

  • [out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosMutexInitEx()

Purpose

Initializes a mutex.

Parameters

  • [out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] recursive – value that defines whether the mutex should be recursive (1 – yes, 0 – no).

Returned values

N/A

KosMutexTryLock()

Purpose

Acquires the mutex.

If the mutex is already acquired, returns control rather than waits for the mutex to be released.

Parameters

  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

If the mutex is already acquired, returns rcBusy.

KosMutexLock()

Purpose

Acquires the mutex.

If the mutex is already acquired, waits indefinitely for it to be released.

Parameters

  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosMutexUnlock()

Purpose

Releases the mutex.

Parameters

  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosMutexLockTimeout()

Purpose

Acquires the mutex.

If the mutex is already acquired, waits for it to be released for a period that does not exceed the specified time.

Parameters

  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] mdelay – time-out (in milliseconds) or INFINITE_TIMEOUT to set an unlimited time-out.

Returned values

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

Returns rcTimeout if the time-out has elapsed.

Read-write locks

A read-write lock is a synchronization primitive used to allow access to resources shared between threads: write access for one thread or read access for multiple threads at the same time.

The standard API usage scenario for working with read-write locks includes the following steps:

  1. A read-write lock is initialized by the KosRWLockInit() function call.
  2. The read-write lock is used by threads:
    1. The read-write lock is captured for write operations (via the KosRWLockWrite() or KosRWLockTryWrite() function call) or for read operations (via the KosRWLockRead() or KosRWLockTryRead() function call).
    2. The read-write lock is freed via the KosRWLockUnlock() function call.

Information about the API read-write lock functions is provided in the table below.

rwlock.h functions

Function

Information about the function

KosRWLockInit()

Purpose

Initializes a read-write lock.

Parameters

  • [out] rwlock – pointer to a read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosRWLockRead()

Purpose

Acquires a read-write lock for reading.

If the read-write lock is already acquired for writing, or if there are threads waiting on the lock to be acquired for writing, waits indefinitely for the lock to be released.

Parameters

  • [in,out] rwlock – pointer to the read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosRWLockTryRead()

Purpose

Acquires the read-write lock for reading.

If the read-write lock is already acquired for writing, or if there are threads waiting on the lock to be acquired for writing, returns control, rather than waits for the lock to be released.

Parameters

  • [in,out] rwlock – pointer to the read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

KosRWLockWrite()

Purpose

Acquires the read-write lock for writing.

If the read-write lock is already acquired for writing or reading, waits indefinitely for the lock to be released.

Parameters

  • [in,out] rwlock – pointer to the read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosRWLockTryWrite()

Purpose

Acquires the read-write lock for writing.

If the read-write lock is already acquired for writing or reading, returns control, rather than waits for the lock to be released.

Parameters

  • [in,out] rwlock – pointer to the read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

KosRWLockUnlock()

Purpose

Releases the read-write lock.

Parameters

  • [in,out] rwlock – pointer to the read-write lock. The read-write lock type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

Additional information

If the read-write lock is acquired for reading, it remains acquired for reading until released by every reading thread.

Semaphores

A semaphore is a synchronization primitive that is based on a counter whose value can be atomically modified. The value of the counter normally reflects the number of available resources shared between threads. To execute a critical section, the thread waits until the counter value becomes greater than zero. If the counter value is greater than zero, it is decremented by one and the thread executes the critical section. After the critical section is executed, the thread signals the semaphore and the counter value is increased.

The standard API usage scenario for working with semaphores includes the following steps:

  1. A semaphore is initialized via the KosSemaphoreInit() function call.
  2. The semaphore is used by threads:
    1. They wait for the semaphore via the KosSemaphoreWait(), KosSemaphoreWaitTimeout() or KosSemaphoreTryWait() function call.
    2. The semaphore is signaled via the KosSemaphoreSignal() or KosSemaphoreSignalN() function call.
  3. Deallocating semaphore resources by calling the KosSemaphoreDeinit() function.

Information about the API semaphore functions is provided in the table below.

semaphore.h functions

Function

Information about the function

KosSemaphoreInit()

Purpose

Initializes a semaphore.

Parameters

  • [out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] count – counter value.

Returned values

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

If the value of the count parameter exceeds the KOS_SEMAPHORE_VALUE_MAX constant, returns rcInvalidArgument. (The KOS_SEMAPHORE_VALUE_MAX constant is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.)

KosSemaphoreDeinit()

Purpose

Deallocates semaphore resources.

Parameters

  • [in] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

If there are threads waiting on the semaphore, returns rcBusy.

KosSemaphoreSignal()

Purpose

Signals the semaphore and increases the counter by one.

Parameters

  • [in, out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

KosSemaphoreSignalN()

Purpose

Signals the semaphore and increases the counter by the specified number.

Parameters

  • [in, out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] n – natural number by which to increase the counter.

Returned values

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

KosSemaphoreWaitTimeout()

Purpose

Waits on the semaphore for a period that does not exceed the specified time.

Parameters

  • [in, out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] mdelay – semaphore time-out in milliseconds or INFINITE_TIMEOUT to set an unlimited time-out.

Returned values

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

Returns rcTimeout if the time-out has elapsed.

KosSemaphoreWait()

Purpose

Waits on the semaphore indefinitely.

Parameters

  • [in, out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

KosSemaphoreTryWait()

Purpose

Waits on the semaphore.

If the semaphore counter has a zero value, returns control, rather than waits for the semaphore counter to increase.

Parameters

  • [in, out] semaphore – pointer to the semaphore. The semaphore type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

If the semaphore counter has a zero value, returns rcBusy.

Conditional variables

A conditional variable is a synchronization primitive that is used to notify one or more threads about the fulfillment of a condition required by these threads. A conditional variable is used together with a mutex. The notifying and notified threads capture a mutex to execute critical sections. During execution of a critical section, the notified thread verifies that its required condition was fulfilled (for example, the data has been prepared by the notifying thread). If the condition is fulfilled, the notified thread executes the critical section and frees the mutex. If the condition is not fulfilled, the notified thread is locked at the conditional variable and waits for the condition to be fulfilled. When this happens, the mutex is automatically freed. During execution of a critical section, the notifying thread verifies fulfillment of the condition required by the notified thread. If the condition is fulfilled, the notifying thread signals this fulfillment through the conditional variable and frees the mutex. The notified thread that was locked and waiting for the fulfillment of its required condition resumes execution of the critical section while automatically capturing the mutex. After the critical section is executed, the notified thread frees the mutex.

The standard API usage scenario for working with conditional variables includes the following steps:

  1. The conditional variable and mutex are initialized.

    To initialize a conditional variable, you need to call the KosCondvarInit() function.

  2. The conditional variable and mutex are used by threads.

Use of a conditional variable and mutex by notified threads includes the following steps:

  1. The mutex is captured.
  2. Condition fulfillment is verified.
  3. The KosCondvarWait() or KosCondvarWaitTimeout() function is called to wait for condition fulfillment.

    After the KosCondvarWait() or KosCondvarWaitTimeout() function is returned, you normally need to re-verify that the condition is fulfilled because another notified thread also received the signal and may have voided this condition again. (For example, another thread could have extracted the data prepared by the notifying thread). To do so, you need to use the following construct:

    while(<condition>) <call of KosCondvarWait() or KosCondvarWaitTimeout()>
  4. The mutex is freed.

Use of a conditional variable and mutex by notifying threads includes the following steps:

  1. The mutex is captured.
  2. Condition fulfillment is verified.
  3. Fulfillment of the condition is signaled via the KosCondvarSignal() or KosCondvarBroadcast() function call.
  4. The mutex is freed.

Information about the API conditional variable functions is provided in the table below.

condvar.h functions

Function

Information about the function

KosCondvarInit()

Purpose

Initializes a conditional variable.

Parameters

  • [out] condvar – pointer to the conditional variable. The conditional variable type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosCondvarWaitTimeout()

Purpose

Waits for condition fulfillment for a period that does not exceed the specified time.

Parameters

  • [in] condvar – pointer to the conditional variable. The conditional variable type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in] mdelay – condition fulfillment time-out in milliseconds, or INFINITE_TIMEOUT to set an unlimited time-out.

Returned values

Returns rcOk if successful.

Returns rcTimeout if the time-out has elapsed.

KosCondvarWait()

Purpose

Waits indefinitely for condition fulfillment.

Parameters

  • [in] condvar – pointer to the conditional variable. The conditional variable type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.
  • [in,out] mutex – pointer to the mutex. The mutex type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

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

KosCondvarSignal()

Purpose

Signals condition fulfillment to one of the threads waiting for it.

Parameters

  • [in, out] condvar – pointer to the conditional variable. The conditional variable type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

KosCondvarBroadcast()

Purpose

Signals condition fulfillment to all threads waiting for it.

Parameters

  • [in, out] condvar – pointer to the conditional variable. The conditional variable type is defined in the sysroot-*-kos/include/kos/sync_types.h header file from the KasperskyOS SDK.

Returned values

N/A

Page top
[Topic libkos_sync_api]

Managing I/O memory isolation (iommu_api.h)

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

The API is intended for managing the isolation of physical memory regions used by devices on a PCIe bus for DMA. (Isolation is provided by the IOMMU.)

Information about API functions is provided in the table below.

Using the API

A device on the PCIe bus cannot use DMA unless the device is attached to the IOMMU domain. After a device is attached to the IOMMU domain, the device can access all DMA buffers that are associated with this IOMMU domain. A device can be attached to only one IOMMU domain at a time, but multiple devices can be attached to the same IOMMU domain. A DMA buffer can be associated with multiple IOMMU domains at the same time. Each process is associated with a separate IOMMU domain.

The API attaches devices on the PCIe bus to an IOMMU domain associated with the calling process, and performs the inverse operation. A device is normally attached to an IOMMU domain when its driver is initialized. A device is usually detached from an IOMMU domain when errors are encountered during driver initialization or driver finalization.

A DMA buffer is associated with an IOMMU domain when calling the KnIoDmaBegin() function that is included in the API dma.h.

Information about API functions

iommu_api.h functions

Function

Information about the function

KnIommuAttachDevice()

Purpose

Attaches a device on a PCIe bus to the IOMMU domain associated with the calling process.

Parameters

  • [in] bdf – address of the device on the PCIe bus in BDF format.

Returned values

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

Additional information

If IOMMU is not enabled, rcOk is returned.

KnIommuDetachDevice()

Purpose

Detaches a device on a PCIe bus from the IOMMU domain associated with the calling process.

Parameters

  • [in] bdf – address of the device on the PCIe bus in BDF format.

Returned values

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

Additional information

If IOMMU is not enabled, rcOk is returned.

Page top
[Topic libkos_iommu_api]

Using queues (queue.h)

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

The API sets up data exchange between threads owned by one process via a queuing mechanism that does not lock threads. In other words, you can add or extract elements of a queue without locking other threads that add or extract elements of this queue.

Information about API functions is provided in the table below.

Using the API

The standard scenario for API usage includes the following steps:

  1. Create a queue abstraction.

    A queue abstraction consists of a structure containing queue metadata and a queue buffer intended for storing elements of the queue. A queue buffer is logically divided into equal segments, each of which is intended for an individual element of the queue. The number of segments in a queue buffer matches the maximum number of elements in the queue. The alignment of segment addresses corresponds to the data types of elements in the queue.

    To complete this step, call the KosQueueCreate() function. This function can allocate memory for the queue buffer or use already allocated memory. The size of the already allocated memory must be sufficient to accommodate the maximum number of elements in the queue. Also take into account that the size of a segment in the queue buffer is rounded to the next largest multiple of the alignment value defined through the objAlign parameter. The initial address of the already allocated memory must also be aligned to correspond to the data types of queue elements. If the memory address alignment specified in the buffer parameter is less than the value defined through the objAlign parameter, the function returns RTL_NULL.

  2. Exchange data between threads by adding and extracting elements of the queue.

    To add one element to the end of the queue, you must reserve a segment in the queue buffer by calling the KosQueueAlloc() function, copy this element to the reserved segment, and call the KosQueuePush() function.

    To add a sequence of elements to the end of the queue, you must reserve the necessary number of segments in the queue buffer via KosQueueAlloc() function calls, copy the elements of this sequence to the reserved segments, and call the KosQueuePushArray() function. The order of elements in a sequence is not changed after this sequence is added to the queue. In other words, elements are added to the queue in the same order in which the pointers to reserved segments in the queue buffer are put into the array that is passed through the objs parameter of the KosQueuePushArray() function.

    To extract the first element of the queue, you must call the KosQueuePop() function. This function returns the pointer to the reserved segment in the queue buffer that contains the first element of the queue. After using an extracted element (for example, after checking or saving the value of an element), you must free the queue buffer segment occupied by this element. To do so, call the KosQueueFree() function.

    To clear the queue and free all registered segments in the queue buffer, you must call the KosQueueFlush() function.

  3. Delete the queue abstraction.

    To complete this step, call the KosQueueDestroy() function. This function deletes the queue buffer if only the memory for this buffer was allocated by the KosQueueCreate() function. Otherwise, you must separately delete the queue buffer.

Information about API functions

queue.h functions

Function

Information about the function

KosQueueCreate()

Purpose

Creates a queue abstraction.

Parameters

  • [in] objCount – maximum number of elements in the queue.
  • [in] objSize – size (in bytes) of an element in the queue.
  • [in] objAlign – alignment of segment addresses in the queue buffer. The addresses of segments in the queue buffer may be unaligned (objAlign=1) or aligned (objAlign=2,4,...,2^N) to the boundary of a 2^N-byte sequence (for example, two-byte or four-byte).
  • [in,optional] buffer – pointer to the allocated memory for the queue buffer, or RTL_NULL to automatically allocate the memory.

Returned values

If successful, the function returns the queue abstraction ID, otherwise it returns RTL_NULL.

KosQueueDestroy()

Purpose

Deletes a queue abstraction.

Parameters

  • [in] queue – queue abstraction ID.

Returned values

N/A

KosQueueAlloc()

Purpose

Reserves a segment in the queue buffer.

Parameters

  • [in] queue – queue abstraction ID.

Returned values

If successful, the function returns the pointer to the reserved segment in the queue buffer, otherwise it returns RTL_NULL.

KosQueueFree()

Purpose

Resets the reservation of the defined segment in the queue buffer.

Parameters

  • [in] queue – queue abstraction ID.
  • [in] obj – pointer to the reserved segment in the queue buffer.

Returned values

N/A

KosQueuePush()

Purpose

Adds an element to the end of the queue.

Parameters

  • [in] queue – queue abstraction ID.
  • [in] obj – pointer to the reserved segment in the queue buffer.

Returned values

N/A

KosQueuePushArray()

Purpose

Adds a sequence of elements to the end of the queue.

Parameters

  • [in] queue – queue abstraction ID.
  • [in] objs – array of pointers to reserved segments in the queue buffer.
  • [in] count – number of elements in the sequence.

Returned values

N/A

KosQueuePop()

Purpose

Extracts the first element of the queue.

Parameters

  • [in] queue – queue abstraction ID.
  • [in] timeout – timeout (in milliseconds) for an element to appear in the queue, or INFINITE_TIMEOUT to set an unlimited timeout.

Returned values

If successful, this function returns the pointer to the reserved segment in the queue buffer containing the first element of the queue. Otherwise it returns RTL_NULL.

KosQueueFlush()

Purpose

Clears the queue and resets the reservation of all registered segments in the queue buffer.

Parameters

  • [in] queue – queue abstraction ID.

Returned values

N/A

Page top
[Topic libkos_queue_api]

Using memory barriers (barriers.h)

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

The API sets barriers for reading from memory and/or writing to memory. A memory barrier is an instruction for a compiler or processor that guarantees that memory access operations specified in source code before setting a barrier will be executed before the memory access operations specified in source code after setting a barrier. Use of memory barriers is required if the specific order of memory write and memory read operations is important. Otherwise, the optimization mechanisms of a compiler and/or processor could cause these operations to be executed in a different order than the order specified in the source code.

Information about API functions is provided in the table below.

barriers.h functions

Function

Information about the function

IoReadBarrier()

Purpose

Sets a barrier for reading from memory.

Parameters

N/A

Returned values

N/A

IoWriteBarrier()

Purpose

Sets a barrier for writing to memory.

Parameters

N/A

Returned values

N/A

IoReadWriteBarrier()

Purpose

Sets a barrier for writing to memory and reading from memory.

Parameters

N/A

Returned values

N/A

Page top
[Topic libkos_barriers_api]

Executing system calls (syscalls.h)

This API is defined in the header file sysroot-*-kos/include/coresrv/syscalls.h from the KasperskyOS SDK.

The API allows execution of the Call(), Recv(), and Reply() system calls for sending and receiving IPC messages.

Information about API functions is provided in the table below.

Using the API

Pointers to buffers containing the constant part and arena of IPC messages are passed to API functions by using an IPC message header whose type is defined in the header file sysroot-*-kos/include/ipc/if_rend.h from the KasperskyOS SDK. Prior to API function calls, the headers of IPC messages must be bound to buffers containing the constant part and arena of IPC messages. To do so, use the PackInMsg() and PackOutMsg() functions that are declared in the header file sysroot-*-kos/include/services/rtl/nk_msg.h from the KasperskyOS SDK.

The Call(), CallEx(), Recv(), and RecvEx() functions lock execution of the calling thread while waiting for the system calls to complete. The CallEx() and RecvEx() functions let you define the timeout for completion of a system call. When this timeout is reached, an uncompleted system call is interrupted and the thread that is waiting for its completion resumes execution. A system call is also interrupted if an error occurs during its execution (such as an error due to termination of a server process). If a thread waiting on the completion of a system call is terminated externally, this system call is also interrupted. A system call executed by the CallEx() or RecvEx() function can be interrupted (for example, for correct termination of a process) by using the API ipc_api.h.

If a system call was interrupted using the API ipc_api.h, the CallEx() and RecvEx() functions return the error code rcIpcInterrupt. If IPC message transmission is prohibited by security mechanisms (such as the Kaspersky Security Module or a capability-based security mechanism implemented by the KasperskyOS kernel), the Call(), CallEx(), and Reply() functions return the error code rcSecurityDisallow.

Information about API functions

syscalls.h functions

Function

Information about the function

Call()

Purpose

Executes the Call() system call with an unlimited timeout for its completion.

Parameters

  • [in] handle – client IPC handle.
  • [in] msgOut – pointer to the header of IPC requests.
  • [in,out] msgIn – pointer to the header of IPC responses.

Returned values

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

CallEx()

Purpose

Executes the Call() system call with a defined timeout for its completion and the capability to interrupt its execution.

Parameters

  • [in] handle – client IPC handle.
  • [in] msgOut – pointer to the header of IPC requests.
  • [in,out] msgIn – pointer to the header of IPC responses.
  • [in] mdelay – timeout (in milliseconds) for completion of a Call() system call, or INFINITE_TIMEOUT to define an unlimited timeout.
  • [in,optional] syncHandle – handle of the IPC synchronization object, or INVALID_HANDLE if an interrupt of the Call() system call is not required.

Returned values

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

Reply()

Purpose

Executes the Reply() system call.

Parameters

  • [in] handle – server IPC handle.
  • [in] msgOut – pointer to the header of IPC responses.

Returned values

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

Recv()

Purpose

Executes the Recv() system call with an unlimited timeout for its completion.

Parameters

  • [in] handle – server IPC handle.
  • [in,out] msgIn – pointer to the header of IPC requests.

Returned values

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

RecvEx()

Purpose

Executes the Recv() system call with a defined timeout for its completion and the capability to interrupt its execution.

Parameters

  • [in] handle – server IPC handle.
  • [in,out] msgIn – pointer to the header of IPC responses.
  • [in] mdelay – timeout (in milliseconds) for completion of a Recv() system call, or INFINITE_TIMEOUT to define an unlimited timeout.
  • [in,optional] syncHandle – handle of the IPC synchronization object, or INVALID_HANDLE if an interrupt of the Recv() system call is not required.

Returned values

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

Page top
[Topic libkos_syscalls_api]

IPC interrupt (ipc_api.h)

This API is defined in the header file sysroot-*-kos/include/coresrv/ipc/ipc_api.h from the KasperskyOS SDK.

The API interrupts the Call() and Recv() system calls if one or more process threads are locked while waiting for these system calls to complete. For example, you may need to interrupt these system calls to correctly terminate a process so that threads waiting for the completion of these system calls can resume execution.

Information about API functions is provided in the table below.

Using the API

The API interrupts system calls in process threads that were locked after the CallEx() or RecvEx() function was called from the API syscalls.h if these functions were called while specifying the IPC synchronization object handle in the syncHandle parameter. To create an IPC synchronization object, call the KnIpcCreateSyncObject() function. (The handle of an IPC synchronization object cannot be transferred to another process because the necessary flag for this operation is not set in the permissions mask of this handle.)

The KnIpcSetInterrupt() function switches an IPC synchronization object to a state that allows interruption of the system calls in those process threads that have been locked after a CallEx() or RecvEx() function call specifying the handle of this IPC synchronization object in the syncHandle parameter. A system call can be interrupted only during certain stages of its execution. A system call that is executed by the CallEx() function can be interrupted only when the server has not yet received a Recv() or RecvEx() function call for the IPC channel whose client IPC handle was specified during the CallEx() function call. A system call executed by the RecvEx() function can be interrupted only while waiting for an IPC request from a client.

The KnIpcClearInterrupt() function cancels the action of the KnIpcSetInterrupt() function.

To delete an IPC synchronization object, close its handle by calling the KnHandleClose() function that is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h from the KasperskyOS SDK.

Information about API functions

ipc_api.h functions

Function

Information about the function

KnIpcCreateSyncObject()

Purpose

Creates an IPC synchronization object.

Parameters

  • [out] syncHandle – pointer to the handle of the IPC synchronization object.

Returned values

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

KnIpcSetInterrupt()

Purpose

Switches the defined IPC synchronization object to a state in which the Call() and Recv() system calls are interrupted.

Parameters

  • [in] syncHandle – handle of the IPC synchronization object.

Returned values

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

KnIpcClearInterrupt()

Purpose

Switches the defined IPC synchronization object to a state in which the Call() and Recv() system calls are not interrupted.

Parameters

  • [in] syncHandle – handle of the IPC synchronization object.

Returned values

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

Page top
[Topic libkos_ipc_api][Topic posix_api]

POSIX support limitations

KasperskyOS has a limited implementation of POSIX oriented toward the POSIX.1-2008 standard. These limitations are primarily due to security precautions.

There is no XSI support or optional functionality.

Limitations affect the following:

  • Interaction between processes
  • Interaction between threads via signals
  • Asynchronous input/output
  • Use of robust mutexes
  • Terminal operations
  • Shell operations
  • File handle management
  • Clock usage
  • Getting system parameters

Limitations include:

  • Unimplemented interfaces
  • Interfaces that are implemented with deviations from the POSIX.1-2008 standard
  • Stub interfaces that do not perform any operations except assign the ENOSYS value to the errno variable and return the value -1

In KasperskyOS, signals cannot interrupt the Call(), Recv(), and Reply() system calls that support the operation of libraries that implement the POSIX interface.

The KasperskyOS kernel does not transmit signals.

Limitations on interaction between processes

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

fork()

Create a new (child) process.

Not implemented

unistd.h

pthread_

atfork()

Register the handlers that are called before and after the child process is created.

Not implemented

pthread.h

wait()

Wait for the child process to stop or complete.

Stub

sys/wait.h

waitid()

Wait for the state of the child process to change.

Not implemented

sys/wait.h

waitpid()

Wait for the child process to stop or complete.

Stub

sys/wait.h

execl()

Run the executable file.

Stub

unistd.h

execle()

Run the executable file.

Stub

unistd.h

execlp()

Run the executable file.

Stub

unistd.h

execv()

Run the executable file.

Not implemented

unistd.h

execve()

Run the executable file.

Not implemented

unistd.h

execvp()

Run the executable file.

Stub

unistd.h

fexecve()

Run the executable file.

Stub

unistd.h

setpgid()

Move the process to another group or create a group.

Stub

unistd.h

setsid()

Create a session.

Stub

unistd.h

getpgrp()

Get the group ID of the calling process.

Stub

unistd.h

getpgid()

Get the group ID.

Stub

unistd.h

getppid()

Get the ID of the parent process.

Stub

unistd.h

getsid()

Get the session ID.

Stub

unistd.h

times()

Get the time values for the process and its descendants.

Stub

sys/times.h

kill()

Send a signal to the process or group of processes.

Only the SIGTERM signal can be sent. The pid parameter is ignored.

signal.h

pause()

Wait for a signal.

Stub

unistd.h

sigpending()

Check for received blocked signals.

Not implemented

signal.h

sigqueue()

Send a signal to the process.

Not implemented

signal.h

sigtimedwait()

Wait for a signal from the defined set of signals.

Not implemented

signal.h

sigwaitinfo()

Wait for a signal from the defined set of signals.

Not implemented

signal.h

sem_init()

Create an unnamed semaphore.

You cannot create an unnamed semaphore for synchronization between processes. If a non-zero value is passed through the pshared parameter, it will return only the value -1 and will assign the ENOTSUP value to the errno variable.

semaphore.h

sem_open()

Create/open a named semaphore.

You cannot open a named semaphore that was created by another process. Named semaphores (like unnamed semaphores) are local, which means that they are accessible only to the process that created them.

semaphore.h

pthread_

spin_init()

Create a spin lock.

You cannot create a spin lock for synchronization between processes. If the PTHREAD_PROCESS_SHARED value is passed through the pshared parameter, this value will be ignored.

pthread.h

mmap()

Map to memory.

You cannot perform memory mapping for interaction between processes. If the MAP_SHARED and PROT_WRITE values are passed through the flags and prot parameters, respectively, it will return only the MAP_FAILED value and will assign the EACCES value to the errno variable. For all other possible values of the prot parameter, the MAP_SHARED value of the flags parameter is ignored. In addition, you cannot pass combinations of the PROT_WRITE|PROT_EXEC and PROT_READ|PROT_WRITE|PROT_EXEC flags through the prot parameter. In this case, it will return only the MAP_FAILED value and will assign the ENOMEM value to the errno variable.

sys/mman.h

mprotect()

Define the memory access permissions.

For security purposes, some configurations of the KasperskyOS kernel prohibit granting simultaneous write-and-execute access to virtual memory regions. If this type of kernel configuration is in use and you pass the PROT_WRITE|PROT_EXEC value through the prot parameter, it will only return the -1 value and will assign the ENOTSUP value to the errno variable.

sys/mman.h

pipe()

Create an unnamed channel.

You cannot use an unnamed channel for data transfer between processes. Unnamed channels are local, which means that they are accessible only to the process that created them.

unistd.h

mkfifo()

Create a special FIFO file (named channel).

Stub

sys/stat.h

mkfifoat()

Create a special FIFO file (named channel).

Not implemented

sys/stat.h

Limitations on interaction between threads via signals

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

pthread_kill()

Send a signal to a thread.

You cannot send a signal to a thread. If a signal number is passed through the sig parameter, only the ENOSYS value is returned.

signal.h

siglongjmp()

Restore the state of the control thread and the signals mask.

Not implemented

setjmp.h

sigsetjmp()

Save the state of the control thread and the signals mask.

Not implemented

setjmp.h

Asynchronous input/output limitations

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

aio_cancel()

Cancel input/output requests that are waiting to be handled.

Not implemented

aio.h

aio_error()

Receive an error from an asynchronous input/output operation.

Not implemented

aio.h

aio_fsync()

Request the execution of input/output operations.

Not implemented

aio.h

aio_read()

Request a file read operation.

Not implemented

aio.h

aio_return()

Get the status of an asynchronous input/output operation.

Not implemented

aio.h

aio_suspend()

Wait for the completion of asynchronous input/output operations.

Not implemented

aio.h

aio_write()

Request a file write operation.

Not implemented

aio.h

lio_listio()

Request execution of a set of input/output operations.

Not implemented

aio.h

Limitations on the use of robust mutexes

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

pthread_mutex_consistent()

Return a robust mutex to a consistent state.

Not implemented

pthread.h

pthread_mutexattr_getrobust()

Get a robust mutex attribute.

Not implemented

pthread.h

pthread_mutexattr_setrobust()

Define a robust mutex attribute.

Not implemented

pthread.h

Terminal operation limitations

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

ctermid()

Get the path to the file of the control terminal.

Only returns or passes an empty string through the s parameter.

stdio.h

tcsetattr()

Define the terminal settings.

The input speed, output speed, and other settings specific to hardware terminals are ignored.

termios.h

tcdrain()

Wait for output completion.

Returns only the value -1.

termios.h

tcflow()

Suspend or resume receipt or transmission of data.

Suspending output and resuming suspended output are not supported.

termios.h

tcflush()

Clear the input queue or output queue, or both of these queues.

Returns only the value -1.

termios.h

tcsendbreak()

Break the connection with the terminal for a set time.

Returns only the value -1.

termios.h

ttyname()

Get the path to the terminal file.

Returns only a null pointer.

unistd.h

ttyname_r()

Get the path to the terminal file.

Returns only an error value.

unistd.h

tcgetpgrp()

Get the ID of a group of processes using the terminal.

Returns only the value -1.

unistd.h

tcsetpgrp()

Define the ID for a group of processes using the terminal.

Returns only the value -1.

unistd.h

tcgetsid()

Get the ID of a group of processes for the leader of the session connected to the terminal.

Returns only the value -1.

termios.h

Shell operation limitations

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

popen()

Create a child process for command execution and an unnamed channel with this process.

Only assigns the ENOSYS value to the errno variable and returns the NULL value.

stdio.h

pclose()

Close the unnamed channel with the child process created by popen(), and wait for the child process to terminate.

Cannot be used because popen() always returns NULL instead of the handle of the unnamed channel that serves as an input parameter for pclose().

stdio.h

system()

Create a child process for command execution.

Stub

stdlib.h

wordexp()

Perform a shell-like expansion of the string.

Not implemented

wordexp.h

wordfree()

Free up the memory allocated for the results from calling wordexp().

Not implemented

wordexp.h

Limitations on file handle management

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

dup()

Make a copy of the handle of an opened file.

Handles of regular files, standard I/O streams, sockets and channels are supported. There is no guarantee that the lowest available handle will be received.

fcntl.h

dup2()

Make a copy of the handle of an opened file.

Handles of regular files, standard I/O streams, sockets and channels are supported. The handle of an opened file needs to be passed through the fildes2 parameter.

fcntl.h

Limitations on clock usage

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

clock_gettime()

Get the time value.

If the CLOCK_PROCESS_CPUTIME_ID value or CLOCK_THREAD_CPUTIME_ID value is passed through the clock_id parameter, it will return only the value -1 and will assign the EINVAL value to the errno variable.

time.h

clock()

Get the CPU time spent on execution of the calling process.

Returns the amount of time (in milliseconds) that has elapsed since the KasperskyOS kernel was started.

time.h

Getting system parameters

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

confstr()

Get a system parameter.

Stub

unistd.h

Page top
[Topic posix_uns_ifaces]

POSIX implementation specifics

In KasperskyOS, the specific implementation of some POSIX interfaces not entirely defined by the POSIX.1-2008 standard differs from the implementation of these interfaces in Linux and other UNIX-like operating systems. Information about these interfaces is provided in the table below.

POSIX interfaces and their implementation specifics

Interface

Purpose

Implementation

Header file based on the POSIX.1-2008 standard

bind()

Assign a name to a socket.

When using a VFS version that supports only network operations, files of sockets in the AF_UNIX family are saved in a special file system implemented by this VFS version when bind() is called. A socket file can be created only in the root of the file system or in the /tmp directory, and it can be re-used after the socket is closed.

sys/socket.h

mmap()

Map to memory.

Mapping more than 4 GB is not supported on hardware platforms running an AArch64 (ARM64) processor architecture.

sys/mman.h

read()

Read from a file.

If the size of the buf buffer exceeds the size of the read data, the remainder of this buffer is filled with zeros.

unistd.h

Page top
[Topic posix_ifaces_impl_features]

Concurrently using POSIX and the libkos API

In a thread that is created using Pthreads, you cannot use the following libkos APIs:

The following libkos APIs can be used together with Pthreads (and other POSIX APIs):

POSIX interfaces cannot be used in threads that were created using the thread.h and thread_api.h APIs.

The syscalls.h API can be used in any threads that were created using Pthreads or the thread.h and thread_api.h APIs.

Page top
[Topic posix_and_libkos]

Obtaining statistical data on the system

The libkos and libc libraries provide APIs for obtaining statistical data on the system. This data includes the following information:

  • CPU time usage by the system and by an individual process
  • Memory usage by the system and by an individual process
  • Info on processes and threads
  • Info on file systems and network interfaces

In this section

Obtaining statistical data on the system through the libkos library API

Obtaining statistical data on the system through the libc library API

Page top
[Topic sys_inf]

Obtaining statistical data on the system through the libkos library API

The libkos library provides an API that obtains statistical data on CPU time and memory usage, and info on processes and threads. This API is defined in the header file sysroot-*-kos/include/coresrv/stat/stat_api.h from the KasperskyOS SDK.

The API defined in the header file sysroot-*-kos/include/coresrv/stat/stat_api.h from the KasperskyOS SDK includes functions that "wrap" the KnProfilerGetCounters() function declared in the header file sysroot-*-kos/include/coresrv/profiler/profiler_api.h from the KasperskyOS SDK. This function requests the values of performance counters. To get this statistical data, you need to build a solution with a KasperskyOS kernel version that supports performance counters (for details, see "Image library").

Receiving information about CPU time

Uptime of CPUs (processor cores) is counted from the startup of the KasperskyOS kernel.

To obtain information about CPU time usage, use the KnGroupStatGetParam(), KnTaskStatGetParam() and KnCpuStatGetParam() functions. The values provided in the table below need to be passed in the param parameter of these functions.

Information about CPU time

Function

Value of the param parameter

Obtained value

KnGroupStatGetParam()

GROUP_PARAM_CPU_KERNEL

Uptime of all processors in kernel mode

KnGroupStatGetParam()

GROUP_PARAM_CPU_USER

Uptime of all processors in user mode

KnGroupStatGetParam()

GROUP_PARAM_CPU_IDLE

Uptime of all processors in idle mode

KnTaskStatGetParam()

TASK_PARAM_TIME_TOTAL

Uptime of all processors used for execution of the defined process

KnTaskStatGetParam()

TASK_PARAM_TIME_USER

Uptime of all processors used for execution of the defined process in user mode

KnCpuStatGetParam()

CPU_STAT_PARAM_IDLE

Uptime of the defined processor in idle mode

KnCpuStatGetParam()

CPU_STAT_PARAM_USER

Uptime of the defined processor in user mode

KnCpuStatGetParam()

CPU_STAT_PARAM_KERNEL

Uptime of the defined processor in kernel mode

The CPU time obtained by calling the KnGroupStatGetParam(), KnTaskStatGetParam() or KnCpuStatGetParam() function is presented in nanoseconds.

The CPU index (enumeration starts with zero) is the input parameter of the KnCpuStatGetParam() function. To get the total number of processors on a hardware platform, use the KnHalGetCpuCount() function declared in the header file sysroot-*-kos/include/coresrv/hal/hal_api.h from the KasperskyOS SDK.

Receiving information about memory usage

To receive information about memory usage, you need to use the KnGroupStatGetParam() and KnTaskStatGetParam() functions. The values provided in the table below need to be passed in the param parameter of these functions.

Information about memory usage

Function

Value of the param parameter

Obtained value

KnGroupStatGetParam()

GROUP_PARAM_MEM_TOTAL

Size of all installed physical memory

KnGroupStatGetParam()

GROUP_PARAM_MEM_FREE

Size of free physical memory

KnGroupStatGetParam()

GROUP_PARAM_MEM_EXEC

Size of physical memory with the "execution access" attribute

KnGroupStatGetParam()

GROUP_PARAM_MEM_SHARED

Size of physical memory used as shared memory

KnTaskStatGetParam()

TASK_PARAM_MEM_PHY

Size of physical memory used by the defined process

KnTaskStatGetParam()

TASK_PARAM_MEM_VIRT

Size of virtual memory of the defined process

KnTaskStatGetParam()

TASK_PARAM_MEM_SHARED

Size of virtual memory of the defined process mapped to shared physical memory

KnTaskStatGetParam()

TASK_PARAM_MEM_PAGE_TABLE

Size of the page table of the defined process

The memory size obtained by calling the KnGroupStatGetParam() or KnTaskStatGetParam() function is presented as the number of memory pages. The size of a memory page is 4 KB for all hardware platforms supported by KasperskyOS.

The size of physical memory used by a process refers only to the memory allocated directly for this process. For example, if the memory of a process is mapped to an MDL buffer created by another process, the size of this buffer is not included in this value.

Obtaining information on processes and threads

In addition to information about CPU time and memory usage, the KnGroupStatGetParam() and KnTaskStatGetParam() functions also let you obtain information on processes and threads. To receive this information, the values provided in the table below need to be passed through the param parameter of these functions.

Information on processes and threads

Function

Value of the param parameter

Obtained value

KnGroupStatGetParam()

GROUP_PARAM_TASKS

Number of user processes (not counting the kernel process)

KnGroupStatGetParam()

GROUP_PARAM_THREADS

Total number of threads (including kernel threads)

KnTaskStatGetParam()

TASK_PARAM_PPID

ID of the parent process of the defined process (PPID)

KnTaskStatGetParam()

TASK_PARAM_PRIO

Priority of the initial thread of the defined process

KnTaskStatGetParam()

TASK_PARAM_STATE

State of the defined process (according to the list of TaskExecutionStates defined in the header file sysroot-*-kos/include/task/pcbpage.h from the KasperskyOS SDK)

KnTaskStatGetParam()

TASK_PARAM_IMGSIZE

Size of the memory-loaded image of the program running in the context of the defined process, in bytes

KnTaskStatGetParam()

TASK_PARAM_TIME_START

Time (in nanoseconds) between startup of the kernel and startup of the defined process

KnTaskStatGetParam()

TASK_PARAM_HANDLES

Number of handles owned by the defined process

KnTaskStatGetParam()

TASK_PARAM_THREADS

Number of threads in the defined process

In addition to the KnGroupStatGetParam() and KnTaskStatGetParam() functions, information on processes and threads can also be obtained by using the following functions:

  • KnTaskStatGetName() – gets the name of the defined process.
  • KnTaskStatGetPath() – gets the name of the executable file in ROMFS that was used to start the defined process.

    This function can be used only if the process was started from an executable file in ROMFS. Otherwise, the function call will result in an empty string.

  • KnTaskStatGetId() – gets the ID of the defined process (PID).
  • KnProfilerGetCounters() – gets the values of performance counters.

    For example, to get the number of kernel threads and the total number of handles, pass the kl.core.Core.threads and handles.total values through the names parameter.

Obtaining information on CPU time and memory usage by each process

To get information about CPU time and memory usage by each process, do the following:

  1. Get the list of processes by calling the KnGroupStatGetTaskList() function.
  2. Get the number of items on the list of processes by calling the KnTaskStatGetTasksCount() function.
  3. Iterate through the list of processes, repeating the following steps:
    1. Get an item from the list of processes by calling the KnTaskStatEnumTaskList() function.
    2. Get the process name by calling the KnTaskStatGetName() function.

      This is necessary to identify the process for which the information about CPU time and memory usage will be received.

    3. Get information about CPU time and memory usage by calling the KnTaskStatGetParam() function.
    4. Verify that the process was not terminated. If the process has terminated, discard the obtained information about the CPU time and memory used by this process.

      To verify that the process was not terminated, you need to call the KnTaskStatGetParam() function, using the param parameter to pass the TASK_PARAM_STATE value. A value other than TaskStateTerminated should be received.

    5. Finish working with the item on the list of processes by calling the KnTaskStatCloseTask() function.
  4. Finish working with the list of processes by calling the KnTaskStatCloseTaskList() function.

Calculating CPU load

Indicators of load on CPUs (processor cores) may be the following values:

  • Percent load of all processors
  • Percent load of all processors by each process
  • Percent load of each processor

These indicators are calculated for a specific time interval, at the start and end of which the information about CPU time utilization was received. (For example, CPU load can be monitored with periodic receipt of information about CPU time utilization.) The values obtained at the start of the interval need to be subtracted from the values obtained at the end of the interval. In other words, the following increments need to be obtained for the interval:

  • TK – uptime of all processors in kernel mode.
  • TKi [i=1,2,...,n] – uptime of the ith processor in kernel mode.
  • TU – uptime of all processors in user mode.
  • TUi [i=1,2,...,n] – uptime of the ith processor in user mode.
  • TIDLE – uptime of all processors in idle mode.
  • TIDLEi [i=1,2,...,n] – uptime of the ith processor in idle mode.
  • Tj [j=1,2,...,m] – CPU time spent on execution of the jth process.

The percent load of all processors is calculated as follows:

(TK+TU)/(TK+TU+TIDLE).

The percent load of the ith processor is calculated as follows:

(TKi+TUi)/(TKi+TUi+TIDLEi).

The percent load of all processors caused by the jth process is calculated as follows:

Tj/(TK+TU+TIDLE).

Page top
[Topic sys_inf_libkos]

Obtaining statistical data on the system through the libc library API

The libkos library provides APIs that let you obtain statistical data on file systems and network interfaces managed by VFS. The functions of these APIs are presented in the table below.

Information on file systems and network interfaces

Function

Header file from the KasperskyOS SDK

Obtained information

statvfs()

sysroot-*-kos/include/strict/posix/sys/statvfs.h

File system information, such as the block size, number of blocks, and number of available blocks

getvfsstat()

sysroot-*-kos/include/sys/statvfs.h

The information on all mounted file systems is identical to the information provided by the statvfs() function

getifaddrs()

sysroot-*-kos/include/ifaddrs.h

Information on network interfaces, such as their name, IP address, and subnet mask

Page top
[Topic sys_inf_libc]

MessageBus component

The MessageBus component implements the message bus that ensures receipt, distribution and delivery of messages between programs running KasperskyOS. This bus is based on the publisher-subscriber model. Use of a message bus lets you avoid having to create a large number of IPC channels to connect each subscriber program to each publisher program.

Messages transmitted through the MessageBus cannot contain data. These messages can be used only to notify subscribers about events.

The MessageBus component provides an additional level of abstraction over KasperskyOS IPC that helps simplify the development and expansion of your programs. MessageBus is a separate program that is accessed through IPC. However, developers are provided with a MessageBus access library that lets you avoid direct use of IPC calls.

The API of the access library provides the following interfaces:

IProviderFactory
Provides factory methods for obtaining access to instances of all other interfaces.
IProviderControl
Interface for registering and deregistering a publisher and subscriber in the bus.
IProvider (MessageBus component)
Interface for transferring a message to the bus.
ISubscriber
Callback interface for sending a message to a subscriber.
IWaiter
Interface for waiting for a callback when the corresponding message appears.

Message structure

Each message contains two parameters:

topic
Identifier of the message subject.
id
Additional parameter that identifies a particular message.

The topic and id parameters are unique for each message. The interpretation of topic+id is determined by the contract between the publisher and subscriber. For example, if there are changes to the configuration data used by the publisher and subscriber, the publisher forwards a message regarding the modified data and the id of the specific entry containing the new data. The subscriber uses mechanisms outside of the MessageBus to receive the new data based on the id key.

In this section

IProviderFactory interface

IProviderControl interface

IProvider interface (MessageBus component)

ISubscriber, IWaiter and ISubscriberRunner interfaces

Page top
[Topic messagebus_component]

IProviderFactory interface

The IProviderFactory interface provides factory methods for receiving the interfaces necessary for working with the MessageBus component.

A description of the IProviderFactory interface is provided in the file named messagebus/i_messagebus_control.h.

An instance of the IProviderFactory interface is obtained by using the free InitConnection() function, which receives the name of the IPC channel between your application and the MessageBus program. The connection name is defined in the init.yaml.in file when describing the solution configuration. If the connection is successful, the output parameter contains a pointer to the IProviderFactory interface.

  • The interface for registering and deregistering (see "IProviderControl interface") publishers and subscribers in the message bus is obtained by using the IProviderFactory::CreateBusControl() method.
  • The interface containing the methods enabling the publisher to send messages to the bus (see "IProvider interface (MessageBus component)") is obtained by using the IProviderFactory::CreateBus() method.
  • The interfaces containing the methods enabling the subscriber to receive messages from the bus (see "ISubscriber, IWaiter and ISubscriberRunner interfaces") are obtained by using the IProviderFactory::CreateCallbackWaiter and IProviderFactory::CreateSubscriberRunner() methods.

    It is not recommended to use the IWaiter interface, because calling a method of this interface is a locking call.

i_messagebus_control.h (fragment)

class IProviderFactory { ... virtual fdn::ResultCode CreateBusControl(IProviderControlPtr& controlPtr) = 0; virtual fdn::ResultCode CreateBus(IProviderPtr& busPtr) = 0; virtual fdn::ResultCode CreateCallbackWaiter(IWaiterPtr& waiterPtr) = 0; virtual fdn::ResultCode CreateSubscriberRunner(ISubscriberRunnerPtr& runnerPtr) = 0; ... }; ... fdn::ResultCode InitConnection(const std::string& connectionId, IProviderFactoryPtr& busFactoryPtr);
Page top
[Topic messagebus_component_iproviderfactory]

IProviderControl interface

The IProviderControl interface provides the methods for registering and deregistering publishers and subscribers in the message bus.

A description of the IProviderControl interface is provided in the file named messagebus/i_messagebus_control.h.

The IProviderFactory interface is used to obtain an interface instance.

Registering and deregistering a publisher

The IProviderControl::RegisterPublisher() method is used to register the publisher in the message bus. This method receives the message subject and puts the unique ID of the bus client into the output parameter. If the message subject is already registered in the bus, the call will be declined and the client ID will not be filled.

The IProviderControl::UnregisterPublisher() method is used to deregister a publisher in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a publisher ID, the call will be declined.

i_messagebus_control.h (fragment)

class IProviderControl { ... virtual fdn::ResultCode RegisterPublisher(const Topic& topic, ClientId& id) = 0; virtual fdn::ResultCode UnregisterPublisher(ClientId id) = 0; ... };

Registering and deregistering a subscriber

The IProviderControl::RegisterSubscriber() method is used to register the subscriber in the message bus. This method accepts the subscriber name and the list of subjects of messages for the necessary subscription, and puts the unique ID of the bus client into the output parameter.

The IProviderControl::UnregisterSubscriber() method is used to deregister a subscriber in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a subscriber ID, the call will be declined.

i_messagebus_control.h (fragment)

class IProviderControl { ... virtual fdn::ResultCode RegisterSubscriber(const std::string& subscriberName, const std::set<Topic>& topics, ClientId& id) = 0; virtual fdn::ResultCode UnregisterSubscriber(ClientId id) = 0; ... };
Page top
[Topic messagebus_component_icontrol]

IProvider interface (MessageBus component)

The IProvider interface provides the methods enabling the publisher to send messages to the bus.

A description of the IProvider interface is provided in the file named messagebus/i_messagebus.h.

The IProviderFactory interface is used to obtain an interface instance.

Sending a message to the bus

The IProvider::Push() method is used to send a message. This method accepts the bus client ID received during registration and the message ID. If the message queue in the bus is full, the call will be declined.

i_messagebus.h (fragment)

class IProvider { public: ... virtual fdn::ResultCode Push(ClientId id, BundleId dataId) = 0; ... };
Page top
[Topic messagebus_component_iprovider]

ISubscriber, IWaiter and ISubscriberRunner interfaces

The ISubscriber, IWaiter, and ISubscriberRunner interfaces provide the methods enabling the subscriber to receive messages from the bus and process them.

Descriptions of the ISubscriber, IWaiter and ISubscriberRunner interfaces are provided in the file named messagebus/i_subscriber.h.

The IProviderFactory interface is used to obtain instances of the IWaiter and ISubscriberRunner interfaces. The implementation of the ISubscriber callback interface is provided by the subscriber application.

Receiving a message from the bus

You can use the IWaiter::Wait() or ISubscriberRunner::Run() method to switch a subscriber to standby mode, waiting for a message from the bus. These methods accept the bus client ID and the pointer to the ISubscriber callback interface. If the client ID is not registered, the call will be declined.

It is not recommended to use the IWaiter interface, because calling the IWaiter::Wait() method is a locking call.

The ISubscriber::OnMessage() method will be called when a message is received from the bus. This method accepts the message subject and message ID.

i_subscriber.h (fragment)

class ISubscriber { ... virtual fdn::ResultCode OnMessage(const std::string& topic, BundleId id) = 0; }; ... class IWaiter { ... [[deprecated("Use ISubscriberRunner::Run method instead.")]] virtual fdn::ResultCode Wait(ClientId id, const ISubscriberPtr& subscriberPtr) = 0; }; ... class ISubscriberRunner { ... virtual fdn::ResultCode Run(ClientId id, const ISubscriberPtr& subscriberPtr) = 0; };
Page top
[Topic messagebus_component_isubwait]

ExecutionManager component

The API is defined in the header files located in the directory sysroot-*-kos/include/component/execution_manager/ from the SDK.

The ExecutionManager component usage scenario is described in the article titled "Starting a process using the KasperskyOS API".

execution_manager_proxy.h interface

The API is defined in the header file sysroot-*-kos/include/component/execution_manager/kos_ipc/execution_manager_proxy.h

The interface contains the factory method CreateExecutionManager() for getting the pointer to the instance of the IExecutionManager interface that is required for working with the ExecutionManager component.

Usage example:

client.cpp

#include <component/execution_manager/kos_ipc/execution_manager_proxy.h> ... namespace execmgr = execution_manager; int main(int argc, const char *argv[]) { // ... execmgr::IExecutionManagerPtr ptr; // name of the IPC channel for connecting to the ExecutionManager process. It must match the MAIN_CONN_NAME value in the CMakeLists.txt file for building ExecutionManager. char mainConnection[] = "ExecMgrEntity"; execmgr::ipc::ExecutionManagerConfig cfg{mainConnection}; if (CreateExecutionManager(cfg, ptr) != eka::sOk) { std::cerr << "Cannot create execution manager" << std::endl; return EXIT_FAILURE; } // ... }

IExecutionManager interface

The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_execution_manager.h

The IExecutionManager interface lets you access pointers to the following interfaces:

  • IApplicationController – interface for starting and stopping processes.
  • ISystemController – interface for managing the system.

Usage example:

client.cpp

int main(int argc, const char *argv[]) { // ... execmgr::IApplicationControllerPtr ac; if (ptr->GetApplicationController(ac) != eka::sOk) { std::cerr << "Cannot get application controller" << std::endl; return EXIT_FAILURE; } execmgr::ISystemControllerPtr sc; if (ptr->GetSystemController(sc) != eka::sOk) { std::cerr << "Cannot get system controller" << std::endl; return EXIT_FAILURE; } // ... }

IApplicationController interface

The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_application_control.h

The IApplicationController interface provides the following methods that let you change the state of a process:

  • StartEntity(

    const std::filesystem::path& runPath,

    const StartEntityInfo& info,

    StartEntityResultInfo& resInfo) – method for starting a process.

  • RestartEntity(EntityId endId) – method for restarting a previously started process.
  • ShutdownEntity(EntityId entId) – method for sending a termination signal to a process.
  • StopEntity(EntityId entId) – method for immediately stopping execution of a process.

The StartEntity() method receives the path to the executable file that should be run and the structure containing the run parameters for the StartEntityInfo process, and returns the structure containing the StartEntityResultInfo process run results. All fields of the StartEntityInfo structure are optional for initialization.

All other methods receive the EntityId structure that identifies the started process.

struct IApplicationController { // All fields of the StartEntityInfo structure are optional for initialization. struct StartEntityInfo { // Process name. Unless otherwise specified, the process class name will be used. // If the process class name is not specified, the executable file name will be used. std::string entityName; // Process class. Unless otherwise specified, the process name will be used. If the process name is not specified, the executable file name will be used. std::string eiid; std::vector<std::string> args; // Command-line arguments. std::vector<std::string> envs; // Environment variables. // Policy for restarting a process when it crashes. Available values: // EntityRestartPolicy::DoNotRestart – do not restart. // EntityRestartPolicy::AlwaysRestart – always restart. EntityRestartPolicy restartPolicy { EntityRestartPolicy::DoNotRestart }; }; struct StartEntityResultInfo { std::string eiid; // Security class assigned to the process. EntityId entId; // Structure that identifies the started process. Uid sid; // Security ID of the started process. std::string taskName; // Name of the started process. }; };

Usage example:

client.cpp

int main(int argc, const char *argv[]) { // ... const fs::path appPath{"/application"}; execmgr::IApplicationController::StartEntityResultInfo result; execmgr::IApplicationController::StartEntityInfo info; info.entityName = std::string{"application.Application"}; info.eiid = std::string{"application.Application"}; info.args = std::vector<std::string>{"1", "ARG1", "ARG2" , "ARG3"}; info.envs = std::vector<std::string>{"ENV1=10", "ENV2=envStr"}; std::cout << "Starting application from elf\n"; if (ac->StartEntity(appPath, info, result) != eka::sOk) { std::cerr << "Can not start application from " << appPath << std::endl; return EXIT_FAILURE; } std::cout << "Application started with process sid " << result.sid << "\n"; auto AppId = result.entId; if (ac->StopEntity(AppId) != eka::sOk) { std::cerr << "Cannot stop process " << appPath << std::endl; return EXIT_FAILURE; } // ... }

ISystemController interface

The API is defined in the header file sysroot-*-kos/include/component/execution_manager/i_system_control.h

The ISystemController interface provides the following method for system management:

  • StopAllEntities() method stops all running processes, then terminates the ExecutionManager process, then sends a device shutdown request to the kernel.

Usage example:

client.cpp

int main(int argc, const char *argv[]) { // ... if (sc->StopAllEntities() != eka::sOk) { std::cerr << "Cannot stop all processes\n"; return EXIT_FAILURE; } // ... }
Page top
[Topic execmgr_component]