mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-19 13:47:56 +00:00
288 lines
13 KiB
Plaintext
288 lines
13 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"/>
|
|
! </parent-provides>
|
|
! <start name="timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <provides> <service name="Timer"/> </provides>
|
|
! <route>
|
|
! <service name="CAP"> <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>
|
|
! </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>
|
|
! </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"/>
|
|
! </parent-provides>
|
|
! <start name="timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <provides><service name="Timer"/></provides>
|
|
! <route>
|
|
! <service name="CAP"> <parent/> </service>
|
|
! </route>
|
|
! </start>
|
|
! <start name="init">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <config>
|
|
! <parent-provides>
|
|
! <service name="Timer"/>
|
|
! <service name="LOG"/>
|
|
! </parent-provides>
|
|
! <start name="test-timer">
|
|
! <resource name="RAM" quantum="1M"/>
|
|
! <route>
|
|
! <service name="Timer"> <parent/> </service>
|
|
! <service name="LOG"> <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>
|
|
! </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.
|
|
|
|
|
|
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.
|