Base API

Core services

Physical memory

The RAM service provides access to physical memory. Each RAM session corresponds to a memory pool with a bounded quota. From this pool, the RAM-session client can allocate memory blocks in the form of dataspaces.

Read-only memory modules

The ROM service provides access to boot-time present binary files such as files loaded by the boot loader or files contained in a non-volatile ROM area. Each ROM session corresponds to an open file. The file's content is made available to the client as a read-only dataspace.

Address-space-layout managment

The region-manager (RM) service manages address-space layouts. A RM session corresponds to an address space layout into which dataspaces can be placed. Hence, a RM session populated with dataspaces provides a generic abstraction for a physical page table referencing memory pages.

Protection domains

The protection-domain (PD) service enables the creation of address spaces that are isolated from each other. Each PD session corresponds to a protection domain. The PD service is rarely used by applications directly. Instead, Genode's process-creation framework uses this service internally.

CPU

The CPU service provides a facility for creating and managing threads. A CPU session corresponds to a CPU time allocator from which multiple threads can be allocated.

Device access

Interrupts

The IRQ service enables user-level device drivers to serve device interrupts. Each IRQ session corresponds to an associated interrupt line.

Memory-mapped I/O

The IO_MEM service enables user-level device drivers to obtain memory-mapped device resources as dataspaces. Each IO_MEM session corresponds to the reservation of a physical address range for which a dataspace is provided to the client. The user-level device driver can make the device resource visible in its address space by attaching the dataspace to its own RM session.

Port I/O

The IO_PORT service provides access to device IO-ports via an RPC interface. Each IO_PORT session corresponds to the access right to a port range.

Debug output

For low-level debugging, core provides a simple LOG service, which enables clients to print textual messages. In the LOG output, each message is tagged with the label of the corresponding client.

Runtime environment

Initial execution environment

A process is a composition of a protection domain (PD session), a memory pool (RAM session), an address-space layout (RM session), and a CPU session from which the main thread is created. These sessions form the environment of the process, which is represented by the Env class:

In addition to the process' initial sessions, the environment contains the heap of the process. The heap can be used for anonymous memory allocation and is the front end for allocating RAM dataspaces from the process' RAM session and attaching these dataspaces to the process' RM session. By default, the process environment holds one initial LOG session used as the standard text-output facility. For debugging purposes, there exists a printf front end for writing text to the initial LOG session.

Interaction with the outside world

At creation time, the only communication partner of a process is its immediate parent process, which provides the following interface:

Data types and structures

Basic data types

Genode provides common integer types in its namespace. Integer types that can be derived from built-in compiler types are defined in:

In addition, there exist definitions for fixed-width integer types. For 32-bit platforms, those are defined in:

Genode facilitates the use of exceptions to signal errors but it uses exception types only as textual expression of error code and for grouping errors. Normally, exceptions do not carry payload. For code consistency, exception types should inherit from the Exception base class.

For parsing structured text such as argument strings or XML, Genode provides simple tokenizing support via the Token class.

Structured data types

Most book-keeping tasks in Genode rely on single-connected lists, which use the List template.

For use cases where associative arrays are needed such as allocators and object pools, Genode relies on AVL trees. Furthermore, there exists a specialized version of the AVL tree for managing pools of strings. Because the List data type inserts new list elements at the list head, it cannot be used for implementing wait queues requiring first-in-first-out semantics. For such use cases, there exists a dedicated Fifo template. For representing an array of bits in a more memory-efficient manner than using an array of bool values, a Bit_array can be used. For allocating indices within a fixed-size range of numbers, a bit allocator is a suitable tool. It is meant to be used for managing ID name spaces.

Argument strings

Genode uses comma-separated sequences of tag=value assignments for specifying session-construction arguments. The Arg_string provides an interface for extracting individual values from argument strings and for argument-string manipulation.

Allocators

All allocators implement the generic Allocator interface. Allocators that operate on address ranges supplement the plain Allocator by implementing the more specific Range_allocator interface.

The Slab allocator is tailored for allocating small fixed-size memory blocks from a big chunk of memory. For the common use case of using slab allocation for a certain type rather than for a known byte-size, there exists a typed slab allocator as a front-end of Slab. In contrast to the rather limited slab allocators, Allocator_avl allows for arbitrary allocations from a list of address regions. It implements a best-fit allocation strategy, supports arbitrary alignments, and allocations at specified addresses. For safely using range allocators from multiple threads, synchronized versions of range-allocator implementations can be constructed with the help of the following template: To protect the quantum of allocated memory from exceeding a predefined limit, the following class can be used. It is used by server implementations that subdivide their heap into client-specific partitions dimensioned according to the resources donated by the respective client.

Inter-process communication

Remote procedure calls

Capability representation

Inter-process communication on Genode is based on capabilities. A capability is a system-wide unique object identity that can be passed between processes. On kernels with support for local names, a capability may have a different name in each process but still refer to the same object.

Each capability is associated with the type of the RPC interface the capability refers to - similar to how a C++ reference refers to the type of a specific C++ object.

RPC marshalling and message transfer

Genode provides a mechanisms for implementing remote procedure calls (RPC). The interaction between the client (the caller) and the server (callee) consists of the following steps:

  1. Client marshals arguments to form a message. The first part of the message is a function opcode.

  2. Client sends message via the kernel to the server and blocks for the result.

  3. Server dispatches request according to the opcode stored in the message.

  4. Server unmarshalls function arguments,

  5. Server executes the function,

  6. Server marshals the produced results.

  7. Server sends result message to the client.

  8. Client unmarshals results.

