Contents
- KasperskyOS API
- libkos library
- Overview of the libkos library
- Memory
- Memory allocation
- Threads
- KosThreadCallback()
- KosThreadCallbackRegister()
- KosThreadCallbackUnregister()
- KosThreadCreate()
- KosThreadCurrentId()
- KosThreadExit()
- KosThreadGetStack()
- KosThreadOnce()
- KosThreadResume()
- KosThreadSleep()
- KosThreadSuspend()
- KosThreadTerminate()
- KosThreadTlsGet()
- KosThreadTlsSet()
- KosThreadWait()
- KosThreadYield()
- Handles
- Notifications
- Processes
- Dynamically created channels
- Synchronization primitives
- KosCondvarBroadcast()
- KosCondvarDeinit()
- KosCondvarInit()
- KosCondvarSignal()
- KosCondvarWait()
- KosCondvarWaitTimeout()
- KosEventDeinit()
- KosEventInit()
- KosEventReset()
- KosEventSet()
- KosEventWait()
- KosEventWaitTimeout()
- KosMutexDeinit()
- KosMutexInit()
- KosMutexInitEx()
- KosMutexLock()
- KosMutexLockTimeout()
- KosMutexTryLock()
- KosMutexUnlock()
- KosRWLockDeinit()
- KosRWLockInit()
- KosRWLockRead()
- KosRWLockTryRead()
- KosRWLockTryWrite()
- KosRWLockUnlock()
- KosRWLockWrite()
- KosSemaphoreDeinit()
- KosSemaphoreInit()
- KosSemaphoreSignal()
- KosSemaphoreTryWait()
- KosSemaphoreWait()
- KosSemaphoreWaitTimeout()
- DMA buffers
- IOMMU
- I/O ports
- IoReadIoPort8(), IoReadIoPort16(), IoReadIoPort32()
- IoReadIoPortBuffer8(), IoReadIoPortBuffer16(), IoReadIoPortBuffer32()
- IoWriteIoPort8(), IoWriteIoPort16(), IoWriteIoPort32()
- IoWriteIoPortBuffer8(), IoWriteIoPortBuffer16(), IoWriteIoPortBuffer32()
- KnIoPermitPort()
- KnRegisterPort8(), KnRegisterPort16(), KnRegisterPort32()
- KnRegisterPorts()
- Memory-mapped I/O (MMIO)
- Interrupts
- Deallocating resources
- Time
- Queues
- Memory barriers
- Receiving information about CPU time and memory usage
- Sending and receiving IPC messages
- POSIX support
- MessageBus component
- Return codes
- libkos library
Overview of the libkos library
The KasperskyOS kernel has a number of endpoints for managing handles, threads, memory, processes, IPC channels, I/O resources, and others. The libkos
library is used for accessing endpoints.
libkos library
The libkos
library consists of two parts:
- The first part provides the C interface for accessing KasperskyOS core endpoints. It is available through the header files in the
coresrv
directory. - The second part of the
libkos
library provides abstractions of synchronization primitives, objects, and queues. It also contains wrapper functions for simpler memory allocation and thread management. Header files of the second part oflibkos
are in thekos
directory.
The libkos
library significantly simplifies the use of core endpoints. The libkos
library functions ensure correct packaging of an IPC message and execution of system calls. Other libraries (including libc
) interact with the kernel through the libkos
library.
To use a KasperskyOS core endpoint, you need to include the libkos
library header file corresponding to this endpoint. For example, to access methods of the IO Manager, you need to include the io_api.h
file:
Files used by the libkos library
An intrinsic implementation of the libkos
library can use the following files exported by the kernel:
- Files in the IDL language (IDL descriptions). They contain descriptions of the interfaces of endpoints. They are used by IPC transport for correct packaging of messages.
- Header files of the kernel. These files are included in the
libkos
library.
Example
The I/O Manager is provided for the user in the following files:
coresrv/io/io_api.h
is a header file of thelibkos
library.services/io/IO.idl
is the IDL description of the I/O manager.io/io_dma.h
andio/io_irq.h
are header files of the kernel.
Memory states
Each page of virtual memory can be free, reserved, or committed.
The transition from a free state to a reserved state is called allocation. Pre-reserving memory (without committing physical pages) enables an application to mark its address space in advance. The transition from a reserved state back to a free state is referred to as freeing memory.
The assignment of physical memory for a previously reserved page of virtual memory is referred to as committing memory, and the inverse transition from the committed state to the reserved state is called returning memory.
Transitions between memory page states
Page topKnVmAllocate()
This function is declared in the coresrv/vmm/vmm_api.h
file.
void *KnVmAllocate(void *addr, rtl_size_t size, int flags);
Reserves a range of physical pages defined by the addr
and size
parameters. If the VMM_FLAG_COMMIT flag is indicated, the function reserves and commits pages for one call.
Parameters:
addr
is the page-aligned base physical address; ifaddr
is set equal to 0, the system chooses a free area of physical memory.size
is the size of the memory area in bytes (must be a multiple of the page size).flags
refers to allocation flags.
Returns the base virtual address of the reserved area. If it is not possible to reserve a memory area, the function returns RTL_NULL.
Allocation flags
In the flags
parameter, you can use the following flags (vmm/flags.h
):
- VMM_FLAG_RESERVE is a required flag.
- VMM_FLAG_COMMIT lets you reserve and commit memory pages to one
KnVmAllocate()
call in so-called "lazy" mode. - VMM_FLAG_LOCKED is used together with VMM_FLAG_COMMIT and lets you immediately commit physical memory pages instead of "lazy" commitment.
- VMM_FLAG_WRITE_BACK, VMM_FLAG_WRITE_THROUGH, VMM_FLAG_WRITE_COMBINE, VMM_FLAG_CACHE_DISABLE and VMM_FLAG_CACHE_MASK manage caching of memory pages.
- VMM_FLAG_READ, VMM_FLAG_WRITE, VMM_FLAG_EXECUTE and VMM_FLAG_RWX_MASK are memory protection attributes.
- VMM_FLAG_LOW_GUARD and VMM_FLAG_HIGH_GUARD add a protective page before and after the allocated memory, respectively.
- VMM_FLAG_GROW_DOWN defines the direction of memory access (from older addresses to newer addresses).
Permissible combinations of memory protection attributes:
- VMM_FLAG_READ allows reading page contents.
- VMM_FLAG_READ | VMM_FLAG_WRITE allows reading and modifying page contents.
- VMM_FLAG_READ | VMM_FLAG_EXECUTE allows reading and executing page contents.
- VMM_FLAG_RWX_MASK or VMM_FLAG_READ | VMM_FLAG_WRITE | VMM_FLAG_EXECUTE refers to full access to page contents (these entries are equivalent).
Example
coredump->base = KnVmAllocate(RTL_NULL, vmaSize,
VMM_FLAG_READ | VMM_FLAG_RESERVE |
VMM_FLAG_WRITE | VMM_FLAG_COMMIT |
VMM_FLAG_LOCKED).
The KnVmProtect()
function can be used to modify the defined memory area protection attributes if necessary.
KnVmCommit()
This function is declared in the coresrv/vmm/vmm_api.h
file.
Retcode KnVmCommit(void *addr, rtl_size_t size, int flags);
Commits a range of physical pages defined by the "addr" and "size" parameters.
All committed pages must be reserved in advance.
Parameters:
addr
is the page-aligned base virtual address of the memory area.size
is the size of the memory area in bytes (must be a multiple of the page size).flags
is an unused parameter (indicate the VMM_FLAG_LOCKED flag in this parameter value to ensure compatibility).
If pages are successfully committed, the function returns rcOk.
Page topKnVmDecommit()
This function is declared in the coresrv/vmm/vmm_api.h
file.
Retcode KnVmDecommit(void *addr, rtl_size_t size);
Frees a range of pages (switches them to the reserved state).
Parameters:
addr
is the page-aligned base virtual address of the memory area.size
is the size of the memory area in bytes (must be a multiple of the page size).
If pages are successfully freed, the function returns rcOk.
Page topKnVmProtect()
This function is declared in the coresrv/vmm/vmm_api.h
file.
Retcode KnVmProtect(void *addr, rtl_size_t size, int newFlags);
Modifies the protection attributes of reserved or committed memory pages.
Parameters:
addr
is the page-aligned base virtual address of the memory area.size
is the size of the memory area in bytes (must be a multiple of the page size).newFlags
refers to new protection attributes.
If the protection attributes are successfully changed, the function returns rcOk.
Permissible combinations of memory protection attributes:
- VMM_FLAG_READ allows reading page contents.
- VMM_FLAG_READ | VMM_FLAG_WRITE allows reading and modifying page contents.
- VMM_FLAG_READ | VMM_FLAG_EXECUTE allows reading and executing page contents.
- VMM_FLAG_RWX_MASK or VMM_FLAG_READ | VMM_FLAG_WRITE | VMM_FLAG_EXECUTE refers to full access to page contents (these entries are equivalent).
KnVmUnmap()
This function is declared in the coresrv/vmm/vmm_api.h
file.
Retcode KnVmUnmap(void *addr, rtl_size_t size);
Frees the memory area.
Parameters:
addr
refers to the page-aligned address of the memory area.size
refers to the memory area size.
If pages are successfully freed, the function returns rcOk.
Page topKosMemAlloc()
This function is declared in the kos/alloc.h
file.
void *KosMemAlloc(rtl_size_t size);
This function allocates (reserves and commits) a memory area equal to the specific size
of bytes.
This function returns a pointer to the allocated area or RTL_NULL if memory could not be allocated.
Memory allocated by using the KosMemAlloc()
function has the following allocation flags: VMM_FLAG_READ | VMM_FLAG_WRITE, VMM_FLAG_RESERVE, VMM_FLAG_COMMIT, VMM_FLAG_LOCKED. To allocate memory with other allocation flags, use the KnVmAllocate()
function.
KosMemAllocEx()
This function is declared in the kos/alloc.h
file.
void *KosMemAllocEx(rtl_size_t size, rtl_size_t align, int zeroed);
This function is analogous to KosMemAlloc()
, but it also has additional parameters:
align
refers to the alignment of the memory area in bytes (power of two).zeroed
determines whether or not the memory area needs to be filled with zeros (1 means fill, 0 means do not fill).
KosMemFree()
This function is declared in the kos/alloc.h
file.
void KosMemFree(void *ptr);
This function frees a memory area that was allocated using the KosMemAlloc()
, KosMemZalloc()
or KosMemAllocEx()
function.
ptr
is the pointer to the freed memory area.
KosMemGetSize()
This function is declared in the kos/alloc.h
file.
rtl_size_t KosMemGetSize(void *ptr);
This function returns the size (in bytes) of the memory area allocated using the KosMemAlloc()
, KosMemZalloc()
or KosMemAllocEx()
function.
ptr
is the pointer to the memory area.
KosMemZalloc()
This function is declared in the kos/alloc.h
file.
void *KosMemZalloc(rtl_size_t size);
This function is analogous to KosMemAlloc()
, but it also fills the allocated memory area with zeros.
KosThreadCallback()
The callback function prototype is declared in the kos/thread.h
file.
typedef void KosThreadCallback(KosThreadCallbackReason reason);
/* Callback function argument */
typedef enum KosThreadCallbackReason {
KosThreadCallbackReasonCreate,
KosThreadCallbackReasonDestroy,
} KosThreadCallbackReason;
When a new thread is created, all registered callback functions will be called with the KosThreadCallbackReasonCreate
argument. When the thread is terminated, they will be called with the KosThreadCallbackReasonDestroy
argument.
KosThreadCallbackRegister()
This function is declared in the kos/thread.h
file.
Retcode KosThreadCallbackRegister(KosThreadCallback *callback);
This function registers a custom callback function. When a thread is created and terminated, all registered callback functions will be called.
Page topKosThreadCallbackUnregister()
This function is declared in the kos/thread.h
file.
Retcode KosThreadCallbackUnregister(KosThreadCallback *callback);
This function deregisters the custom callback function (removes it from the list of called functions).
Page topKosThreadCreate()
This function is declared in the kos/thread.h
file.
Retcode KosThreadCreate(Tid *tid,
rtl_uint32_t priority,
rtl_uint32_t stackSize,
ThreadRoutine routine,
void *context,
int suspended);
This function creates a new thread.
Input parameters:
priority
must be within the interval from 0 to 31; the following priority constants are available:ThreadPriorityLowest
(0),ThreadPriorityNormal
(15) andThreadPriorityHighest
(31).stackSize
is the size of the stack.routine
is the function that will be executed in the thread.context
is the argument that will be passed to theroutine
function.suspended
lets you create a thread in the suspended state (1 means create suspended, 0 means create not suspended).
Output parameters:
tid
is the ID of the created thread.
Example
int main(int argc, char **argv)
{
Tid tidB;
Tid tidC;
Retcode rcB;
Retcode rcC;
static ThreadContext threadContext[] = {
{.ddi = "B", .deviceName = "/pci/bus0/dev2/fun0/DDI_B"},
{.ddi = "C", .deviceName = "/pci/bus0/dev2/fun0/DDI_C"},
};
rcB = KosThreadCreate(&tidB, ThreadPriorityNormal,
ThreadStackSizeDefault,
FbHotplugThread,
&threadContext[0], 0);
if (rcB != rcOk)
ERR("Failed to start thread %s", threadContext[0].ddi);
rcC = KosThreadCreate(&tidC, ThreadPriorityNormal,
ThreadStackSizeDefault,
FbHotplugThread,
&threadContext[1], 0);
if (rcC != rcOk)
ERR("Failed to start thread %s", threadContext[1].ddi);
/* Waiting for the threads to complete */
...
}
KosThreadCurrentId()
This function is declared in the kos/thread.h
file.
Tid KosThreadCurrentId(void);
This function requests the TID of the calling thread.
If successful, the function returns the thread ID (TID).
Page topKosThreadExit()
This function is declared in the kos/thread.h
file.
void KosThreadExit(rtl_int32_t exitCode);
This function forcibly terminates the current thread with the exitCode
.
KosThreadGetStack()
This function is declared in the kos/thread.h
file.
void *KosThreadGetStack(Tid tid, rtl_uint32_t *size);
This function gets the stack of the thread with the specific tid
.
Output parameter size
contains the stack size.
If successful, the function returns the pointer to the beginning of the stack.
Page topKosThreadOnce()
This function is declared in the kos/thread.h
file.
typedef int KosThreadOnceState;
Retcode KosThreadOnce(KosThreadOnceState *onceControl,
void (* initRoutine) (void));
This function lets you call the defined initRoutine
procedure precisely one time, even when it is called from multiple threads.
The onceControl
parameter is designed to control the one-time call of the procedure.
If the procedure is successfully called, and if it was called previously, the KosThreadOnce()
function returns rcOk.
KosThreadResume()
This function is declared in the kos/thread.h
file.
Retcode KosThreadResume(Tid tid);
This function resumes the thread with the identifier tid
that was created in the suspended state.
If successful, the function returns rcOk.
Page topKosThreadSleep()
This function is declared in the kos/thread.h
file.
Retcode KosThreadSleep(rtl_uint32_t mdelay);
Suspends execution of the current thread for mdelay
(in milliseconds).
If successful, the function returns rcOk.
Page topKosThreadSuspend()
This function is declared in the kos/thread.h
file.
Retcode KosThreadSuspend(Tid tid);
Permanently stops the current thread without finishing it.
The tid
parameter must be equal to the identifier of the current thread (a limitation of the current implementation).
If successful, the function returns rcOk.
Page topKosThreadTerminate()
This function is declared in the kos/thread.h
file.
Retcode KosThreadTerminate(Tid tid, rtl_int32_t exitCode);
This function terminates the thread of the calling process. The tid
parameter defines the ID of the thread.
If the tid
points to the current thread, the exitCode
parameter defines the thread exit code.
If successful, the function returns rcOk.
Page topKosThreadTlsGet()
This function is declared in the kos/thread.h
file.
void *KosThreadTlsGet(void);
This function returns the pointer to the local storage of the thread (TLS) or RTL_NULL if there is no TLS.
Page topKosThreadTlsSet()
This function is declared in the kos/thread.h
file.
Retcode KosThreadTlsSet(void *tls);
This function defines the address of the local storage for the thread (TLS).
Input argument tls
contains the TLS address.
KosThreadWait()
This function is declared in the kos/thread.h
file.
int KosThreadWait(rtl_uint32_t tid, rtl_uint32_t timeout);
This function suspends execution of the current thread until termination of the thread with the identifier tid
or until the timeout
(in milliseconds).
The KosThreadWait()
call with a zero value for timeout
is analogous to the KosThreadYield()
call .
If successful, the function returns rcOk. In case of timeout, it returns rcTimeout.
Page topKosThreadYield()
This function is declared in the kos/thread.h
file.
void KosThreadYield(void);
Passes execution of the thread that called it to the next thread.
The KosThreadYield()
call is analogous to the KosThreadSleep()
call with a zero value for mdelay
.
KnHandleClose()
This function is declared in the coresrv/handle/handle_api.h
file.
Retcode KnHandleClose(Handle handle);
Deletes the handle
.
If successful, the function returns rcOk, otherwise it returns an error code.
Deleting a handle does not invalidate its ancestors and descendants (in contrast to revoking a handle, which actually invalidates the descendants of the handle – see KnHandleRevoke()
and KnHandleRevokeSubtree()
). When a handle is deleted, the integrity of the handle inheritance tree is also preserved. The location of a deleted handle is taken over by its parent, which becomes the immediate ancestor of the descendants of the deleted handle.
KnHandleCreateBadge()
This function is declared in the coresrv/handle/handle_api.h
file.
Retcode KnHandleCreateBadge(Notice notice, rtl_uintptr_t eventId,
void *context, Handle *handle);
This function creates a resource transfer context object for the specified resource transfer context
and configures a notification receiver named notice
for receiving notifications about this object. The notification receiver is configured to receive notifications about events that match the EVENT_OBJECT_DESTROYED
and EVENT_BADGE_CLOSED
flags of the event mask.
Input parameter eventId
defines the ID of a "resource–event mask" entry in the notification receiver.
Output parameter handle
contains the handle of the resource transfer context object.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnHandleCreateUserObject()
This function is declared in the coresrv/handle/handle_api.h
file.
Retcode KnHandleCreateUserObject(rtl_uint32_t type, rtl_uint32_t rights,
void *context, Handle *handle);
Creates the specified handle
of the specified type
with the rights
permissions mask.
The type
parameter can take values ranging from HANDLE_TYPE_USER_FIRST
to HANDLE_TYPE_USER_LAST
.
The HANDLE_TYPE_USER_FIRST
and HANDLE_TYPE_USER_LAST
macros are defined in the handle/handletype.h
header file.
The context
parameter defines the context of the user resource. If successful, the function returns rcOk, otherwise it returns an error code.
Example
Retcode ServerPortInit(ServerPort *serverPort)
{
Retcode rc = rcInvalidArgument;
Notice serverEventNotice;
rc = KnHandleCreateUserObject(HANDLE_TYPE_USER_FIRST, OCAP_HANDLE_SET_EVENT | OCAP_HANDLE_GET_EVENT,
serverPort, &serverPort->handle);
if (rc == rcOk) {
KosRefObject(serverPort);
rc = KnNoticeSubscribeToObject(serverEventNotice,
serverPort->handle,
EVENT_OBJECT_DESTROYED,
(rtl_uintptr_t) serverPort);
if (rc != rcOk) {
KosPutObject(serverPort);
KnHandleClose(serverPort->handle);
serverPort->handle = INVALID_HANDLE;
}
}
return rc;
}
KnHandleRevoke()
This function is declared in the coresrv/handle/handle_api.h
file.
Retcode KnHandleRevoke(Handle handle);
Deletes the handle
and revokes all of its descendants.
If successful, the function returns rcOk, otherwise it returns an error code.
Revoked handles are not deleted. However, you cannot query resources via revoked handles. Any function that accepts a handle will end with the rcHandleRevoked error if this function is called with a revoked handle.
KnHandleRevokeSubtree()
This function is declared in the coresrv/handle/handle_api.h
file.
Retcode KnHandleRevokeSubtree(Handle handle, Handle badge);
This function revokes the handles that make up the handle inheritance subtree of the specified handle
.
The root node of the inheritance subtree is the handle that was generated by the transfer of the specified handle
associated with the badge
resource transfer context object.
If successful, the function returns rcOk, otherwise it returns an error code.
Revoked handles are not deleted. However, you cannot query resources via revoked handles. Any function that accepts a handle will end with the rcHandleRevoked error if this function is called with a revoked handle.
nk_get_badge_op()
This function is declared in the nk/types.h
file.
static inline
nk_err_t nk_get_badge_op(const nk_handle_desc_t *desc,
nk_rights_t operation,
nk_badge_t *badge);
This function extracts the pointer to the badge
resource transfer context from the transport container of desc
if the operation
flags are set in the permissions mask that is placed in the transport container of desc
.
If successful, the function returns NK_EOK, otherwise it returns an error code.
Page topnk_is_handle_dereferenced()
This function is declared in the nk/types.h
file.
static inline
nk_bool_t nk_is_handle_dereferenced(const nk_handle_desc_t *desc);
This function returns a non-zero value if the handle in the transport container of desc
was obtained as a result of a handle dereferencing operation.
This function returns zero if the handle in the transport container of desc
was obtained as a result of a handle transfer operation.
Managing handles
Handles are managed by using functions of the Handle Manager and Notification Subsystem.
The Handle Manager is provided for the user in the following files:
coresrv/handle/handle_api.h
is a header file of thelibkos
library.services/handle/Handle.idl
is an IDL description of the Handle Manager's IPC interface.
The Notification Subsystem is provided for the user in the following files:
coresrv/handle/notice_api.h
is a header file of thelibkos
library.services/handle/Notice.idl
is an IDL description of the IPC interface of the Notification Subsystem.
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 services/ocap.h
header file). 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 services/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 services/ocap.h
header file. The resource provider must export the public header files describing the structure 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 services/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
The handles of user resources are created by the providers of the resources. The KnHandleCreateUserObject()
function declared in the coresrv/handle/handle_api.h
header file is used to create handles of user resources.
handle_api.h (fragment)
/**
* Creates the specified handle of the specified type with the rights permissions mask.
* The "type" parameter can take values ranging from HANDLE_TYPE_USER_FIRST to
* HANDLE_TYPE_USER_LAST. The HANDLE_TYPE_USER_FIRST and HANDLE_TYPE_USER_LAST macros
* are defined in the handletype.h header file. The "context" parameter defines the context
* of the user resource.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleCreateUserObject(rtl_uint32_t type, rtl_uint32_t rights,
void *context, Handle *handle);
The user resource context is the data that allows the resource provider to identify the resource and its state when access to the resource is requested by other programs. 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.
The type
parameter of the KnHandleCreateUserObject()
function is reserved for potential future use and does not affect the behavior of the function, but it must take a value from the interval specified in the function comments.
For details about a handle permissions mask, see "Handle permissions mask".
Page topTransferring handles
Overview
Handles are transferred between programs so that clients (programs that utilize resources) can obtain 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 program. This handle is registered as a descendant of the transferred handle and identifies the same resource.
One handle can be transferred multiple times by one or multiple programs. Each transfer initiates the creation of a new descendant of the transferred handle on the recipient program side. A program can transfer the handles that it received from other programs or from the KasperskyOS kernel (when creating handles of system resources). 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 program can transfer handles for user resources and system resources if the access rights of these handles permit such a transfer. A descendant may have less access rights than an ancestor. For example, a transferring program with read-and-write permissions for a file can transfer read-only permissions. The transferring program can also prohibit the recipient program from further transferring the handle. Access rights are defined in the transferred permissions mask for the handle.
Conditions for transferring handles
For programs to transfer handles to other programs, the following conditions must be met:
- An IPC channel is created between the programs.
- The solution security policy (
security.psl
) allows interaction between the programs. - Interface methods are implemented for transferring handles.
- The client program received the endpoint ID (RIID) of the server program that has methods for transferring handles.
Interface methods for transferring handles are declared in the IDL language with input (in
) and/or output (out
) parameters of the Handle
type. Methods with input parameters of the Handle
type are intended for transferring handles from the client program to the server program. Methods with output parameters of the Handle
type are intended for transferring handles from the server program to the client program. No more than seven input and seven output parameters of the Handle
type can be declared for one method.
Example IDL description containing declarations of interface methods for transferring handles:
package IpcTransfer
interface {
PublishResource1(in Handle handle, out UInt32 result);
PublishResource7(in Handle handle1, in Handle handle2,
in Handle handle3, in Handle handle4,
in Handle handle5, in Handle handle6,
in Handle handle7, out UInt32 result);
OpenResource(in UInt32 ID, out Handle handle);
}
For each parameter of the Handle
type, the NK compiler generates a field in the *_req
request structure and/or *_res
response structure of the nk_handle_desc_t
type (hereinafter also referred to as the transport container of the handle). This type is declared in the nk/types.h
header file 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 is the data that allows the server program 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. A server program receives a pointer to the resource transfer context when dereferencing a handle.
Regardless of whether or not a server program is the resource provider, it 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 request.
If the server program 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 program that is the resource provider can use 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 program uses multiple various-type resources of the server program, the resource transfer contexts (or contexts of user resources if they are used as resource transfer contexts) must be specialized objects of the KosObject
type. This is necessary so that the server program can verify that the client program 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 program could mistakenly send the interface method a resource handle that does not correspond to this method. For example, a client program receives a file handle and sends it to an interface method for working with volumes.
To associate a handle transfer with a resource transfer context, the server program 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 object that stores the pointer to the resource transfer context. The resource transfer context object is created by the KnHandleCreateBadge()
function, which is declared in the coresrv/handle/handle_api.h
header file. This function is bound to the Notification Subsystem regarding the state of resources because a server program needs to know when a resource transfer context object will be closed and terminated. The server program 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 when deleting or revoking the handle descendants (see Deleting handles, Revoking handles) that were generated during its transfer in association with this object. (A transferred handle may be deleted intentionally or unintentionally, such as when a recipient client program is unexpectedly terminated.) After receiving a notification regarding the closure of a resource transfer context object, the server program deletes the handle of this object. After this, the resource transfer context object is terminated. After receiving a notification regarding the termination of the resource transfer context object, the server program 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.
handle_api.h (fragment)
/**
* Creates a resource transfer context object for
* the resource transfer "context" and configures the
* notification receiver "notice" to receive notifications about
* this object. The notification receiver is configured to
* receive notifications about events that match the
* event mask flags OBJECT_DESTROYED and EVENT_BADGE_CLOSED.
* Input parameter eventId defines the identifier of the
* "resource–event mask" entry in the notification receiver.
* Output parameter handle contains the handle of the
* resource transfer context.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleCreateBadge(Notice notice, rtl_uintptr_t eventId,
void *context, Handle *handle);
Packaging data into the transport container of a handle
The nk_handle_desc()
macro declared in the nk/types.h
header file is used to package a handle, handle permissions mask and resource transfer context object handle into a handle transport container. This macro receives a variable number of arguments.
If no argument is passed to the macro, the NK_INVALID_HANDLE
value will be written in the handle
field of the nk_handle_desc_t
structure.
If one argument is passed to the macro, this argument is interpreted as the handle.
If two arguments are passed to the macro, the first argument is interpreted as the handle and the second argument is interpreted as the handle permissions mask.
If three arguments are passed to the macro, the first argument is interpreted as the handle, the second argument is interpreted as the handle permissions mask, and the third argument is interpreted as the resource transfer context object handle.
Extracting data from the transport container of a handle
The nk_get_handle()
, nk_get_rights()
and nk_get_badge_op()
(or nk_get_badge()
) functions that are declared in the nk/types.h
header file are used to extract the handle, handle permissions mask, and pointer to the resource transfer context, respectively, from the transport container of a handle. The nk_get_badge_op()
and nk_get_badge()
functions are used only when dereferencing handles.
Handle transfer scenarios
A scenario for transferring handles from a client program to the server program includes the following steps:
- The transferring client program packages the handles and handle permissions masks into the fields of the
*_req
requests structure of thenk_handle_desc_t
type. - The transferring client program calls the interface method for transferring handles to the server program. This method executes the
Call()
system call. - The recipient server program receives the request by executing the
Recv()
system call. - The dispatcher on the recipient server program side calls the method corresponding to the request. This method extracts the handles and handle permissions masks from the fields of the
*_req
request structure of thenk_handle_desc_t
type.
A scenario for transferring handles from the server program to a client program includes the following steps:
- The recipient client program calls the interface method for receiving handles from the server program. This method executes the
Call()
system call. - The transferring server program receives the request by executing the
Recv()
system call. - The dispatcher on the transferring server program side calls the method corresponding to the request. This method packages the handles, handle permissions masks and resource transfer context object handles into the fields of the
*_res
response structure of thenk_handle_desc_t
type. - The transferring server program responds to the request by executing the
Reply()
system call. - On the recipient client program side, the interface method returns control. After this, the recipient client program extracts the handles and handle permissions masks from the fields of the
*_res
response structure of thenk_handle_desc_t
type.
If the transferring program 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 made by the transferring or recipient client program or the Reply()
system call made by the transferring server program ends with the rcSecurityDisallow
error.
Dereferencing handles
When dereferencing a handle, the client program sends the server program the handle, and the server program 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 program and already owned by the server program. Dereferencing occurs when a client program that called methods for working with a resource (such as read/write or access closure) sends the server program the handle that was received from this server program 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 program packages the handle into a field of the
*_req
request structure of thenk_handle_desc_t
type. - The client program calls the interface method for sending the handle to the server program for the purpose of performing operations with the resource. This method executes the
Call()
system call. - The server program receives the request by executing the
Recv()
system call. - The dispatcher on the server program side calls the method corresponding to the 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 program) 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 verifications, the server program utilizes the nk_is_handle_dereferenced()
and nk_get_badge_op()
functions that are declared in the nk/types.h
header file.
types.h (fragment)
/**
* Returns a value different from null if
* the handle in the transport container of
* "desc" is received as a result of dereferencing
* the handle. Returns null if the handle
* in the transport container of "desc" is received
* as a result of a handle transfer.
*/
static inline
nk_bool_t nk_is_handle_dereferenced(const nk_handle_desc_t *desc)
/**
* Extracts the pointer to the resource transfer context
* "badge" from the transport container of "desc"
* if the permissions mask that was put in the transport
* container of the desc handle has the operation flags set.
* If successful, the function returns NK_EOK, otherwise it returns an error code.
*/
static inline
nk_err_t nk_get_badge_op(const nk_handle_desc_t *desc,
nk_rights_t operation,
nk_badge_t *badge)
Generally, the server program does not require the handle that was received from dereferencing because the server program normally retains the handles that it owns, for example, within the contexts of user resources. However, the server program can extract this handle from the handle transport container if necessary.
Page topRevoking handles
A program can revoke descendants of a handle that it owns. Handles are revoked according to the handle inheritance tree.
Revoked handles are not deleted. However, you cannot query resources via revoked handles. Any function that accepts a handle will end with the rcHandleRevoked
error if this function is called with a revoked handle.
Handles are revoked by using the KnHandleRevoke()
and KnHandleRevokeSubtree()
functions declared in the coresrv/handle/handle_api.h
header file. The KnHandleRevokeSubtree()
function uses the resource transfer context object that is created when transferring handles.
handle_api.h (fragment)
/**
* Deletes the handle and revokes all of its descendants.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleRevoke(Handle handle);
/**
* Revokes handles that form the
* inheritance subtree of the handle. The root node of the inheritance subtree
* is the handle that is generated by transferring
* the handle associated with the object of the
* "badge" resource transfer context.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleRevokeSubtree(Handle handle, Handle badge);
Notifying about the state of resources
Programs can track events that occur with resources (system resources as well as user resources), and inform other programs about events involving user resources.
Functions of the Notification Subsystem are declared in the coresrv/handle/notice_api.h
header file. The Notification Subsystem provides for the use of event masks.
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 general part describes the general events that are not specific to any particular resource (the flags of these events are defined in the handle/event_descr.h
header file). For example, the general part contains the EVENT_OBJECT_DESTROYED
flag, which defines the "resource termination" event. The specialized part describes the events that are specific to a particular user resource. The structure of the specialized part is defined by the resource provider by using the OBJECT_EVENT_SPEC()
macro that is defined in the handle/event_descr.h
header file. The resource provider must export the public header files describing the structure of the specialized part.
The scenario for receiving notifications about events that occur with a resource consists of the following steps:
- The
KnNoticeCreate()
function creates a notification receiver (object that stores notifications). - The
KnNoticeSubscribeToObject()
function adds "resource–event mask" entries to the notification receiver to configure it to receive notifications about events that occur with relevant resources. The set of tracked events is defined for each resource by an event mask. - The
KnNoticeGetEvent()
function is called to extract notifications from the notification receiver.
The KnNoticeSetObjectEvent()
function is used to notify a program about events that occur with a user resource. A call of this function initiates the corresponding notifications in the notification receivers that are configured to track these events that occur with this resource.
notice_api.h (fragment)
/**
* Creates the notification receiver named "notice".
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeCreate(Notice *notice);
/**
* Adds a "resource–event mask" entry
* to the "notice" notification receiver so that it will receive notifications about
* events that occur with the "object" resource and that match the
* evMask event mask. Input parameter evId defines the identifier
* of the entry that is assigned by the user and used to
* identify the entry in received notifications.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeSubscribeToObject(Notice notice,
Handle object,
rtl_uint32_t evMask,
rtl_uintptr_t evId);
/**
* Extracts notifications from the "notice" notification receiver
* while waiting for events to occur within the specific number of milliseconds.
* Input parameter countMax defines the maximum number
* of notifications that can be extracted. Output parameter
* "events" contains a set of extracted notifications of the EventDesc type.
* Output parameter "count" contains the number of notifications that
* were extracted.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeGetEvent(Notice notice,
rtl_uint64_t msec,
rtl_size_t countMax,
EventDesc *events,
rtl_size_t *count);
/* Notification structure */
typedef struct {
/* Identifier of the "resource–event mask" entry
* in the notification receiver */
rtl_uintptr_t eventId;
/* Mask of events that occurred. */
rtl_uint32_t eventMask;
} EventDesc;
/**
* Signals that events from event mask
* evMask occurred with the "object" user resource.
* You cannot set flags of the general part of an event mask
* because events from the general part of an event mask can be
*signaled only by the KasperskyOS kernel.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode Kn
NoticeSetObjectEvent(Handle object, rtl_uint32_t evMask);
Deleting handles
A program can delete the handles that it owns. Deleting 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 deleted handle can still be used to provide access to the resource that they identify. Also, deleting a handle does not disrupt the handle inheritance tree associated with the resource identified by the particular handle. The place of a deleted handle is occupied by its ancestor. In other words, the ancestor of a deleted handle becomes the direct ancestor of the descendants of the deleted handle.
Handles are deleted by using the KnHandleClose()
function, which is declared in the coresrv/handle/handle_api.h
header file.
handle_api.h (fragment)
/**
* Deletes the handle.
* If successful, the function returns rcOk, otherwise it returns an error code.
*/
Retcode KnHandleClose(Handle handle);
OCap usage example
This article describes an OCap usage scenario in which the server program 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 client program uses these methods.
IDL description of interface methods:
package SimpleOCap
interface {
OpenResource(in UInt32 ID, out Handle handle);
UseResource(in Handle handle, in UInt8 param, out UInt8 result);
CloseResource(in Handle handle);
}
The scenario includes the following steps:
- 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 client 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 or termination 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 client, which means that the client receives a descendant of the handle owned by the resource provider.
- The
OpenResource()
method call completes successfully. The client 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 client to query the resource, but is transferred so that the client can find out its permissions for accessing the resource.
- The resource provider creates the resource transfer context and calls the
- The client calls the
UseResource()
method to utilize the resource.- The handle that was received from the resource provider at step 2 is used as an argument of the
UseResource()
method. Before calling this method, the client 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 client) 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 client. Then the resource provider sends the client the results of this operation.
- The
UseResource()
method call completes successfully. The client receives the results of the operation performed on the resource.
- The handle that was received from the resource provider at step 2 is used as an argument of the
- The client 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 an argument of the
CloseResource()
method. Before calling this method, the client uses thenk_handle_desc()
macro to package the handle into the handle transport container. After theCloseResource()
method is called, the client uses theKnHandleClose()
function to delete 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 client. The resource handle owned by the resource provider and the handle of the resource transfer context object are used as arguments 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 client does not have to be revoked because the client already deleted it. However, the revoke operation is performed in case the resource provider is not sure if the client actually deleted the handle). - The
CloseResource()
method call completes successfully.
- The handle that was received from the resource provider at step 2 is used as an argument of the
- The resource provider frees up the memory that was allocated for the resource transfer context and the 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 delete 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 has been terminated, and frees up the memory that was allocated for the resource transfer context. - The resource provider uses the
KnHandleClose()
function to delete the resource handle and to free up the memory that was allocated for the user resource context.
- The resource provider calls the
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 general part describes the general events that are not specific to any particular resource (the flags of these events are defined in the handle/event_descr.h
header file). For example, the general part contains the EVENT_OBJECT_DESTROYED
flag, which defines the "resource termination" event. The specialized part describes the events that are specific to a particular user resource. The structure of the specialized part is defined by the resource provider by using the OBJECT_EVENT_SPEC()
macro that is defined in the handle/event_descr.h
header file. The resource provider must export the public header files describing the structure of the specialized part.
EventDesc
The structure describing the notification is declared in the file coresrv/handle/notice_api.h
.
typedef struct {
rtl_uintptr_t eventId;
rtl_uint32_t eventMask;
} EventDesc;
eventId
is the ID of the "resource–event mask" entry in the notification receiver.
eventMask
is the mask of events that occurred.
KnNoticeCreate()
This function is declared in the file coresrv/handle/notice_api.h
.
Retcode KnNoticeCreate(Notice *notice);
This function creates a notification receiver named notice
(object that stores notifications).
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnNoticeGetEvent()
This function is declared in the file coresrv/handle/notice_api.h
.
Retcode KnNoticeGetEvent(Notice notice,
rtl_uint64_t msec,
rtl_size_t countMax,
EventDesc *events,
rtl_size_t *count);
This function extracts notifications from the notice
notification receiver while waiting for events to occur within the specific number of milliseconds (msec
).
Input parameter countMax
defines the maximum number of notifications that can be extracted.
Output parameter events
contains a set of extracted notifications of the EventDesc
type.
Output parameter count
contains the number of notifications that were extracted.
If successful, the function returns rcOk, otherwise it returns an error code.
Example
const int maxEventsPerNoticeCall = 10;
Retcode rc;
EventDesc events[maxEventsPerNoticeCall];
rtl_size_t eventCount;
rc = KnNoticeGetEvent(notice, INFINITE_TIMEOUT, rtl_countof(events),
&events[0], &eventCount);
KnNoticeSetObjectEvent()
This function is declared in the file coresrv/handle/notice_api.h
.
Retcode KnNoticeSetObjectEvent(Handle object, rtl_uint32_t evMask);
This function signals that events from the event mask evMask
occurred with the object
resource.
You cannot set flags of the general part of an event mask because only the KasperskyOS kernel can provide signals regarding events from the general part of an event mask.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnNoticeSubscribeToObject()
This function is declared in the file coresrv/handle/notice_api.h
.
Retcode KnNoticeSubscribeToObject(Notice notice,
Handle object,
rtl_uint32_t evMask,
rtl_uintptr_t evId);
This function adds a "resource–event mask" entry to the notice
notification receiver so that it can receive notifications about events that occur with the object
resource and match the event mask evMask
.
Input parameter evId
defines the entry ID that is assigned by the user and is used to identify the entry in received notifications.
If successful, the function returns rcOk, otherwise it returns an error code.
For a usage example, see KnHandleCreateUserObject()
.
EntityConnect()
This function is declared in the header file coresrv/entity/entity_api.h
.
Retcode EntityConnect(Entity *cl, Entity *sr);
This function connects processes with an IPC channel. To do so, the function creates IPC handles for the client process cl
and the server process sr
, and then binds the handles to each other. The created channel will be included into the default group of channels (the name of this group matches the name of the server process). The connected processes must be in the stopped state.
If successful, the function returns rcOk.
Page topEntityConnectToService()
This function is declared in the header file coresrv/entity/entity_api.h
.
Retcode EntityConnectToService(Entity *cl, Entity *sr, const char *name);
This function connects processes with an IPC channel. To do so, the function creates IPC handles for the client process cl
and the server process sr
, and then binds the handles to each other. The created channel will be added to the group of channels with the specified name
. The connected processes must be in the stopped state.
If successful, the function returns rcOk.
Page topEntityInfo
The EntityInfo
structure describing the process is declared in the file named if_connection.h
.
typedef struct EntityInfo {
/* process class name */
const char *eiid;
/* maximum number of endpoints */
nk_iid_t max_endpoints;
/* information about the process endpoints */
const EndpointInfo *endpoints;
/* arguments to be passed to the process when it is started */
const char *args[ENTITY_ARGS_MAX + 1];
/* environment variables to be passed to the process when it is started */
const char *envs[ENTITY_ENV_MAX + 1];
/* process flags */
EntityFlags flags;
/* process components tree */
const struct nk_component_node *componentTree;
} EntityInfo;
typedef struct EndpointInfo {
char *name; /* fully qualified name of the endpoint */
nk_iid_t riid; /* endpoint ID */
char *iface_name; /* name of the interface implemented by the endpoint */
} EndpointInfo;
typedef enum {
ENTITY_FLAGS_NONE = 0,
/* the process is reset if an unhandled exception occurs */
ENTITY_FLAG_DUMPABLE = 1,
} EntityFlags;
EntityInit()
This function is declared in the header file coresrv/entity/entity_api.h
.
Entity *EntityInit(const EntityInfo *info);
This function creates a process. The info
parameter defines the name of the process class and (optionally) its endpoints, arguments and environment variables.
The created process will have the default name (matching the process class name), and the default name for the executable file (also matching the process class name).
If successful, the function returns the structure describing the new process. The created process is in the stopped state.
If an error occurs, the function returns RTL_NULL.
Page topEntityInitEx()
This function is declared in the header file coresrv/entity/entity_api.h
.
Entity *EntityInitEx(const EntityInfo *info, const char *name,
const char *path);
This function creates a process.
The info
parameter defines the name of the process class and (optionally) its endpoints, arguments and environment variables.
The name
parameter defines the name of the process. If it has the RTL_NULL value, the process class name from the info
parameter will be used as the process name.
The path
parameter defines the name of the executable file in the solution's ROMFS image. If it has the RTL_NULL value, the process class name from the info
parameter will be used as the file name.
If successful, the function returns the structure describing the new process. The created process is in the stopped state.
If an error occurs, the function returns RTL_NULL.
Page topEntityRun()
This function is declared in the header file coresrv/entity/entity_api.h
.
Retcode EntityRun(Entity *entity);
This function starts a process that is in the stopped state. The process is described by the entity
structure.
If successful, the function returns rcOk.
Page topKnCmAccept()
This function is declared in the coresrv/cm/cm_api.h
file.
Retcode KnCmAccept(const char *client, const char *service, rtl_uint32_t rsid,
Handle listener, Handle *handle);
This function accepts the client process channel creation request that was previously received using the KnCmListen()
call. This function is called by the server process.
Input parameters:
client
is the name of the client process that sent the request to create the channel.service
is the fully qualified name of the endpoint requested by the client process (for example,blkdev.ata
).rsid
is the endpoint ID.listener
is the listener handle; if it has the INVALID_HANDLE value, a new listener handle is created and will be used as the server IPC handle of the channel being created.
Output parameter handle
contains the server IPC handle of the channel being created.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnCmConnect()
This function is declared in the coresrv/cm/cm_api.h
file.
Retcode KnCmConnect(const char *server, const char *service,
rtl_uint32_t msecs, Handle *handle,
rtl_uint32_t *rsid);
This function sends a request to create a channel with the server process. This function is called by the client process.
Input parameters:
server
is the name of the server process that provides the endpoint.service
is the fully qualified name of the endpoint (for example,blkdev.ata
).msecs
is the timeout for accepting the request, in milliseconds.
Output parameters:
handle
is the client IPC handle.rsid
is the endpoint ID.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnCmDrop()
This function is declared in the coresrv/cm/cm_api.h
file.
Retcode KnCmDrop(const char *client, const char *service);
This function rejects the client process channel creation request that was previously received using the KnCmListen()
call. This function is called by the server process.
Parameters:
client
is the name of the client process that sent the request to create the channel.service
is the fully qualified name of the endpoint requested by the client process (for example,blkdev.ata
).
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnCmListen()
This function is declared in the coresrv/cm/cm_api.h
file.
Retcode KnCmListen(const char *filter, rtl_uint32_t msecs, char *client,
char *service);
This function checks for channel creation requests from client processes. This function is called by the server process.
Input parameters:
filter
is an unused parameter.msecs
is the request timeout, in milliseconds.
Output parameters:
client
is the name of the client process.service
is the fully qualified name of the endpoint requested by the client process (for example,blkdev.ata
).
If successful, the function returns rcOk, otherwise it returns an error code.
Page topNsCreate()
This function is declared in the coresrv/ns/ns_api.h
file.
Retcode NsCreate(const char *name, rtl_uint32_t msecs, NsHandle *ns);
This function attempts to connect to name server name
for the specified number of milliseconds (msecs
). If the name
parameter has the RTL_NULL value, the function attempts to connect to name server ns
(the default name server).
Output parameter ns
contains the handle for the connection with the name server.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topNsEnumServices()
This function is declared in the coresrv/ns/ns_api.h
file.
Retcode NsEnumServices(NsHandle ns, const char *type, unsigned index,
char *server, rtl_size_t serverSize,
char *service, rtl_size_t serviceSize);
This function enumerates the endpoints with the defined interface that are published on the name server.
Input parameters:
ns
is the handle of the connection with the name server that was previously received by using theNsCreate()
call.type
is the name of the interface implemented by the endpoint (for example,kl.drivers.Block
).index
is the index for enumerating endpoints.serverSize
is the maximum size of the buffer for theserver
output parameter in bytes.serviceSize
is the maximum size of the buffer for theservice
output parameter in bytes.
Output parameters:
server
is the name of the server process that provides the endpoint (for example,kl.drivers.Ata
).service
is the fully qualified name of the endpoint (for example,blkdev.ata
).
For example, you can receive a full list of server processes that provide an endpoint with the kl.drivers.Block
interface as follows.
rc = NsEnumServices(ns, "kl.drivers.Block", 0, outServerName, ServerNameSize, outServiceName, ServiceNameSize);
rc = NsEnumServices(ns, "kl.drivers.Block", 1, outServerName, ServerNameSize, outServiceName, ServiceNameSize);
...
rc = NsEnumServices(ns, "kl.drivers.Block", N, outServerName, ServerNameSize, outServiceName, ServiceNameSize);
Function calls with index incrementation continue until the function returns rcResourceNotFound.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topNsPublishService()
This function is declared in the coresrv/ns/ns_api.h
file.
Retcode NsPublishService(NsHandle ns, const char *type, const char *server,
const char *service);
This function publishes the endpoint with the defined interface on the name server.
Parameters:
ns
is the handle of the connection with the name server that was previously received by using theNsCreate()
call.type
is the name of the interface implemented by the published endpoint (for example,kl.drivers.Block
).server
is the name of the server process (for example,kl.drivers.Ata
).service
is the fully qualified name of the endpoint (for example,blkdev.ata
).
If successful, the function returns rcOk, otherwise it returns an error code.
Page topNsUnPublishService()
This function is declared in the coresrv/ns/ns_api.h
file.
Retcode NsUnPublishService( NsHandle ns, const char *type, const char *server,
const char *service);
This function unpublishes the endpoint on the name server.
Parameters:
ns
is the handle of the connection with the name server that was previously received by using theNsCreate()
call.type
is the name of the interface implemented by the published endpoint (for example,kl.drivers.Block
).server
is the name of the server process (for example,kl.drivers.Ata
).service
is the fully qualified name of the endpoint (for example,blkdev.ata
).
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKosCondvarBroadcast()
This function is declared in the kos/condvar.h
file.
void KosCondvarBroadcast(KosCondvar *condvar);
This function wakes all threads from the queue of threads that are blocked by the conditional variable condvar
.
KosCondvarDeinit()
This function is declared in the kos/condvar.h
file.
void KosCondvarDeinit(KosCondvar *condvar);
De-initializes the conditional variable condvar
.
KosCondvarInit()
This function is declared in the kos/condvar.h
file.
void KosCondvarInit(KosCondvar *condvar);
Initializes the conditional variable condvar
.
KosCondvarSignal()
This function is declared in the kos/condvar.h
file.
void KosCondvarSignal(KosCondvar *condvar);
This function wakes one thread from the queue of threads that are blocked by the conditional variable condvar
.
KosCondvarWait()
This function is declared in the kos/condvar.h
file.
Retcode KosCondvarWait(KosCondvar *condvar, KosMutex *mutex);
This function blocks execution of the current thread via the conditional variable condvar
until it is awakened using KosCondvarSignal() or KosCondvarBroadcast().
mutex
refers to the mutex that will be used for protecting the critical section.
If successful, the function returns rcOk.
Page topKosCondvarWaitTimeout()
This function is declared in the kos/condvar.h
file.
Retcode KosCondvarWaitTimeout(KosCondvar *condvar, KosMutex *mutex,
rtl_uint32_t mdelay);
This function blocks execution of the current thread via the conditional variable condvar
until it is awakened using KosCondvarSignal() or KosCondvarBroadcast(). The thread is blocked for no more than mdelay
(in milliseconds).
mutex
refers to the mutex that will be used for protecting the critical section.
This function returns rcOk if successful, or rcTimeout if it times out.
Page topKosEventDeinit()
This function is declared in the kos/event.h
file.
void KosEventDeinit(KosEvent *event);
This function frees the resources associated with an event
(deletes the event).
KosEventInit()
This function is declared in the kos/event.h
file.
void KosEventInit(KosEvent *event);
This function creates an event
.
The created event is in a non-signaling state.
Page topKosEventReset()
This function is declared in the kos/event.h
file.
void KosEventReset(KosEvent *event);
This function switches an event
to the non-signaling state (resets the event).
KosEventSet()
This function is declared in the kos/event.h
file.
void KosEventSet(KosEvent *event);
This function switches an event
to the signaling state (signals the event) and thereby wakes all threads that are waiting for it.
KosEventWait()
This function is declared in the kos/event.h
file.
void KosEventWait(KosEvent *event, rtl_bool reset);
Waits for the event to switch to signaling state.
The reset
parameter indicates whether the event should be automatically reset when the wait successfully ends.
Returns rcOk if successful.
Page topKosEventWaitTimeout()
This function is declared in the kos/event.h
file.
Retcode KosEventWaitTimeout(KosEvent *event, rtl_bool reset,
rtl_uint32_t msec);
Waits for the event to switch to signaling state for a period of msec
(milliseconds).
The reset
parameter indicates whether the event should be automatically reset when the wait successfully ends.
This function returns rcOk if successful, or rcTimeout if the timeout is exceeded.
Page topKosMutexDeinit()
This function is declared in the kos/mutex.h
file.
void KosMutexDeinit(KosMutex *mutex);
Deletes the specified mutex
.
KosMutexInit()
This function is declared in the kos/mutex.h
file.
void KosMutexInit(KosMutex *mutex);
Initializes the mutex
in an unlocked state.
KosMutexInitEx()
This function is declared in the kos/mutex.h
file.
void KosMutexInitEx(KosMutex *mutex, int recursive);
Initializes the mutex
in an unlocked state.
To initialize a recursive mutex, you need to pass the value 1 to the recursive
parameter.
KosMutexLock()
This function is declared in the kos/mutex.h
file.
void KosMutexLock(KosMutex *mutex);
Captures the specified mutex
.
If the mutex is already captured, the thread is locked and waits to be unlocked.
Page topKosMutexLockTimeout()
This function is declared in the kos/mutex.h
file.
Retcode KosMutexLockTimeout(KosMutex *mutex, rtl_uint32_t mdelay);
Captures the specified mutex
.
If the mutex is already captured, the thread is locked for mdelay
and waits to be unlocked.
This function returns rcOk if successful, or rcTimeout if it times out.
Page topKosMutexTryLock()
This function is declared in the kos/mutex.h
file.
Retcode KosMutexTryLock(KosMutex *mutex);
Attempts to capture the specified mutex
.
This function returns rcOk if the mutex could be captured, and returns rcBusy if the mutex could not be captured because it is already captured.
Page topKosMutexUnlock()
This function is declared in the kos/mutex.h
file.
void KosMutexUnlock(KosMutex *mutex);
Unlocks the specified mutex
.
To unlock a recursive mutex, you need to perform the same amount of KosMutexUnlock()
calls to match the amount of times the recursive mutex was locked.
KosRWLockDeinit()
This function is declared in the kos/rwlock.h
file.
void KosRWLockDeinit(KosRWLock *rwlock);
De-initializes the read-write lock rwlock
.
KosRWLockInit()
This function is declared in the kos/rwlock.h
file.
void KosRWLockInit(KosRWLock *rwlock);
Initializes the read-write lock rwlock
.
KosRWLockRead()
This function is declared in the kos/rwlock.h
file.
void KosRWLockRead(KosRWLock *rwlock);
Locks the read threads.
Page topKosRWLockTryRead()
This function is declared in the kos/rwlock.h
file.
Retcode KosRWLockTryRead(KosRWLock *rwlock);
Attempts to lock the read threads.
If successful, the function returns rcOk.
Page topKosRWLockTryWrite()
This function is declared in the kos/rwlock.h
file.
Retcode KosRWLockTryWrite(KosRWLock *rwlock);
Attempts to lock the write threads.
If successful, the function returns rcOk.
Page topKosRWLockUnlock()
This function is declared in the kos/rwlock.h
file.
void KosRWLockUnlock(KosRWLock *rwlock);
Removes the read-write lock rwlock
.
KosRWLockWrite()
This function is declared in the kos/rwlock.h
file.
void KosRWLockWrite(KosRWLock *rwlock);
Locks the write threads.
Page topKosSemaphoreDeinit()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreDeinit(KosSemaphore *semaphore);
This function destroys the specified semaphore
that was previously initialized by the KosSemaphoreInit()
function.
It is safe to destroy an initialized semaphore on which there are currently no locked threads. There could be an unpredictable effect from destroying a semaphore on which other threads are currently locked.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore; - rcFail if there are threads being locked by this semaphore.
KosSemaphoreInit()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreInit(KosSemaphore *semaphore, unsigned count);
Initializes the defined semaphore
with the initial count
value.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore; - rcFail if the
count
value exceeds KOS_SEMAPHORE_VALUE_MAX.
KosSemaphoreSignal()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreSignal(KosSemaphore *semaphore);
Frees (signals) the defined semaphore
.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore.
KosSemaphoreTryWait()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreTryWait(KosSemaphore *semaphore);
Attempts to acquire the defined semaphore
.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore; - rcBusy if the semaphore is already acquired.
KosSemaphoreWait()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreWait(KosSemaphore *semaphore);
Waits for acquisition of the defined semaphore
.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore.
KosSemaphoreWaitTimeout()
This function is declared in the kos/semaphore.h
file.
Retcode KosSemaphoreWaitTimeout(KosSemaphore *semaphore, rtl_uint32_t mdelay);
Waits for acquisition of the defined semaphore
for a period of mdelay
in milliseconds.
The function returns the following:
- rcOk if successful;
- rcInvalidArgument, if the
semaphore
points to an invalid semaphore; - rcTimeout if the timeout expired.
DmaInfo
The structure describing the DMA buffer is declared in the io/io_dma.h
file.
typedef struct {
/** DMA flags (attributes). */
DmaAttr flags;
/** Minimum order of DMA blocks in the buffer. */
rtl_size_t orderMin;
/** DMA buffer size. */
rtl_size_t size;
/** Number of DMA blocks (less than or equal to DMA_FRAMES_COUNT_MAX).
* It may be equal to 0 if a DMA buffer is not available for the device. */
rtl_size_t count;
/** Array of DMA block descriptors. */
union DmaFrameDescriptor {
struct {
/** Order of the DMA block. The number of pages in a block is equal to two
* to the power of the specified order. */
DmaAddr order: DMA_FRAME_ORDER_BITS;
/** Physical or IOMMU address of the DMA block. */
DmaAddr frame: DMA_FRAME_BASE_BITS;
};
/** DMA block descriptor */
DmaAddr raw;
} descriptors[1];
} DmaInfo;
DMA flags
DMA flags (attributes) are declared in the io/io_dma.h
file.
- DMA_DIR_TO_DEVICE allows transactions from the main memory to the device memory.
- DMA_DIR_FROM_DEVICE allows transactions from the device memory to the main memory.
- DMA_DIR_BIDIR allows transactions from the main memory to the device memory, and vice versa.
- DMA_ZONE_DMA32 allows the use of only the first 4 GB of memory for the buffer.
- DMA_ATTR_WRITE_BACK, DMA_ATTR_WRITE_THROUGH, DMA_ATTR_CACHE_DISABLE, and DMA_ATTR_WRITE_COMBINE are for managing the cache of memory pages.
KnIoDmaBegin()
This function is declared in the coresrv/io/dma.h
file.
Retcode KnIoDmaBegin(Handle rid, Handle *handle);
Allows the device to access the DMA buffer with the handle rid
.
Output parameter handle
contains the handle of this permission.
If successful, the function returns rcOk.
For a usage example, see KnIoDmaCreate().
To prevent a device from accessing the DMA buffer, you need to call the KnIoClose()
function while passing the specified handle
of the permission in this function.
KnIoDmaCreate()
This function is declared in the coresrv/io/dma.h
file.
Retcode KnIoDmaCreate(rtl_uint32_t order, rtl_size_t size, DmaAttr flags,
Handle *outRid);
This function registers and allocates a physical DMA buffer.
Input parameters:
order
is the minimum permissible order of DMA block allocation; the actual order of each block in the DMA buffer is chosen by the kernel (but will not be less than the specified order) and is indicated in the block handle; the order of a block determines the number of pages in it. For example, a block with an order of N consists of 2^N pages.size
refers to the size of the DMA buffer, in bytes (must be a multiple of the page size); the sum of all sizes of allocated DMA blocks will be no less than the specifiedsize
.flags
refers to DMA flags.
Output parameter outRid
contains the handle of the allocated DMA buffer.
If successful, the function returns rcOk.
If a DMA buffer is no longer being used, it must be freed by using the KnIoClose()
function.
Example
Retcode RegisterDmaMem(rtl_size_t size,
DmaAttr attr,
Handle *handle,
Handle *dmaHandle,
Handle *mappingHandle,
void **addr)
{
Retcode ret;
*handle = INVALID_HANDLE;
*dmaHandle = INVALID_HANDLE;
*mappingHandle = INVALID_HANDLE;
ret = KnIoDmaCreate(rtl_roundup_order(size >> PAGE_SHIFT),
size,
attr,
handle);
if (ret == rcOk) {
ret = KnIoDmaBegin(*handle, dmaHandle);
}
if (ret == rcOk) {
ret = KnIoDmaMap(*handle,
0,
size,
RTL_NULL,
VMM_FLAG_READ | VMM_FLAG_WRITE,
addr,
mappingHandle);
}
if (ret != rcOk) {
if (*mappingHandle != INVALID_HANDLE)
KnHandleClose(*mappingHandle);
if (*dmaHandle != INVALID_HANDLE)
KnHandleClose(*dmaHandle);
if (*handle != INVALID_HANDLE)
KnHandleClose(*handle);
}
return ret;
}
KnIoDmaGetInfo()
This function is declared in the coresrv/io/dma.h
file.
Retcode KnIoDmaGetInfo(Handle rid, DmaInfo **outInfo);
This function gets information about the DMA buffer with the handle rid
.
Output parameter outInfo
contains information about the DMA buffer.
If successful, the function returns rcOk.
In contrast to KnIoDmaGetPhysInfo()
, the outInfo
parameter contains IOMMU addresses of DMA blocks instead of physical addresses.
KnIoDmaGetPhysInfo()
This function is declared in the coresrv/io/dma.h
file.
Retcode KnIoDmaGetPhysInfo(Handle rid, DmaInfo **outInfo);
This function gets information about the DMA buffer with the handle rid
.
Output parameter outInfo
contains information about the DMA buffer.
If successful, the function returns rcOk.
In contrast to KnIoDmaGetInfo()
, the outInfo
parameter contains physical addresses of DMA blocks instead of IOMMU addresses.
KnIoDmaMap()
This function is declared in the coresrv/io/dma.h
file.
Retcode KnIoDmaMap(Handle rid, rtl_size_t offset, rtl_size_t length, void *hint,
int vmflags, void **addr, Handle *handle);
This function maps a DMA buffer area to the address space of a process.
Input parameters:
rid
is the handle of the DMA buffer allocated usingKnIoDmaCreate()
.offset
refers to the page-aligned offset of the start of the area from the start of the buffer, indicated in bytes.length
refers to the size of the area; it must be a multiple of the page size and must not exceed <buffer size -offset
>.hint
is the virtual address of the start of mapping; if it is equal to 0, the address is selected by the kernel.vmflags
refers to allocation flags.
In the vmflags
parameter, you can use the following allocation flags (vmm/flags.h
):
- VMM_FLAG_READ and VMM_FLAG_WRITE are memory protection attributes.
- VMM_FLAG_LOW_GUARD and VMM_FLAG_HIGH_GUARD add a protective page before and after the allocated memory, respectively.
Permissible combinations of memory protection attributes:
- VMM_FLAG_READ allows reading page contents.
- VMM_FLAG_WRITE allows modification of page contents.
- VMM_FLAG_READ | VMM_FLAG_WRITE allows reading and modifying page contents.
Output parameters:
addr
is the pointer to the virtual address of the start of the mapped area.handle
refers to the handle of the created mapping.
If successful, the function returns rcOk.
For a usage example, see KnIoDmaCreate().
To delete a created mapping, you must call the KnIoClose()
function and pass the specified mapping handle
in this function.
KnIommuAttachDevice()
This function is declared in the coresrv/iommu/iommu_api.h
file.
Retcode KnIommuAttachDevice(rtl_uint16_t bdf);
This function adds the PCI device with the bdf
identifier to the IOMMU group of the calling process (IOMMU domain).
Returns rcOk if successful.
Page topKnIommuDetachDevice()
This function is declared in the coresrv/iommu/iommu_api.h
file.
Retcode KnIommuDetachDevice(rtl_uint16_t bdf);
This function removes the PCI device with the bdf
identifier from the IOMMU group of the calling process (IOMMU domain).
If successful, the function returns rcOk.
Page topIoReadIoPort8(), IoReadIoPort16(), IoReadIoPort32()
These functions are declared in the coresrv/io/ports.h
file.
rtl_uint8_t IoReadIoPort8(rtl_size_t port);
rtl_uint16_t IoReadIoPort16(rtl_size_t port);
rtl_uint32_t IoReadIoPort32(rtl_size_t port);
These functions read one, two, or four bytes, respectively, from the specified port
and return the read value.
IoReadIoPortBuffer8(), IoReadIoPortBuffer16(), IoReadIoPortBuffer32()
These functions are declared in the coresrv/io/ports.h
file.
void IoReadIoPortBuffer8(rtl_size_t port, rtl_uint8_t *dst, rtl_size_t cnt);
void IoReadIoPortBuffer16(rtl_size_t port, rtl_uint16_t *dst, rtl_size_t cnt);
void IoReadIoPortBuffer32(rtl_size_t port, rtl_uint32_t *dst, rtl_size_t cnt);
These functions read the sequence of one-, two-, or four-byte values, respectively, from the specified port
and write the values to the dst
array.
cnt
is the length of sequence.
IoWriteIoPort8(), IoWriteIoPort16(), IoWriteIoPort32()
These functions are declared in the coresrv/io/ports.h
file.
void IoWriteIoPort8(rtl_size_t port, rtl_uint8_t data);
void IoWriteIoPort16(rtl_size_t port, rtl_uint16_t data);
void IoWriteIoPort32(rtl_size_t port, rtl_uint32_t data);
The functions write a one-, two-, or four-byte data
value to the specified port
.
IoWriteIoPortBuffer8(), IoWriteIoPortBuffer16(), IoWriteIoPortBuffer32()
These functions are declared in the coresrv/io/ports.h
file.
void IoWriteIoPortBuffer8(rtl_size_t port, const rtl_uint8_t *src,
rtl_size_t cnt);
void IoWriteIoPortBuffer16(rtl_size_t port, const rtl_uint16_t *src,
rtl_size_t cnt);
void IoWriteIoPortBuffer32(rtl_size_t port, const rtl_uint32_t *src,
rtl_size_t cnt);
These functions write the sequence of one-, two-, or four-byte values, respectively, from the src
array to the specified port
.
cnt
is the length of sequence.
KnIoPermitPort()
This function is declared in the coresrv/io/ports.h
file.
Retcode KnIoPermitPort(Handle rid, Handle *handle);
This function allows a process to access the port (range of ports) with the handle rid
.
Output parameter handle
contains the handle of this permission.
Returns rcOk if successful.
Example
static Retcode PortInit(IOPort *resource)
{
Retcode rc = rcFail;
rc = KnRegisterPorts(resource->base,
resource->size,
&resource->handle);
if (rc == rcOk)
rc = KnIoPermitPort(resource->handle, &resource->permitHandle);
resource->addr = (void *) (rtl_uintptr_t) resource->base;
return rc;
}
KnRegisterPort8(), KnRegisterPort16(), KnRegisterPort32()
These functions are declared in the coresrv/io/ports.h
file.
Retcode KnRegisterPort8(rtl_uint16_t port, Handle *outRid);
Retcode KnRegisterPort16(rtl_uint16_t port, Handle *outRid);
Retcode KnRegisterPort32(rtl_uint16_t port, Handle *outRid);
These functions register an eight-, sixteen-, or thirty-two-bit port, respectively, with the port
address and assign the outRid
handle to it.
Return rcOk if the port allocation is successful.
If a port is no longer being used, it must be freed by using the KnIoClose()
function.
KnRegisterPorts()
This function is declared in the coresrv/io/ports.h
file.
Retcode KnRegisterPorts(rtl_uint16_t port, rtl_size_t size, Handle *outRid);
This function registers a range of ports (memory area) with the base address port
and the specified size
(in bytes) and assigns the outRid
handle to it.
Returns rcOk if allocation of the port range is successful.
For a usage example, see KnIoPermitPort()
.
If a range of ports is no longer being used, it must be freed by using the KnIoClose()
function.
IoReadMmBuffer8(), IoReadMmBuffer16(), IoReadMmBuffer32()
These functions are declared in the coresrv/io/mmio.h
file.
void IoReadMmBuffer8(volatile rtl_uint8_t *baseReg, rtl_uint8_t *dst,
rtl_size_t cnt);
void IoReadMmBuffer16(volatile rtl_uint16_t *baseReg, rtl_uint16_t *dst,
rtl_size_t cnt);
void IoReadMmBuffer32(volatile rtl_uint32_t *baseReg, rtl_uint32_t *dst,
rtl_size_t cnt);
These functions read the sequence of one-, two-, or four-byte values, respectively, from the register mapped to the baseReg
address and write the values to the dst
array. cnt
is the length of the sequence.
IoReadMmReg8(), IoReadMmReg16(), IoReadMmReg32()
These functions are declared in the coresrv/io/mmio.h
file.
rtl_uint8_t IoReadMmReg8(volatile void *reg);
rtl_uint16_t IoReadMmReg16(volatile void *reg);
rtl_uint32_t IoReadMmReg32(volatile void *reg);
These functions read one, two, or four bytes, respectively, from the register mapped to the reg
address and return the read value.
IoWriteMmBuffer8(), IoWriteMmBuffer16(), IoWriteMmBuffer32()
These functions are declared in the coresrv/io/mmio.h
file.
void IoWriteMmBuffer8(volatile rtl_uint8_t *baseReg, const rtl_uint8_t *src,
rtl_size_t cnt);
void IoWriteMmBuffer16(volatile rtl_uint16_t *baseReg, const rtl_uint16_t *src,
rtl_size_t cnt);
void IoWriteMmBuffer32(volatile rtl_uint32_t *baseReg, const rtl_uint32_t *src,
rtl_size_t cnt);
These functions write the sequence of one-, two-, or four-byte values, respectively, from the src array to the register mapped to the baseReg
address. cnt
is the length of the sequence.
IoWriteMmReg8(), IoWriteMmReg16(), IoWriteMmReg32()
These functions are declared in the coresrv/io/mmio.h
file.
void IoWriteMmReg8(volatile void *reg, rtl_uint8_t data);
void IoWriteMmReg16(volatile void *reg, rtl_uint16_t data);
void IoWriteMmReg32(volatile void *reg, rtl_uint32_t data);
These functions write a one-, two-, or four-byte data
value to the register mapped to the reg
address.
KnIoMapMem()
This function is declared in the coresrv/io/mmio.h
file.
Retcode KnIoMapMem(Handle rid, rtl_uint32_t prot, rtl_uint32_t attr,
void **addr, Handle *handle);
This function maps the registered memory area that was assigned the handle rid
to the address space of the process.
You can use the prot
and attr
input parameters to change the memory area protection attributes, or to disable caching.
Output parameters:
addr
is the pointer to the starting address of the virtual memory area.handle
refers to the handle of the virtual memory area.
Returns rcOk if successful.
prot
refers to the attributes of memory area protection via MMU, with the following possible values:
- VMM_FLAG_READ – allow read.
- VMM_FLAG_WRITE – allow write.
- VMM_FLAG_READ | VMM_FLAG_WRITE – allow read and write.
- VMM_FLAG_RWX_MASK or VMM_FLAG_READ | VMM_FLAG_WRITE | VMM_FLAG_EXECUTE – full access to the memory area (these entries are equivalent).
attr
– memory area attributes. Possible values:
- VMM_FLAG_CACHE_DISABLE – disable caching.
- VMM_FLAG_LOW_GUARD and VMM_FLAG_HIGH_GUARD add a protective page before and after the allocated memory, respectively.
- VMM_FLAG_ALIAS – flag indicates that the memory area may have multiple virtual addresses.
Example
static Retcode MemInit(IOMem *resource)
{
Retcode rc = rcFail;
rc = KnRegisterPhyMem(resource->base,
resource->size,
&resource->handle);
if (rc == rcOk)
rc = KnIoMapMem(resource->handle,
VMM_FLAG_READ | VMM_FLAG_WRITE,
VMM_FLAG_CACHE_DISABLE,
(void **) &resource->addr, &resource->permitHandle);
if (rc == rcOk)
resource->addr = ((rtl_uint8_t *) resource->addr
+ resource->offset);
return rc;
}
KnRegisterPhyMem()
This function is declared in the coresrv/io/mmio.h
file.
Retcode KnRegisterPhyMem(rtl_uint64_t addr, rtl_size_t size, Handle *outRid);
This function registers a memory area with the specified size
(in bytes) and beginning at the address addr
.
If registration is successful, the handle assigned to the memory area will be passed to the outRid
parameter, and the function will return rcOk.
The address addr
must be page-aligned, and the specified size
must be a multiple of the page size.
For a usage example, see KnIoMapMem()
.
If a memory area is no longer being used, it must be freed by using the KnIoClose()
function.
Interrupts
The interface described here is a low-level interface. In most cases, it is recommended to use the interface provided by the kdf library to manage interrupts.
KnIoAttachIrq()
This function is declared in the coresrv/io/irq.h
file.
Retcode KnIoAttachIrq(Handle rid, rtl_uint32_t flags, Handle *handle);
This function attaches the calling thread to the interrupt.
Input parameters:
rid
is the interrupt handle received by using theKnRegisterIrq()
call.flags
refer to the interrupt flags.
Output parameter handle
contains the IPC handle that will be used by the calling thread to wait for the interrupt after making the Recv()
call.
If successful, the function returns rcOk, otherwise it returns an error code.
Interrupt flags
- IRQ_LEVEL_LOW indicates low level generation.
- IRQ_LEVEL_HIGH indicates high level generation.
- IRQ_EDGE_RAISE indicates rising edge generation.
- IRQ_EDGE_FALL indicates falling edge generation.
- IRQ_SHARED indicates a shared interrupt.
- IRQ_PRIO_LOW indicates a low-priority interrupt.
- IRQ_PRIO_NORMAL indicates normal priority.
- IRQ_PRIO_HIGH indicates high priority.
- IRQ_PRIO_RT indicates real-time priority.
KnIoDetachIrq()
This function is declared in the coresrv/io/irq.h
file.
Retcode KnIoDetachIrq(Handle rid);
This function detaches the calling thread from the interrupt.
rid
is the interrupt handle received by using the KnRegisterIrq()
call.
If successful, the function returns rcOk, otherwise it returns an error code.
Page topKnIoDisableIrq()
This function is declared in the coresrv/io/irq.h
file.
Retcode KnIoDisableIrq(Handle rid);
Masks (prohibits) the interrupt with the handle rid
.
If successful, the function returns rcOk.
Page topKnIoEnableIrq()
This function is declared in the coresrv/io/irq.h
file.
Retcode KnIoEnableIrq(Handle rid);
Unmasks (allows) the interrupt with the handle rid
.
If successful, the function returns rcOk.
Page topKnRegisterIrq()
This function is declared in the coresrv/io/irq.h
file.
Retcode KnRegisterIrq(int irq, Handle *outRid);
Registers the interrupt with the number irq
.
Output parameter outRid
contains the interrupt handle.
If successful, the function returns rcOk.
If an interrupt is no longer being used, it must be freed by using the KnIoClose()
function.
KnIoClose()
This function is declared in the coresrv/io/io_api.h
file.
Retcode KnIoClose(Handle rid);
This function frees a registered input/output resource (I/O port(s), DMA buffer, interrupt or memory area for MMIO) with the rid
handle.
If successfully freed, the function returns rcOk.
For a usage example, see KnIoDmaCreate().
Page topKnGetMSecSinceStart()
This function is declared in the coresrv/time/time_api.h
file.
rtl_size_t KnGetMSecSinceStart(void);
Returns the number of milliseconds that have elapsed since the start of the system.
Page topKnGetRtcTime()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnGetRtcTime(RtlRtcTime *rt);
This function writes the POSIX system time (in RTC format) to the rt
parameter.
If successful, returns rcOk, or returns rcFail if an error occurs.
The RTC time format is defined by the RtlRtcTime
structure (declared in the rtl/rtc.h
file.):
typedef struct {
rtl_uint32_t msec; /**< milliseconds */
rtl_uint32_t sec; /**< second (0..59) */
rtl_uint32_t min; /**< minute (0..59) */
rtl_uint32_t hour; /**< hour (0..23) */
rtl_uint32_t mday; /**< day (1..31) */
rtl_uint32_t month; /**< month (0..11) */
rtl_int32_t year; /**< year - 1900 */
rtl_uint32_t wday; /**< week day (0..6) */
} RtlRtcTime;
KnGetSystemTime()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnGetSystemTime(RtlTimeSpec *time);
This function lets you get the system time.
Output parameter time
contains the POSIX system time in RtlTimeSpec
format.
KnSetSystemTime()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnSetSystemTime(RtlTimeSpec *time);
This function lets you set the system time.
The time
parameter must contain the POSIX time in RtlTimeSpec
format.
It is not recommended to call the KnSetSystemTime()
function in the interrupt handler thread.
KnGetSystemTimeRes()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnGetSystemTimeRes(RtlTimeSpec *res);
This function lets you get the resolution of the system time source.
Output parameter res
contains the resolution in RtlTimeSpec
format.
KnGetUpTime()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnGetUpTime(RtlTimeSpec *time);
This function lets you get the time that has elapsed since the start of the system.
Output parameter time
contains the time in RtlTimeSpec
format.
KnGetUpTimeRes()
This function is declared in the coresrv/time/time_api.h
file.
Retcode KnGetUpTimeRes(RtlTimeSpec *res);
This function receives the resolution of the source of time whose value can be obtained via KnGetUpTime()
.
Output parameter res
contains the resolution in RtlTimeSpec
format.
RtlTimeSpec
The timespec time format is defined by the RtlTimeSpec
structure (declared in the rtl/rtc.h
file).
typedef struct {
rtl_time_t sec; /**< integer number of seconds that have elapsed since the start of the Unix epoch
* or another defined point in time */
rtl_nsecs_t nsec; /**< adjustment in nanoseconds (number of nanoseconds
* that have elapsed since the point in time defined by the number of seconds*/
} RtlTimeSpec;
KosQueueAlloc()
This function is declared in the kos/queue.h
file.
void *KosQueueAlloc(KosQueueHandle queue);
Allocates memory for the new object from the queue
buffer.
If successful, the function returns the pointer to the memory for this object. If the buffer is full, it returns RTL_NULL.
Page topKosQueueCreate()
This function is declared in the kos/queue.h
file.
KosQueueHandle KosQueueCreate(unsigned objCount,
unsigned objSize,
unsigned objAlign,
void *buffer);
This function creates a queue of objects (fifo) and the buffer associated with this queue.
Parameters:
objCount
is the maximum number of objects in the queue.objSize
is the object size (bytes).objAlign
is the object alignment in bytes, and must be a power of two.buffer
is the pointer to the external buffer for objects; if it is set equal to RTL_NULL, the buffer will be allocated by using theKosMemAlloc()
function.
Returns the handle of the created queue and RTL_NULL if there is an error.
Page topKosQueueDestroy()
This function is declared in the kos/queue.h
file.
void KosQueueDestroy(KosQueueHandle queue);
This function deletes the specified queue
and frees its allocated buffer.
KosQueueFlush()
This function is declared in the kos/queue.h
file.
void KosQueueFlush(KosQueueHandle queue);
This function extracts all objects from the specified queue
and frees all the memory occupied by it.
KosQueueFree()
This function is declared in the kos/queue.h
file.
void KosQueueFree(KosQueueHandle queue, void *obj);
This function frees the memory occupied by object obj
in the buffer of the specified queue
.
The obj
pointer can be received by calling the KosQueueAlloc()
or KosQueuePop()
function.
For a usage example, see KosQueuePop()
.
KosQueuePop()
This function is declared in the kos/queue.h
file.
void *KosQueuePop(KosQueueHandle queue, rtl_uint32_t timeout);
This function extracts the object from the start of the specified queue
and returns the pointer to it.
The timeout
parameter determines the behavior of the function if the queue is empty:
- 0 – immediately return RTL_NULL.
- INFINITE_TIMEOUT – lock and wait for a new object in the queue.
- Any other value of
timeout
means that the system is waiting for a new object in the queue for the specifiedtimeout
in milliseconds; when this timeout expires, RTL_NULL is returned.
Example
int GpioEventDispatch(void *context)
{
GpioEvent *event;
GpioDevice *device = context;
rtl_bool proceed = rtl_true;
do {
event = KosQueuePop(device->queue, INFINITE_TIMEOUT);
if (event != RTL_NULL) {
if (event->type == GPIO_EVENT_TYPE_THREAD_ABORT) {
proceed = rtl_false;
} else {
GpioDeliverEvent(device, event);
}
KosQueueFree(device->queue, event);
}
} while (proceed);
KosPutObject(device);
return rcOk;
}
KosQueuePush()
This function is declared in the kos/queue.h
file.
void KosQueuePush(KosQueueHandle queue, void *obj);
Adds the obj
object to the end of the specified queue
.
The obj
pointer can be received by calling the KosQueueAlloc()
or KosQueuePop()
function.
IoReadBarrier()
This function is declared in the coresrv/io/barriers.h
file.
void IoReadBarrier(void);
Adds a read memory barrier. Linux equivalent: rmb()
.
IoReadWriteBarrier()
This function is declared in the coresrv/io/barriers.h
file.
void IoReadWriteBarrier(void);
Adds a combined barrier. Linux equivalent: mb()
.
IoWriteBarrier()
This function is declared in the coresrv/io/barriers.h
file.
void IoWriteBarrier(void);
Adds a write memory barrier. Linux equivalent: wmb()
.
Receiving information about CPU time and memory usage
The libkos
library provides an API that lets you receive information about CPU time and memory usage. This API is defined in the header file sysroot-*-kos/include/coresrv/stat/stat_api.h
from the KasperskyOS SDK.
To get information about CPU time and memory usage and other statistical data, you need to build a solution with a KasperskyOS kernel version that supports performance counters. For more details, refer to "Image library".
Receiving information about CPU time
CPU uptime is counted from the startup of the KasperskyOS kernel.
To receive information about CPU time, you need to use the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions. The values provided in the table below need to be passed in the param
parameter of these functions.
Information about CPU time
Function |
Value of the |
Obtained value |
---|---|---|
|
|
CPU uptime in kernel mode |
|
|
CPU uptime in user mode |
|
|
CPU uptime in idle mode |
|
|
CPU uptime spent on process execution |
|
|
CPU uptime spent on process execution in user mode |
The CPU uptime obtained by calling the KnGroupStatGetParam()
or KnTaskStatGetParam()
function is presented in nanoseconds.
Receiving information about memory usage
To receive information about memory usage, you need to use the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions. The values provided in the table below need to be passed in the param
parameter of these functions.
Information about memory usage
Function |
Value of the |
Obtained value |
---|---|---|
|
|
Total size of all installed RAM |
|
|
Size of free RAM |
|
|
Size of RAM used by the process |
The memory size obtained by calling the KnGroupStatGetParam()
or KnTaskStatGetParam()
function is presented as the number of memory pages. The size of a memory page is 4 KB for all hardware platforms supported by KasperskyOS.
The amount of RAM used by a process refers only to the memory allocated directly for this process. For example, if the memory of a process is mapped to an MDL buffer created by another process, the size of this buffer is not included in this value.
Enumerating processes
To get information about CPU time and memory usage by each process, do the following:
- Get the list of processes by calling the
KnGroupStatGetTaskList()
function. - Get the number of items on the list of processes by calling the
KnTaskStatGetTasksCount()
function. - Iterate through the list of processes, repeating the following steps:
- Get an item from the list of processes by calling the
KnTaskStatEnumTaskList()
function. - Get the process name by calling the
KnTaskStatGetName()
function.This is necessary to identify the process for which the information about CPU time and memory usage will be received.
- Get information about CPU time and memory usage by calling the
KnTaskStatGetParam()
function. - Verify that the process was not terminated. If the process was terminated, do not use the obtained information about CPU time and memory usage by this process.
To verify that the process was not terminated, you need to call the
KnTaskStatGetParam()
function, using theparam
parameter to pass theTASK_PARAM_STATE
value. A value other thanTaskStateTerminated
should be received. - Finish working with the item on the list of processes by calling the
KnTaskStatCloseTask()
function.
- Get an item from the list of processes by calling the
- Finish working with the list of processes by calling the
KnTaskStatCloseTaskList()
function.
Calculating CPU load
CPU load can be indicated as a percentage of total CPU load or as a percentage of CPU load by each process. These indicators are calculated for a specific time interval, at the start and end of which the information about CPU time utilization was received. (For example, CPU load can be monitored with periodic receipt of information about CPU time utilization.) The values obtained at the start of the interval need to be subtracted from the values obtained at the end of the interval. In other words, the following increments need to be obtained for the interval:
- TK – CPU uptime in kernel mode.
- TU – CPU uptime in user mode.
- TIDLE – CPU uptime in idle mode.
- Ti [i=1,2,...,n] – CPU time spent on execution of the ith process.
The percentage of total CPU load is calculated as follows:
(TK+TU)/(TK+TU+TIDLE).
The percentage of CPU load by the ith process is calculated as follows:
Ti/(TK+TU+TIDLE).
Receiving additional information about processes
In addition to information about CPU time and memory usage, the KnGroupStatGetParam()
and KnTaskStatGetParam()
functions also let you obtain the following information:
- Number of processes
- Number of threads
- Number of threads in one process
- Parent process ID (PPID)
- Process priority
- Number of handles owned by a process
- Size of virtual memory of a process
The KnTaskStatGetId()
function gets the process ID (PID).
Call()
This function is declared in the coresrv/syscalls.h
file.
Retcode Call(Handle handle, const SMsgHdr *msgOut, SMsgHdr *msgIn);
This function sends an IPC request to the server process and locks the calling thread until an IPC response or error is received. This function is called by the client process.
Parameters:
handle
is the client IPC handle of the utilized channel.msgOut
is the buffer containing the IPC request.msgIn
is the buffer for the IPC response.
Returned value:
- rcOk means that the exchange of IPC messages was successfully completed.
- rcInvalidArgument means that the IPC request and/or IPC response has an invalid structure.
- rcSecurityDisallow means that the Kaspersky Security Module prohibits forwarding of the IPC request or IPC response.
- rcNotConnected means that the server IPC handle of the channel was not found.
Other return codes are available.
Page topRecv()
This function is declared in the coresrv/syscalls.h
file.
Retcode Recv(Handle handle, SMsgHdr *msgIn);
This function locks the calling thread until an IPC request is received. This function is called by the server process.
Parameters:
handle
is the server IPC handle of the utilized channel.msgIn
is the buffer for an IPC request.
Returned value:
- rcOk means that an IPC request was successfully received.
- rcInvalidArgument means that the IPC request has an invalid structure.
- rcSecurityDisallow means that IPC request forwarding is prohibited by the Kaspersky Security Module.
Other return codes are available.
Page topReply()
This function is declared in the coresrv/syscalls.h
file.
Retcode Reply(Handle handle, const SMsgHdr *msgOut);
This function sends an IPC response and locks the calling thread until the client receives a response or until an error is received. This function is called by the server process.
Parameters:
handle
is the server IPC handle of the utilized channel.msgOut
is the buffer containing an IPC response.
Returned value:
- rcOk means that an IPC response was successfully received by the client.
- rcInvalidArgument means that the IPC response has an invalid structure.
- rcSecurityDisallow means that IPC response forwarding is prohibited by the Kaspersky Security Module.
Other return codes are available.
Page topPOSIX support limitations
KasperskyOS uses a limited POSIX interface oriented toward the POSIX.1-2008 standard (without XSI support). These limitations are primarily due to security precautions.
Limitations affect the following:
- Interaction between processes
- Interaction between threads via signals
- Standard input/output
- Asynchronous input/output
- Use of robust mutexes
- Terminal operations
- Shell usage
- Management of file handles
Limitations include:
- Unimplemented interfaces
- Interfaces that are implemented with deviations from the POSIX.1-2008 standard
- Stub interfaces that do not perform any operations except assign the
ENOSYS
value to theerrno
variable and return the value-1
In KasperskyOS, signals cannot interrupt the Call()
, Recv()
, and Reply()
system calls that support the operation of libraries that implement the POSIX interface.
The KasperskyOS kernel does not transmit signals.
Limitations on interaction between processes
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Create a new (child) process. |
Stub |
|
|
Register the handlers that are called before and after the child process is created. |
Not implemented |
|
|
Wait for the child process to stop or complete. |
Stub |
|
|
Wait for the state of the child process to change. |
Not implemented |
|
|
Wait for the child process to stop or complete. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Run the executable file. |
Stub |
|
|
Move the process to another group or create a group. |
Stub |
|
|
Create a session. |
Not implemented |
|
|
Get the group ID for the calling process. |
Not implemented |
|
|
Get the group ID. |
Stub |
|
|
Get the ID of the parent process. |
Not implemented |
|
|
Get the session ID. |
Stub |
|
|
Get the time values for the process and its descendants. |
Stub |
|
|
Send a signal to the process or group of processes. |
Only the |
|
|
Wait for a signal. |
Not implemented |
|
|
Check for received blocked signals. |
Not implemented |
|
|
Get and change the set of blocked signals. |
Stub |
|
|
Wait for a signal. |
Stub |
|
|
Wait for a signal from the defined set of signals. |
Stub |
|
|
Send a signal to the process. |
Not implemented |
|
|
Wait for a signal from the defined set of signals. |
Not implemented |
|
|
Wait for a signal from the defined set of signals. |
Not implemented |
|
|
Create an unnamed semaphore. |
You cannot create an unnamed semaphore for synchronization between processes. If a non-zero value is passed to the function through the |
|
|
Create/open a named semaphore. |
You cannot open a named semaphore that was created by another process. Named semaphores (like unnamed semaphores) are local, which means that they are accessible only to the process that created them. |
|
|
Define the mutex attribute that allows the mutex to be used by multiple processes. |
You cannot define the mutex attribute that allows the mutex to be used by multiple processes. If the |
|
|
Define the barrier attribute that allows the barrier to be used by multiple processes. |
You cannot define the barrier attribute that allows the barrier to be used by multiple processes. If the |
|
|
Define the conditional variable attribute that allows the conditional variable to be used by multiple processes. |
You cannot define the conditional variable attribute that allows the conditional variable to be used by multiple processes. If the |
|
|
Define the read/write lock object attribute that allows the read/write lock object attribute to be used by multiple processes. |
You cannot define the read/write lock object attribute that allows the read/write lock object attribute to be used by multiple processes. If the |
|
|
Create a spin lock. |
You cannot create a spin lock for synchronization between processes. If the |
|
|
Create or open a shared memory object. |
Not implemented |
|
|
Map to memory. |
You cannot perform memory mapping for interaction between processes. If the |
|
|
Define the memory access permissions. |
This function works as a stub by default. To use this function, define special settings for the KasperskyOS kernel. |
|
|
Create an unnamed channel. |
You cannot use an unnamed channel for data transfer between processes. Unnamed channels are local, which means that they are accessible only to the process that created them. |
|
|
Create a special FIFO file (named channel). |
Stub |
|
|
Create a special FIFO file (named channel). |
Not implemented |
|
Limitations on interaction between threads via signals
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Send a signal to a thread. |
You cannot send a signal to a thread. If a signal number is passed to the function through the |
|
|
Get and change the set of blocked signals. |
Stub |
|
|
Restore the state of the control thread and the signals mask. |
Not implemented |
|
|
Save the state of the control thread and the signals mask. |
Not implemented |
|
Standard input/output limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Formatted print to file. |
Not implemented |
|
|
Use memory as a data stream. |
Not implemented |
|
|
Use dynamically allocated memory as a data stream. |
Not implemented |
|
|
Formatted print to file. |
Not implemented |
|
Asynchronous input/output limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Cancel input/output requests that are waiting to be handled. |
Not implemented |
|
|
Receive an error from an asynchronous input/output operation. |
Not implemented |
|
|
Request the execution of input/output operations. |
Not implemented |
|
|
Request a file read operation. |
Not implemented |
|
|
Get the status of an asynchronous input/output operation. |
Not implemented |
|
|
Wait for the completion of asynchronous input/output operations. |
Not implemented |
|
|
Request a file write operation. |
Not implemented |
|
|
Request execution of a set of input/output operations. |
Not implemented |
|
Limitations on the use of robust mutexes
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Return a robust mutex to a consistent state. |
Not implemented |
|
|
Get a robust mutex attribute. |
Not implemented |
|
|
Define a robust mutex attribute. |
Not implemented |
|
Terminal operation limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Get the path to the file of the control terminal. |
This function only returns or passes an empty string through the |
|
|
Define the terminal settings. |
The input speed, output speed, and other settings specific to hardware terminals are ignored. |
|
|
Wait for output completion. |
This function only returns the value |
|
|
Suspend or resume receipt or transmission of data. |
Suspending output and resuming suspended output are not supported. |
|
|
Clear the input queue or output queue, or both of these queues. |
This function only returns the value |
|
|
Break the connection with the terminal for a set time. |
This function only returns the value |
|
|
Get the path to the terminal file. |
This function only returns a null pointer. |
|
|
Get the path to the terminal file. |
This function only returns an error value. |
|
|
Get the ID of a group of processes using the terminal. |
This function only returns the value |
|
|
Define the ID for a group of processes using the terminal. |
This function only returns the value |
|
|
Get the ID of a group of processes for the leader of the session connected to the terminal. |
This function only returns the value |
|
Shell operation limitations
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Create a child process for command execution and a channel for this process. |
This function only assigns the |
|
|
Close the channel with the child process created by the |
This function cannot be used because its input parameter is the data stream handle returned by the |
|
|
Create a child process for command execution. |
Stub |
|
|
Perform a shell-like expansion of the string. |
Not implemented |
|
|
Free up the memory allocated for the results of calling the |
Not implemented |
|
Limitations on management of file handles
Interface |
Purpose |
Implementation |
Header file based on the POSIX.1-2008 standard |
---|---|---|---|
|
Make a copy of the handle of an opened file. |
Handles of regular files, standard I/O streams, sockets and channels are supported. There is no guarantee that the lowest available handle will be received. |
|
|
Make a copy of the handle of an opened file. |
Handles of regular files, standard I/O streams, sockets and channels are supported. The handle of an opened file needs to be passed through the |
|
Concurrently using POSIX and other interfaces
Using libkos together with Pthreads
In a thread created using Pthreads, you cannot use the following libkos interfaces:
The following libkos interfaces can be used together with Pthreads (and other POSIX interfaces):
Using POSIX together with libkos threads
POSIX methods cannot be used in threads that were created using libkos threads.
Using IPC together with Pthreads/libkos threads
Methods for IPC can be used in any threads that were created using Pthreads or libkos threads.
Page topMessageBus component
The MessageBus
component implements the message bus that ensures receipt, distribution and delivery of messages between applications running KasperskyOS. This bus is based on the publisher-subscriber model. Use of a message bus lets you avoid having to create a large number of IPC channels to connect each subscriber application to each publisher application.
Messages transmitted through the MessageBus
cannot contain data. These messages can be used only to notify subscribers about events. See "Message structure" section below.
The MessageBus
component provides an additional level of abstraction over KasperskyOS IPC that helps simplify the development and expansion of application-layer applications. MessageBus
is a separate program that is accessed through IPC. However, developers are provided with a MessageBus
access library that lets you avoid direct use of IPC calls.
The API of the access library provides the following interfaces:
IProviderFactory
provides factory methods for obtaining access to instances of all other interfaces.IProviderControl
is the interface for registering and deregistering a publisher and subscriber in the bus.IProvider (MessageBus component)
is the interface for transferring a message to the bus.ISubscriber
is the callback interface for sending a message to a subscriber.IWaiter
is the interface for waiting for a callback when the corresponding message appears.
Message structure
Each message contains two parameters:
topic
is the identifier of the message subject.id
is an additional parameter that identifies a particular message.
The topic
and id
parameters are unique for each message. The interpretation of topic
+id
is determined by the contract between the publisher and subscriber. For example, if there are changes to the configuration data used by the publisher and subscriber, the publisher forwards a message regarding the modified data and the id
of the specific entry containing the new data. The subscriber uses mechanisms outside of the MessageBus
to receive the new data based on the id
key.
IProviderFactory interface
The IProviderFactory
interface provides factory methods for receiving the interfaces necessary for working with the MessageBus
component.
A description of the IProviderFactory
interface is provided in the file named messagebus/i_messagebus_control.h
.
An instance of the IProviderFactory
interface is obtained by using the free InitConnection()
function, which receives the name of the IPC connection between the application software and the MessageBus
program. The connection name is defined in the init.yaml.in
file when describing the solution configuration. If the connection is successful, the output parameter contains a pointer to the IProviderFactory
interface.
- The interface for registering and deregistering (see "IProviderControl interface") publishers and subscribers in the message bus is obtained by using the
IProviderFactory::CreateBusControl()
method. - The interface containing the methods enabling the publisher to send messages to the bus (see "IProvider interface (MessageBus component)") is obtained by using the
IProviderFactory::CreateBus()
method. - The interfaces containing the methods enabling the subscriber to receive messages from the bus (see "ISubscriber, IWaiter and ISubscriberRunner interfaces") are obtained by using the
IProviderFactory::CreateCallbackWaiter
andIProviderFactory::CreateSubscriberRunner()
methods.It is not recommended to use the
IWaiter
interface, because calling a method of this interface is a locking call.
i_messagebus_control.h (fragment)
class IProviderFactory
{
...
virtual fdn::ResultCode CreateBusControl(IProviderControlPtr& controlPtr) = 0;
virtual fdn::ResultCode CreateBus(IProviderPtr& busPtr) = 0;
virtual fdn::ResultCode CreateCallbackWaiter(IWaiterPtr& waiterPtr) = 0;
virtual fdn::ResultCode CreateSubscriberRunner(ISubscriberRunnerPtr& runnerPtr) = 0;
...
};
...
fdn::ResultCode InitConnection(const std::string& connectionId, IProviderFactoryPtr& busFactoryPtr);
IProviderControl interface
The IProviderControl
interface provides the methods for registering and deregistering publishers and subscribers in the message bus.
A description of the IProviderControl
interface is provided in the file named messagebus/i_messagebus_control.h
.
The IProviderFactory
interface is used to obtain an interface instance.
Registering and deregistering a publisher
The IProviderControl::RegisterPublisher()
method is used to register the publisher in the message bus. This method receives the message subject and puts the unique ID of the bus client into the output parameter. If the message subject is already registered in the bus, the call will be declined and the client ID will not be filled.
The IProviderControl::UnregisterPublisher()
method is used to deregister a publisher in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a publisher ID, the call will be declined.
i_messagebus_control.h (fragment)
class IProviderControl
{
...
virtual fdn::ResultCode RegisterPublisher(const Topic& topic, ClientId& id) = 0;
virtual fdn::ResultCode UnregisterPublisher(ClientId id) = 0;
...
};
Registering and deregistering a subscriber
The IProviderControl::RegisterSubscriber()
method is used to register the subscriber in the message bus. This method accepts the subscriber name and the list of subjects of messages for the necessary subscription, and puts the unique ID of the bus client into the output parameter.
The IProviderControl::UnregisterSubscriber()
method is used to deregister a subscriber in the message bus. This method accepts the bus client ID received during registration. If the indicated ID is not registered as a subscriber ID, the call will be declined.
i_messagebus_control.h (fragment)
class IProviderControl
{
...
virtual fdn::ResultCode RegisterSubscriber(const std::string& subscriberName, const std::set<Topic>& topics, ClientId& id) = 0;
virtual fdn::ResultCode UnregisterSubscriber(ClientId id) = 0;
...
};
IProvider interface (MessageBus component)
The IProvider
interface provides the methods enabling the publisher to send messages to the bus.
A description of the IProvider
interface is provided in the file named messagebus/i_messagebus.h
.
The IProviderFactory
interface is used to obtain an interface instance.
Sending a message to the bus
The IProvider::Push()
method is used to send a message. This method accepts the bus client ID received during registration and the message ID. If the message queue in the bus is full, the call will be declined.
i_messagebus.h (fragment)
class IProvider
{
public:
...
virtual fdn::ResultCode Push(ClientId id, BundleId dataId) = 0;
...
};
ISubscriber, IWaiter and ISubscriberRunner interfaces
The ISubscriber
, IWaiter
, and ISubscriberRunner
interfaces provide the methods enabling the subscriber to receive messages from the bus and process them.
Descriptions of the ISubscriber
, IWaiter
and ISubscriberRunner
interfaces are provided in the file named messagebus/i_subscriber.h
.
The IProviderFactory
interface is used to obtain instances of the IWaiter
and ISubscriberRunner
interfaces. The implementation of the ISubscriber
callback interface is provided by the subscriber application.
Receiving a message from the bus
You can use the IWaiter::Wait()
or ISubscriberRunner::Run()
method to switch a subscriber to standby mode, waiting for a message from the bus. These methods accept the bus client ID and the pointer to the ISubscriber
callback interface. If the client ID is not registered, the call will be declined.
It is not recommended to use the IWaiter
interface, because calling the IWaiter::Wait()
method is a locking call.
The ISubscriber::OnMessage()
method will be called when a message is received from the bus. This method accepts the message subject and message ID.
i_subscriber.h (fragment)
class ISubscriber
{
...
virtual fdn::ResultCode OnMessage(const std::string& topic, BundleId id) = 0;
};
...
class IWaiter
{
...
[[deprecated("Use ISubscriberRunner::Run method instead.")]]
virtual fdn::ResultCode Wait(ClientId id, const ISubscriberPtr& subscriberPtr) = 0;
};
...
class ISubscriberRunner
{
...
virtual fdn::ResultCode Run(ClientId id, const ISubscriberPtr& subscriberPtr) = 0;
};
Return codes
Overview
In a KasperskyOS-based solution, the return codes of functions of various APIs (for example, APIs of the libkos
and kdf
libraries, drivers, transport code, and application software) are 32-bit signed integers. This type is defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK as follows:
typedef __INT32_TYPE__ Retcode;
The set of return codes consists of a success code with a value of 0
and error codes. An error code is interpreted as a data structure whose format is described in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. This format provides for multiple fields that contain not only information about the results of a function call, but also the following additional information:
- Flag in the
Customer
field indicating that the error code was defined by the developers of the KasperskyOS-based solution and not by the developers of software from the KasperskyOS SDK.Thanks to the flag in the
Customer
field, developers of a KasperskyOS-based solution and developers of software from the KasperskyOS SDK can define error codes from non-overlapping sets. - Global ID of the error code in the
Space
field.Global IDs let you define non-overlapping sets of error codes. Error codes can be generic or specific. Generic error codes can be used in the APIs of any solution components and in the APIs of any constituent parts of solution components (for example, a driver or VFS may be a constituent part of a solution component). Specific error codes are used in the APIs of one or more solution components or in the APIs of one or more constituent parts of solution components.
For example, the
RC_SPACE_GENERAL
ID corresponds to generic errors, theRC_SPACE_KERNEL
ID corresponds to error codes of the kernel, and theRC_SPACE_DRIVERS
ID corresponds to error codes of drivers. - Local ID of the error code in the
Facility
field.Local IDs let you define non-overlapping subsets of error codes within the set of error codes corresponding to one global ID. For example, the set of error codes with the global ID
RC_SPACE_DRIVERS
includes non-overlapping subsets of error codes with the local IDsRC_FACILITY_I2C
,RC_FACILITY_USB
, andRC_FACILITY_BLKDEV
.
The global and local IDs of specific error codes are assigned by the developers of a KasperskyOS-based solution and by the developers of software from the KasperskyOS SDK independently of each other. In other words, two sets of global IDs are generated. Each global ID has a unique meaning within one set. Each local ID has a unique meaning within a set of local IDs related to one global ID. Generic error codes can be used in any API.
This type of centralized approach helps avoid situations in which the same error codes have various meanings within a KasperskyOS-based solution. This is necessary to eliminate a potential problem transmitting error codes through different APIs. For example, this problem occurs when drivers call kdf
library functions, receive error codes, and return these codes through their own APIs. If error codes are generated without a centralized approach, the same error code can have different meanings for the kdf
library and for the driver. Under these conditions, drivers return correct error codes only if the error codes of the kdf
library are converted into error codes of each driver. In other words, error codes in a KasperskyOS-based solution are assigned in such way that does not require conversion of these codes during their transit through various APIs.
The information about return codes provided here does not apply to functions of a POSIX interface or the APIs of third-party software used in KasperskyOS-based solutions.
Generic return codes
Return codes that are generic for APIs of all solution components and their constituent parts are defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. Descriptions of generic return codes are provided in the table below.
Generic return codes
Return code |
Description |
---|---|
|
The function completed successfully. |
|
Invalid function argument. |
|
No connection between the client and server sides of interaction. For example, there is no server IPC handle. |
|
Insufficient memory to perform the operation. |
|
Buffer too small. |
|
The function ended with an internal error related to incorrect logic. Some examples of internal errors include values outside of the permissible limits, and null indicators and values where they are not permitted. |
|
Error sending an IPC message. |
|
Error receiving an IPC message. |
|
IPC message was not transmitted due to the IPC message source. |
|
IPC message was not transmitted due to the IPC message recipient. |
|
IPC was interrupted by another process thread. |
|
Indicates that the function needs to be called again. |
|
The function ended with an error. |
|
The operation cannot be performed on the resource. |
|
Initialization failed. |
|
The function was not implemented. |
|
Buffer too large. |
|
Resource temporarily unavailable. |
|
Resource not found. |
|
Timed out. |
|
The operation was denied by security mechanisms. |
|
The operation will result in a block. |
|
The operation was aborted. |
|
Invalid function called in the interrupt handler. |
|
Set of elements already contains the element being added. |
|
Operation cannot be completed. |
|
Resource access rights were revoked. |
|
Resource quota exceeded. |
|
Device not found. |
Defining error codes
To define an error code, the developer of a KasperskyOS-based solution needs to use the MAKE_RETCODE()
macro defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK. The developer must also use the customer
parameter to pass the symbolic constant RC_CUSTOMER_TRUE
.
Example:
An error description that is passed via the desc
parameter is not used by the MAKE_RETCODE()
macro. This description is needed to create a database of error codes when building a KasperskyOS-based solution. At present, a mechanism for creating and using such a database has not been implemented.
Reading error code structure fields
The RC_GET_CUSTOMER()
, RC_GET_SPACE()
, RC_GET_FACILITY()
and RC_GET_CODE()
macros defined in the sysroot-*-kos/include/rtl/retcode.h
header file from the KasperskyOS SDK let you read error code structure fields.
The RETCODE_HR_PARAMS()
and RETCODE_HR_FMT()
macros defined in the sysroot-*-kos/include/rtl/retcode_hr.h
header file from the KasperskyOS SDK are used for formatted display of error details.