Fork of the Genode OS framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

862 lines
42 KiB

Release notes for the Genode OS Framework 12.02
Genode Labs
The release of Genode 12.02 marks an exciting point in the history of the
project as it is the first version developed in the open rather than within the
chambers of Genode Labs. Thereby, we have embraced GitHub as central facility
for discussion and source-code management. This change has benefits for users
and developers of the framework alike. For users, it has become possible to get
hold of the latest developments using the official 'genodelabs/master' branch and
get involved with discussing the current activities. For regular Genode
developers, the public Git repository replaces a former mix of public
Subversion and company-internal Mercurial repositories, making life much
easier. In Section [Liberation of the development process], we outline the
motivation behind this change and give pointers to the new resources.
The major new additions to the base system are a new framework API for accessing
memory-mapped I/O resources, special support for using Genode as user-level
component framework on Linux, and API support for the reuse of existing
components in the form of sandboxed libraries. These changes are accompanied
with new device-driver infrastructure such as the first version of a device
driver manager and a new ACPI parser.
Feature-wise, the current release takes the first steps towards the goal of the
[ - Roadmap for 2012], turning Genode into a
general-purpose OS ready for everyday use by its developers. According to the
roadmap, we enhanced the Noux runtime with fork semantics so that we can run
command-line based GNU programs such as the bash shell and coreutils unmodified
and natively on various microkernels. Furthermore, the library infrastructure
has been enhanced by porting and updating libraries such as Qt 4.7.4 and the
MuPDF PDF rendering engine.
Liberation of the development process
In summer 2011, we started a discussion within Genode Labs about changing the
mode of how Genode is developed. Until then, most design discussions and the
actual development work took place locally at the company. At quarterly
intervals, we used to publish our work in the form of official Genode
releases. This way of development seemed to work quite well for us, we were
satisfied about the pace of development, and with each release, our project got
more recognition.
However, the excellent book [ - Producing Open Source Software]
made us realize that even though we released our work under an Open-Source
license, our development process was actually far from being open and may have
discouraged participation of people outside the inner circle of developers.
Because we believe that the framework has reached a state where it becomes
interesting for a wider audience of users and developers, the idea was born
to liberate the project from its closed fashion of development.
In the beginning of December, the vague idea has become a plan. So we finally
brought the topic to our mailing list
([ - Steps towards an open development process]).
We decided to take the release cycle for Genode 12.02 as the opportunity to put
our plan to practice. The central element of this endeavour was moving the
project over to GitHub and adapt our workflows and tooling support accordingly.
First, we started to embrace GitHub's issue tracker for the management of
working topics:
:[]: Issue Tracker
The most significant step was leaving our Genode-Labs-internal code
repositories behind and starting a completely public Git repository instead:
:[]: Genode Labs at GitHub
With the code repository going public, the way was cleared to opening up design
discussions as well. Instead of having such discussions internally at Genode
Labs, we try to increasingly take them to our mailing list and issue tracker.
With this new way of development, we hope to make the project much more
approachable for people who want to get involved and let Genode reach far out
beyond the reach of our little company.
The changes mentioned above are actually just the tip of the iceberg. For
example, the transition phase required us to rethink the way the project
website is maintained. From now on, almost all of the content of
comes directly from the project's Git repository. So maintaining website
content is done in the same coherent and transparent way as working on Genode's
code base. So we could finally put the old Wiki to rest. In the process, we
largely revisited the existing content. For example, we rewrote the
[ - contributions] document in a
tutorial-like style and incorporated several practical hints, in particular
related to the recommended use of Git.
Although it is probably too early to judge the outcome of our transition, we
are excited how smooth this massive change went. We attribute this pleasant
experience mostly to the excellent GitHub hosting platform, which instantly
ignited a spirit of open collaboration among us. We are excited to see new
people approaching us and showing their interest for teaming up, and we are
curious about where this new model of development will take Genode in the
Base framework, low-level OS infrastructure
RPC framework refinements
Until now, the RPC framework did not support const RPC functions. Rather than
being a limitation inherent to the concept, const RPC functions plainly did not
exist. So supporting them was not deemed too important. However, there are uses
of RPC interfaces that would benefit from a way to declare an RPC function as
const. Candidates are functions like 'Framebuffer::Session::mode()' and
With the current version, we clear the way towards declaring such functions as
const. Even though the change is pretty straight-forward, the thorough support
for const-qualified RPC functions would double the number of overloads for the
'call_member' function template (in 'base/include/util/meta.h'). For this
reason, as of now, the support of const functions is limited to typical getter
functions with no arguments. This appears to be the most common use of such
API support for enslaving services
While evolving and using the framework, we always keep an eye on recurring
patterns of how its API is used. Once such a pattern becomes obvious, we try
to take a step back, generalize the observed pattern, and come up with a new
building block that unifies the former repetitive code fragments.
One of those patterns that was far from obvious when we designed Genode years
ago is the use of a service running as child of its own client. At the first
glance, this idea seems counter-intuitive because normally, services are
understood as components that operate independently and protected from their
(untrusted) clients. But there is a class of problems where this approach
becomes extremely useful: The reuse of protocol implementations as a
library-like building block. Most services are actually protocol stacks that
translate a low-level protocol to a more abstract API. For example, a block
device driver translates a specific device API to the generic 'Block_session'
interface. Or the 'iso9660' service translates the 'Block_session' interface to
the 'Rom_session' interface by parsing the ISO9660 file system. Or similarly,
the 'tar_rom' service parses the tar file format to make its content available
via the 'Rom_session' interface.
If a particular functionality is needed by multiple programs, it is common
practice to move this functionality into a dedicated library to avoid the
duplication of the same code at many places. For example, if a program would
need to parse a tar archive, it might be tempting to move the tar-parsing code
from the 'tar_rom' service into a dedicated library, which can then be used by
both the 'tar_rom' service and the new program. An alternative approach is to
just re-use the 'tar_rom' service as a black box and treat it like it was a
library. That is, start the 'tar_rom' service as a child process, supply the
resources the component needs to operate and, in turn, use its API (now in the
form of an RPC interface) to get work done. Because the service is started as a
mere tool at the discretion of its client, we call it *slave*. It turns out
that this idea works exceedingly well in many cases. In a way, it resembles the
Unix philosophy to solve complex problems by combining many small tools each
with a specific purpose. In contrast to the use of libraries, the reuse of
entire components has benefits with regard to fault isolation. Because the
reused functionality is sandboxed within a distinct process, the environment
exposed to this code can be tailored to a rigid subset of the host program's
environment. In the event of a fault within the reused component, the reach of
problem is therefore limited.
On the other hand, we observed that even though this idea works as intended,
implementing the idea for a particular use case involved a good deal of
boiler-plate code where most of this code is needed to define the slave's
environment and resources. Hence, we reviewed the existing occurrences of the
slave pattern and condensed their common concerns into the 'Slave_policy' and
'Slave' classes residing in 'os/include/os/slave.h'. The 'Slave' class is meant
to be used as is. It is merely a convenience wrapper for a child process and
its basic resources. The 'Slave_policy' is meant as a hook for service-specific
customizations. The best showcase is the new 'd3m' component located at
'gems/src/server/d3m'. D3m extensively uses the slave pattern by instantiating
and destroying drivers and file-system instances on-the-fly. A further instance
of this pattern can be found in the new ACPI driver introduced with the current
Support for resizable framebuffers
The framebuffer-session interface has remained largely untouched since the
original release of Genode in 2008. Back then, we were used to rely on C-style
out parameters in RPC functions. The current RPC framework, however, promotes
the use of a more object-oriented style. So the time has come to revisit the
framebuffer session interface. Instead of using C-style out parameters, the new
'mode()' RPC call returns the mode information as an object of type 'Mode'.
Consequently, mode-specific functions such as 'bytes_per_pixel()' have been
moved to the new 'Framebuffer::Mode' class. The former 'info()' function is
In addition to the overhaul of the RPC interface, we introduced basic support
for resizable framebuffers. The new 'mode_sigh()' function enables a client to
register a signal handler at the framebuffer session. This signal handler gets
notified in the event of server-side mode changes. Via the new 'release()'
function, the client is able to acknowledge a mode change. By calling it, the
client tells the framebuffer service that it no longer uses the original
framebuffer dataspace. So the server can replace it by a new one. After having
called 'release()', the client can obtain the dataspace for the new mode by
calling 'dataspace()' again.
MMIO access framework
As the arsenal of native device drivers for Genode grows, we are observing
an increased demand to formalize the style of how drivers are written to
foster code consistency. One particular cause of inconsistency used to be
the way of how memory-mapped I/O registers are accessed. C++ has poor support
for defining bit-accurate register layouts in memory. Therefore, driver code
usually carries along a custom set of convenience functions for reading and
writing registers of different widths as well as a list of bit definitions in
the form of enum values or '#define' statements. To access parts of a register,
the usual pattern is similar to the following example (taken from the pl011
UART driver:
! enum {
! UARTCR = 0x030, /* control register */
! UARTCR_UARTEN = 0x0001, /* enable bit in control register */
! ...
! }
! ...
! /* enable UART */
! _write_reg(UARTCR, _read_reg(UARTCR) | UARTCR_UARTEN);
This example showcases two inconveniences: The way the register layout is
expressed and the manual labour needed to access parts of registers. In the
general case, a driver needs to also consider 'MASK' and 'SHIFT' values to
implement access to partial registers properly. This is not just inconvenient
but also error prone. For lazy programmers as ourselves, it's just too easy to
overwrite "undefined" bits in a register instead of explicitly masking the
access to the actually targeted bits. Consequently, the driver may work fine
with the current generation of devices but break with the next generation.
So the idea was born to introduce an easy-to-use formalism for this problem. We
had two goals: First, we wanted to cleanly separate the declaration of register
layouts from the program logic of the driver. The actual driver program should
be free from any intrinsics in the form of bit-masking operations. Second, we
wanted to promote uncluttered driver code that uses names (i.e., in the form of
type names) rather than values to express its operations. The latter goal is
actually achieved by the example above by the use of enum values, but this is
only achieved through discipline. We would prefer to have an API that
facilitates the use of proper names as the most convenient way to express an
The resulting MMIO API comes in the form of two new header files located at
'base/include/util/register.h' and 'base/include/util/mmio.h'.
Register declarations
The class templates found in 'util/register.h' provide a means to express
register layouts using C++ types. In a way, these templates make up for
C++'s missing facility to define accurate bitfields. Let's take a look at
a simple example of the 'Register' class template that can be used to define
a register as well as a bitfield within this register:
! struct Vaporizer : Register<16>
! {
! struct Enable : Bitfield<2,1> { };
! struct State : Bitfield<3,3> {
! enum{ SOLID = 1, LIQUID = 2, GASSY = 3 };
! };
! static void write (access_t value);
! static access_t read ();
! };
In the example, 'Vaporizer' is a 16-bit register, which is expressed via the
'Register' template argument. The 'Register' class template allows for
accessing register content at a finer granularity than the whole register
width. To give a specific part of the register a name, the 'Register::Bitfield'
class template is used. It describes a bit region within the range of the
compound register. The bit 2 corresponds to true if the device is enabled and
bits 3 to 5 encode the 'State'. To access the actual register, the methods
'read()' and 'write()' must be provided as backend, which performs the access
of the whole register. Once defined, the 'Vaporizer' offers a handy way to
access the individual parts of the register, for example:
! /* read the whole register content */
! Vaporizer::access_t r = Vaporizer::read();
! /* clear a bit field */
! Vaporizer::Enable::clear(r);
! /* read a bit field value */
! unsigned old_state = Vaporizer::State::get(r);
! /* assign new bit field value */
! Vaporizer::State::set(r, Vaporizer::State::LIQUID);
! /* write whole register */
! Vaporizer::write(r);
Memory-mapped I/O
The utilities provided by 'util/mmio.h' use the 'Register' template class as
a building block to provide easy-to-use access to memory-mapped I/O registers.
The 'Mmio' class represents a memory-mapped I/O region taking its local base
address as constructor argument. Let's take a simple example to see how it is
supposed to be used:
! class Timer : Mmio
! {
! struct Value : Register<0x0, 32> { };
! struct Control : Register<0x4, 8> {
! struct Enable : Bitfield<0,1> { };
! struct Irq : Bitfield<3,1> { };
! struct Method : Bitfield<1,2>
! {
! enum { ONCE = 1, RELOAD = 2, CYCLE = 3 };
! };
! };
! public:
! Timer(addr_t base) : Mmio(base) { }
! void enable();
! void set_timeout(Value::access_t duration);
! bool irq_raised();
! };
The memory-mapped timer device consists of two registers: The 32-bit 'Value'
register and the 8-bit 'Control' register. They are located at the MMIO offsets
0x0 and 0x4, respectively. Some parts of the 'Control' register have specific
meanings as expressed by the 'Bitfield' definitions within the 'Control'
Using these declarations, accessing the individual bits becomes almost a
verbatim description of how the device is used. For example:
! void enable()
! {
! /* access an individual bitfield */
! write<Control::Enable>(true);
! }
! void set_timeout(Value::access_t duration)
! {
! /* write complete content of a register */
! write<Value>(duration);
! /* write all bitfields as one transaction */
! write<Control>(Control::Enable::bits(1) |
! Control::Method::bits(Control::Method::ONCE) |
! Control::Irq::bits(0));
! }
! bool irq_raised()
! {
! return read<Control::Irq>();
! }
In addition to those basic facilities, further noteworthy features of the new
API are the support for register arrays and the graceful overflow handling
with respect to register and bitfield boundaries.
C Runtime
We extended our FreeBSD-based C runtime to accommodate the needs of the Noux
runtime environment and our port of the MuPDF application.
* The dummy implementation of '_ioctl()' has been removed. This function is
called internally within the libc, i.e., by 'tcgetattr()'. For running
libreadline in Noux, we need to hook into those ioctl operations via the
libc plugin interface.
* The 'libc/regex' and 'libc/compat' modules have been added to the libc.
These libraries are needed by the forthcoming port of Slashem to Noux.
* We added a default implementation of 'chdir()'. It relies on the sequence of
'open()', 'fchdir()', 'close()'.
* The new libc plugin 'libc_rom' enables the use of libc file I/O functions
to access ROM files as provided by Genode ROM session.
* We changed the libc dummy implementations to always return ENOSYS. Prior
this change, 'errno' used to remain untouched by those functions causing
indeterministic behaviour of code that calls those functions, observes the
error return value (as returned by most dummies), and evaluates the error
condition reported by errno.
* If using the libc for Noux programs, the default implementations of
time-related functions such as 'gettimeofday()' cannot be used because they
rely on a dedicated timeout-scheduler thread. Noux programs, however, are
expected to contain only the main thread. By turning the functions into weak
symbols, we enabled the noux libc-plugin to provide custom implementations.
Linux DDE used to implement Linux spin locks based on 'dde_kit_lock'. This
works fine if a spin lock is initialized only once and used from then on. But
if spin locks are initialized on-the-fly at a high rate, each initialization
causes the allocation of a new 'dde_kit_lock'. Because in contrast to normal
locks, spinlocks cannot be explicitly destroyed, the spin-lock emulating locks
are never freed. To solve the leakage of locks, we complemented DDE Kit with
the new 'os/include/dde_kit/spin_lock.h' API providing the semantics as
expected by Linux drivers.
Libraries and applications
New and updated libraries
:Qt4 updated to version 4.7.4:
We updated Qt4 from version 4.7.1 to version 4.7.4. For the most part, the
update contains bug fixes as detailed in the release notes for the versions
[ - 4.7.2],
[ - 4.7.3], and
[ - 4.7.4].
:Update of zlib to version 1.2.6:
Because zlib 1.2.5 is no more available at, we bumped the zlib
version to 1.2.6.
:New ports of openjpeg, jbig2dec, and mupdf:
MuPDF is a fast and versatile PDF rendering library with only a few
dependencies. It depends on openjpeg (JPEG2000 codec) and jbig2dec (b/w image
compression library). With the current version, we integrated those libraries
alongside the MuPDF library to the 'libports' repository.
GDB monitor refinements and automated test
We improved the support for GDB-based user-level debugging as introduced with
the previous release.
For the x86 architecture, we fixed a corner-case problem with using the
two-byte 'INT 0' instruction for breakpoints. The fix changes the breakpoint
instruction to the single-byte 'HLT'. 'HLT' is a privileged instruction and
triggers an exception when executed in user mode.
The new '' script extends the original
'' script with a startup sequence that automates the
initial break-in at the 'main()' function of a dynamically linked binary.
The revised '' script has been improved to become a full
automated test case for GDB functionalities. It exercises the following
features (currently on Fiasco.OC only):
* Breakpoint in 'main()'
* Breakpoint in a shared-library function
* Stack trace when not in a syscall
* Thread info
* Single stepping
* Handling of segmentation-fault exception
* Stack trace when in a syscall
PDF viewer
According to our road map for 2012, we pursued the port of an existing PDF
viewer as native application to Genode.
We first looked at the [ - libpoppler],
which seems to be the most popular PDF rendering engine in the world of To get a grasp on what the porting effort of this engine may
be, we looked at projects using this library as well as the library source
code. By examining applications such as the light-weight epdfview
application, we observed that libpoppler's primary design goal is to integrate
well with existing infrastructure rather than to reimplement
functionality that is provided by another library. For example, fontconfig is
used to obtain font information and Cairo is used as rendering backend. In the
context of, this makes perfect sense. But in our context,
porting libpoppler would require us to port all this infrastructure to Genode
as well. To illustrate the order of magnitude of the effort needed, epdfview
depends on 65 shared libraries. Of course, at some point in the future, we will
be faced to carry out this porting work. But for the immediate goal to have a
PDF rendering engine available on Genode, it seems overly involved. Another
criterion to evaluate the feasibility of integrating libpoppler with Genode is
its API. By glancing at the API, it seems to be extremely feature rich and
complex - certainly not a thing to conquer in one evening with a glass of wine.
The Qt4 backend of the library comprises circa 8000 lines of code. This value
can be taken as a vague hint at the amount of work needed to create a custom
backend, i.e., for Genode's framebuffer-session interface.
Fortunately for us, there exists an alternative PDF rendering engine named
MuPDF. The concept of MuPDF is quite the opposite of that of libpoppler.
MuPDF tries to be as self-sufficient as possible in order to be suitable
for embedded systems without comprehensive OS infrastructure. It comes with a
custom vector-graphics library (instead of using an existing library such as
Cairo) and it even has statically linked-in all font information needed to
display PDF files that come with no embedded fonts. That said, it does not
try to reinvent the wheel in every regard. For example, it relies on
common libraries such as zlib, libpng, jpeg, freetype, and openjpeg. Most
of them are already available on Genode. And the remaining libraries are rather
free-standing and easy to port. To illustrate the low degree of dependencies,
the MuPDF application on GNU/Linux depends on only 15 shared libraries. The
best thing about MuPDF from our perspective however, is its lean and clean API,
and the wonderfully simple example application. Thanks to this example, it was
a breeze to integrate the MuPDF engine with Genode's native framebuffer-session
and input-session interfaces. The effort needed for this integration work lies
in the order of less than 300 lines of code.
At the current stage, the MuPDF rendering engine successfully runs on Genode
in the form of a simple interactive test program, which can be started
via the 'libports/run/mupdf' run script. The program supports the basic key
handling required to browse through a multi-page PDF document
(page-up or enter -> next page, page-down or backspace -> previous page).
Improved terminal performance
The terminal component used to make all intermediate states visible to the
framebuffer in a fully synchronous fashion. This is an unfortunate behaviour
when scrolling through large text outputs. By decoupling the conversion of the
terminal state to pixels from the 'Terminal::write()' RPC function,
intermediate terminal states produced by sub sequential write operations do not
end up on screen one by one but only the final state becomes visible. This
improvement drastically improves the speed in situations with a lot of
intermediate states.
Noux support for fork semantics
Genode proclaims to be a framework out of which operating systems can be built.
There is no better way of putting this claim to the test than to use the
framework for building a Unix-like OS. This is the role of the Noux runtime
During the past releases, Noux evolved into a runtime environment that is able
to execute complex command-line-based GNU software such as VIM with no
modification. However, we cannot talk of Unix without talking about its
fundamental concept embodied in the form of the 'fork()' system call. We did
not entirely dismiss the idea of implementing 'fork()' into Noux but up to now,
it was something that we willingly overlooked. However, the primary goal of
Noux is to run the GNU userland natively on Genode. This includes a good deal
of programs that rely on fork semantics. We could either try to change all the
programs to use a Genode-specific way of starting programs or bite in the
bullet and implement fork. With the current release, we did the latter.
The biggest challenge of implementing fork was to find a solution that is not
tied to one kernel but one that works across all the different base platforms.
The principle problem of starting a new process in a platform-independent
manner is already solved by Genode in the form of the 'Process' API. But this
startup procedure is entirely different from the semantics of fork. The key to
the solution was Genode's natural ability to virtualize the access to low-level
platform resources. To implement fork semantics, all Noux has to do is to
provide local implementations of core's RAM, RM, and CPU session interfaces.
The custom implementation of the CPU session interface is used to tweak the
startup procedure as performed by the 'Process' class. Normally, processes
start execution immediately at creation time at the ELF entry point. For
implementing fork semantics, however, this default behavior does not work.
Instead, we need to defer the start of the main thread until we have finished
copying the address space of the forking process. Furthermore, we need to start
the main thread at a custom trampoline function rather than at the ELF entry
point. Those customizations are possible by wrapping core's CPU service.
The custom implementation of the RAM session interface provides a pool of RAM
shared by Noux and all Noux processes. The use of a shared pool alleviates the
need to assign RAM quota to individual Noux processes. Furthermore, the custom
implementation is needed to get hold of the RAM dataspaces allocated by each
Noux process. When forking a process, the acquired information is used to
create a shadow copy of the forking address space.
Finally, a custom RM service implementation is used for recording all RM
regions attached to the region-manager session of a Noux process. Using the
recorded information, the address-space layout can then be replayed onto a new
process created via fork.
With the virtualized platform resources in place, the only puzzle piece that
is missing is the bootstrapping of the new process. When its main thread is
started, it has an identical address-space content as the forking process but
it has to talk to a different parent entrypoint and a different Noux session.
The procedure of re-establishing the relationship of the new process to its
parent is achieved via a small trampoline function that re-initializes the
process environment and then branches to the original forking point via
setjmp/longjmp. This mechanism is implemented in the libc_noux plugin.
As the immediate result of this work, a simple fork test can be executed across
all base platforms except for Linux (Linux is not supported yet). The test
program is located at 'ports/src/test/noux_fork' and can be started with the
'ports/run/' script.
Furthermore, as a slightly more exciting example, there is a run script for
running a bash shell on a tar file system that contains coreutils. By starting
the 'ports/run/' script, you get presented an interactive bash
shell. The shell is displayed via the terminal service and accepts user input.
It allows you to start one of the coreutils programs such as ls or cat. Please
note that the current state is still largely untested, incomplete, and flaky.
But considering that Noux is comprised of less than 2500 lines of code, we are
quite surprised of how far one can get with such little effort.
Device drivers
Driver improvements to accommodate dynamic (re-)loading
To support the dynamic probing of devices as performed by the new d3m
component, the ATAPI and USB device drivers have been enhanced to support the
subsequent closing and re-opening of sessions.
First bits of the d3m device-driver manager
The abbreviation d3m stands for demo device-driver manager. It is our current
solution for the automated loading of suitable drivers as needed for running
Genode from a Live CD or USB stick. Because of the current narrow focus of d3m,
it is not a generic driver-management solution but a first step in this
direction. We hope that in the long run, d3m will evolve to become a generic
driver-management facility so that we can remove one of the "D"s from its name.
In the current form d3m solves the problems of merging input-event streams,
selecting the boot device, and dealing with failing network drivers.
When using the live CD, we expect user input to come from USB HID devices or
from a PS/2 mouse and keyboard. The live system should be operational if at
least one of those sources of input is available. In the presence of multiple
sources, we want to accumulate the events of all of them.
The live CD should come in the form of a single ISO image that can be burned onto a CDROM or
alternatively copied to an USB stick. The live system should boot fine in both
cases. The first boot stage is accommodated by syslinux and the GRUB boot
loader using BIOS functions. But once Genode takes over control, it needs to
figure out on its own from where to fetch data. A priori, there is no way to
guess whether the ATAPI driver or the USB storage driver should be used.
[image d3m_what_next]
Therefore, d3m implements a probing mechanism that starts each of the drivers,
probes for the presence of a particular file on an iso9660 file system.
[image d3m_probing]
Once d3m observes a drivers that is able to successfully access the magic file,
it keeps the driver and announces the driver's service to its own parent. For
the system outside of d3m, the probing procedure is completely transparent. D3m
appears to be just a service that always provides the valid block session for
the boot medium.
[image d3m_ready]
The network device drivers that we ported from the iPXE project cover the
range of most common wired network adaptors, in particular the E1000 family.
But we cannot presume that a computer running the live system comes equipped
with one of the supported devices. If no supported network card could be
detected the driver would simply fail. Applications requesting a NIC session
would block until a NIC service becomes available, which won't happen. To
prevent this situation, d3m wraps the NIC driver and provides a dummy NIC
service in the event the drivers fails. This way, the client application won't
block infinitely but receive an error on the first attempt to use the NIC.
ACPI support
To accommodate kernels like Fiasco.OC or NOVA that take advantage of x86's
APIC, we have introduced a simple ACPI parser located at 'os/src/drivers/acpi'.
The server traverses the ACPI tables and sets the interrupt line of devices
within the PCI config space to the GSIs found in the ACPI tables. Internally it
uses Genode's existing PCI driver as a child process for performing PCI access
and, in turn, announces a PCI service itself.
For obtaining the IRQ routing information from the ACPI tables without
employing a full-blown ACPI interpreter, the ACPI driver uses an ingenious
technique invented by Bernhard Kauer, which is described in the following
:[ - ATARE - ACPI Tables and Regular Expressions]:
_TU Dresden technical report TUD-FI09-09, Dresden, Germany, August 2009_
Start the 'acpi_drv' in your Genode environment. Do not start the 'pci_drv'
since this will be used as a slave of the 'acpi_drv'. You still must load the
'pci_drv' in your boot loader. To integrate the ACPI driver into your boot
configuration, you may take the following snippet as reference:
!<start name="acpi">
! <resource name="RAM" quantum="2M"/>
! <binary name="acpi_drv"/>
! <provides><service name="PCI"/></provides>
! <route>
! <service name="ROM"> <parent/> </service>
! <any-service> <any-child/> <parent/> </any-service>
! </route>
:Limitations and known issues:
Currently there is no interface to set the interrupt mode for core's IRQ
sessions (e.g., level or edge triggered). This is required by Fiasco.OCs kernel
interface. We regard this as future work.
Platform support
Fiasco.OC microkernel
The support for the Fiasco.OC base platform is still lacking proper handling
for releasing resources such as kernel capabilities. Although this is a known
issue, we underestimated the reach of the problem when Genode's signal API is
used. Each emitted signal happens to consume one kernel capability within core,
ultimately leading to a resource outage when executing signal-intensive code
such as the packet-stream interface. The current release comes with an interim
solution. To remedy the acute problem, we extended the 'Capability_allocator'
class with the ability to register the global ID of a Genode capability so
that the ID gets associated with a process-local kernel capability. Whenever
a Genode capability gets unmarshalled from an IPC message, the
capability-allocator is asked, with the global ID as key, whether the
kernel-cap already exists. This significantly reduces the waste of
kernel-capability slots.
To circumvent problems of having one and the same ID for different kernel
objects, the following problems had to be solved:
* Replace pseudo IDs with unique ones from core's badge allocator
* When freeing a session object, free the global ID _after_ unmapping
the kernel object, otherwise the global ID might get re-used in some
process and the registry will find a valid but wrong capability
for the ID
Because core aggregates all capabilities of all different processes, its
capability registry needs much more memory compared to a regular process.
By parametrizing capability allocators differently for core and non-core
processes, the global memory overhead for capability registries is kept
at a reasonable level.
Please note that this solution is meant as an interim fix until we have
resolved the root of the problem, which is the proper tracking and releasing
of capability selectors.
Linux is one of the two original base platforms of Genode. The original
intension behind supporting Linux besides a microkernel was to facilitate
portability of the API design and to have a convenient testing environment for
platform-independent code. Running Genode in the form of a bunch of plain Linux
processes has become an invaluable feature for our fast-paced development.
To our delight, we lately discovered that the use of running Genode on Linux
can actually go far beyond this original incentive. Apparently, on Linux, the
framework represents an equally powerful component framework as on the other
platforms. Hence, Genode has the potential to become an attractive option for
creating complex component-based user-level software on Linux.
For this intended use, however, the framework has to fulfill the following
additional requirements:
* Developers on Linux expect that their components integrate seamlessly with
their existing library infrastructure including all shared libraries
installed on Linux.
* The use of a custom tool chain is hard to justify to developers who regard
Genode merely as an application framework. Hence, a way to use the normal
tool chain as installed on the Linux host system is desired.
* Application developers are accustomed with using GDB for debugging and expect
that GDB can be attached to an arbitrary Genode program in an intuitive way.
Genode's original support for Linux as base platform did not meet those
expectations. Because Genode's libc would ultimately collide with the Linux
glibc, Genode is built with no glibc dependency at all. It talks to the kernel
directly using custom kernel bindings. In particular, Genode threads are created
directly via the 'clone()' system call and thread-local storage (TLS) is managed
in the same way as for the other base platforms. This has two implications.
First, because Genode's TLS mechanism is different than the Linux TLS
mechanism, Genode cannot be built with the normal host tool chain. This
compiler would generate code that would simply break on the first attempt to
use TLS. We solved this problem with our custom tool chain, which is configured
for Genode's needs. The second implication is that GDB is not able to handle
threads created differently than those created via the pthread library. Even
though GDB can be attached to each thread individually, the debugger would not
correctly handle a multi-threaded Genode process as a multi-threaded Linux
program. With regard to the use of Linux shared libraries, Genode used to
support a few special programs that used both the Genode API and Linux
libraries. Those programs (called hybrid Linux/Genode programs) were typically
pseudo device drivers that translate a Linux API to a Genode service. For
example, there exists a framebuffer service that uses libSDL as back end.
Because those programs were a rarity, the support by the build system for such
hybrid targets was rather poor.
Fortunately, all the problems outlined above could be remedied pretty easily.
It turns out that our custom libc is simply not relevant when Genode is
used as plain application framework on Linux. For this intended use, we always
want to use the host's normal libc. This way, the sole reason for using plain
system calls instead of the pthread library vanishes, which, in turn,
alleviates the need for a custom tool chain. Genode threads are then simply
pthreads compatible with the TLS code as emitted by the host compiler and
perfectly recognised by GDB. With the surprisingly little effort of creating a
new implementation of Genode's thread API to target pthreads instead of using
syscalls, we managed to provide a decent level of support for using Genode as
user-level component framework on Linux.
These technical changes alone, however, are not sufficient to make Genode
practical for real-world use. As stated above, the few hybrid Linux/Genode
programs used to be regarded as some leprous artifacts. When using Genode as
Linux application framework, however, this kind of programs are becoming the
norm rather than an exception. For this reason, we introduced new support for
such hybrid programs into the build system. By listing the 'lx_hybrid'
library in the 'LIBS' declaration of a target, this target magically becomes a
hybrid Linux/Genode program. It gets linked to the glibc and uses pthreads
instead of direct syscalls. Furthermore, host libraries can be linked to the
program by stating their respective names in the 'LX_LIBS' variable. For an
example, please refer to the libSDL-based framebuffer at
To enforce the build of all targets as hybrid Linux/Genode programs, the build
system features the 'alyways_hybrid' 'SPEC' value. To make it easy to create a
build directory with all targets forced to be hybrid, we have added the new
'lx_hybrid_x86' platform to the 'create_builddir' tool.
:Sending an invalid-opcode exception IPC on OKL4:
When an invalid opcode gets executed, OKL4 switches to the kernel debugger
console instead of sending an exception IPC to the userland. We fixed this
problem by removing the code that invokes the debugger console from the kernel.
:Hard-wire OKL4 elfweaver to Python 2:
Recent Linux distributions use Python version 3 by default. But OKL4's
elfweaver is not compatible with this version of Python. For this reason, we
introduced a patch for pinning the Python version used by elfweaver to
version 2.
Both patches get automatically applied when preparing the 'base-okl4'
repository via 'make prepare'.
Build system and tools
:Facility for explicitly building all libraries:
During its normal operation, the build system creates libraries as mere side
effects of building targets. There is no way to explicitly trigger the build of
libraries only. However, in some circumstances (for example for testing the
thorough build of all libraries), a mechanism for explicitly building libraries
would be convenient. Hence we introduced this feature in the form of the pseudo
target located at 'base/src/lib/'. By issuing 'make lib' in a build
directory, this target triggers the build of all libraries available for the
given platform.