Contents
- libkos library
- Managing handles (handle_api.h)
- Managing virtual memory (vmm_api.h)
- Allocating and freeing memory (alloc.h)
- Using DMA (dma.h)
- Memory-mapped I/O (mmio.h)
- Managing interrupt processing (irq.h)
- Managing threads (high-level API thread.h)
- Managing threads (low-level API thread_api.h)
- Managing processes (high-level API task.h)
- Managing processes (low-level API task_api.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)
- Using sessions (session.h)
- Using KosObjects (objects.h)
- Using KosObject containers (objcontainer.h)
- Using KosStrings (strings.h)
- KasperskyOS kernel XHCI DbC driver management (xhcidbg_api.h)
- Receiving security audit data (vlog_api.h)
- Using futexes (sync.h)
- Getting IPC handles and RIIDs to use statically created IPC channels (sl_api.h)
- Power management (pm_api.h)
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.
On hardware platforms running an Arm processor architecture, input and output parameters of the libkos
library API cannot be saved in "Device memory" because this could lead to unpredictable behavior. (Exceptions to this rule are the addr
parameter of the KnVmQuery()
function from the vmm_api.h API, the reg
and baseReg
parameters of functions from the mmio.h API, the va
parameter of the KnHalFlushCache()
function from the hal_api.h API, and the va
parameter of the KosCpuCacheFlush()
function from the cpucache.h API.) Parameters of the libkos
library API must be saved in "Normal memory". To copy data from "Device memory" to "Normal memory" and vice versa, you must use the RtlPedanticMemcpy()
function declared in the header file sysroot-*-kos/include/rtl/string_pedantic.h
from the KasperskyOS SDK.
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.
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 topCreating handles
Information about API functions is provided in the table below.
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 Runtime Implementation Identifier (RIID) in the proxy object initialization function. To create a callable handle, call the KnHandleCreateUserObjectEx()
function and specify the server IPC handle and the 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 |
---|---|
|
Purpose Creates a handle. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a handle. Parameters
Returned values If successful, the function returns |
|
Purpose Creates and connects the client, server, and listener IPC handles. Parameters
Returned values If successful, the function returns |
|
Purpose Creates and connects the client, server, and listener IPC handles. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a client IPC handle for querying the Kaspersky Security Module through the security interface. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a listener handle that is not associated with a client IPC handle and server IPC handle. Parameters
Returned values If successful, the function returns |
Transferring handles
Information about API functions is provided in the table below.
Overview of handle transfers
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:
- An IPC channel is created between the processes.
- The solution security policy (
security.psl
) allows interaction between process classes. - 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:
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 typified objects of the KosObject
type (for details about KosObjects
, see Using KosObjects (objects.h)). 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:
- The client packages the handles and handle permissions masks into fields of the
*_req
IPC requests structure of thenk_handle_desc_t
type. - The client calls the interface method for transferring handles to the server. The
Call()
system call is executed when this method is called. - The server receives an IPC request by executing the
Recv()
system call. - 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 thenk_handle_desc_t
type.
The scenario for transferring handles from the server to a client includes the following steps:
- The client calls the interface method for receiving handles from the server. The
Call()
system call is executed when this method is called. - The server receives an IPC request by executing the
Recv()
system call. - 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 thenk_handle_desc_t
type. - The server responds to the IPC request by executing the
Reply()
system call. - 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 thenk_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 |
---|---|
|
Purpose Creates a resource transfer context object and configures a notification mechanism for monitoring the life cycle of this object. Parameters
Returned values If successful, the function returns Additional information The notification receiver is configured to receive notifications about events that match the |
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 |
---|---|
|
Purpose Duplicates a handle. As a result of duplication, the calling process receives the handle descendant. Parameters
Returned values If successful, the function returns |
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:
- The client packages the handle into a field of the
*_req
IPC request structure of thenk_handle_desc_t
type. - 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. - The server receives the IPC request by executing the
Recv()
system call. - 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 thenk_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)
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 topRevoking 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 |
---|---|
|
Purpose Closes a handle and revokes its descendants. Parameters
Returned values If successful, the function returns |
|
Purpose Revokes the handles that make up the inheritance subtree of the specified handle. Parameters
Returned values If successful, the function returns |
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 |
---|---|
|
Purpose Closes a handle. Parameters
Returned values If successful, the function returns |
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 a security ID 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 |
---|---|
|
Purpose Receives a security ID (SID) based on a handle. Parameters
Returned values If successful, the function returns |
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:
The scenario includes the following steps:
- 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. - The resource consumer calls the
OpenResource()
method to open access to the resource.- 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. - 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. - 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.
- 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 thenk_get_handle()
andnk_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.
- The resource provider creates the resource transfer context and calls the
- The resource consumer calls the
UseResource()
method to utilize the resource.- 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 thenk_handle_desc()
macro to package the handle into the handle transport container. - The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
- The resource provider uses the
nk_is_handle_dereferenced()
function to verify that the dereferencing operation was completed instead of a handle transfer. - 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. - 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.
- The
UseResource()
method call completes successfully. The resource consumer receives the results of the operation performed with the resource.
- The handle that was received from the resource provider at step 2 is used as a parameter of the
- The resource consumer calls the
CloseResource()
method to close access to the resource.- 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 thenk_handle_desc()
macro to package the handle into the handle transport container. After theCloseResource()
method is called, the resource consumer uses theKnHandleClose()
function to close the handle. - The handle is dereferenced, after which the resource provider receives the pointer to the resource transfer context.
- The resource provider uses the
nk_is_handle_dereferenced()
function to verify that the dereferencing operation was completed instead of a handle transfer. - The resource provider uses the
nk_get_badge()
function to extract the pointer to the resource transfer context from the handle transport container. - 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). - The
CloseResource()
method call completes successfully.
- The handle that was received from the resource provider at step 2 is used as a parameter of the
- The resource provider frees up the memory that was allocated for the resource transfer context and user resource context.
- The resource provider calls the
KnNoticeGetEvent()
function to receive a notification that the resource transfer context object was closed, and uses theKnHandleClose()
function to close the handle of the resource transfer context object. - 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. - 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.
- The resource provider calls the
Managing virtual memory (vmm_api.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/vmm/vmm_api.h
from the KasperskyOS SDK.
The API is designed to allocate and free memory, create shared memory, and prepare ELF image segments to be loaded into process memory.
Allocating and freeing memory
Information about API functions is provided in the table below.
Using the API
Virtual memory pages may be free, reserved, or committed. Free pages that are used to allocate a virtual memory region become reserved pages. Reserved pages may or may not be mapped to physical memory. Reserved pages that are mapped to physical memory are committed pages.
Pages of a virtual memory region that are allocated by the KnVmAllocate()
function call can be committed in three different ways:
- Fully committed when the region is allocated.
- Fully or partially committed after allocation of the region (by calling the
KnVmCommit()
function). - Committed whenever virtual addresses are queried (so-called "lazy" mode).
When the first option is used, the entire required volume of physical memory is allocated immediately after the virtual memory region is reserved. When the second or third option is used, the virtual memory region is reserved without allocating physical memory. This lets you conserve physical memory if a reserved virtual memory region will not actually be required or will only be partially used. In addition, a virtual memory region can be allocated faster if its allocation does not include commitment.
When in "lazy" mode, physical memory is allocated only when the virtual memory region is actually queried. In this case, the page containing the queried address and several pages before and after this address are committed.
If you call the KnVmAllocate()
function with the VMM_FLAG_COMMIT
and VMM_FLAG_LOCKED
flags, the virtual memory region will be reserved and fully committed. If you call the KnVmAllocate()
function with the VMM_FLAG_COMMIT
flag but without the VMM_FLAG_LOCKED
flag, the virtual memory region will be reserved and committed only in "lazy" mode. If you call the KnVmAllocate()
function with the VMM_FLAG_LOCKED
flag but without the VMM_FLAG_COMMIT
flag, the virtual memory region will be reserved without commitment and a subsequent call of the KnVmCommit()
function will fully commit this region. If you call the KnVmAllocate()
function without the VMM_FLAG_COMMIT
and VMM_FLAG_LOCKED
flags, the virtual memory region will be reserved without commitment and a subsequent call of the KnVmCommit()
function will commit this region in "lazy" mode.
A virtual memory region is allocated when the MDL buffer, DMA buffer or MMIO memory region is mapped to process memory. This region is allocated by the mapping function.
A protected page may reside at the beginning and/or end of the virtual memory region. This page is never committed. Any query of this page will result in an exception signaling that the region boundaries have been exceeded.
To change the access rights to the virtual memory region, call the KnVmProtect()
function. You can fully close and then re-open access to a region while retaining is contents.
To free physical memory while retaining the reservation of virtual addresses, call the KnVmDecommit()
or KnVmReset()
function. In this case, the contents of the virtual memory region will be lost. After freeing physical memory by calling the KnVmDecommit()
function, you must call the KnVmCommit()
function to subsequently use the virtual memory region. After freeing physical memory by calling the KnVmReset()
function, the virtual memory region can be used without any additional actions required. This virtual memory region will correspond to memory allocated by calling the KnVmAllocate()
function with the VMM_FLAG_COMMIT
flag but without the VMM_FLAG_LOCKED
flag.
The KnVmProtect()
, KnVmDecommit()
, and KnVmReset()
functions cannot be used if the MDL buffer, DMA buffer or MMIO memory region is mapped to the virtual memory region.
To free the virtual memory region, call the KnVmUnmap()
function. When this function is called, the reserved pages will become free regardless of whether or not they were committed, and the physical memory mapped to committed pages is also freed.
The KnVmUnmap()
function frees the virtual addresses of a region mapped to the MDL buffer, but does not delete the MDL buffer. In addition, this function cannot be used if the DMA buffer or MMIO memory region is mapped to the virtual memory region.
The KnVmCommit()
, KnVmProtect()
, KnVmDecommit()
, KnVmReset()
, and KnVmUnmap()
functions can be applied for the entire allocated virtual memory region or for a portion of it.
The functions presented in this section provide the basis for implementing memory allocation/deallocation functions of the libkos library, and functions of POSIX interfaces such as malloc()
, calloc()
, realloc()
, free()
, mmap()
, and munmap()
.
Information about API functions
vmm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Allocates (reserves and optionally commits) a virtual memory region. Parameters
Returned values If successful, the function returns the base address of the allocated virtual memory region, otherwise it returns Additional information In the
Permissible combinations of flags defining the access rights to the virtual memory region:
|
|
Purpose Commits a virtual memory region. Parameters
Returned values If successful, the function returns |
|
Purpose Decommits a virtual memory region. Parameters
Returned values If successful, the function returns |
|
Purpose Modifies the access rights to the virtual memory region. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Frees up the virtual memory region. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information about a virtual memory page. Parameters
Returned values If successful, the function returns |
|
Purpose Decommits a virtual memory region. Parameters
Returned values If successful, the function returns |
Creating shared memory
Information about API functions is provided in the table below.
Using the API
An MDL buffer is used to create shared memory. An MDL buffer consists of one or more physical memory regions that can be mapped to the memory of multiple processes at the same time. A kernel object known as a memory descriptor list is used to map the MDL buffer to process memory. A memory descriptor list (MDL) is a data structure containing the addresses and sizes of physical memory regions comprising the MDL buffer. The MDL buffer handle identifies the memory descriptor list.
To create shared memory, a process needs to create an MDL buffer, map it to its own memory, and pass the MDL buffer handle via IPC to other processes that also need to map this MDL buffer to their own memory.
To create an MDL buffer, call the KnPmmMdlCreate()
, KnPmmMdlCreateFromBuf()
or KnPmmMdlCreateFromVm()
function. The KnPmmMdlCreateFromBuf()
function creates an MDL buffer and copies data to it. The KnPmmMdlCreateFromVm()
function creates an MDL buffer and maps it to the memory of the calling process.
To reserve a virtual memory region and map the MDL buffer to it, call the KnPmmMdlMap()
function. An MDL buffer can be mapped to multiple virtual memory regions of the same process.
An MDL buffer can be used to transfer large volumes of data between processes without creating shared memory. In this case, you must make sure that the MDL buffer is not mapped to the memory of multiple processes at the same time. Interacting processes must take turns mapping the MDL buffer to their own memory, reading and/or writing data and then freeing the virtual memory region that is mapped to this MDL buffer.
The Kaspersky Security Module cannot control data that is transferred between processes via the MDL buffer.
Deleting an MDL buffer
To delete an MDL buffer, complete the following steps:
- Free the virtual memory regions mapped to the MDL buffer in all processes that are using this MDL buffer.
To complete this step, use the
KnVmUnmap()
function. - Close or revoke each MDL buffer handle in all processes that own these handles.
To complete this step, use the
KnHandleClose()
and/orKnHandleRevoke()
functions that are declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
Information about API functions
vmm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates an MDL buffer. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Creates an MDL buffer from physical memory that is mapped to the defined virtual memory region and maps the created MDL buffer to this region. Parameters
Returned values If successful, the function returns Additional information This function can be used if the defined virtual memory region is allocated with commitment. In the
|
|
Purpose Creates an MDL buffer and copies data from the defined buffer to the MDL buffer. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Gets the size of the MDL buffer. Parameters
Returned values If successful, the function returns |
|
Purpose Reserves a virtual memory region and maps the MDL buffer to it. Parameters
Returned values If successful, the function returns Additional information In the
Permissible combinations of flags defining the access rights to the virtual memory region:
|
|
Purpose Creates an MDL buffer based on an existing one. The MDL buffer is created from the same regions of physical memory as the original buffer. Parameters
Returned values If successful, the function returns |
Preparing ELF image segments to be loaded into process memory
Information about API functions is provided in the table below.
Using the API
MDL buffers are used not only to create shared memory, but also to load ELF image segments into the memory of a new process. (ELF image segments are loaded by the Einit
initializing program, for example.)
The KnVmSegInitFromVm()
and KnVmSegInitFromBuf()
functions create an MDL buffer and put the ELF image segment into it so that this segment can then be loaded into the memory of a new process.
Deleting MDL buffers containing ELF image segments
To delete MDL buffers containing ELF image segments, you must terminate the new process whose memory is mapped to these MDL buffers. You also need to complete the following steps before or after termination of this process:
- Free the virtual memory regions that are mapped to the MDL buffers in the process that created these MDL buffers.
This step is required only for MDL buffers that were created using the
KnVmSegInitFromVm()
function.To complete this step, use the
KnVmUnmap()
function. - Close the handles of MDL buffers in the process that created these MDL buffers.
To complete this step, use the
KnHandleClose()
function, which is declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
Information about API functions
vmm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates an MDL buffer from physical memory that is mapped to the defined virtual memory region containing the ELF image segment. Parameters
Returned values If successful, the function returns Additional information This function can be used if the defined virtual memory region is allocated with commitment. In the
|
|
Purpose Creates an MDL buffer and copies the ELF image segment from the defined buffer to the MDL buffer. Parameters
Returned values If successful, the function returns Additional information In the
|
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 |
---|---|
|
Purpose Allocates memory. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Allocates memory. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Allocates memory and initializes it with zeros. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Deallocates memory. Parameters
Returned values N/A |
|
Purpose Allocates memory and (optionally) copies the contents of previously allocated memory to the newly allocated memory and frees the previously allocated memory after the copy operation is complete. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
|
Purpose Allocates memory and (optionally) copies the contents of previously allocated memory to the newly allocated memory and frees the previously allocated memory after the copy operation is complete. Parameters
Returned values If successful, the function returns the pointer to the allocated memory, otherwise it returns |
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:
- 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()
orKnIoDmaCreateContinuous()
function. TheKnIoDmaCreateContinuous()
function creates a DMA buffer consisting of one block. TheKnIoDmaCreate()
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, theKnIoDmaCreate()
function can create a DMA buffer consisting of multiple blocks.The DMA buffer handle can be transferred to another process via IPC.
- 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. - Opening access to the DMA buffer for a device via the
KnIoDmaBegin()
orKnIoDmaBeginEx()
function call.The
KnIoDmaBegin()
orKnIoDmaBeginEx()
function must be called to create a kernel object containing the addresses and sizes of blocks comprising the DMA buffer. A device needs this information to use the DMA buffer. A device can work with physical addresses and/or virtual addresses depending on whether IOMMU is enabled. If IOMMU is enabled, you must use theKnIoDmaBegin()
orKnIoDmaBeginEx()
function, and the object will contain virtual and physical addresses of blocks. If IOMMU is not enabled, you must use theKnIoDmaBegin()
function, and the object will contain physical addresses of blocks only.When IOMMU is enabled, the device must be attached to an IOMMU domain.
A handle received when calling the
KnIoDmaBegin()
orKnIoDmaBeginEx()
function cannot be transferred to another process via IPC. - 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()
orKnIoDmaBeginEx()
function. The received addresses and sizes will need to be passed to the device by using MMIO, for example. After receiving this information, the device can write to the DMA buffer and/or read from it.To complete this step, call one of the following functions:
KnIoDmaGetInfo()
KnIoDmaGetPhysInfo()
KnIoDmaContinuousGetDmaAddr()
KnIoDmaContinuousGetPhysAddr()
The
KnIoDmaGetInfo()
andKnIoDmaGetPhysInfo()
functions get the memory page number (frame
) and theorder
for each block. (The memory page number multiplied by the memory page size results in the block address. The 2^order value is the block size in memory pages.) These functions use the output parameteroutInfo
to pass theKosObject
that contains the structure with information about the DMA buffer (for details aboutKosObjects
, see Using KosObjects (objects.h)). The type of structure is defined in the header filesysroot-*-kos/include/io/io_dma.h
from the KasperskyOS SDK. TheKnIoDmaGetInfo()
function inserts data into all fields of the structure, while theKnIoDmaGetPhysInfo()
function inserts data only into thecount
anddescriptors
fields while writing zeros to all other fields. When using theKnIoDmaGetInfo()
function, thedescriptors
array contains virtual memory page numbers if IOMMU is enabled, and it contains physical memory page numbers if IOMMU is not enabled. When using theKnIoDmaGetPhysInfo()
function, thedescriptors
array contains physical memory page numbers regardless of whether or not IOMMU is enabled.The
KnIoDmaContinuousGetDmaAddr()
andKnIoDmaContinuousGetPhysAddr()
functions can be used if the DMA buffer consists of one block. These functions let you get the block address. (The accepted block size should be the DMA buffer size that was defined when it was created.) TheKnIoDmaContinuousGetDmaAddr()
function uses the output parameteraddr
to pass the virtual address if IOMMU is enabled or the physical address if IOMMU is not enabled. TheKnIoDmaContinuousGetPhysAddr()
function uses the output parameteraddr
to pass the physical address, regardless of whether or not IOMMU is enabled.On Raspberry Pi 4 B, the
KnIoDmaGetPhysInfo()
function lets you get the actual physical page numbers, while theKnIoDmaGetInfo()
function lets you get the offset physical page numbers because some devices use Visual Processing Unit (VPU) translation for DMA. Similarly, theKnIoDmaContinuousGetPhysAddr()
andKnIoDmaContinuousGetDmaAddr()
functions let you get the actual and offset physical address, respectively.
Closing access to the DMA buffer for a device
Access to the DMA buffer can be closed only if IOMMU is enabled. If you delete the kernel object that was created when the KnIoDmaBegin()
or KnIoDmaBeginEx()
function was called, device access to the DMA buffer will be closed. To delete this object, you must close its handle.
Deleting a DMA buffer
To delete a DMA buffer, complete the following steps:
- Free the virtual memory regions that were reserved during
KnIoDmaMap()
function calls.To complete this step, you must close the handles that were received from
KnIoDmaMap()
function calls.This step must be completed for all processes whose memory is mapped to the DMA buffer.
- Delete the kernel object that was created by the
KnIoDmaBegin()
orKnIoDmaBeginEx()
function call.To complete this step, you must close the handle that was received when the
KnIoDmaBegin()
orKnIoDmaBeginEx()
function was called. - Close or revoke each DMA buffer handle in all processes that own these handles.
Information about API functions
dma.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a DMA buffer. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Creates a DMA buffer consisting of one block. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Reserves a virtual memory region and maps the DMA buffer to it. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Modifies the DMA buffer cache settings. Parameters
Returned values If successful, the function returns Additional information This function can be used if the following conditions are fulfilled:
In the
|
|
Purpose Gets information about a DMA buffer. This information includes the addresses and sizes of blocks. Parameters
Returned values If successful, the function returns Additional information To delete an object that was received via the |
|
Purpose Gets the physical addresses and sizes of DMA buffer blocks. Parameters
Returned values If successful, the function returns Additional information To delete an object that was received via the |
|
Purpose Gets the block address for a DMA buffer consisting of one block. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the block physical address for a DMA buffer consisting of one block. Parameters
Returned values If successful, the function returns |
|
Purpose Opens access to a DMA buffer for a device. Parameters
Returned values If successful, the function returns |
|
Purpose Opens access to a DMA buffer for a device. Parameters
Returned values If successful, the function returns |
Memory-mapped I/O (mmio.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/io/mmio.h
from the KasperskyOS SDK.
The API is intended for working with MMIO memory. MMIO memory consists of physical addresses that are mapped to registers and memory of devices. (Portions of one physical address space are used for physical memory addressing and access to registers and memory of devices.)
Information about API functions is provided in the table below.
Using the API
The standard scenario for API usage includes the following steps:
- The MMIO memory region corresponding to the relevant device is registered.
To complete this step, call the
KnRegisterPhyMem()
function.The handle of an MMIO memory region can be transferred to another process via IPC.
- Mapping the MMIO memory region to the memory of a process.
An MMIO memory region can only be mapped to one virtual memory region of only one process at one time. (However, an MMIO memory region may be mapped to the virtual memory of different processes that own the handle of this region at different times.) Mapping allows the process to receive read-and/or-write access to the MMIO memory region.
To reserve a virtual memory region and map the MMIO memory region to it, call the
KnIoMapMem()
function.A handle received when calling the
KnIoMapMem()
function cannot be transferred to another process via IPC. - Reading data from the MMIO memory region and writing data to it via process memory.
The 8-, 16- and 32-bit words that are read from the MMIO memory region or written to it are values of device registers or contents of device memory.
To complete this step, use the
IoReadMmReg8|16|32()
,IoReadMmBuffer8|16|32()
,IoWriteMmReg8|16|32()
, andIoWriteMmBuffer8|16|32()
functions.
Deregistering an MMIO memory region
To deregister an MMIO memory region, complete the following steps:
- Free the virtual memory region that was allocated when the
KnIoMapMem()
function was called.To complete this step, call the
KnHandleClose()
function and specify the handle that was received when theKnIoMapMem()
function was called. (TheKnHandleClose()
function is declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.) - Close or revoke each MMIO memory region handle in all processes that own these handles.
To complete this step, use the
KnHandleClose()
and/orKnHandleRevoke()
functions that are declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.
Information about API functions
mmio.h functions
Function(s) |
Information about the function(s) |
---|---|
|
Purpose Registers an MMIO memory region. Parameters
Returned values If successful, the function returns |
|
Purpose Reserves the virtual memory region and maps the MMIO memory region to it. Parameters
Returned values If successful, the function returns Additional information In the
In the
|
|
Purpose Get an 8-, 16- or 32-bit word from an MMIO memory region. Parameters
Returned values 8-, 16- or 32-bit word from the MMIO memory region. |
|
Purpose Save a sequence of 8-, 16- or 32-bit words from the MMIO memory region to the buffer. Parameters
Returned values N/A Additional information The functions can be used if the processor has an x86 or x86-64 architecture. |
|
Purpose Write an 8-, 16- or 32-bit word to an MMIO memory region. Parameters
Returned values N/A |
|
Purpose Write a sequence of 8-, 16- or 32-bit words from the buffer to an MMIO memory region. Parameters
Returned values N/A Additional information The functions can be used if the processor has an x86 or x86-64 architecture. |
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:
- 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.
- 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 separateKnRegisterIrq()
function calls. In this case, theKnIoAttachIrq()
function must be called with the same flags in theflags
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:
- Adding information about the failure or success of interrupt processing to an IPC message by calling the
IoSetIrqAnswer()
function. - Sending the IPC message to the kernel and receiving an IPC message from the kernel.
To complete this step, call the
Call()
functions. In thehandle
parameter, you must specify the handle that was received when theKnIoAttachIrq()
function was called. You must use themsgOut
parameter to define the IPC message that will be sent to the kernel, and use themsgIn
parameter to define the IPC message that will be received from the kernel. - Extracting a request from the IPC message received from the kernel by calling the
IoGetIrqRequest()
function. - 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:
- Detach the thread from the interrupt.
To complete this step, call the
KnThreadDetachIrq()
function. - Close the handle that was received when the
KnIoAttachIrq()
function was called.To complete this step, call the
KnHandleClose()
function. (TheKnHandleClose()
function is declared in the header filesysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK.) - Close or revoke each interrupt handle in all processes that own these handles.
To complete this step, use the
KnHandleClose()
and/orKnHandleRevoke()
functions that are declared in the header filesysroot-*-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 |
---|---|
|
Purpose Registers an interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Attaches the calling thread to an interrupt. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Sends a request to a thread. When this request is fulfilled, the thread must detach from the interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Allows (unmasks) an interrupt. Parameters
Returned values If successful, the function returns |
|
Purpose Denies (masks) an interrupt. Parameters
Returned values If successful, the function returns |
Managing threads (high-level API thread.h)
The API is defined in the header file sysroot-*-kos/include/kos/thread.h
from the KasperskyOS SDK.
Main capabilities of the API:
- Create, terminate, and lock threads.
- Resume execution of locked threads.
- Register functions that are called when creating and terminating threads.
Information about API functions is provided in the table below.
The libkos
library also provides a low-level API for thread management. This API is defined in the header file sysroot-*-kos/include/coresrv/thread/thread_api.h
from the KasperskyOS SDK. The low-level API should be used only if the high-level API does not have sufficient capabilities.
Creating threads
To create a thread, call the KosThreadCreate()
or KosThreadCreateDetached()
function. These functions create a standard thread with priority ranging from 0–15. (For details about standard threads, see Managing threads (low-level API thread_api.h)).
Terminating threads
When a thread is terminated, it is permanently deleted from the scheduler. A thread is terminated in the following cases:
- The function executed by the thread is exited.
The root function (not a nested function) performed by the thread must be exited by the
return
operator. - The
KosThreadTerminate()
orKosThreadExit()
function is called.The
KosThreadExit()
function terminates the thread even if the function is called from a nested function instead of the root function executed by the thread. - The process was terminated or switched to the "frozen" state.
For details about the "frozen" state, see Managing processes (low-level API task_api.h).
Thread exit codes are defined by the developer of the KasperskyOS-based solution. These codes should be specified in the exitCode
parameter of the KosThreadTerminate()
and KosThreadExit()
functions, and when calling the return
operator in the function executed by the thread. To get the thread exit code, call the KosThreadWait()
function. If successful, this function returns the thread exit code. Otherwise, it returns -1
. Therefore, thread exit codes must differ from -1
to avoid ambiguity.
Registering functions that are called when creating and terminating threads
To register a function that is called when threads of a process are created and terminated, call the KosThreadCallbackRegister()
function from this process.
The registered function is called in the following cases:
- A thread is created by calling the
KosThreadCreate()
function. - The thread that was created by calling the
KosThreadCreate()
function is terminated as a result of exiting a function executed by this thread. - The thread is terminated by calling the
KosThreadExit()
function.
You can register multiple functions, and each of them will be called when a thread is created and terminated. When a thread is created, the registered function is called with the KosThreadCallbackReasonCreate
argument in the reason
parameter. When the thread is terminated, the registered function is called with the KosThreadCallbackReasonDestroy
argument in the reason
parameter.
Guaranteeing that a function can only be called once
The callback function defined through the initRoutine
parameter is called only when the KosThreadOnce()
function is called for the first time. This does not occur on repeated calls of the KosThreadOnce()
function (even from other threads), and the KosThreadOnce()
function simply returns control. For example, this ensures that a driver is initialized only once when multiple software components use this driver and start its initialization irrespective of each other.
Special considerations for threads attached to interrupts
After a thread is attached to an interrupt, the thread becomes a real-time thread with a FIFO scheduler class and a priority higher than 31. (For details about attaching a thread to an interrupt, see Managing interrupt processing (irq.h). For details about the FIFO real-time thread scheduler class, see Managing threads (low-level API thread_api.h)).
Many API functions cannot be applied for threads that are attached to interrupts. These functions include KosThreadCreate()
, KosThreadSuspend()
, KosThreadResume()
, KosThreadTerminate()
, KosThreadWait()
, KosThreadSleep()
, and KosThreadYield()
.
Defining and getting the base address of the TLS for threads
A thread local storage (TLS) is process memory in which a thread can store data in isolation from other threads. The base address for TLS is defined and received by the KosThreadTlsSet()
and KosThreadTlsGet()
functions, respectively. These functions are intended for use by the libc
library.
Getting the base address and stack limit of threads
The KosThreadGetStack()
function gets the base address and stack limit of the thread. This function is intended for use by the libc
library.
Freeing resources of terminated threads
Resources of a thread include its stack, context, and TCB (for details about a TCB, see Managing threads (low-level API thread_api.h)). To release thread resources after the thread terminates, call the KosThreadWait()
function to wait for completion of the thread. (Exceptions to this case are the initial thread of a process and a thread created by the KosThreadCreateDetached()
function.) Thread resources are also released upon termination of the process that includes these threads.
Information about API functions
thread.h functions
Function |
Information about the function |
---|---|
|
Purpose Registers the function that is called when creating and terminating threads of the calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Deregisters the function that is called when creating and terminating threads of the calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Creates an unlocked thread and closes its handle. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the ID (TID) of the calling thread. Parameters N/A Returned values Thread ID. The ID type is defined in the header file |
|
Purpose Locks the calling thread. Parameters
Returned values If successful, the function returns |
|
Purpose Resumes execution of a locked thread. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates the calling thread. Parameters
Returned values N/A |
|
Purpose Locks the calling thread until the defined thread is terminated. Parameters
Returned values If successful, the function returns the thread exit code, otherwise it returns |
|
Purpose Locks the calling thread for the specified duration. Parameters
Returned values If successful, the function returns |
|
Purpose Gives up the time slice of the calling thread to the next thread in the queue. Parameters N/A Returned values N/A Additional information The |
|
Purpose Terminates a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the base address of the thread local storage (TLS) for the calling thread. Parameters N/A Returned values Pointer to the TLS, or |
|
Purpose Defines the base address of the thread local storage (TLS) for the calling thread. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the base address and stack limit of the thread. Parameters
Returned values Pointer to the thread stack. |
|
Purpose Guarantees that the defined function will be called only once. Parameters
Returned values If successful, the function returns |
Managing threads (low-level API thread_api.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/thread/thread_api.h
from the KasperskyOS SDK.
Main capabilities of the API:
- Create, terminate, and lock threads.
- Resume execution of locked threads.
- Change the priorities and scheduler classes of threads.
- Handle exceptions.
- Detach threads from interrupts.
- Attach threads to processors (processor cores).
API functions that take the thread ID (TID) as input or return it are obsolete and will be removed in the future. These functions are not described in this section.
Information about API functions is provided in the table below.
The libkos
library also provides a high-level API for thread management. This API is defined in the header file sysroot-*-kos/include/kos/thread.h
from the KasperskyOS SDK. It is recommended to use this specific API. The low-level API should be used only if the high-level API does not have sufficient capabilities.
Creating threads. Thread scheduler classes
To create a thread, call the KnThreadCreateByHandle()
function. You can use the flags
parameter of this function for the created thread to define the following scheduler classes:
- Class for scheduling standard threads.
- Class for scheduling FIFO real-time threads (First In, First Out).
- Class for scheduling RR real-time threads Round-Robin).
The priority of a standard thread can take values ranging from 0 to 15. The higher the priority of a standard thread, the larger the time slice allocated to this thread and the more frequently these time slices are allocated. One standard thread with a high priority can occupy multiple consecutive places in the thread queue. Standard threads cannot supersede other standard threads or real-time threads, regardless of their respective priorities. If a real-time thread appears in the queue, the current standard thread immediately transfers control to the real-time thread. If there are no real-time threads in the queue, the current standard thread transfers control to the next standard thread if the current one is terminated, becomes locked, uses up its time slice or gives it up to the next standard thread.
The priority of a real-time thread can take values ranging from 0 to 31. Higher-priority real-time threads supersede lower-priority real-time threads. Real-time threads also supersede standard threads, regardless of their respective priorities. Control is transferred from the current real-time thread to the next real-time thread in the queue in the following cases:
- The current real-time thread with a FIFO scheduler class is terminated or locked, or a real-time thread with a higher priority appears in the queue.
Until the real-time thread with a FIFO scheduler class is terminated, locked, or superseded by a real-time thread with a higher priority, it can retain control indefinitely and not transfer control to the next real-time thread.
- The current real-time thread with an RR scheduler class is terminated, locked or uses up its time slice, or a real-time thread with a higher priority appears in the queue.
A real-time thread with an RR scheduler class that is not terminated, locked, or superseded by a higher-priority real-time thread transfers control to the next one upon expiry of its time slice if the next one is a real-time thread with the same priority. Otherwise, it transfers control to itself again.
- The current real-time thread has given up its time slice to the next real-time thread with the same priority in the queue.
A real-time thread can give up its time slice to the next one in the queue if the next one is a real-time thread with the same priority. Otherwise, it transfers control to itself again.
Execution of lower-priority real-time threads begins only after each of the real-time threads with a higher priority is terminated or locked. Real-time threads with the same priority form a queue based on the FIFO principle.
The thread scheduler class can be changed by calling the KnThreadSetSchedPolicyByHandle()
function. This function also changes the size of the time slice allocated to a real-time thread with an RR scheduler class. This value is the only parameter of a real-time thread RR scheduler class that has a default value of 10 ms but can take values ranging from 2 ms to 100 ms. The scheduler class of standard threads and the scheduler class of FIFO real-time threads do not have parameters.
The priority of a thread can be changed by calling the KnThreadSetSchedPolicyByHandle()
or KnThreadSetPriorityByHandle()
function.
After a thread is attached to an interrupt, the thread becomes a real-time thread with a FIFO scheduler class and a priority higher than 31, irrespective of the specific scheduler class and priority this thread had before it was attached to the interrupt. Managing interrupt processing (irq.h).)
Many API functions cannot be applied for threads that are attached to interrupts. These functions include KnThreadCreateByHandle()
, KnThreadSetPriorityByHandle()
, KnThreadSuspendCurrent()
, KnThreadResumeByHandle()
, KnThreadTerminateByHandle()
, KnThreadWaitByHandle()
, KnSleep()
, and KnThreadSetSchedPolicyByHandle()
.
Creating thread handles
A thread handle is created whenever a thread is created by the KnThreadCreateByHandle()
function call. A thread (including the initial thread) can also create its own handle by calling the KnThreadOpenCurrent()
function.
A thread handle cannot be transferred to another process via IPC.
Handling exceptions
To register an exception handling function for a thread, call the KnThreadSetExceptionHandler()
function. This function deregisters the previous exception handling function and returns its ID. By saving this ID, you can subsequently register the previous exception handling function again.
To get information about the last exception of a thread, call the KnThreadGetLastException()
function in the exception handler.
Detaching threads from interrupts
To detach a thread from an interrupt, call the KnThreadDetachIrq()
function. (For more details about using the KnThreadDetachIrq()
function, see Managing interrupt processing (irq.h).)
After a thread is detached from an interrupt, the thread receives the scheduler class and priority that it had before it was attached to the interrupt.
Attaching threads to processors (processor cores)
To restrict the set of processors (processor cores) that can be used to execute a thread, define an affinity mask for this thread. An affinity mask is a bit mask indicating which specific processors or processor cores must execute the thread.
To create, adjust, and perform other operations with affinity masks, use the API that is defined in the header file sysroot-*-kos/include/rtl/cpuset.h
from the KasperskyOS SDK.
To define a thread affinity mask, call the KnThreadSetAffinityByHandle()
function.
Terminating threads
When a thread is terminated, it is permanently deleted from the scheduler. A thread is terminated in the following cases:
- The function executed by the thread is exited.
The root function (not a nested function) performed by the thread must be exited by the
return
operator. - The
KnThreadTerminateByHandle()
orKnThreadExit()
function is called.The
KnThreadExit()
function terminates the thread even if the function is called from a nested function instead of the root function executed by the thread. - The process was terminated or switched to the "frozen" state.
For details about the "frozen" state, see Managing processes (low-level API task_api.h).
Thread exit codes are defined by the developer of the KasperskyOS-based solution. These codes should be specified in the code
parameter of the KnThreadTerminateByHandle()
and KnThreadExit()
functions, and when calling the return
operator in the function executed by the thread. To get the thread exit code, call the KnThreadWaitByHandle()
function.
Getting the address of the TCB and defining the base address of a TLS for threads
A thread control block (TCB) is a structure containing thread information that is used by the kernel to manage this specific thread. The type of structure is defined in the header file sysroot-*-kos/include/thread/tcbpage.h
from the KasperskyOS SDK. A TCB contains the base address of the thread local storage (TLS). The KnThreadGetTcb()
function gets the TCB address. The KnThreadSetTls()
function defines the base address of the TLS. These functions are intended for use by the libc
library.
Getting thread information
The KnThreadGetInfoByHandle()
function gets the base address and stack limit of the thread, and the thread identifier (TID). This function is intended for use by the libc
library.
Freeing resources of terminated threads
Resources of a thread include its stack, context, and TCB. To free the resources of a thread after the thread is terminated, you need to close its handle before or after termination of this thread by calling the KnHandleClose()
function, which is declared in the header file sysroot-*-kos/include/coresrv/handle/handle_api.h
from the KasperskyOS SDK (an exception to this case is the initial thread of a process whose resources are freed without calling the KnHandleClose()
function if the initial thread did not create its own handle by calling the KnThreadOpenCurrent()
function). Resources of threads are also freed upon termination of the process that includes these threads.
Information about API functions
thread_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a thread. Parameters
Returned values If successful, the function returns Additional information In the
A standard thread is created by default. Handles of threads cannot be transferred between processes via IPC. |
|
Purpose Creates the handle of the calling thread. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the priority of a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Defines the priority of a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Locks the calling thread. Parameters N/A Returned values If successful, the function returns |
|
Purpose Resumes execution of a locked thread. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates the calling thread. Parameters
Returned values Error code. |
|
Purpose Gets information about a thread. Parameters
Returned values If successful, the function returns |
|
Purpose Locks the calling thread until the defined thread is terminated. Parameters
Returned values If successful, the function returns |
|
Purpose Locks the calling thread for the specified duration. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the address of the thread control block (TCB) for the calling thread. Parameters N/A Returned values Pointer to the TCB. The data type for TCB storage is defined in the header file |
|
Purpose Defines the base address of the thread local storage (TLS) for the calling thread. Parameters
Returned values If successful, the function returns |
|
Purpose Detaches the calling thread from the interrupt handled in its context. Parameters N/A Returned values If successful, the function returns |
|
Purpose Registers the exception handling function for the calling thread. Parameters
Returned values ID of the previously registered exception handling function if one exists, otherwise |
|
Purpose Gets information about the last exception of the calling thread. Parameters
Returned values N/A |
|
Purpose Gets a thread affinity mask. Parameters
Returned values If successful, the function returns |
|
Purpose Defines a thread affinity mask. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information about the thread scheduler class. Parameters
Returned values If successful, the function returns Additional information In the In the |
|
Purpose Defines the scheduler class and priority of the thread. Parameters
Returned values If successful, the function returns Additional information In the In the |
Managing processes (high-level API task.h)
The API is defined in the header file sysroot-*-kos/include/kos/task.h
from the KasperskyOS SDK.
The API lets you create, start and terminate processes, and statically create IPC channels and pass handles.
Information about API functions is provided in the table below.
The libkos
library also provides a low-level API for process management. This API is defined in the header file sysroot-*-kos/include/coresrv/task/task_api.h
from the KasperskyOS SDK. The low-level API should be used only if the high-level API does not have sufficient capabilities.
Creating processes
To create a process, call one of the following functions:
KosTaskInitEx()
KosTaskInit()
KosTaskInitFromSegEx()
KosTaskInitFromSeg()
KosTaskLaunch()
Using the params
parameter, these functions receive parameters of the created process via a structure containing the following fields:
eiid
– pointer to the process class name.endpointsCount
– number of provided endpoints.The field can have a zero value if the process does not provide endpoints.
endpoints
– pointer to the array of structures containing the characteristics of provided endpoints (names and RIIDs of endpoints, names of interfaces).The type of structure is defined in the header file
sysroot-*-kos/include/services/handle/if_connection.h
from the KasperskyOS SDK.The field can have the
RTL_NULL
value if the process does not provide endpoints.args
– pointer to the array of program startup parameters.The
RTL_NULL
value must be the last element of the array.envs
– pointer to the array of environment variables of the program.The
RTL_NULL
value must be the last element of the array.flags
:KOS_TASK_FLAGS_NONE
– no flags.KOS_TASK_FLAG_DUMPABLE
– the process switches to the "frozen" state as a result of an unhandled exception.For details about the "frozen" state, see Managing processes (low-level API task_api.h).
componentTree
– pointer to the structure containing information from the formal specification of the solution component.The type of structure is defined in the header file
sysroot-*-kos/include/nk/types.h
from the KasperskyOS SDK. This structure is an element of automatically generated transport code.If the field has a value other than
RTL_NULL
, the values in theeiid
,endpointsCount
andendpoints
fields will be ignored, and the process class name and parameters of the provided endpoints (including the parameters of endpoints of embedded components) will be taken from the formal specification of the solution component.
The KosTaskInitEx()
, KosTaskInit()
, KosTaskInitFromSegEx()
and KosTaskInitFromSeg()
functions use the outTask
output parameter to pass the pointer to the address of the object describing the child process. This object is a structure that is created in the memory of the parent process and the child process. The developer of a KasperskyOS-based solution does not need to perform operations with the fields of this structure. However, the pointer to this structure must be used as the process ID when calling API functions. A child process can get the address of the object describing it by calling the KosTaskGetSelf()
function.
If statically created IPC channels are used for access to endpoints provided by a server process, the object describing this server process must be linked to the structures containing information about endpoints from the formal specification of the solution component. This is necessary so that client processes can receive information about endpoints provided by the server process when creating a static IPC channel. To link an object describing a child server process to structures containing information about endpoints from the formal specification of the solution component, this information must be passed through the componentTree
field of the params
parameter when calling the KosTaskInit*()
functions or KosTaskLaunch()
function. A server process that is already running can link the object describing it to structures containing information about endpoints from the formal specification of the solution component by calling the KosTaskSetComponentTree()
function. This is required if the running server process does not have a parent process.
When the KosTaskInitEx()
, KosTaskInit()
or KosTaskLaunch()
function is called, the ELF image from the defined executable file in ROMFS is loaded into the memory of the created process. If the ELF image contains the symbol table .symtab
and string table .strtab
, they are loaded into process memory. Using these tables, the kernel receives the names of functions for generating stack backtrace data (call stack information).
To get information about the ELF image loaded into process memory, call the KosTaskGetElfSegs()
function.
When the KosTaskInit()
or KosTaskLaunch()
function is called, one of the following values is used as the process name and executable file name:
- Value of the
eiid
field, if the value of thecomponentTree
field is equal toRTL_NULL
. - Name of the process class from the formal specification of the solution component, if the
componentTree
field value is different fromRTL_NULL
.
Likewise, these values are applied as the process name and/or executable file name if you call the KosTaskInitEx()
or KosTaskInitFromSegEx()
function with the RTL_NULL
value in the name
parameter and/or the path
parameter. These values are also applied as the process name if you call the KosTaskInitFromSeg()
function with the RTL_NULL
value in the name
parameter.
To use the KosTaskInitFromSegEx()
and KosTaskInitFromSeg()
functions, MDL buffers containing the ELF image segments must be created in advance. You must use the segs
parameter to define the ELF image segments to be loaded into the memory of the created process.
You must use the entry
and relocBase
parameters of the KosTaskInitFromSegEx()
function to define the program entry point and the ELF image load offset, respectively. The program entry point is the sum of the address specified in the e_entry
field of the ELF image header and the ELF image load offset. The KnElfCreateVmSegEx()
function declared in the header file sysroot-*-kos/include/coresrv/elf/elf_api.h
from the KasperskyOS SDK generates a random offset for loading the ELF image and calculates the address of the program entry point according to this offset. (The ELF image load offset must be a random value to ensure ASLR support. For details about ASLR, see Managing processes (low-level API task_api.h).)
You can use the KosTaskInitFromSegEx()
function to load the symbol table .symtab
, string table .strtab
and ELF image header into the memory of the created process. The ELF image header should be loaded if data from this header must be available in the created process.
Data passed to the KosTaskInitFromSegEx()
function via the segs
, entry
, and relocBase
parameters and the parameters associated with loading the symbol table .symtab
and string table .strtab
are prepared by the KnElfCreateVmSegEx()
function declared in the header file sysroot-*-kos/include/coresrv/elf/elf_api.h
from the KasperskyOS SDK.
The KosTaskInitFromSeg()
function is a simplified version of the KosTaskInitFromSegEx()
function and does not let you load the symbol table .symtab
, string table .strtab
and ELF image header into process memory, and does not let you define the ELF image load offset (instead, it sets a null offset).
The KosTaskLaunch()
function creates and immediately starts a process without the capability to statically create IPC channels.
Statically created IPC channels
Before starting processes, you can create IPC channels between them. You can create multiple IPC channels with different names between one client process and one server process. You can create IPC channels with the same name between one server process and multiple client processes.
To create an IPC channel with a name matching the name of a server process class, call the KosTaskConnect()
function.
To create an IPC channel with a defined name, call the KosTaskConnectToService()
function.
To use the created IPC channel, you need to get the client IPC handle on the client process side by calling the ServiceLocatorConnect()
function. On the server process side, you need to get the server IPC handle by calling the ServiceLocatorRegister()
function. These functions use the channelName
parameter to receive the name of the IPC channel. (The ServiceLocatorConnect()
and ServiceLocatorRegister()
functions are declared in the header file sysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK.)
Transferring handles
A parent process can transfer one or more handles to a child process that is not yet running. (General information about transferring handles is provided in the Transferring handles section.)
To pass a handle to a child process, call the KosTaskTransferResource()
function while specifying the handle of the resource transfer context object, the permissions mask, and the conditional name of the descendant of the transferred handle in addition to the other parameters.
To find the descendant of a handle transferred by a parent process, call the KosTaskLookupResource()
function while specifying the conditional name of the descendant of the handle that was defined by the parent process when calling the KosTaskTransferResource()
function.
Starting processes
To create and immediately start a process without statically creating IPC channels, call the KosTaskLaunch()
function.
To start an already created process for which you can create the necessary IPC channels before starting this process, call the KosTaskRunEx()
or KosTaskRun()
function.
Use the fsBackend
parameter of the KosTaskRunEx()
function to specify whether to use the kernel or the system program fsusr
to support the ROMFS file system for the started process. Use of the fsusr
program ensures that the ROMFS image resides in the user space. The user space can host a significantly larger ROMFS image than the kernel space.
To enable the use of a ROMFS file system residing in the user space, include the fsusr
program into the KasperskyOS-based solution and create an IPC channel named kl.core.FSUsr
from the process that needs to use this file system to the process named kl.core.FSUsr
. (The client portion of the fsusr
program is included in the libkos
library.) To check whether the fsusr
program is included in your KasperskyOS SDK, verify that the sysroot-*-kos/bin/fsusr
executable file is available.
To specify how to support the file system in an already running process, call the KosTaskSetSelfFSBackend()
function. This function can be used as follows. The parent process indicates that the fsusr
program supports its ROMFS file system, and loads the required ROMFS image by calling the KnFsChange()
function. (The KnFsChange()
function is declared in the header file sysroot-*-kos/include/coresrv/fs/fs_api.h
from the KasperskyOS SDK.) Then the parent process starts the child process by calling the KosTaskRunEx()
function and specifies that the ROMFS file system for the child process is supported by the fsusr
program. As a result, the child process will use the ROMFS image that is placed in the user space by the parent process by default.
If the parent process does not need to terminate the child process or wait for its termination, the object describing the child process must be deleted and the counter for links to it must be reset by using the KosTaskPut()
function after the child process is started. The KosTaskLaunch()
function calls the KosTaskPut()
function after the child process is started.
Terminating processes
The API terminates and waits for termination of child processes.
To terminate a child process, call the KosTaskStop()
or KosTaskStopAndWait()
function.
To wait for a child process to terminate on its own initiative, call the KosTaskWait()
function.
To ensure that the kernel object describing a child process is deleted after this process is terminated, its handle in the parent process must be closed before or after termination of the child process. The handle of the child process is closed when the object describing the child process is deleted from the memory of the parent process. To delete an object describing a child process, reset the counter for links to this object by using the KosTaskPut()
function. The KosTaskLaunch()
function calls the KosTaskPut()
function after the child process is started.
For details about terminating processes, see Managing processes (low-level API task_api.h).
Information about API functions
task.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information about the ELF image loaded in process memory. Parameters
Returned values If successful, the function returns |
|
Purpose Creates an IPC channel. Parameters
Returned values If successful, the function returns |
|
Purpose Creates an IPC channel with the defined name. Parameters
Returned values If successful, the function returns |
|
Purpose Defines the program startup parameters. Parameters
Returned values If successful, the function returns Additional information The function is used by the |
|
Purpose Gets the program startup parameters. Parameters
Returned values If successful, the function returns Additional information To delete an array of program startup parameters, call the |
|
Purpose Deletes the array of program startup parameters that was received from the Parameters
Returned values N/A |
|
Purpose Defines environment variables of a program. Parameters
Returned values If successful, the function returns Additional information The function is used by the |
|
Purpose Gets the environment variables of a program. Parameters
Returned values If successful, the function returns Additional information To delete an array of program environment variables, call the |
|
Purpose Deletes the array of program environment variables that was received from calling the Parameters
Returned values N/A |
|
Purpose Starts a process. Parameters
Returned values If successful, the function returns |
|
Purpose Starts a process. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates a process and waits for the termination of this process. Parameters
Returned values If successful, the function returns |
|
Purpose Waits for the termination of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the process handle. Parameters
Returned values Process handle. |
|
Purpose Creates and starts a process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the address of an object that describes a calling process. Parameters N/A Returned values Pointer to the object describing the process, or Additional information The function adds 1 to the counter for links to the object describing the process, therefore the |
|
Purpose Subtracts 1 from the counter for links to the object describing the process. Parameters
Returned values N/A |
|
Purpose Registers endpoints. Parameters
Returned values If successful, the function returns Additional information The function is used by the |
|
Purpose Defines whether the ROMFS file system for a calling process is supported by the kernel or by a system program. Parameters
Returned values N/A |
|
Purpose Gets information indicating whether the ROMFS file system for a calling process is supported by the kernel or by a system program. Parameters N/A Returned values Value indicating whether the ROMFS file system is supported by the kernel or by a system program. |
|
Purpose Creates structures containing information about endpoints from the formal specification of the solution component and links these structures to the object describing the process. Parameters
Returned values If successful, the function returns |
|
Purpose Transfers a handle to a process that is not yet running. Parameters
Returned values If successful, the function returns |
|
Purpose Searches for a handle based on the name defined by the Parameters
Returned values If successful, it returns the handle. Otherwise, it returns |
Managing processes (low-level API task_api.h)
The API is defined in the header file sysroot-*-kos/include/coresrv/task/task_api.h
from the KasperskyOS SDK.
Main capabilities of the API:
- Create, start, and terminate processes.
- Handle exceptions.
- Get information about processes, including information about the reasons for their termination.
- Define priorities and scheduler classes for initial threads of processes.
Information about API functions is provided in the table below.
The libkos
library also provides a high-level API for process management. This API is defined in the header file sysroot-*-kos/include/kos/task.h
from the KasperskyOS SDK. It is recommended to use this specific API. The low-level API should be used only if the high-level API does not have sufficient capabilities.
Creating and starting processes
To create a process, call the KnTaskCreate()
or KnTaskCreateEx()
function. These functions create an "empty" process, which is a process in whose memory the ELF image of the program is not loaded. Before starting this process, complete the following steps:
- Create a seed value by calling the
KosRandomGenerate()
function, which is declared in the header filesysroot-*-kos/include/kos/random/random_api.h
from the KasperskyOS SDK.This step is required for completion of the next step.
- Define the seed value by calling the
KnTaskReseedAslr()
function.This step is required for address space layout randomization. Address Space Layout Randomization (ASLR) is the use of random addresses for the location of data structures (ELF image, dynamic libraries, stack and heap) in process memory to make it harder to exploit vulnerabilities associated with a conventional process address space structure that is known by a hacker in advance.
The
KnTaskReseedAslr()
function defines the seed value of the random number generator that is used to automatically select the base address of an allocated virtual memory region in functions such asKnVmAllocate()
,KnPmmMdlMap()
,KnIoDmaMap()
, andKnTaskVmReserve()
. The stack and heap are created in the process and dynamic libraries are loaded into the process memory by the operating system using theKnVmAllocate()
function. Theaddr
parameter is set to zero so that the address of the allocated virtual memory region is selected automatically (as a random value). - Wipe the seed value created at step 1 from memory.
This step is required for security purposes. To complete this step, use the
RtlRandomMemSanitize()
function, which is declared in the header filesysroot-*-kos/include/rtl/random.h
from the KasperskyOS SDK. - Load ELF image segments into process memory by using the
KnTaskLoadSeg()
function.In the
loadAddr
field of theseg
parameter, specify the load address of the ELF image segment. To ensure ASLR support (as a supplement to step 2), the load address of the ELF image segment specified in the ELF file must be increased by the ELF image load offset. The ELF image load offset must be a random value. TheKnElfCreateVmSegEx()
function declared in the header filesysroot-*-kos/include/coresrv/elf/elf_api.h
from the KasperskyOS SDK generates the random offset for loading the ELF image and calculates the load addresses of ELF image segments according to this offset.As a result of this step, MDL buffers that contain ELF image segments will be mapped to virtual memory of the process.
- [Optional] Load the symbol table
.symtab
and string table.strtab
into process memory by calling theKnTaskLoadElfSyms()
function.The load addresses of ELF image segments containing the symbol table
.symtab
and string table.strtab
must be calculated just like the load addresses of other ELF image segments. This calculation is performed by theKnElfCreateVmSegEx()
function declared in the header filesysroot-*-kos/include/coresrv/elf/elf_api.h
from the KasperskyOS SDK.The kernel uses the symbol table
.symtab
and string table.strtab
to get the names of functions for generating stack backtrace data (call stack information). - Define the program entry point and the ELF image load offset by calling the
KnTaskSetInitialState()
function.The program entry point is the sum of the address specified in the
e_entry
field of the ELF image header and the ELF image load offset. TheKnElfCreateVmSegEx()
function declared in the header filesysroot-*-kos/include/coresrv/elf/elf_api.h
from the KasperskyOS SDK generates a random offset for loading the ELF image and calculates the address of the program entry point according to this offset. - [Optional] Load the ELF image header into process memory by calling the
KnTaskSetElfHdr()
function.This step must be completed if data from the ELF image header must be available in the created process.
The handle of a process can be transferred to another process via IPC.
By default, the initial thread of a process is a standard thread whose priority can take values ranging from 0 to 15. (For details about thread scheduler classes, see Managing threads (low-level API thread_api.h).) To change the scheduler class and/or priority of the initial thread of a process, call the KnTaskSetInitialPolicy()
function. To change the priority of the initial thread of a process, call the KnTaskSetInitialThreadPriority()
function. The KnTaskSetInitialPolicy()
and KnTaskSetInitialThreadPriority()
functions can be used after the program entry point is defined. These functions can also be used after starting the process.
To start the process, call the KnTaskResume()
function. A running process cannot be stopped.
Before a process is started, it receives data from its parent process via a static connection page. A static connection page (SCP) is a set of structures containing data for statically creating IPC channels, startup parameters, and environment variables of a program. A parent process writes data to the SCP of a child process by calling the KnTaskSetEnv()
function. When a child process is started, it reads data from the SCP by calling the KnTaskGetEnv()
function, then it deletes the SCP by calling the KnTaskFreeEnv()
function. All three functions do not need to be explicitly called because their calls are made by the libkos
library.
Terminating processes
Process termination includes the following:
- Terminating all threads of the process.
- Freeing the memory of the process.
- Freeing system resources and user resources that are exclusively owned by the process.
When a process is terminated, all the handles that it owns are closed. If a closed handle was the only handle of a resource, this resource is freed.
A process can be terminated for the following reasons:
- On its own initiative.
The
KnTaskExit()
function is called, or all threads of the process are terminated. - By external request.
The
KnTaskTerminate()
function is called. - As a result of an unhandled exception (crash).
By calling the
KnTaskPanic()
function, a process can purposefully initiate an exception that cannot be handled and leads to the process terminating. This assures that the process can terminate itself. (For example, if the process contains threads attached to interrupts, theKnTaskExit()
cannot terminate the process, while theKnTaskPanic()
function can.)
The exit codes of processes are defined by the developer of the KasperskyOS-based solution. These codes must be specified in the status
parameter of the KnTaskExit()
function. If a process was terminated due to the termination of all its threads, the exit code of this process will be the exit code of its initial thread. To get the exit code of a process that was terminated on its own initiative, call the KnTaskGetExitCode()
function.
To get information regarding the reason for process termination, call the KnTaskGetExitStatus()
function. This function determines whether a process was terminated on its own initiative, by external request, or unexpectedly.
To get information about an unhandled exception that led to an unexpected termination of a process, call the KnTaskGetExceptionInfo()
function. This information includes the exception code and the context of the thread in which this exception occurred.
If the process was created by calling the KnTaskCreateEx()
function with the TaskExceptionFreezesTask
flag in the flags
parameter, an unhandled exception will cause this process to switch to a "frozen" state instead of terminating. When a process is switched to a "frozen" state, its threads are terminated as a result of the unhandled exception but its resources are not freed, which means that you can get information about this process. To get the context of a thread that is part of a frozen process, call the KnTaskGetThreadContext()
function. To get information about the virtual memory region that belongs to a frozen process, call the KnTaskGetNextVmRegion()
function. This information includes the base address and size of the virtual memory region, and the access rights to this virtual memory region. Before a process switches to the frozen state, stack backtrace data (call stack information) is printed for the thread in which the unhandled exception occurred. To terminate a frozen process, call the KnTaskTerminateAfterFreezing()
function.
To ensure that the kernel object describing a process is deleted after this process is terminated, each of its handles must be closed or revoked in all processes that own these handles before or after termination of this process. To do so, 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.
Handling exceptions
To register an exception handling function, call the KnTaskSetExceptionHandler()
function. This function deregisters the previous exception handling function and returns its ID. By saving this ID, you can subsequently register the previous exception handling function again.
An exception handling function is called when an exception occurs in any process thread. If the exception is successfully handled, this function returns a value other than zero. Otherwise it returns zero. The input parameter of the exception handling function is a structure containing information about the exception. The type of this structure is defined in the header file sysroot-*-kos/include/thread/tcbpage.h
from the KasperskyOS SDK.
If an exception handling function registered at the process level failed to successfully handle an exception, the exception handling function registered at the level of the thread in which the exception occurred will be called. (For details about handling exceptions at the thread level, see Managing threads (low-level API thread_api.h)).
Reserving memory in a child process
The API includes the KnTaskVmReserve()
and KnTaskVmFree()
functions, which reserve and free virtual memory regions, respectively, in a child process that does not yet have a defined program entry point. These functions comprise only a part of the functionality intended to increase control of the parent process over the virtual address space of a child process. This functionality is currently under development.
Getting the GSI address
Global system information (GSI) is a structure containing system information, such as the timer count since the kernel started, the timer counts per second, the number of processors (processor cores) in active state, and processor cache data. The type of structure is defined in the header file sysroot-*-kos/include/task/pcbpage.h
from the KasperskyOS SDK. The KnTaskGetGsi()
function gets the GSI address. This function is intended for use by the libc
library.
Getting the PCB address
A process control block (PCB) is a structure containing process information that is used by the kernel to manage this process. The type of structure is defined in the header file sysroot-*-kos/include/task/pcbpage.h
from the KasperskyOS SDK. The KnTaskGetPcb()
function gets the PCB address. This function is intended for use by the libc
library.
Information about API functions
task_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Creates a process. Parameters
Returned values If successful, the function returns Additional information In the
|
|
Purpose Gets the GSI address for the calling process. Parameters N/A Returned values Pointer to the GSI. The data type for GSI storage is defined in the header file |
|
Purpose Gets the address of the process control block (PCB) for the calling process. Parameters N/A Returned values Pointer to the PCB. The data type for PCB storage is defined in the header file |
|
Purpose Gets the SCP address of the calling process. Parameters
Returned values Pointer to the SCP, or |
|
Purpose Writes data to the SCP of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Deletes the SCP of the calling process. Parameters N/A Returned values If successful, the function returns |
|
Purpose Writes the ELF image header to the PCB of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Starts a process. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates the calling process. Parameters
Returned values Error code. Additional information Does not terminate the process as long as it contains threads attached to interrupts. |
|
Purpose Terminates a process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information about the reason for process termination. Parameters
Returned values If successful, the function returns Additional information You can use the
|
|
Purpose Gets information about an unhandled exception that led to an unexpected termination of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the context of a thread that is part of a frozen process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information about the virtual memory region that belongs to a frozen process. Parameters
Returned values If successful, the function returns |
|
Purpose Terminates a frozen process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the exit code of a process that terminated on its own initiative. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the process ID (PID) for the calling process. Parameters N/A Returned values Process ID. The ID type is defined in the header file |
|
Purpose Gets the name of a calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the name of the executable file (in ROMFS) that was used to create the calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the priority of the initial thread of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Defines the priority of the initial thread of a process. Parameters
Returned values If successful, the function returns |
|
Purpose Registers the exception handling function for the calling process. Parameters
Returned values ID of the previously registered exception handling function if one exists, otherwise |
|
Purpose Loads an ELF image segment into process memory. Parameters
Returned values If successful, the function returns |
|
Purpose Reserves a virtual memory region in a process. Parameters
Returned values If successful, the function returns Additional information In the
Permissible combinations of flags defining the access rights to the virtual memory region:
|
|
Purpose Frees the virtual memory region that was reserved by calling the Parameters
Returned values If successful, the function returns |
|
Purpose Defines the program entry point and the ELF image load offset. Parameters
Returned values If successful, the function returns |
|
Purpose Loads the symbol table Parameters
Returned values If successful, the function returns |
|
Purpose Defines the scheduler class and priority of the initial thread of a process. Parameters
Returned values If successful, the function returns Additional information In the In the |
|
Purpose Defines the seed value for ASLR support in the defined process. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the addresses and sizes of the symbol table Parameters
Returned values If successful, the function returns Additional information If the symbol table The function is intended for a mechanism that displays stack backtrace data and runs in a process and not in the kernel. This mechanism is currently under development. |
|
Purpose Gets the process ID (PID). Parameters
Returned values If successful, the function returns |
|
Purpose Initiates an exception that cannot be handled and leads to the process terminating. Parameters N/A Returned values N/A |
|
Purpose Transfers a handle to a process that is not yet running. Parameters
Returned values If successful, the function returns Additional information In contrast to the |
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:
- Initialize IPC transport on the client side and on the server side.
IPC transport is an add-on that works on top of system calls for sending and receiving IPC messages and works separately with the constant part and arena of IPC messages. Transport code works on top of this add-on.
- Start the loop for processing IPC requests on the server side.
- Copy data to the IPC message arena.
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
Operations.cdl
FileIface.idl
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:
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:
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:
- Receive an IPC request.
- Process the IPC request.
- 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()
, NkKosDoDispatch()
or NkKosDoDispatchEx()
function in which this loop is completed in its entirety. (The NkKosTransport_Dispatch()
function is declared in the header file transport-kos.h
, and the NkKosDoDispatch()
and NkKosDoDispatchEx()
functions are declared in the header file transport-kos-dispatch.h
.) Use of the NkKosDoDispatch()
or NkKosDoDispatchEx()
function is more convenient than the NkKosTransport_Dispatch()
function because they require fewer preparatory operations (for example, you do not need to initialize IPC transport).
You can initialize the structure passed to the NkKosDoDispatch()
or NkKosDoDispatchEx()
function via the info
parameter by using the macros defined in the header file transport-kos-dispatch.h
. You can use the syncHandle
parameter of the NkKosDoDispatchEx()
function to interrupt the system call Recv()
. You can use the cb
parameter of the NkKosDoDispatchEx()
function to define the callback functions that are called after completing the Recv()
and Reply()
system calls, and after exiting the IPC request processing loop (you do not need to define all functions). The callback function corresponding to the enterDispatchProcessing
pointer is called immediately after the Recv()
system call is completed. The callback function corresponding to the leaveDispatchProcessing
pointer is called immediately after the Reply()
system call is completed. The callback function corresponding to the stopDispatchLoop
pointer is called immediately after exiting the IPC request processing loop. The callbackContext
pointer is passed to each of these callback functions. These callback functions can be used to implement a thread pool, for example.
The NkKosTransport_Dispatch()
, NkKosDoDispatch()
, and NkKosDoDispatchEx()
functions can be called from parallel threads if you are using a thread-safe implementation of endpoint methods.
Example use of the NkKosDoDispatch()
function:
Example use of the NkKosTransport_Dispatch()
function:
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:
Information about API functions
transport-kos.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes IPC transport. Parameters
Returned values N/A |
|
Purpose Initializes IPC transport with support for interrupting the Parameters
Returned values N/A |
|
Purpose Defines the maximum lockout duration for Parameters
Returned values N/A |
|
Purpose Starts the IPC request processing loop. Parameters
Returned values If unsuccessful, it returns an error code. |
|
Purpose Reserves a segment of the arena and copies a string to this segment. Parameters
Returned values If successful, the function returns |
transport-kos-dispatch.h functions
Function |
Information about the function |
---|---|
|
Purpose Starts the IPC request processing loop. Parameters
Returned values N/A |
|
Purpose Starts the IPC request processing loop. Parameters
Returned values N/A |
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
Approve.idl
Fragment of the policy description in the example:
security.psl
Using the API
To initialize IPC transport for querying the security module, call the NkKosSecurityTransport_Init()
function.
Example use of the NkKosSecurityTransport_Init()
function:
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 |
---|---|
|
Purpose Initializes IPC transport for querying the Kaspersky Security Module through the security interface. Parameters
Returned values If successful, the function returns |
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:
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:
- 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 thecallback
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 specifiedsize
of bytes from the entropy source and writes this data to theoutput
buffer. If the function returnsrcOk
, 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. - 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 theseed
parameter must be guaranteed by the calling process. - 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.
- 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 thesize
parameter greater than zero and each successful call of theKosRandomGenerate()
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 theseed
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:
- Register at least one source of entropy by calling the
KosRandomRegisterSrc()
function. - Generate a sequence of random values by calling the
KosRandomGenerateEx()
function. - 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.
- 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 |
---|---|
|
Purpose Initializes the random number generator. Parameters
Returned values If successful, the function returns |
|
Purpose Generates a sequence of random byte values. Parameters
Returned values If successful, the function returns |
|
Purpose Generates a sequence of random byte values. Parameters
Returned values If successful, the function returns |
|
Purpose Registers an entropy source. Parameters
Returned values If successful, the function returns |
|
Purpose Deregisters the source of entropy. Parameters
Returned values If successful, the function returns |
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 |
---|---|
|
Purpose Gets the resolution of the system time source. Parameters
Returned values If successful, the function returns |
|
Purpose Sets the system time. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the system time. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the resolution of the source of monotonic time that has elapsed since the KasperskyOS kernel was started. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the monotonic time that has elapsed since the moment the KasperskyOS kernel was started. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the system time. Parameters
Returned values If successful, the function returns |
|
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. |
|
Purpose Starts gradual adjustment of the system time. Parameters
Returned values If successful, the function returns Additional information If a new adjustment is started before a previously running adjustment is finished, the previously running adjustment is interrupted. |
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 associated with (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 associated with resources consists of the following steps:
- Creating a notification receiver (KasperskyOS kernel object that collects notifications) by calling the
KnNoticeCreate()
function.The notification receiver ID is the pointer to the
KosObject
containing the handle of the notification receiver (for details aboutKosObjects
, see Using KosObjects (objects.h)). Destruction of this object will cause the destruction of the notification receiver. If you need to create a copy of the notification receiver ID (for example, for use in another thread), you must increment the counter of links to theKosObject
by calling theKosRefObject()
function to ensure that this object exists while the created copy of the ID exists. If the copy of the notification receiver ID is no longer needed, you must decrement the counter of links to theKosObject
by calling theKosPutObject()
function to make sure that this object is destroyed if there are no other links. - Adding "resource—event mask" entries to the notification receiver to configure it to get notifications about events associated with relevant resources.
To add a "resource—event mask" entry to the notification receiver, you need to call the
KnNoticeSubscribeToObject()
function. (TheOCAP_HANDLE_GET_EVENT
flag should be set in the handle permissions mask of the resource stated in theobject
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 theKnNoticeUnsubscribeFromObject()
function. To remove from the receiver a "resource—event mask" entry with a specific identifier, you need to call theKnNoticeUnsubscribeFromEvent()
function."Resource—event mask" entries can be added to the notification receiver or removed from it throughout the entire lifetime of this notification receiver.
- Extracting notifications from the receiver by using the
KnNoticeGetEvent()
orKnNoticeGetEventEx()
function.You can set the waiting time for notifications to appear in the receiver when calling the
KnNoticeGetEvent()
orKnNoticeGetEventEx()
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 that were added to the receiver during the waiting period.Threads that are locked while waiting for notifications to appear in the receiver will resume their execution and will not be locked upon subsequent calls of the
KnNoticeGetEvent()
orKnNoticeGetEventEx()
function if all "resource—event mask" entries are removed from this receiver by calling theKnNoticeDropAndWake()
function. If you add at least one "resource—event mask" entry to the notification receiver after calling theKnNoticeDropAndWake()
function, threads that get notifications from that receiver will be locked again onKnNoticeGetEvent()
orKnNoticeGetEventEx()
function calls for the defined waiting time as long as there are no notifications. If all "resource—event mask" entries are removed from the notification receiver by using theKnNoticeUnsubscribeFromObject()
and/orKnNoticeUnsubscribeFromEvent()
function, threads waiting for notifications to appear in the receiver will not resume until the timeout elapses, and they will be locked during the waiting period upon subsequent calls of theKnNoticeGetEvent()
orKnNoticeGetEventEx()
function. - Terminate operations with the notification receiver by calling the
KnNoticeRelease()
function.The
KnNoticeRelease()
function removes all "resource—event mask" entries from the notification receiver and makes it impossible to add new ones. Threads that are locked while waiting for notifications to appear in the receiver will resume their execution and will not be locked upon subsequent calls of theKnNoticeGetEvent()
orKnNoticeGetEventEx()
function. In addition, theKnNoticeRelease()
function decrements the counter of links to theKosObject
containing the handle of the notification receiver. If there are no other links to this object, the notification receiver will be destroyed. Otherwise, the notification receiver will exist until all other links are deleted.
To notify other processes and/or threads about events associated with 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 and associated with 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 |
---|---|
|
Purpose Creates a notification receiver. Parameters
Returned values If successful, the function returns |
|
Purpose Adds a "resource–event mask" entry to the notification receiver so that it can receive notifications about events associated with the defined resource and match the defined event mask. Parameters
Returned values If successful, the function returns |
|
Purpose Extracts notifications from the receiver. Parameters
Returned values If successful, the function returns If the time-out for notifications to appear in the receiver has elapsed, returns If all "resource—event mask" entries are removed from the notification receiver by calling the |
|
Purpose Extracts notifications from the receiver. Parameters
Returned values If successful, the function returns If the time-out for notifications to appear in the receiver has elapsed, returns If all "resource—event mask" entries are removed from the notification receiver by calling the |
|
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
Returned values If successful, the function returns Additional information Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver. |
|
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
Returned values If successful, the function returns Additional information Notifications that correspond to the removed "resource—event mask" entries will be removed from the receiver. |
|
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
Returned values If successful, the function returns |
|
Purpose Removes all "resource—event mask" entries from the specified notification receiver and makes it impossible to add new ones, resumes all threads waiting for notifications to appear in the specified receiver, and decrements the counter of links to the Parameters
Returned values If successful, the function returns |
|
Purpose Signals the occurrence of events that are related to the defined user resource and match the defined event mask. Parameters
Returned values If successful, the function returns |
Dynamically creating IPC channels (cm_api.h, ns_api.h)
APIs are defined in the following header files from the KasperskyOS SDK:
sysroot-*-kos/include/coresrv/cm/cm_api.h
sysroot-*-kos/include/coresrv/ns/ns_api.h
The APIs dynamically create 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:
- Connect to the name server by calling the
NsCreate()
function. - Publish the provided endpoints on the name server by using the
NsPublishService()
function.To unpublish an endpoint, call the
NsUnPublishService()
function. - 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 theKnCmListen()
function will provide the same result. A request is deleted from the queue when theKnCmAccept()
orKnCmDrop()
function is called. - 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 theINVALID_HANDLE
value in thelistener
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 theKnCmAccept()
function is called with theINVALID_HANDLE
value in thelistener
parameter. The second and subsequent IPC channels associated with the listener handle are created during the second and subsequent calls of theKnCmAccept()
function specifying the handle that was obtained during the first call.) In thelistener
parameter of theKnCmAccept()
function, you can specify the listener handle received using theKnHandleConnect()
,KnHandleConnectEx()
andKnHandleCreateListener()
functions from the API handle_api.h, and theServiceLocatorRegister()
function declared in the header filesysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK. In thersid
parameter of theKnCmAccept()
function, you must specify the endpoint ID (RIID), which is a constant in the automatically generated transport code (for example:FsDriver_operationsComp_fileOperations_iid
).
Dynamic creation of an IPC channel on the client side includes the following steps:
- Connect to the name server by calling the
NsCreate()
function. - 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. - 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.
To delete a connection to the name server, call the NsDestroy()
function.
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 |
---|---|
|
Purpose Creates a connection to a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Deletes a connection to the name server. Parameters
Returned values If successful, the function returns |
|
Purpose Publishes an endpoint on a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Unpublishes an endpoint on a name server. Parameters
Returned values If successful, the function returns |
|
Purpose Enumerates the endpoints published on a name server. Parameters
Returned values If successful, the function returns |
cm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Requests to create an IPC channel with a server for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Receives a client request to create an IPC channel for use of an endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Rejects a client request to create an IPC channel for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
|
Purpose Accepts a client request to create an IPC channel for use of the defined endpoint. Parameters
Returned values If successful, the function returns |
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:
- An event is initialized via the
KosEventInit()
function call. - 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()
orKosEventWaitTimeout()
function call. - The notifying threads change the state of the event via the
KosEventSet()
andKosEventReset()
function calls.
- The notified threads wait for the event to switch from non-signaling state to signaling state via the
Information about the API event functions is provided in the table below.
event.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes an event. The event is in a non-signaling state after it is initialized. Parameters
Returned values N/A |
|
Purpose Sets the event state to signaling. Parameters
Returned values N/A |
|
Purpose Sets the event state to non-signaling. Parameters
Returned values N/A |
|
Purpose Waits for the event to change its state from non-signaling to signaling. Parameters
Returned values N/A |
|
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
Returned values If successful, the function returns Returns |
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:
- A mutex is initialized via the
KosMutexInit()
orKosMutexInitEx()
function call. - The mutex is used by threads:
- The mutex is captured via the
KosMutexTryLock()
,KosMutexLock()
orKosMutexLockTimeout()
function call. - The mutex is freed via the
KosMutexUnlock()
function call.
- The mutex is captured via the
Information about the API mutex functions is provided in the table below.
mutex.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a non-recursive mutex. Parameters
Returned values N/A |
|
Purpose Initializes a mutex. Parameters
Returned values N/A |
|
Purpose Acquires the mutex. If the mutex is already acquired, returns control rather than waits for the mutex to be released. Parameters
Returned values If successful, the function returns If the mutex is already acquired, returns |
|
Purpose Acquires the mutex. If the mutex is already acquired, waits indefinitely for it to be released. Parameters
Returned values N/A |
|
Purpose Releases the mutex. Parameters
Returned values N/A |
|
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
Returned values If successful, the function returns Returns |
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:
- A read-write lock is initialized by the
KosRWLockInit()
function call. - The read-write lock is used by threads:
- The read-write lock is captured for write operations (via the
KosRWLockWrite()
orKosRWLockTryWrite()
function call) or for read operations (via theKosRWLockRead()
orKosRWLockTryRead()
function call). - The read-write lock is freed via the
KosRWLockUnlock()
function call.
- The read-write lock is captured for write operations (via the
Information about the API read-write lock functions is provided in the table below.
rwlock.h functions
Function |
Information about the function |
---|---|
|
Purpose Initializes a read-write lock. Parameters
Returned values N/A |
|
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, waits indefinitely for the lock to be released. Parameters
Returned values N/A |
|
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
Returned values If successful, the function returns |
|
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
Returned values N/A |
|
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
Returned values If successful, the function returns |
|
Purpose Releases the read-write lock. Parameters
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:
- A semaphore is initialized via the
KosSemaphoreInit()
function call. - The semaphore is used by threads:
- They wait for the semaphore via the
KosSemaphoreWait()
,KosSemaphoreWaitTimeout()
orKosSemaphoreTryWait()
function call. - The semaphore is signaled via the
KosSemaphoreSignal()
orKosSemaphoreSignalN()
function call.
- They wait for the semaphore via the
- 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 |
---|---|
|
Purpose Initializes a semaphore. Parameters
Returned values If successful, the function returns If the value of the |
|
Purpose Deallocates semaphore resources. Parameters
Returned values If successful, the function returns If there are threads waiting on the semaphore, returns |
|
Purpose Signals the semaphore and increases the counter by one. Parameters
Returned values If successful, the function returns |
|
Purpose Signals the semaphore and increases the counter by the specified number. Parameters
Returned values If successful, the function returns |
|
Purpose Waits on the semaphore for a period that does not exceed the specified time. Parameters
Returned values If successful, the function returns Returns |
|
Purpose Waits on the semaphore indefinitely. Parameters
Returned values If successful, the function returns |
|
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
Returned values If successful, the function returns If the semaphore counter has a zero value, returns |
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:
- The conditional variable and mutex are initialized.
To initialize a conditional variable, you need to call the
KosCondvarInit()
function. - The conditional variable and mutex are used by threads.
Use of a conditional variable and mutex by notified threads includes the following steps:
- The mutex is captured.
- Condition fulfillment is verified.
- The
KosCondvarWait()
orKosCondvarWaitTimeout()
function is called to wait for condition fulfillment.After the
KosCondvarWait()
orKosCondvarWaitTimeout()
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()> - The mutex is freed.
Use of a conditional variable and mutex by notifying threads includes the following steps:
- The mutex is captured.
- Condition fulfillment is verified.
- Fulfillment of the condition is signaled via the
KosCondvarSignal()
orKosCondvarBroadcast()
function call. - 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 |
---|---|
|
Purpose Initializes a conditional variable. Parameters
Returned values N/A |
|
Purpose Waits for condition fulfillment for a period that does not exceed the specified time. Parameters
Returned values Returns Returns |
|
Purpose Waits indefinitely for condition fulfillment. Parameters
Returned values If successful, the function returns |
|
Purpose Signals condition fulfillment to one of the threads waiting for it. Parameters
Returned values N/A |
|
Purpose Signals condition fulfillment to all threads waiting for it. Parameters
Returned values N/A |
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 IOMMU domain is associated with only one process, but multiple IOMMU domains can be associated with the same process. The capability to create multiple IOMMU domains associated with the same process allows you to restrict access to DMA buffers for different devices managed by the same process.
The API lets you create IOMMU domains, attach devices on a PCIe bus to IOMMU domains, and detach devices on the PCIe bus from IOMMU domains. 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()
and KnIoDmaBeginEx()
functions that are included in the dma.h API. The KnIoDmaBegin()
function associates a DMA buffer with an automatically created IOMMU domain. Each process may be associated with only one such IOMMU domain. This IOMMU domain is created after the first successful call of the KnIommuAttachDevice()
function and exists throughout the entire lifetime of the process. The KnIoDmaBeginEx()
function associates a DMA buffer with the IOMMU domain that was created by calling the KnIommuCreateDomain()
function. Each process may be associated with multiple IOMMU domains that were created by KnIommuCreateDomain()
function calls. These IOMMU domains may be removed prior to process termination by closing their handles.
To attach a device to an automatically created IOMMU domain or detach it, you must call the KnIommuAttachDevice()
or KnIommuDetachDevice()
function, respectively.
To attach a device to an IOMMU domain that was created by calling the KnIommuCreateDomain()
function or detach it, you must call the KnIommuAttachDeviceToDomain()
or KnIommuDetachDeviceFromDomain()
function, respectively.
Information about API functions
iommu_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Attaches a device on a PCIe bus to the automatically created IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns Additional information If IOMMU is not enabled, |
|
Purpose Detaches a device on a PCIe bus from the automatically created IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns Additional information If IOMMU is not enabled, |
|
Purpose Creates an IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Attaches a device on a PCIe bus to the defined IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns |
|
Purpose Detaches a device on a PCIe bus from the defined IOMMU domain associated with the calling process. Parameters
Returned values If successful, the function returns |
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:
- 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 theobjAlign
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 thebuffer
parameter is less than the value defined through theobjAlign
parameter, the function returnsRTL_NULL
. - 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 theKosQueuePush()
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 theKosQueuePushArray()
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 theobjs
parameter of theKosQueuePushArray()
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 theKosQueueFree()
function.To clear the queue and free all registered segments in the queue buffer, you must call the
KosQueueFlush()
function. - 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 theKosQueueCreate()
function. Otherwise, you must separately delete the queue buffer.
Information about API functions
queue.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a queue abstraction. Parameters
Returned values If successful, the function returns the queue abstraction ID, otherwise it returns |
|
Purpose Deletes a queue abstraction. Parameters
Returned values N/A |
|
Purpose Reserves a segment in the queue buffer. Parameters
Returned values If successful, the function returns the pointer to the reserved segment in the queue buffer, otherwise it returns |
|
Purpose Resets the reservation of the defined segment in the queue buffer. Parameters
Returned values N/A |
|
Purpose Adds an element to the end of the queue. Parameters
Returned values N/A |
|
Purpose Adds a sequence of elements to the end of the queue. Parameters
Returned values N/A |
|
Purpose Extracts the first element of the queue. Parameters
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 |
|
Purpose Clears the queue and resets the reservation of all registered segments in the queue buffer. Parameters
Returned values N/A |
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 |
---|---|
|
Purpose Sets a barrier for reading from memory. Parameters N/A Returned values N/A |
|
Purpose Sets a barrier for writing to memory. Parameters N/A Returned values N/A |
|
Purpose Sets a barrier for writing to memory and reading from memory. Parameters N/A Returned values N/A |
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 |
---|---|
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
|
Purpose Executes the Parameters
Returned values If successful, the function returns |
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 |
---|---|
|
Purpose Creates an IPC synchronization object. Parameters
Returned values If successful, the function returns |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
Returned values If successful, the function returns |
|
Purpose Switches the defined IPC synchronization object to a state in which the Parameters
Returned values If successful, the function returns |
Using sessions (session.h)
The API is defined in the header file sysroot-*-kos/include/kos/session.h
from the KasperskyOS SDK.
The API is intended for use in the code of resource providers that provide access to user resources via IPC, and in the code of programs that utilize user resources locally (without IPC). The API lets you organize session-based management of access to user resources. A session consists of a sequence of actions, such as opening access to a resource, using the resource, and closing access to the resource. When access to a resource is opened, a session is created. Closing access to the resource terminates the session. Multiple operations can be simultaneously performed with a resource in one session. A resource can be used in exclusive mode or multi-access mode. In the latter case, the resource must be opened multiple times (possibly with different access permissions). In other words, the resource will be used via multiple, simultaneously existing sessions (parallel sessions).
In the API description, an "object" refers to a KosObject
, which is an abstraction of a resource (for details about KosObjects
, see Using KosObjects (objects.h)). The type of session and session context are also KosObjects
. (A session context contains information about this session, such as the resource access permissions and the counter for active operations with the resource.)
Information about API functions is provided in the table below.
Creating a session type
A session type is created before sessions are created. To create a session type, call the KosSessionCreateType()
function.
You must use the objectType
parameter to define the type of objects for which the created type of sessions are intended. The object type must inherit the type with the ID kosSessionBaseObjectType
.
Use the sessionType
parameter to define the type of session contexts. You can specify the base type of session contexts with the ID kosSessionBaseSessionType
or the type that inherits it. The base type of session contexts corresponds to the structure containing data such as the pointer to the object, the resource access permissions, the resource handle permissions mask that is transferred to resource consumers, and the counter of active operations with the resource during the session (the type of this structure is not exported to programs, but instead is used by the libkos
library). You can add additional information to the session context by creating your own session context type that inherits the base type. For example, the session context for device usage may additionally contain pointers to functions that are called by solution programs to work with the device.
You must use the ops
parameter to define the callback functions that are called when creating and terminating sessions. The callback functions for the open
and close
IDs must be defined. You do not have to define the callback function for the closeComplete
ID, therefore this ID can be set to RTL_NULL
.
The callback function corresponding to the open
ID is called when creating a session and receives the pointers to the object, session context, data passed to the KosSessionOpenObject()
function via the openCtx
parameter, and the buffer for saving the resource handle permissions mask transferred to resource consumers. In this callback function, you can check the status of the resource, define the resource handle permissions mask transferred to resource consumers, and write data to the object or session context, for example. If the return code is different from rcOk
, a session will not be created.
The callback function corresponding to the close
ID is called when the session is terminated, irrespective of whether operations with the resource were completed (the counter of active operations in the closed session may be greater than zero), and receives pointers to the object and session context. In this callback function, you can interrupt active operations in a terminated session and write data to an object or session context, for example. Any return code is acceptable.
The callback function corresponding to the closeComplete
ID is called when a session is terminated only after completion of all operations with the resource (the counter of active operations in the completed session is equal to zero) and receives the pointers to the object and session context. In this callback function, you can write data to an object, for example.
Registering an object
After creating a session type, you must register the required objects as objects that can be linked to sessions of this type. To register an object, call the KosSessionRegisterObject()
function.
You cannot register one object to be linked to different types of sessions at the same time.
After registration, the object is ready to be opened.
Opening an object
When an object is opened, a session linked to this object is created, and an object is opened when there is a request to open access to a resource. This may be a request via IPC from the resource consumer or a local request from another program component.
To open an object, call the KosSessionOpenObject()
function. You must use the requiredRights
parameter to define the resource access permissions in the created session, and use the shareMode
parameter to define the multi-access permissions for a resource to properly restrict access to this resource for parallel sessions. For example, if read-and-write permissions are defined via the requiredRights
parameter and the shareMode
parameter is set to 0
, the created session requires exclusive read-and-write access to the resource. If read-and-write permissions are defined via the requiredRights
parameter but only read permissions are defined via the shareMode
parameter, the created session requires read-and-write permissions and allows read operations in parallel sessions. If read permissions are defined via the requiredRights
parameter but read-and-write permissions are defined via the shareMode
parameter, the created session requires read-access and allows read-and-write operations in parallel sessions.
If a resource needs to be used in multi-access mode, you must consider the following conditions when the object is opened a second time and subsequent times:
- The access permissions defined via the
requiredRights
parameter cannot exceed the minimal access permissions defined via theshareMode
parameter for all parallel sessions linked to this object. - The access permissions defined via the
shareMode
parameter must include all of the access permissions defined via therequiredRights
parameter for all parallel sessions linked to this object.
The session context will be created after the object is opened.
If a program uses resources locally, after the object is opened, the program component managing the resources must pass the pointer of the created session context to the program component that uses the resources. When subsequently requesting operations with a resource, the program component using the resources must pass this pointer to the program component managing the resources for session identification.
After opening an object, the resource provider must transfer the resource handle to the consumer. The session context must be used as the resource transfer context so that the consumer can receive the handle of the resource linked to the session. When subsequently requesting operations with a resource, the consumer must put the received resource handle into IPC requests so that the provider receives the pointer to the session context after dereferencing this handle. To fill the resource handle transport container with data for transfer to a resource consumer, call the KosSessionContextToIpcHandle()
function. This function receives the session context via the sessionCtx
parameter and transfers the resource handle transport container via the desc
parameter.
The KosSessionContextToIpcHandle()
function takes the permissions mask defined in the callback function that is called when creating the session and puts this permissions mask into the resource handle transport container. The access permissions defined in this mask must not exceed the session access permissions defined via the requiredRights
parameter of the KosSessionOpenObject()
function. However, the same access permission may correspond to different bits in the permissions mask and in the value defined via the requiredRights
parameter of the KosSessionOpenObject()
function.
The KosSessionContextToIpcHandle()
function defines the session context as the resource transfer context. The session context will be automatically deleted (and therefore the session will be terminated) after closure or revocation of the resource handles that were generated by the transfer of the resource handle that was received by calling the KosSessionContextToIpcHandle()
function. If you need to prevent automatic termination of a session for subsequent termination via the KosSessionCloseObject()
function call, prior to transferring the resource handle you must increment the counter for links to the context of this session by calling the KosRefObject()
function from the objects.h API. In this case, after calling the KosSessionCloseObject()
function, you must decrement the counter for links to the session context by calling the KosPutObject()
function from the objects.h API.
Performing operations with a resource
An operation with a resource (for example, reading, writing, or getting parameters) is performed by request via IPC from the resource consumer or by local request from another program component.
The scenario for performing an operation with a resource includes the followings steps:
- Verify the resource access permissions.
When processing a local request, call the
KosSessionGetOpenRights()
function. This function gets information about session resource access permissions defined via therequiredRights
parameter of theKosSessionOpenObject()
function to compare these permissions with the permissions needed to perform the operation.During IPC request processing, you must call the
KosSessionIpcHandleToSession()
function, which gets the session context and verifies whether the permissions mask of the resource handle that the resource consumer put into the IPC request corresponds to the requested operation. The resource handle transport container received from the resource consumer must be passed to the function via thedesc
parameter. In theoperation
parameter, you must specify the resource access permissions that are needed to perform the requested operation. In thetype
parameter, you must specify the ID of the session type to verify that the type of session context matches the session type. If you set thetype
parameter toRTL_NULL
, you must verify the type of session context prior to converting the pointer type that was received via thesessionCtx
parameter. Verification of the session context type is mandatory because the resource provider may create sessions with contexts of different types while the resource consumer may erroneously make an IPC request with the resource handle matching a session with a different type of context. To verify the context type, use the objects.h API. - Increment the counter for active operations in the session by calling the
KosSessionRef()
function.This function verifies that the session has not terminated. In the
type
parameter, you must specify the ID of the session type to verify that the type of session context matches the session type. You can use theobject
parameter to receive a pointer to the object. - Perform the requested actions with the resource.
- Decrement the counter for active operations in the session by calling the
KosSessionPut()
function.
Getting a resource handle
A resource handle may be required for using notifications or for querying the Kaspersky Security Module through the security interface, for example. To get a resource handle for an object or session context, call the KosSessionGetObjectHandle()
or KosSessionGetObjectHandleBySession()
function, respectively.
Enumerating sessions linked to an object
You may need to enumerate sessions linked to an object to notify each of the resource consumers using a resource about the state of that resource, for example. To enumerate sessions linked to an object, call the KosSessionWalk()
function. Use the handler
parameter to define the callback function that is called for each session during enumeration and receives pointers to the object, session context and data that is passed to the KosSessionWalk()
function via the walkCtx
parameter. In this callback function, you can send a notification about the state of a resource to consumers of that resource, for example.
Closing an object
When an object is closed, one of the sessions linked to this object is terminated. A session will not be terminated until all operations in this session are terminated (in other words, when the counter of active operations is equal to zero). When a session is terminated, the handles of the resource linked to this session are automatically revoked.
An object is closed when there is a request to close access to the resource. This may be a request via IPC from the resource consumer or a local request from another program component. After making a request to close access to a resource, the resource consumer must close the handle of this resource. (If the resource consumer terminates without making a request to close access to the resource, or closes the resource handle without making this request, the corresponding session will be automatically terminated under the condition that the resource consumer has not additionally incremented the counter for links to the context of this session.)
To close an object, call the KosSessionCloseObject()
or KosSessionCloseObjectByIpcHandle()
function.
The KosSessionCloseObject()
function terminates the session corresponding to the session context that was defined via the sessionCtx
parameter.
The KosSessionCloseObjectByIpcHandle()
function terminates the session linked to the resource handle that the resource consumer put into the IPC request to close access to the resource. The received resource handle transport container must be passed to the function via the desc
parameter. In the type
parameter, you must specify the ID of the session type to verify that the type of session context matches the session type.
Deregistering an object
When an object is deregistered, all sessions linked to this object are terminated and it is no longer possible to open this object. A session will not be terminated until all operations in this session are terminated (in other words, when the counter of active operations is equal to zero). When a session is terminated, the handles of the resource linked to this session are automatically revoked. (Revocation does not close resource handles, therefore revoked handles need to be closed.)
To deregister an object, call the KosSessionUnregisterObject()
function.
Deleting a session type
A session type must be deleted if there are no registered objects for which sessions of this type can be used, and there will be no more registrations. For example, a session type must be deleted when the resource provider is terminated.
To delete a session type, call the KosSessionDestroyType()
function.
Information about API functions
session.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a session type. Parameters
Returned values If successful, the function returns |
|
Purpose Deletes a session type. Parameters
Returned values N/A |
|
Purpose Registers an object as an object that can be linked to sessions of the defined type. Parameters
Returned values If successful, the function returns |
|
Purpose Deregisters an object that was registered by calling the Parameters
Returned values If successful, the function returns |
|
Purpose Opens an object. Parameters
Returned values If successful, the function returns |
|
Purpose Closes an object. Parameters
Returned values If successful, the function returns |
|
Purpose Increments the counter for active operations in a session. Parameters
Returned values If successful, the function returns |
|
Purpose Decrements the counter for active operations in a session. Parameters
Returned values N/A |
|
Purpose Enumerates the sessions linked to a defined object. Parameters
Returned values If successful, the function returns Additional information If the callback function defined via the The callback function defined via the |
|
Purpose Gets information about resource access permissions in the specified session. These permissions are defined via the Parameters
Returned values If successful, the function returns the value indicating the resource access permissions, otherwise it returns |
|
Purpose Fills the resource handle transport container with data for transfer to a resource consumer when processing an IPC request to open access to the resource. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the session context when processing an IPC request from a resource consumer. Parameters
Returned values If successful, the function returns |
|
Purpose Closes an object when processing an IPC request from a resource consumer to close access to the resource. Parameters
Returned values If successful, the function returns |
|
Purpose Gets a resource handle for an object. Parameters
Returned values If successful, the function returns |
|
Purpose Gets a resource handle for a session context. Parameters
Returned values If successful, the function returns |
Using KosObjects (objects.h)
The API is defined in the header file sysroot-*-kos/include/kos/objects.h
from the KasperskyOS SDK.
The API lets you use typified KosObjects
. A KosObject
is a process memory region consisting of segments containing service data and payload data. Service data includes a pointer to the structure describing the object type, a counter for links to the object, and other information about the object. Payload data may consist of any data depending on the purpose of the object. For example, segments of payload data in an object representing a device abstraction save the structures that contain the device operating parameters, pointers to low-level methods for working with the device, and the parameters of software/hardware interfaces for device management.
A type inheritance mechanism is supported for KosObjects
. For example, an abstraction of the NS16550 UART device contains the following structures in different segments of payload data: a structure specific to NS16550 UART devices, a structure specific to the UART device class, and common structures for all devices. The NS16550 UART device abstraction type corresponds only to the structure that is specific to NS16550 UART devices. All other structures correspond to types that inherited the NS16550 UART device abstraction type.
In the API, the pointer to an object is the pointer to the segment of payload data in this object.
Information about API functions is provided in the table below.
Creating an object type
An object type is created before objects are created. An object type is a process memory region containing the structure that includes data such as the type name, pointers to functions that are called when objects of this type are created and destroyed, a pointer to the parent type, and a counter for links to the type. Each object stores the pointer to this structure, which means that the objects are typified.
To create an object type, call the KosCreateObjectType()
or KosCreateObjectTypeEx()
function. In contrast to the KosCreateObjectType()
function, the KosCreateObjectTypeEx()
function lets you create a type that inherits other types. This type contains the address of the parent type that is defined via the parentType
parameter. You can also use the parentType
parameter to define a type that also contains the address of its parent type. As a result, you can use the KosCreateObjectTypeEx()
function to create a type inheritance tree. Each type in this tree will inherit the entire chain of types that form the path from the root of the tree to this specific type.
Depending on whether type inheritance is being employed, objects may be simple or composite. Objects that correspond to one type are known as simple objects. Objects that correspond to a type inheritance chain are known as composite objects. Composite ones consist of simple objects. The number of simple objects in a composite object is equal to the number of types in the inheritance chain. The simple objects form an inheritance chain corresponding to the chain of inheritance of their types. In other words, each object except the first one in the inheritance chain contains the address of the parent object.
You need to use the defaultObjSize
parameter of the KosCreateObjectType()
and KosCreateObjectTypeEx()
functions to define the default size of objects (you can define a different size when creating an object). When creating a type that inherits other types, this parameter pertains only to the last type in the inheritance chain. The defaultObjSize
parameter effectively defines the minimum size of a segment of payload data in simple objects because the actual size of objects will be larger than the defined size due to the alignment of memory regions, inheritance of types, and the presence of segments containing service data.
You can use the ops
parameter of the KosCreateObjectType()
and KosCreateObjectTypeEx()
functions to define the callback functions that are called when objects are created and destroyed (you can define both functions or just one of them). When a composite object is created, a set of simple objects is created within it. When the composite object is destroyed, the set of simple objects within it is also destroyed. When each simple object is created and destroyed, separate callback functions that were defined when creating the type of this object are called. The first object in the inheritance chain is created first, and the last object in the inheritance chain is created last. The last object in the inheritance chain is destroyed first, and the first object in the inheritance chain is destroyed last.
The callback function that is called when creating an object receives pointers to the object and the data passed to the KosCreateObjectEx()
function via the context
parameter. In this callback function, you can allocate resources and initialize data in an object, for example. If the return code differs from rcOk
, the object will not be created. If a composite object is being created without creating at least one simple object, this composite object will not be created. Also, when creating a composite object, the data passed to the KosCreateObjectEx()
function via the context
parameter will be received only by the callback function that is called when creating the last simple object in the inheritance chain.
The callback function that is called when an object is destroyed receives the pointer to the object. In this callback function, you can free up resources, for example. Any return code is acceptable.
Creating an object
After creating an object type, you can create objects. To create an object, you must call the KosCreateObject()
or KosCreateObjectEx()
function.
Use the type
parameter to define the object type. If you define a type that does not inherit other types, a simple object will be created. If you define a type that inherits other types, a composite object will be created and the last simple object in the inheritance chain for this composite object will have the defined type.
Use the size
parameter to define the object size, or specify 0
to set the default object size specified in the type of this object. If a composite object is created, this parameter pertains only to the last simple object in the inheritance chain. The size
parameter effectively defines the minimum size of a segment of payload data in a simple object because the actual size of the object will be larger than the defined size due to the alignment of the memory region, inheritance of types, and the presence of segments containing service data.
In contrast to the KosCreateObject()
function, the KosCreateObjectEx()
function has the context
parameter, which can be used to pass data to the callback function that is called when an object is created. When creating a composite object, the data passed via the context
parameter will be received only by the callback function that is called when creating the last simple object in the inheritance chain.
The outObject
parameter is used by the KosCreateObject()
and KosCreateObjectEx()
functions to transfer the pointer of the created object. The pointer to the created composite object is the pointer to the last simple object in the inheritance chain.
After it is created, the object can be used to write data to it and read data from it.
Obtaining access to simple objects in a composite object
To gain access to simple objects in a composite object, you need to use the KosGetObjectParent()
function and/or the KosGetObjectAncestor()
function. The KosGetObjectParent()
function gets the pointer to the object that is the direct ancestor of the defined object. The KosGetObjectAncestor()
function gets the pointer to the object that is the ancestor of the defined object and has the defined type.
Managing the lifetime of objects and their types
An object exists as long as the counter for links to that object has a value greater than zero. Likewise, an object type exists as long as the counter for links to that object has a value greater than zero. When an object or object type is created, the value of the links counter is equal to 1. This counter can then be incremented or decremented to manage the lifetime of the object or object type. For example, after an object is created, you can decrement the counter for links to its type because the libkos
library increments the counter for links to this object type when an object is created. In this case, the object type will be destroyed automatically after the object is destroyed because the libkos
library decrements the counter for links to the object type when an object is destroyed. In addition, when saving an object address in another object, you must increment the counter for links to the first object to ensure that it exists throughout the lifetime of the second object. When you destroy an object that contains the address of another object, you must decrement the counter for links to the second object to ensure that it is destroyed when no other links remain.
The lifetime of a simple object within a composite object corresponds to the lifetime of this composite object. You cannot separately manage the lifetime of a simple object within a composite object.
To increment the counter for links to an object or object type, call the KosRefObject()
or KosRefObjectType()
function, respectively.
To decrement the counter for links to an object or object type, call the KosPutObject()
or KosPutObjectType()
function, respectively.
Verifying an object type
The KosCheckParentType()
and KosObjectTypeInheritsType()
functions let you check whether one object type is a parent to another object type.
The KosObjectOfType()
function verifies whether an object has the defined type.
The KosObjectInheritsType()
function checks whether an object has the defined type or a type inherited by the defined type.
Getting the type and name of an object
To get the object type, call the KosObjectGetType()
function.
To get the object name, call the KosGetObjectName()
function. (Each simple object in a composite object has the name of this composite object.)
Getting the name of an object type
To get the name of an object type, call the KosObjectGetTypeName()
function.
Information about API functions
objects.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates an object type. Parameters
Returned values If successful, the function returns |
|
Purpose Creates an object type. Parameters
Returned values If successful, the function returns |
|
Purpose Creates an object. Parameters
Returned values If successful, the function returns Additional information When an object is created, memory is allocated for that object and this memory is filled with zeros. |
|
Purpose Increments the counter for links to an object type. Parameters
Returned values N/A |
|
Purpose Decrements the counter for links to an object type. Parameters
Returned values N/A |
|
Purpose Verifies that the specific object Parameters
Returned values If the verification is successful, the function returns |
|
Purpose Verifies that the specific object Parameters
Returned values If the verification is successful, the function returns |
|
Purpose Creates an object. Parameters
Returned values If successful, the function returns Additional information When an object is created, memory is allocated for that object and this memory is filled with zeros. |
|
Purpose Increments the counter for links to an object. Parameters
Returned values N/A |
|
Purpose Decrements the counter for links to an object. Parameters
Returned values N/A |
|
Purpose Gets the object name. Parameters
Returned values Returns the ID of the Additional information The |
|
Purpose Gets the pointer to the object that is the direct ancestor of the defined object. Parameters
Returned values If successful, the function returns the pointer for the object that is the direct ancestor of the defined object, otherwise it returns |
|
Purpose Gets the pointer to the object that is the ancestor of the defined object and has the defined type. Parameters
Returned values If successful, the function returns the pointer for the object that is the ancestor of the defined object and has the defined type, otherwise it returns Additional information If the |
|
Purpose Verifies that the object has the defined type. Parameters
Returned values If the verification is successful, the function returns |
|
Purpose Verifies that the object has the defined type or the type that inherits the defined type. Parameters
Returned values If the verification is successful, the function returns |
|
Purpose Gets the ID of the object type. Parameters
Returned values Object type ID. Additional information Increments the counter for links to an object type. If the received object type ID is no longer needed, you must decrement the number of links to the object type by calling the |
|
Purpose Gets the name of the object type. Parameters
Returned values Returns the ID of the Additional information The |
Using KosObject containers (objcontainer.h)
The API is defined in the header file sysroot-*-kos/include/kos/objcontainer.h
from the KasperskyOS SDK.
The API lets you merge KosObjects
into containers to conveniently use sets of these objects (for details about KosObjects
, see Using KosObjects (objects.h)). The containers are also KosObjects
and may be elements of other containers. The same KosObject
may be an element of multiple containers at the same time.
Information about API functions is provided in the table below.
Creating a container
To create a container, call the KosCreateObjContainer()
function. In the parent
parameter, you can specify the ID of the parent container, which is the container to which the created container will be added.
Adding an object to a container
To add an object to a container, call the KosInsertObjContainerObject()
function. An object can be another container. You must use the name
parameter to define the object name that this object will have within the container. This name is not related to the name that was defined when the object was created. The name of an object within a container must be unique so that this object can be unambiguously identified among other objects in this container. When an object is added to a container, the counter for links to this object is incremented.
Removing an object from a container
To remove an object from a container, call the KosRemoveObjContainerObjectByName()
or KosRemoveObjContainerObject()
function. When an object is removed from a container, the counter for links to this object is decremented.
Searching for an object in a container
To search for an object with the defined name in a container, call the KosGetObjContainerObjectByName()
function. Child containers will not be searched for the object. This function increments the counter for links to the found object.
Enumerating objects in a container
Enumeration may be necessary to perform specific actions with multiple objects in a container. To enumerate objects in a container, call the KosWalkObjContainerObjects()
function. Use the walk
parameter to define the callback function that is called for each object during enumeration and receives pointers to the object and data that is passed to the KosWalkObjContainerObjects()
function via the context
parameter. Objects in child containers are not enumerated.
Enumerating the names of objects in a container
To enumerate the names of objects in a container, use the KosEnumObjContainerNames()
function. The order of enumeration for object names matches the order in which these objects are added to the container. Object names in child containers are not enumerated.
Getting the number of objects in a container
To get the number of objects in a container, call the KosCountObjContainerObjects()
function. Objects in child containers are not taken into account.
Clearing a container
To remove all objects from a container, call the KosClearObjContainer()
function. This function decrements the counters for links to objects that are removed from the container.
Checking whether an object is a container
To check whether an object is a container, call the KosIsContainer()
function.
Deleting a container
To delete a container, call the KosDestroyObjContainer()
function. When a container is deleted, the counters for links to objects within this container are decremented.
Information about API functions
objcontainer.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates a container. Parameters
Returned values If successful, the function returns |
|
Purpose Deletes a container. Parameters
Returned values If successful, the function returns |
|
Purpose Adds an object to a container. Parameters
Returned values If successful, the function returns |
|
Purpose Removes the object with the defined name from a container. Parameters
Returned values If successful, the function returns |
|
Purpose Removes an object from a container. Parameters
Returned values If successful, the function returns |
|
Purpose Searches a container for the object with the defined name. Parameters
Returned values If successful, the function returns Additional information If an object with the defined name is not found, the function returns the |
|
Purpose Enumerates objects in a container and calls the defined function for each object during enumeration. Parameters
Returned values If successful, the function returns Additional information The callback function defined via the If a callback function defined via the If a callback function defined via the The |
|
Purpose Enumerates the names of objects in a container. Parameters
Returned values If successful, the function returns Additional information The |
|
Purpose Gets the number of objects in a container. Parameters
Returned values Number of objects in a container. |
|
Purpose Clears a container. Parameters
Returned values If successful, the function returns |
|
Purpose Checks whether an object is a container. Parameters
Returned values If the verification is successful, the function returns |
Using KosStrings (strings.h)
The API is defined in the header file sysroot-*-kos/include/kos/strings.h
from the KasperskyOS SDK.
The API lets you use KosStrings
, which have the following distinguishing characteristics:
- A
KosString
is a C-string (with a terminating zero) that follows the header containing the service data. KosStrings
are stored in hash tables, which are arrays whose elements are lists of strings. (The pointers for creating linked lists of strings are stored in the headers of strings.) Each list consists of one or more strings. Each list contains the strings who have the same value for the remainder after dividing the hash value of the string by the number of lists in the hash table. The main property of a hash table is that the computational complexity of searching for a string does not depend on the degree of occupancy of this table if all lists include only one string.When adding
KosStrings
to a hash table in real-world scenarios, lists containing multiple strings may be created. This leads to varying computational complexity when searching for strings in the hash table for the following reason. When searching for a key, the hash value is calculated and then converted into a string list index (by calculating the remainder from dividing the hash value by the number of lists). If there is only one string in the list containing the calculated index, the search will be complete after comparing this string to the key. However, if there are multiple strings in the list containing the calculated index, you may need to compare the key to all strings in this list to complete the search. In this case, the computational complexity of searching for a string may be higher than when there is only one string in the list.KosStrings
from one hash table are unique, which means that there are no identical strings in a hash table. If the strings from one hash table have different IDs, that means that these are different strings. (In the API, the ID of theKosString
is the pointer to this string.) This lets you compare strings based on their IDs instead of their contents, thereby ensuring that the computational complexity of the comparison does not depend on the sizes of strings.- The size of a
KosString
is stored in the header instead of being calculated based on a search for a terminating zero, thereby ensuring that the computational complexity of getting the size does not depend on the actual size of the string. KosStrings
cannot be modified. This characteristic is a result of characteristics 2–4. Modification of a string may lead to the appearance of identical strings in the same hash table, a discrepancy between the actual size of a string and the size stored in the header, and a discrepancy between the string contents and the index of the string list in which this string is listed in the hash table.- A
KosString
exists as long as the counter for links to it remains greater than zero. (The string links counter is stored in the header.)
Information about API functions is provided in the table below.
Creating a hash table
You can use the default hash table, which is created automatically during initialization of the libkos
library. This table may include no more than 2039 lists of strings with a string size of no more than 65,524 bytes without taking into account the terminating zero.
You can create a hash table with the required maximum number of string lists and the required maximum size of strings by calling the KosCreateStringRoot()
function.
A hash table is created empty without any strings.
Adding a KosString to a hash table
To add a string to the default hash table, call the KosCreateString()
function.
To add a string to a defined hash table, call the KosCreateStringEx()
function.
If the string being added is already listed in the hash table, these functions do not add a new string but instead pass the ID of the already existing string via the outStr
parameter.
Searching for a KosString in a hash table
To search for a string in the default hash table, call the KosGetString()
function.
To search for a string in a defined hash table, call the KosGetStringEx()
function.
The search is completed successfully if the key completely matches the string.
Managing the lifetime of KosStrings
A string exists as long as the counter for links to it remains greater than zero. The KosCreateString()
and KosCreateStringEx()
functions add a string whose links counter has a value of 1 to the hash table. If the string has already been added to the hash table, these functions increment the counter for links to this string. The KosGetString()
and KosGetStringEx()
functions increment the counter for links to the found string. This counter can then be incremented or decremented to manage the lifetime of the string. When passing the ID of a string to another program component, you must increment the counter for links to the corresponding string to ensure that this string will exist for the amount of time required by this component. If the string is no longer needed, you must decrement the counter for links to this string to ensure that it is destroyed when no other links remain.
To increment the counter for links to a string, call the KosRefString()
or KosRefStringEx()
function.
To decrement the counter for links to a string in the default hash table, call the KosPutString()
function.
To decrement the counter for links to a string in a defined hash table, call the KosPutStringEx()
function.
Getting the size of a KosString
To get the size of a string without taking into account the terminating zero, call the KosGetStringLen()
function.
To get the size of a string while taking into account the terminating zero, call the KosGetStringSize()
function.
Deleting a hash table
To delete a hash table, call the KosDestroyStringRoot()
function. The table will be deleted only if it is empty.
Information about API functions
strings.h functions
Function |
Information about the function |
---|---|
|
Purpose Creates an empty hash table. Parameters
Returned values ID of the hash table. |
|
Purpose Deletes an empty hash table. Parameters
Returned values N/A |
|
Purpose Searches for a Parameters
Returned values If successful, the function returns the ID of the |
|
Purpose Searches for a Parameters
Returned values If successful, the function returns the ID of the |
|
Purpose Adds a Parameters
Returned values If successful, the function returns |
|
Purpose Adds a Parameters
Returned values If successful, the function returns |
|
Purpose Increments the counter for links to a Parameters
Returned values N/A |
|
Purpose Increments the counter for links to a Parameters
Returned values N/A |
|
Purpose Decrements the counter for links to a Parameters
Returned values N/A |
|
Purpose Decrements the counter for links to a Parameters
Returned values N/A |
|
Purpose Gets the size of the Parameters
Returned values Size of the Additional information If the parameter has the |
|
Purpose Gets the size of the Parameters
Returned values Size of the Additional information If the parameter has the |
KasperskyOS kernel XHCI DbC driver management (xhcidbg_api.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/xhcidbg/xhcidbg_api.h
from the KasperskyOS SDK.
This API is intended for use in the code of drivers that implement a USB stack (kusb class drivers) and lets you stop and start the XHCI DbC (Debug Capability) driver of the KasperskyOS kernel. (The KasperskyOS kernel includes the XHCI DbC driver, which is a simplified XHCI controller driver and is used by the kernel for diagnostic output or by the kernel GDB server to interact with the GDB debugger.) If a kusb class driver needs to restart the XHCI controller, the kernel's XHCI DbC driver must be stopped by calling the KnXhcidbgStop()
function before the restart and then started by calling the KnXhcidbgStart()
function after the restart. If you do not do this, a restart of the XHCI controller will lead to a failure of diagnostic output (or debugging) via the USB port.
This API has not been implemented for the Raspberry Pi 4 B and Radxa ROCK 3A hardware platforms (the functions return rcUnimplemented
).
Information about API functions is provided in the table below.
xhcidbg_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Starts the XHCI DbC driver of the KasperskyOS kernel. Parameters N/A Returned values If successful, the function returns |
|
Purpose Stops the XHCI DbC driver of the KasperskyOS kernel. Parameters N/A Returned values If successful, the function returns |
Receiving security audit data (vlog_api.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/vlog/vlog_api.h
from the KasperskyOS SDK.
The API lets you read from the KasperskyOS kernel log containing security audit data and is used in the code of the static library sysroot-*-kos/lib/libklog_system_audit.a
, which is linked to the Klog
system program.
Information about API functions is provided in the table below.
Using the API
The API usage scenario includes the following steps:
- Open the kernel log containing security audit data by calling the
KnAuOpen()
function. - Receive messages from the kernel log containing security audit data via calls of the
KnAuRead()
function.This log is a cyclic buffer, and therefore you must prevent overwrites in this log because they could lead to a loss of security audit data that has not yet been read. To monitor overwrites in the log, the
outDropMsgs
parameter of theKnAuRead()
function is used to pass the number of dropped messages. (The counter of these messages is reset to zero after this value is read each time the function is called.) If messages have been dropped, you must either increase the speed at which messages are read from the log, for example, by performing the read operation from parallel threads, or reduce the speed of message generation by editing the security audit profile. - Close the kernel log containing security audit data by calling the
KnAuClose()
function.
Information about API functions
vlog_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Opens the kernel log containing security audit data. Parameters
Returned values If successful, the function returns |
|
Purpose Closes the kernel log containing security audit data. Parameters
Returned values If successful, the function returns |
|
Purpose Receives a message from the kernel log containing security audit data. Parameters
Returned values If successful, the function returns |
Using futexes (sync.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/sync/sync.h
from the KasperskyOS SDK.
The API is intended for working with futexes and is used in code of the API functions in event.h, mutex.h, rwlock.h, semaphore.h, and condvar.h. A futex is a low-level synchronization primitive that supports two operations: lock a thread and add it to the futex-linked queue of locked threads, and resume execution of threads from the futex-linked queue of locked threads. A futex is a kernel object linked to an integer variable in the user space. The kernel object provides the capability to store a queue of locked threads linked to the futex. The value of the integer variable in the user space (futex value) is atomically modified by the synchronized threads to signal the changed state of shared resources. For example, the futex value may indicate the specific state of an event (signaling or non-signaling state), indicate whether the mutex has been captured or freed, and indicate the specific value of the semaphore counter. The futex value determines whether to lock a thread that attempts to obtain access to shared resources.
Information about API functions is provided in the table below.
Using the API
To use a futex, you must create only an integer variable for storing its value. The API functions receive the pointer to this variable via the ftx
parameter. The kernel object linked to this variable is created or deleted automatically when using the API functions.
The KnFutexWait()
function locks the calling thread if the futex value matches the value of the val
parameter. (A futex value can be changed by another thread during execution of a function.) The thread is locked for the mdelay
time period, but execution of this thread may be resumed before the mdelay
period elapses by calling the KnFutexWake()
function from another thread. The KnFutexWake()
function resumes execution of threads from a futex-linked queue of locked threads. The number of threads whose execution is resumed is limited by the value of the nThreads
parameter. Thread execution is resumed starting with the beginning of the queue.
Information about API functions
sync.h functions
Function |
Information about the function |
---|---|
|
Purpose Locks the calling thread if the futex value is equal to the expected value. Parameters
Returned values If successful, the function returns If the thread lockout duration has expired, the function returns If the futex value is not equal to the expected value, the function returns |
|
Purpose Resumes execution of threads that were blocked by Parameters
Returned values If successful, the function returns |
Getting IPC handles and RIIDs to use statically created IPC channels (sl_api.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/sl/sl_api.h
from the KasperskyOS SDK.
The API allows servers to get the listener handles, and allows clients to get the client IPC handles and Runtime Implementation Identifiers (RIIDs) for the purpose of using statically created IPC channels.
Closing an obtained IPC handle will cause the IPC channel to become unavailable. After an IPC handle is closed, it is impossible to obtain it again or restore access to the IPC channel.
Information about API functions is provided in the table below.
sl_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Gets the listener handle based on the name of the IPC channel. Parameters
Returned values If successful, it returns the listener handle. Otherwise, it returns |
|
Purpose Gets the client IPC handle based on the name of the IPC channel. Parameters
Returned values If successful, it returns the client IPC handle. Otherwise, it returns |
|
Purpose Gets the endpoint ID (RIID). Parameters
Returned values If successful, the function returns the endpoint ID (RIID), otherwise it returns |
Power management (pm_api.h)
This API is defined in the header file sysroot-*-kos/include/coresrv/pm/pm_api.h
from the KasperskyOS SDK.
The API changes the power mode of the hardware platform (for example, shutdown or restart), and enables and disables processors (processor cores).
Information about API functions is provided in the table below.
Using the API
To make a request to change the power mode of the hardware platform, call the KnPmRequest()
function.
The KnPmSetCpusOnline()
and KnPmGetCpusOnline()
functions have the mask
parameter, which is used to pass the processor mask. A processor mask is a bit mask indicating the set of processors (processor cores). A flag set in the ith bit indicates that a processor with the i index is included in the set (numbering starts at zero). To work with a processor mask, you must use the functions defined in the header file sysroot-*-kos/include/rtl/cpuset.h
.
To enable and/or disable processors, call the KnPmSetCpusOnline()
function while specifying the processor mask indicating the required set of active processors. The flag corresponding to the bootstrap processor must be set in this mask, otherwise the function will end with an error. To get the bootstrap processor index, call the KnPmGetBootstrapCpuNum()
function.
To find out which processors are in the active state, call the KnPmGetCpusOnline()
function. The output parameter of this function is the processor mask indicating the actual set of active processors.
If the KnPmSetCpusOnline()
function ended with an error, this could lead to an unpredictable change in the set of active processors. To make a repeated attempt to configure the set of active processors, you must find out which processors are in the active state by calling the KnPmGetCpusOnline()
function and use this information to adjust the required set of active processors during the subsequent KnPmSetCpusOnline()
function call.
Information about API functions
pm_api.h functions
Function |
Information about the function |
---|---|
|
Purpose Requests to change the power mode of a hardware platform. Parameters
Returned values If successful, the function returns |
|
Purpose Requests to enable and/or disable processors. Parameters
Returned values If successful, the function returns |
|
Purpose Gets information regarding which processors are in the active state. Parameters
Returned values If successful, the function returns |
|
Purpose Gets the index of the bootstrap processor. Parameters
Returned values If successful, the function returns |