An RPC interface is declared by annotating its abstract C++ class using the macros GENODE_RPC_FUNCTION and GENODE_RPC_INTERFACE.

repos/base/include/base/rpc.h
Genode::Rpc_arg_in
Genode::Rpc_arg_out
Genode::Rpc_arg_inout
Genode::Trait::Rpc_direction
Genode::Trait::Call_return
Genode::Trait::Exc_list
Genode::Rpc_transfer_size
Genode::Rpc_args_size
Genode::Rpc_retval_size
Genode::Rpc_msg_payload_size
Genode::Rpc_function_list_msg_size
Genode::Rpc_interface_msg_size
Genode::Rpc_interface_is_inherited
The RPC API supports the use of basic types and compound types both as values and references. For carrying more sophistic payload as RPC function arguments or return values, the following RPC-specific types become handy: The client-side API for performing synchronous RPC communication is represented by the Rpc_client class template accompanied with the Capability class template defined in base/capability.h.
repos/base/include/base/rpc_client.h
Genode::Cap_para_out
Genode::Cap_return
Genode::Rpc_caps_out
Genode::Rpc_function_caps_out
On the server side, the RPC API consists of the Rpc_object class template and Rpc_entrypoint class. An RPC entry point is a thread that manages a number of RPC object and dispatches incoming RPC requests. An RPC object is a C++ object that becomes remotely accessible once it is associated with an RPC entry point (using the manage function). At association time, a capability for the RPC object is created, which can be passed to other processes. The RPC object can then be invoked by everyone who is in possession of this capability.

Asynchronous notifications

Inter-process communication via remote procedure calls requires both communication partners to operate in a synchronous fashion. The signalling framework complements the synchronous RPC mode of communication with an interface for issuing and receiving asynchronous notifications.

It defines interfaces for signal transmitters and signal receivers. A signal receiver can receive signals from multiple sources, whereas the sources of incoming signals are distinguishable. One or multiple threads can either poll or block for incoming signals. Each signal receiver is addressable via a capability. The signal transmitter provides fire-and-forget semantics for submitting signals to exactly one signal receiver. Signals are communicated in a reliable fashion, which means that the exact number of signals submitted to a signal transmitter are communicated to the corresponding signal receiver. Signals serve as raw notifications and cannot carry any payload.

Shared memory

Genode supports shared memory between multiple processes using dataspaces. Dataspaces are memory containers as provided by core's RAM, IO_MEM, or ROM services. Each dataspace is referenced by a capability that can be passed among multiple processes. Each process with the capability to a dataspace can access the dataspace's content by attaching the dataspace to its RM session. In addition to be used as arguments for RM-session calls, dataspaces provide the following interface:

Client-server framework

Server sessions

In the Genode architecture, servers provide their services over session-based communication channels. Each session provides an interface inherited from the Session base class.

Each service type is represented as a RPC object implementing the root interface. The server announces its service type by providing the service name and the capability of the service's root interface (announce function of the parent interface). Via the capability to the root interface, the parent is then able to create and destroy sessions. Because defining root interfaces for services follows a recurring pattern, there exists a default template that implements the standard behaviour of the root interface (Root_component).

Connecting to services

The interaction of a client with a server involves the definition of session-construction arguments, the request of the session creation via its parent, the initialization of the matching RPC-client stub code with the received session capability, the actual use of the session interface, and the closure of the session. The Connection template provides a way to greatly simplify the handling of session arguments, session creation, and destruction on the client side. By implementing a service-specific connection class inherited from Connection, session arguments become plain constructor arguments, session functions can be called directly on the Connection object, and the session gets properly closed when destructing the Connection.

Threads and synchronization

Threads

On Genode, a thread is created by constructing an object of a class inherited from Thread. The new thread starts execution at the entry member function. With this design, each thread runs in the context of its object and can access context-specific information by accessing its member variables. Threads use a statically allocated stack, which is dimensioned as specified via a template argument.

There exists no timed sleep function in the Genode base framework. Timed sleep is instead be provided by a server implementing a timer-device driver. However, the special case of infinite blocking is covered by the following base API function. Genode allows all blocking operations of threads to be canceled via a cancel_blocking mechanism provided by core's CPU service. The cancellation of a blocking operation is reflected at the API level by a raised exception of the type Blocking_canceled.

Synchronization

For mutual exclusive execution of critical sections, there exists a simple lock interface providing lock and unlock semantics. The lock comes in two flavours. Cancelable locks can be unblocked by force via core's cancel-blocking mechanism. In contrast, a non-cancelable lock (Lock) does not reflect the cancellation of its blocking operation at the API level but transparently re-enters its blocking state after a cancellation.

For the use case of using locks for protecting critical sections, the Lock_guard provides a convenient mechanism for the automated unlocking of a lock when leaving a variable scope. Alongside lock-based mutual exclusion of entering critical sections, organizing threads in a producer-consumer relationship is a common design pattern for thread synchronization. The Semaphore interface enables the implementation of this synchronization scheme. For managing object lifetimes in a way that prevents dangling pointers, there is a thread-safe weak-pointer mechanism.

Child management

Child policy

For processes that manage a number of child processes, each child process is represented by an instance of the Child class. This instance contains the policy to be applied to the child (for example how session requests are routed to services) and contains the child's execution environment including the RAM session holding the child's memory quota.

The Service_pool classes support the management of services announced by children or provided locally, and the synchronization of service requests with service announcements.

Process creation

The Process class provides a convenient front end for the procedure of creating a new process.

Utilities

Basic functions for handling strings and integers

C++ utilities

ELF binary parser

Format-string handling