mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-29 18:18:54 +00:00
ce67b73dca
With the change of the Timer::Session interface, all scenarios that use the timer use core's SIGNAL service. So we need to route sessions accordingly. In addition to the adaptation to the changed timer, this patch removes some stale examples that predate the run tool and are no longer used.
320 lines
15 KiB
Plaintext
320 lines
15 KiB
Plaintext
|
|
======================================
|
|
Configuring the init process of Genode
|
|
======================================
|
|
|
|
Norman Feske
|
|
|
|
|
|
The Genode architecture facilitates the flexible construction of complex usage
|
|
scenarios out of Genode's process nodes used as generic building blocks. Thanks
|
|
to the strictly hierarchic and, at the same time, recursive structure of
|
|
Genode, a parent has full control over the way, its children interact with each
|
|
other and with the parent. The init process plays a special role in that
|
|
picture. At boot time, it gets started by core, gets assigned all physical
|
|
resources, and controls the execution of all further process nodes, which can
|
|
be further instances of init. Init's policy is driven by a configuration file,
|
|
which declares a number of children, their relationships, and resource
|
|
assignments. This document describes the configuration mechansism to steer the
|
|
policy of the init process. The configuration is described in a single XML file
|
|
called 'config' supplied via core's ROM service.
|
|
|
|
|
|
Configuration
|
|
#############
|
|
|
|
At the parent-child interface, there are two operations that are subject to
|
|
policy decisions of the parent, the child announcing a service and the
|
|
child requesting a service. If a child announces a service, the parent is up
|
|
to decide if and how to make this service accessible to its other children.
|
|
When a child requests a service, the parent may deny the session request,
|
|
delegate the request to its own parent, implement the requested service
|
|
locally, or open a session at one of its other children. This decision may
|
|
depend on the requested service or session-construction arguments provided
|
|
by the child. Apart from assigning resources to children, the central
|
|
element of the policy implemented in the parent is a set of rules to
|
|
route session requests. Therefore, init's configuration concept is laid out
|
|
around processes and the routing of session requests. The concept is best
|
|
illustrated by an example (the following config file can be used on Linux):
|
|
|
|
! <config>
|
|
! <parent-provides>
|
|
! <service name="CAP"/>
|
|
! <service name="LOG"/>
|
|
! <service name="SIGNAL"/>
|
|
! </parent-provides>
|
|
! <start name="timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <provides> <service name="Timer"/> </provides>
|
|
! <route>
|
|
! <service name="CAP"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! <start name="test-timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <route>
|
|
! <service name="Timer"> <child name="timer"/> </service>
|
|
! <service name="LOG"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! </config>
|
|
|
|
First, there is the declaration of services provided by the parent of the
|
|
configured init instance. In this case, we declare that the parent provides a
|
|
CAP service and a LOG service. For each child to start, there is a '<start>'
|
|
node describing resource assignments, declaring services provided by the child,
|
|
and holding a routing table for session requests originating from the child.
|
|
The first child is called "timer" and implements the "Timer" service. To
|
|
implement this service, the timer requires a CAP session. In the routing table,
|
|
we define that a CAP session request gets delegated to init's parent. The
|
|
second process called "test-timer" is a client of the timer service. In its
|
|
routing table, we see that requests for "Timer" sessions should be routed to
|
|
the "timer" child whereas requests for "LOG" sessions should be delegated to
|
|
init's parent. Per-child service routing rules provide a flexible way to
|
|
express arbitrary client-server relationships. For example, service requests
|
|
may be transparently mediated through special policy components acting upon
|
|
session-construction arguments. There might be multiple children implementing
|
|
the same service, each addressed by different routing tables. If there is no
|
|
valid route to a requested service, the service is denied. In the example
|
|
above, the routing tables act effectively as a whitelist of services the child
|
|
is allowed to use.
|
|
|
|
In practice, usage scenarios become more complex than the basic example,
|
|
increasing the size of routing tables. Furthermore, in many practical cases,
|
|
multiple children may use the same set of services, and require duplicated
|
|
routing tables within the configuration. In particular during development, the
|
|
elaborative specification of routing tables tend to become an inconvenience.
|
|
To alleviate this problem, there are two mechanisms, wildcards and a default
|
|
route. Instead of specifying a list of single service routes targeting the same
|
|
destination, the wildcard '<any-service>' becomes handy. For example, instead
|
|
of specifying
|
|
! <route>
|
|
! <service name="ROM"> <parent/> </service>
|
|
! <service name="RAM"> <parent/> </service>
|
|
! <service name="RM"> <parent/> </service>
|
|
! <service name="PD"> <parent/> </service>
|
|
! <service name="CPU"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
the following shortcut can be used:
|
|
! <route>
|
|
! <any-service> <parent/> </any-service>
|
|
! </route>
|
|
The latter version is not as strict as the first one because it permits the
|
|
child to create sessions at the parent, which were not whitelisted in the
|
|
elaborative version. Therefore, the use of wildcards is discouraged for
|
|
configuring untrusted components. Wildcards and explicit routes may be combined
|
|
as illustrated by the following example:
|
|
! <route>
|
|
! <service name="LOG"> <child name="nitlog"/> </service>
|
|
! <any-service> <parent/> </any-service>
|
|
! </route>
|
|
The routing table is processed starting with the first entry. If the route
|
|
matches the service request, it is taken, otherwise the remaining
|
|
routing-table entries are visited. This way, the explicit service route of
|
|
"LOG" sessions to "nitlog" shadows the LOG service provided by the parent.
|
|
|
|
To emulate the traditional init policy, which allowed a child to use services
|
|
provided by arbitrary other children, there is a further wildcard called
|
|
'<any-child>'. Using this wildcard, such a policy can be expressed as follows:
|
|
! <route>
|
|
! <any-service> <parent/> </any-service>
|
|
! <any-service> <any-child/> </any-service>
|
|
! </route>
|
|
This rule would delegate all session requests referring to one of the parent's
|
|
services to the parent. If no parent service matches the session request, the
|
|
request is routed to any child providing the service. The rule can be further
|
|
reduced to:
|
|
! <route>
|
|
! <any-service> <parent/> <any-child/> </any-service>
|
|
! </route>
|
|
Potential ambiguities caused by multiple children providing the same service
|
|
are detected automatically. In this case, the ambiguity must be resolved using
|
|
an explicit route preceding the wildcards.
|
|
|
|
To reduce the need to specify the same routing table for many children
|
|
in one configuration, there is a '<default-route>' mechanism. The default
|
|
route is declared within the '<config>' node and used for each '<start>'
|
|
entry with no '<route>' node. In particular during development, the default
|
|
route becomes handy to keep the configuration tidy and neat.
|
|
|
|
The combination of explicit routes and wildcards is designed to scale well from
|
|
being convenient to use during development towards being highly secure at
|
|
deployment time. If only explicit rules are present in the configuration, the
|
|
permitted relationships between all processes are explicitly defined and can be
|
|
easily verified. Note however that the degree those rules are enforced at the
|
|
kernel-interface level depends on the used base platform.
|
|
|
|
|
|
Advanced features
|
|
#################
|
|
|
|
In addition to the service routing facility described in the previous section,
|
|
the following features are worth noting:
|
|
|
|
|
|
Resource quota saturation
|
|
=========================
|
|
|
|
If a specified resource (i.e., RAM quota) exceeds the available resources.
|
|
The available resources are assigned completely to the child. This makes
|
|
it possible to assign all remaining resources to the last child by
|
|
simply specifying an overly large quantum.
|
|
|
|
|
|
Multiple instantiation of a single ELF binary
|
|
=============================================
|
|
|
|
Each '<start>' node requires a unique 'name' attribute. By default, the
|
|
value of this attribute is used as file name for obtaining the ELF
|
|
binary at the parent's ROM service. If multiple instances of the same
|
|
ELF binary are needed, the binary name can be explicitly specified
|
|
using a '<binary>' sub node of the '<start>' node:
|
|
! <binary name="filename"/>
|
|
This way, the unique child names can be chosen independently from the
|
|
binary file name.
|
|
|
|
|
|
Nested configuration
|
|
====================
|
|
|
|
Each '<start>' node can host a '<config>' sub node. The content of this sub
|
|
node is provided to the child when a ROM session for the file name "config" is
|
|
requested. Thereby, arbitrary configuration parameters can be passed to the
|
|
child. For example, the following configuration starts 'timer-test' within an
|
|
init instance within another init instance. To show the flexibility of init's
|
|
service routing facility, the "Timer" session of the second-level 'timer-test'
|
|
child is routed to the timer service started at the first-level init instance.
|
|
! <config>
|
|
! <parent-provides>
|
|
! <service name="CAP"/>
|
|
! <service name="LOG"/>
|
|
! <service name="ROM"/>
|
|
! <service name="RAM"/>
|
|
! <service name="CPU"/>
|
|
! <service name="RM"/>
|
|
! <service name="PD"/>
|
|
! <service name="SIGNAL"/>
|
|
! </parent-provides>
|
|
! <start name="timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <provides><service name="Timer"/></provides>
|
|
! <route>
|
|
! <service name="CAP"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! <start name="init">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <config>
|
|
! <parent-provides>
|
|
! <service name="Timer"/>
|
|
! <service name="LOG"/>
|
|
! <service name="SIGNAL"/>
|
|
! </parent-provides>
|
|
! <start name="test-timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <route>
|
|
! <service name="Timer"> <parent/> </service>
|
|
! <service name="LOG"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! </config>
|
|
! <route>
|
|
! <service name="Timer"> <child name="timer"/> </service>
|
|
! <service name="LOG"> <parent/> </service>
|
|
! <service name="ROM"> <parent/> </service>
|
|
! <service name="RAM"> <parent/> </service>
|
|
! <service name="CAP"> <parent/> </service>
|
|
! <service name="CPU"> <parent/> </service>
|
|
! <service name="RM"> <parent/> </service>
|
|
! <service name="PD"> <parent/> </service>
|
|
! <service name="SIGNAL"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! </config>
|
|
The services ROM, RAM, CPU, RM, and PD are required by the second-level
|
|
init instance to create the timer-test process.
|
|
|
|
As illustrated by this example, the use of the nested configuration feature
|
|
enables the construction of arbitrarily complex process trees via a single
|
|
configuration file.
|
|
|
|
Alternatively to specifying all nested configurations in a single config file,
|
|
any sub configuration can be placed in a separate file specified via the
|
|
'configfile' node. For example:
|
|
! <start name="nitpicker">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <configfile name="nitpicker.config"/>
|
|
! </start>
|
|
|
|
|
|
Priority support
|
|
================
|
|
|
|
The number of CPU priorities to be distinguished by init can be specified with
|
|
'prio_levels' attribute of the '<config>' node. The value must be a power of
|
|
two. By default, no priorities are used. To assign a priority to a child
|
|
process, a priority value can be specified as 'priority' attribute of the
|
|
corresponding '<start>' node. Valid priority values lie in the range of
|
|
-prio_levels + 1 (maximum priority degradation) to 0 (no priority degradation).
|
|
|
|
|
|
Verbosity
|
|
=========
|
|
|
|
To ease the debugging, init can be directed to print various status information
|
|
as LOG output. To enable the verbose mode, assign the value "yes" to the
|
|
'verbose' attribute of the '<config>' node.
|
|
|
|
|
|
Executing children in chroot environments on Linux
|
|
==================================================
|
|
|
|
On the Linux base platform, each process started by init can be assigned to
|
|
a chroot environment by specifying the new root location as 'root' attribute
|
|
to the corresponding '<start>' node. Root environments can be nested. The
|
|
root path of a nested init instance will be appended to the root path of
|
|
the outer instance.
|
|
|
|
When using the chroot mechanism, core will mirror the current working
|
|
directory within the chroot environment via the a bind mount operation. This
|
|
step is needed to enable execve to obtain the ELF binary of the new process.
|
|
|
|
In order to use the chroot mechanism when starting Genode's core as a non-root
|
|
user process, the core executable must be equipped with the 'CAP_SYS_ADMIN' and
|
|
'CAP_SYS_CHROOT' capabilities. 'CAP_SYS_ADMIN' is needed for bind mounting.
|
|
'CAP_SYS_CHROOT' is needed to perform the 'chroot' syscall:
|
|
|
|
! sudo setcap cap_sys_admin,cap_sys_chroot=ep core
|
|
|
|
For an example of using chroot, please refer to the 'os/run/chroot.run' script.
|
|
|
|
|
|
Using the configuration concept
|
|
###############################
|
|
|
|
To get acquainted with the configuration format, there are two example
|
|
configuration files located at 'os/src/init/', which are both ready-to-use with
|
|
the Linux version of Genode. Both configurations produce the same scenario but
|
|
they differ in the way policy is expressed. The 'explicit_routing'
|
|
configuration is an example for the elaborative specification of all service
|
|
routes. All service requests not explicitly specified are denied. So this
|
|
policy is a whitelist enforcing mandatory access control on each session
|
|
request. The example illustrates well that such a elaborative specification is
|
|
possible in an intuitive manner. However, it is pretty comprehensive. In cases
|
|
where the elaborative specification of service routing is not fundamentally
|
|
important, in particular during development, the use of wildcards can help to
|
|
simplify the configuration. The 'wildcard' example demonstrates the use of a
|
|
default route for session-request resolution and wildcards. This variant is
|
|
less strict about which child uses which service. For development, its
|
|
simplicity is beneficial but for deployment, we recommend to remove wildcards
|
|
('<default-route>', '<any-child>', and '<any-service>') altogether. The
|
|
absence of such wildcards is easy to check automatically to ensure that service
|
|
routes are explicitly whitelisted.
|
|
|
|
Further configuration examples can be found in the 'os/config/' directory.
|