base-okl4: remove unmaintained porting steps

This commit is contained in:
Norman Feske 2016-01-20 19:59:56 +01:00 committed by Christian Helmuth
parent e6729316ff
commit 6de763cb0b
26 changed files with 18 additions and 2181 deletions

View File

@ -1,10 +1,6 @@
This repository contains the implementation of Genode for the OKL4
kernel version 2.1. For further information, please refer to the
following documents:
:[http://genode.org/community/wiki/GenodeOnOKL4 - Genode on OKL4 Wiki page]:
This Wiki page contains the information on how to build and use
Genode with OKL4.
following website:
:[http://genode.org/documentation/articles/genode-on-okl4 - Bringing Genode to OKL4]:
This article explains the OKL4-specific porting work.

View File

@ -1,863 +0,0 @@
===================================================
Bringing the Genode OS Framework to the OKL4 kernel
===================================================
Norman Feske
This article documents the process of bringing the Genode OS Framework to a new
kernel platform, namely the OKL4 kernel developed by OK-Labs. OKL4 is an
industry-grade kernel that is deployed in millions of mobile phones.
For our work, we went for the OKL4 version 2.1 for two reasons. First,
whereas this version officially supports the x86 architecture, the later
version 3 is pretty much focused on the ARM architecture. At present, the x86
architecture is our primary platform for Genode development. Second, we like
to follow the evolution of OKL4 from its genesis (L4ka::Pistachio) to the
capability-based kernel design as pursued with the later versions. On this
path, the version 2.1 is an important milestone, which we wont like to miss.
Nevertheless of having chosen version 2.1 to begin with, we plan to bring
Genode to later versions of OKL4 as well.
In the article, we face numerous challenges such as integrating OKL4 support
into Genode's build system, exploring the OKL4 kernel interface and the
boot procedure, adapting Genode's framework libraries to the feature set
provided by the new kernel, and accessing interrupts and other hardware
resources.
The intended audience are developers interested in exploring
the realms of the L4-microkernel world and kernel developers who consider
running Genode as user-land infrastructure on top of their kernel.
For the latter group, we laid out the article as a rough step-by-step
guide providing our proposed methodology for approaching the port of
Genode to a new kernel platform. At many places, the article refers
to the source code of Genode, in particular the 'base-okl4' repository.
You can read the code online via our subversion repository:
[http://genode.svn.sourceforge.net/viewvc/genode/trunk/ - Browse the Genode subversion repository...]
Build-system support
####################
The first step is to create a simple hello-world program that can be executed
directly on the OKL4 kernel as roottask-replacement. This program does not rely
on any kernel features but uses port I/O to output some characters to the
serial interface. We need to understand the following things:
* We need a program that outputs some characters to the serial interface.
This program can be developed on a known kernel platform. Once we have a
working hello program, we only need to port it to the new kernel platform
but can assume that the test program itself is correct.
* How must the OKL4 rootask be linked in order to be executed by the kernel?
* How does the OKL4 boot procedure work? OKL4 relies on a tool called elfweaver,
which creates a bootable ELF-image (often called single image) from multiple
binaries, in particular the kernel and roottask. We need to create a
minimalist elfweaver configuration file that just starts the kernel and our
hello example.
The result of this first step can be found in 'src/test/okl4_01_hello_raw':
:'crt0': is the assembly startup code taken from the L4/Fiasco version of
Genode. This code defines the initial stack, contains the entry point of
the hello program, which calls a C function called '_main'.
:'hello.cc': is the implementation of the '_main' function, which outputs
some characters directly via the serial interface of a PC. It does not
contain any kernel-specific code nor it depends on any include files.
:'genode.ld': is the linker script that we already use for Genode programs
on other base platforms.
:'weaver.xml': is the description file of the single image to be created
by OKL4's elfweaver tool. It is useful to take a close look at this file. The
most important bits are the filename of the kernel specified in the
'<kernel>' tag and the filename of the hello program specified in the
'<rootprogram>' tag.
:'Makefile': contains the steps needed to compile the hello program and
invoke elfweaver to create the bootable single image.
To boot the single image, you can use your favorite boot loader such as
Grub. The single-image file must be specified as kernel. When booted, the
program should print a message over the serial line.
The next step is the proper integration of the hello example into the
Genode build system. For this, we create a new source-code repository called
'base-okl4' with the following structure:
! base-okl4/lib/mk/x86/startup.mk
! base-okl4/mk/spec-okl4.mk
! base-okl4/mk/spec-okl4_x86.mk
! base-okl4/src/test/okl4_02_hello/target.mk
! base-okl4/src/test/okl4_02_hello/hello.cc
! base-okl4/src/platform/x86/_main.cc
! base-okl4/src/platform/x86/crt0.s
! base-okl4/src/platform/genode.ld
! base-okl4/etc/specs.conf
The OKL4-specific build-system support is contained in the files 'specs.conf',
'spec-okl4.mk', and 'spec-okl_x86.mk'. The 'specs.conf' file steers the build
process once the 'base-okl4' repository is specified in the 'REPOSITORIES'
declaration in the 'etc/build.conf' file in the build directory.
The 'spec-okl4_x86.mk' file describes the build specifics via the mechanism
described in Genode's getting-started documentation:
! SPECS = genode okl4_x86
Driven by the content of this 'SPECS' declaration, the build system first
includes the 'spec' files for 'spec-genode.mk' (found in the 'base/' repository)
and 'spec-okl4_x86.mk' (found in the 'base-okl4/' repository).
The latter file contains all build options for OKL4 on the x86 architecture,
extends the 'SPECS' declaration by the platform specifics 'x86_32' and 'okl4'
(which both apply for 'okl4_x86'), and aggregates the corresponding 'spec'
files:
! SPECS += x86_32 okl4
!
! LD_SCRIPT ?= $(call select_from_repositories,src/platform/genode.ld)
! CXX_LINK_OPT += -Wl,-T$(LD_SCRIPT) -Wl,-Ttext=0x01000000
!
! include $(call select_from_repositories,mk/spec-x86_32.mk)
! include $(call select_from_repositories,mk/spec-okl4.mk)
The 'spec' file for 'x86_32' is contained in the 'base/'
repository. The one for 'okl4' is provided by 'base-okl4/'. It contains
all build options that are independent from the hardware platform, OKL4
is deployed on:
! -include $(call select_from_repositories,etc/okl4.conf)
! -include $(BUILD_BASE_DIR)/etc/okl4.conf
!
! INC_DIR += $(OKL4_DIR)/build/iguana/include
! INC_DIR += $(REP_DIR)/include
!
! PRG_LIBS += startup
!
! CC_OPT_NOSTDINC += -nostdinc
! CXX_LINK_OPT += -static -nostdlib -Wl,-nostdlib
! EXT_OBJECTS += $(shell $(CUSTOM_CXX_LIB) -print-file-name=libsupc++.a) \
! $(shell $(CUSTOM_CXX_LIB) -print-file-name=libgcc_eh.a) \
! $(shell $(CUSTOM_CXX_LIB) -print-libgcc-file-name)
!
! EXT_OBJECTS += $(OKL4_DIR)/build/iguana/lib/libl4.a
The most interesting point is that this file reads an OKL4-specific config
file from the 'etc/' subdirectory of the build directory. From this file,
it obtains the location of the OKL4 distribution via the 'OKL4_DIR'
declaration. The 'spec-okl4.mk' file above adds the 'build/iguana/include'
path to the default include search locations. We need this path for including
the headers from the 'l4/' subdirectory. Unfortunately, 'build/iguana/include/'
contains a lot of further includes, which we don't want to use. In contrary,
these includes pollute our include-search space. This is particularly problematic
for headers such as 'stdio.h', which will inevitably collide with Genode's own
libC headers. Hence we need to find a way, to isolate the 'l4/' headers from
the remaining Iguna headers. One elegant way is to shadow the 'build/iguana/include/l4'
directory in our local Genode build directory. This can be accomplished either
manually by creating a symbolic link from OKL4's 'build/iguana/include/l4' to
an include file within our Genode build directory, or by letting 'make' create
such a link automatically. The corresponding rules for this approach can be
found in the 'spec-okl4.mk' file.
On Genode, the startup code is encapsulated in a library called 'startup',
which is linked to each program by default. This library essentially consists
of a little snipped of assembly startup code 'crt0.s', which calls a platform-
independent C startup function called '_main' implemented in '_main.cc'. The
library-description file for the startup library is called 'startup.mk'
and has the following content:
! REQUIRES = okl4 x86
! SRC_S = crt0.s
! SRC_CC = _main.cc
!
! vpath crt0.s $(REP_DIR)/src/platform/x86
! vpath _main.cc $(REP_DIR)/src/platform/x86
We will use a '_main.cc' from another platform as template for the OKL4-
specific startup code but strip it down to an absolute minimum (leaving
out everything except the call the actual 'main' function. Note that
for this simple setup, we need to explicitly reference a symbol of 'crt0.s'
from '_main.cc' to prevent the linker from discarding the otherwise
unreferenced object file (which only contains our entry point). The easiest
way is to reference the '__dso_handle' variable, which is defined in
'crt0.s'. However, this is an intermediate work-around, which we will
remove in the next step. Alternatively, we could rely on the '-u' option
of the linker to prevent the entry symbol ('_start') from being discarded.
The implementation of the hello program equals the version of
'okl4_01_hello_raw' except that the main function is actually called
'main' rather than '_main'. The corresponding target description file
'target.mk' is straight forward:
! TARGET = hello
! REQUIRES = okl4
! SRC_CC = hello.cc
Creating dummy versions of the 'env' and 'cxx' libraries
########################################################
So far, the hello program does rely neither on OKL4-specific nor
Genode-specific code. The goal of the next step is to remove the
differences between the '_main.cc' file in our repository and the
'_main.cc' file of the other base platforms. We will add proper
C++ initialization, the calling of static constructors, and a
proper console implementation.
The first step is to include the 'cxx' libary to our target.
This is a Genode-specific C++ support library, which contains
functions used as back end of the GCC's 'libsupc++' and 'libgcc_eh'.
To include the 'cxx' library for building our hello program, we
add the following declaration to the 'target.mk' file:
! LIBS = cxx
On a rebuild, the build system will try to compile the 'cxx' library,
which, in turn, depends on a number of Genode header files. Most
of these header files are generic and hence contained in the 'base/'
repositories. However, the following header files are specific for
the actual base platform and, therefore, must be provided by ourself:
:'base/capability.h': This file defines the representation of an object
capability on the actual platform. For now, we can use the following
version, which we will expand later on (at the current stage, the
Capability class is not actually used but we need its definition for
successful compilation. The OKL4-specific 'capability.h' file must
be placed in 'include/base/' of the 'base-okl4/' repository.
! #ifndef _INCLUDE__BASE__CAPABILITY_H_
! #define _INCLUDE__BASE__CAPABILITY_H_
!
! namespace Genode {
! class Capability {
! public: bool valid() const { return false; }
! }
! typedef int Connection_state;
! }
!
! #endif /* _INCLUDE__BASE__CAPABILITY_H_ */
:'base/native_types.h': This file defines platform representations of
thread IDs, locks etc. Please take a look at the 'native_types.h' file
of another platform to get an overview on these types. For now, the
following simple version suffices:
! #ifndef _INCLUDE__BASE__NATIVE_TYPES_H_
! #define _INCLUDE__BASE__NATIVE_TYPES_H_
!
! namespace Genode {
! typedef volatile int Native_lock;
! typedef int Native_thread_id;
! typedef int Native_thread;
! }
!
! #endif /* _INCLUDE__BASE__NATIVE_TYPES_H_ */
In fact, at this point, the types are just dummies, which we will
replace later when porting further parts of the framework.
:'base/ipc.h': This is a platform-specific wrapper for Genode's
IPC API. Usually, this file just includes 'base/ipc_generic.h'.
Optionally, it can host platform-specific IPC functionality.
! #ifndef _INCLUDE__BASE__IPC_H_
! #define _INCLUDE__BASE__IPC_H_
!
! #include <base/ipc_generic.h>
!
! #endif /* _INCLUDE__BASE__IPC_H_ */
:'base/ipc_msgbuf.h': This file defines the IPC message-buffer layout.
Naturally, it is highly platform specific. For now, the following dummy
message-buffer layout will do:
! #ifndef _INCLUDE__BASE__IPC_MSGBUF_H_
! #define _INCLUDE__BASE__IPC_MSGBUF_H_
!
! namespace Genode {
! class Msgbuf_base { };
!
! template <unsigned BUF_SIZE>
! class Msgbuf : public Msgbuf_base { };
! }
!
! #endif /* _INCLUDE__BASE__IPC_MSGBUF_H_ */
Once, we have created these platform-specific header files, the 'cxx' libary
should compile successfully. However, there are a number of unresolved
symbols when linking the hello program. The 'cxx' library uses Genode's
'env()->heap()' as back end for its local malloc implementation. But so far,
we do not have ported Genode's 'env' library. Furthermore, there are
unresolved references to 'Genode::printf' as provided by Genodes console
implementation and some functions of the IPC framework.
Let us first resolve the 'Genode::printf' references by creating an
OKL4-specific version of Genode's console library. For this, we create
a new back end in 'src/base/console/okl4_console.cc' that uses the
serial output mechanism that we employed for our first 'hello_raw' program.
The corresponding library description file 'lib/mk/printf_okl4.mk' looks
as follows:
! SRC_CC = okl4_console.cc
! LIBS = cxx console
!
! vpath %.cc $(REP_DIR)/src/base/console
Now, we can add 'printf_okl4' to the 'LIBS' declaration of hello's 'target.mk'
file. When recompiling the hello program, the new 'printf_okl4' library will
be built and resolve the 'Genode::printf' symbols. There remain the unresolved
references to 'Genode::env()' and parts of the IPC framework.
The IPC implementation in 'src/base/ipc/ipc.cc' is not straight forward
and we defer it for now. Hence, we place only the following dummy functions
into the 'ipc.cc' file:
! #include <base/ipc.h>
!
! using namespace Genode;
!
! Ipc_ostream::Ipc_ostream(Capability dst, Msgbuf_base *snd_msg) :
! Ipc_marshaller(0, 0) { }
!
! void Ipc_istream::_wait() { }
!
! Ipc_istream::Ipc_istream(Msgbuf_base *rcv_msg) :
! Ipc_unmarshaller(0, 0) { }
!
! Ipc_istream::~Ipc_istream() { }
!
! void Ipc_client::_call() { }
!
! Ipc_client::Ipc_client(Capability &srv, Msgbuf_base *snd_msg,
! Msgbuf_base *rcv_msg) :
! Ipc_istream(rcv_msg), Ipc_ostream(srv, snd_msg), _result(0) { }
!
! void Ipc_server::_wait() { }
!
! void Ipc_server::_reply() { }
!
! void Ipc_server::_reply_wait() { }
!
! Ipc_server::Ipc_server(Msgbuf_base *snd_msg,
! Msgbuf_base *rcv_msg) :
! Ipc_istream(rcv_msg), Ipc_ostream(Capability(), snd_msg) { }
The corresponding library-description file 'lib/mk/ipc.mk' looks as
follows:
! SRC_CC = ipc.cc
! vpath ipc.cc $(REP_DIR)/src/base/ipc
By adding 'ipc' to the 'LIBS' declaration in hello's 'target.mk' file, the
IPC-related linker errors should disappear and only the reference to
'Genode::env()' remains. To resolve this symbol, we add the following dummy
function directly into the code of 'hello.cc'.
! namespace Genode {
! void *env() { return 0; }
! }
Before we can use the Genode framework, which is written in C++, we need to
make sure that all static constructors are executed in the startup code
('_main'). Therefore, we add the following code to the '_main' function:
! void (**func)();
! for (func = &_ctors_end; func != &_ctors_start; (*--func)());
The referenced symbols '_ctors_start' and '_ctors_end' are created by the
linker script. The corresponding declarations are provided by
'base/include/base/crt0'..
Now, its time to replace the direct I/O port access in 'hello.cc' by
Genode's 'printf' implementation. Just add the following line to the main
function of 'hello.cc' and make sure to include '<base/printf.h>':
! Genode::printf("This is Genode's printf\n");
When starting the resulting program, this message should appear via the
serial interface comport 0.
Initializing the C++ exception handling
#######################################
The Genode OS Framework makes use of C++ exceptions. Hence, we need to
make sure to properly initialize the 'libsupc++'. This initialization
comes down to calling the function
! __register_frame(__eh_frame_start__);
which is performed by the function 'init_exception_handling' as provided
by the generic 'cxx' library. Normally, 'init_exception_handling' is called
from '_main'. It is important to know that the initialization code does
use 'malloc', which is mapped to Genode's 'env()->heap()' by the 'cxx'
library. Consequently, we need a working heap to successfully initialize
the exception handling.
Therefore, we have to replace the dummy 'env()' function in our hello
program with something more useful. The header file 'src/test/minimal_env.h'
provides the heap functionality by using a minimalistic custom environment,
which contains a heap with static pool of memory. With such an environment
in place, we can safely call 'init_exception_handling' from the '_main'
startup code. The test 'okl4_02_hello' is the result of this step. It
first prints some text via Genode's 'printf' implementation and then triggers
a C++ exception.
Thread creation
###############
So far, we have not performed any OKL4 system call. The first system call that
we will explore is the 'L4_ThreadControl' to create a thread. A corresponding
test for this functionality is implemented in the 'test/okl4_03_thread'
example. This example creates a new thread with the thread number 1. Note that
the matching L4 thread ID uses the lowest 14 bits as version number, which is
always set to 1. Hence, the L4 thread ID of thread number 1 will be 0x4001. If
you happen to need to look up this thread in OKL4's kernel debugger, you will
find its thread control block (TCB) via this number.
Another important thing to note is that rootask's main thread runs initially
at the priority of 255 whereas newly created threads get assigned a default
priority of 100. To make OKL4's preemtive scheduling to work as expected, we
need to assign the same priority to both threads by calling 'L4_Set_Priority'.
IPC framework
#############
Now that we can start multiple threads, we can fill Genode's IPC framework with
life.
However, before we can get started with communication between threads, the
communication partners must have a way to get to know each other. In particular,
a receiver of IPC communication needs a way to make its communication address
known to a sender. OKL4 uses 'L4_ThreadId_t' as communication address. The
thread's ID is assigned to each thread by its creator. The thread itself however,
does not know its own identity when started up. In contrast to other L4 kernels
that provide a way for thread to determine its own identity via a 'L4_Myself'
call, this functionality is not supported on OKL4. Therefore, the creator of
a new thread must communicate the assigned thread ID to the new thread via
a startup protocol. We use OKL4's 'UserDefinedHandle' for this purpose. This
is an entry of the threads UTCB that can be remotely accessed by the creating
thread. Before starting the new thread, the creator writes the assigned thread
ID to the new thread's user-defined handle. In turn, the startup code of the
new thread copies the supplied value from the user-defined handle to a
thread-local entry of the UTCB (a designated 'ThreadWord'). In the following,
the thread can always determine its own global ID by reading this 'ThreadWord'
from its UTCB. We declare the convention about which 'ThreadWord' to use for
this purpose in Genode's 'base/native_types.h' ('UTCB_TCR_THREAD_WORD_MYSELF').
IPC send and wait
=================
The test program 'okl4_04_ipc_send_wait' sends an IPC messages via Genode's
'Ipc_istream' and 'Ipc_ostream' framework. To make this example functional,
we have to work on the following parts of the 'base-okl4/' repository.
:'include/base/capability.h':
Genode uses the 'Capability' class to address an IPC communication and a
referenced object. Therefore, we must provide a valid representation of these
information. Because all IPC operations on OKL4 always address threads, we
use 'L4_ThreadId_t' as representation of communication address. There are no
kernel objects representing user-level objects in OKL4 (version 2). So we
need to manage object identities on the user level, unprotected by the
kernel. For now, we simply use a globally unique object ID for this purpose.
:'include/base/ipc_msgbuf.h':
The message-buffer representation used for OKL4 does not use any
kernel-specific layout because IPC payload is always transferred through the
communicating thread's UTCBs. Hence, the 'Msgbuf' template does only need to
provide some space for storing messages but no control information.
:'src/base/ipc/ipc.cc':
For the send-and-wait test, we need to implement the 'Ipc_istream' and
'Ipc_ostream' class functions: the constructors of 'Ipc_istream' and
'Ipc_ostream', the '_wait' function, and the '_send' function. It is useful
to take a look at the other platform's implementations for reference.
Because the Genode IPC Framework provides the functionality for marshalling
and unmarshalling of messages, we skip OKL4 'message.h' convenience
abstraction in favor of addressing UTCB message registers 'ipc.h' directly.
IPC call
========
The test program 'okl4_05_ipc_call' performs IPC communication using Genode's
'Ipc_client' and 'Ipc_server' classes. To make this test work, the corresponding
functions in 'src/base/ipc/ipc.cc' must be implemented, in particular the
functions '_reply_wait' and '_call'.
Address-space creation and page-fault handling
##############################################
There are the following Peculiarities of OKL4 with regard to address-spaces.
OKL4 does not use IPC to establish memory mappings but an independent
system call 'L4_MapControl' to configure the local or an remote address
space. In the line of other L4 kernels, page faults are handled via
an IPC-based pager protocol. The typical mode of operation of a pager
looks like:
# A page fault occurs, the kernel translates the page fault into a
page-fault message delivered to the pager of the faulting thread.
# The pager receives a page-fault message, decodes the page-fault
address, the fault type (read, write, execute), and the instruction
pointer of the faulter from the page-fault message.
# The pager resolves the page fault by populating the faulter's
address spaces with valid pages via 'L4_MapControl'.
# The pager answers the page-fault message with an empty IPC to
resume the operation of the faulter.
In contrast to L4/Fiasco and L4ka::Pistachio, which incorporate the
memory mapping into the reply message, this procedure involves
an additional system call. However, it is more flexible and allows
the construction of a fully populated address space without employing
an IPC-based protocol. Furthermore, the permissions for establishing
memory mappings are well separated from IPC-communication rights.
In contrast to the L4/Fiasco and L4ka::Pistachio kernels, which take
a virtual address of the mapper as argument, the OKL4 map operation
always refers to a physical page. This enables the configuration of a
remote address space without having all the used pages locally mapped
as well. For specifying a local virtual address for a mapping, we
can use the 'L4_ReadFpage' function to look up a physical-memory
descriptor for a given virtual address.
The test 'okl4_06_pager' creates an address space to be one-to-one
mapped with roottask. In the new address space, a thread is created.
For the new thread, we use the roottask thread as pager. Once started,
the new that raises a number page faults:
# Reading the first instruction of the entry point
# Accessing the first stack element
# Reading data
# Writing data
The pager receives the corresponding page-fault messages, prints
the decoded information, and resolves the page faults accordingly.
Determining the memory configuration and boot modules
#####################################################
OKL4 provides its boot information to roottask via a boot-info structure, which
is located at the address provided in roottask's UTCB message register 1. This
structure is created by OKL4's elfweaver during the creation of the boot image.
It has no fixed layout but it contains a batch of operations such as "add
memory pool" or "create protection domain". In short, it (loosely) resembles
the content of the elfweaver XML config file in binary form. Most of
elfweaver's features will remain unused when running Genode on OKL4. However,
there are some important bits of information we need to know:
* Memory configuraion
* Information on the boot modules
For parsing the boot-info structure, there exists a convenient library located
in the OKL4 source tree at 'libs/bootinfo'. The test program
'okl4_07_boot_info' uses this library to obtain the information we are
interested in.
Note that we link the library directly to the test program by using the
'EXT_OBJECTS' declaration in the 'target.mk' file. We are not adding this
library to the global 'spec-okl4.mk' file because we need the bootinfo-library
only at a very few places (this test program and core).
We obtain the memory configuration by assigning a callback function to the
'init_mem' entry of the 'bi_callbacks_t' structure supplied to the parser
library. There are indeed two 'init_mem' function called 'init_mem' and
'init_mem2'. The second instance is called during a second parsing stage.
However, both functions seem to be called with the same values. So we just
disregard the values supplied to 'init_mem2' at this point.
To include other modules than the 'rootprogram' to the boot image, we use the
help of elfweaver's '<pd>' declaration. We create a pseudo protection domain as
a container for several memory sections, each section loaded with the content
of a file. An example declaration for including the files 'init' and 'config'
into the boot image looks like this:
!<pd name="modules">
! <memsection name="init" file="init" direct="true" />
! <memsection name="config" file="config" direct="true" />
!</pd>
The 'direct="true"' attribute has the effect that the memory section will
have equal physical and virtual addresses.
When observing the output of 'okl4_07_boot_info', the relevant information
are the 'new_ms' (new memory section) lines with owner != 0 (another PD
than roottask) and virtpool != 1. These memory sections correspond to
the files. However, the association of the memory sections with their file
names is still missing at this point. To resolve this problem, we also observe
the 'export_object' calls. For each memory section, 'export_object' gets
called with the type parameter set to 'BI_EXPORT_MEMSECTION_CAP' and the key
parameter set to the name of the file. Note that the file name is converted to
upper case. For associating memory sections with file names, we assume that
the order of 'new_ms' calls corresponds to the order of matching
'export_object' calls.
Interrupt handling and time source
##################################
In contrast to most of the classical L4 kernels, OKL4 provides no means
for accessing wall-clock time from the user land. Internally, OKL4 uses
a scheduling timer to perform preemptive scheduling but it does not expose
a time source to the user land via IPC timeouts. Hence, we need an alternative
way to obtain a user-level time source. We follow the same path as Iguana
by driving the programmable interval timer (PIT) directly from a
user-level service. Because OKL4 uses the more modern APIC timer, which is
completely independent of the PIT, both the kernel and the user land
can use entirely different timer devices as their respective time source.
The PIT is connected to the interrupt line 0 of the programmable interrupt
controller (PIC). The test program 'okl4_08_timer_pit' switches the PIT
into one-shot mode and waits for timer interrupts. Each time a timer
interrupt occurs, the next one-shot is scheduled. The program tests two
important things: How does the interrupt handling work on OKL4 and
how to provide a user-level time source?
The following things are worth mentioning with regard to IRQ handling:
* By default, no one (roottask included) has the right to handle interrupts.
We have to explicitly grant ourself the right to handle a particular
interrupt by calling 'L4_AllowInterruptControl'.
* When calling 'L4_RegisterInterrupt', the kernel expects a real global
thread ID, not the magic ID returned by 'L4_Myself()'.
* Interrupts are delivered in an asynchronous fashion by using OKL4's
notification mechanism. To block for incoming asynchronous messages,
the corresponding notification bit must be unmasked and notifications
must be accepted.
* The interrupt-handler loop invokes two system calls per interrupt,
'L4_ReplyWait' for blocking for the next interrupt and 'L4_AcknowledgeInterrupt'
for interrupt acknowledgement. Both syscalls could be consolidated into a
call of 'L4_AcknowledgeWaitInterrupt'.
Porting core
############
Now that we have discovered the most functional prerequisites for running
Genode on OKL4, we can start porting Genode's core. I suggest to take
another platform's core version as a template. For OKL4, the 'base-pistachio'
version becomes handy. First, make a copy of 'src/core' to the 'base-okl4/'
repository. Then we revisit all individual files and remove all
platform-specific code with the goal to create a skeleton of core that
compiles successfully. Thereby, we can already apply some simple type
substitutions, for example by using the types declared in 'native_types.h'
we can avoid using platform-specific types such as 'L4_ThreadId_t'.
By trying to compile core, we will see that there are still a few framework
libraries missing, namely 'pager', 'lock', and 'raw_signal'. For resolving the
dependency on the _lock library_, we can use a simple spinlock implementation
as an intermediate step. The implementation at 'src/base/lock/lock.cc' looks
like this:
!#include <base/cancelable_lock.h>
!#include <cpu/atomic.h>
!
!using namespace Genode;
!
!Cancelable_lock::Cancelable_lock(Cancelable_lock::State initial)
!: _native_lock(UNLOCKED)
!{
! if (initial == LOCKED)
! lock();
!}
!
!void Cancelable_lock::lock()
!{
! while (!cmpxchg(&_native_lock, UNLOCKED, LOCKED));
!}
!
!void Cancelable_lock::unlock()
!{
! _native_lock = UNLOCKED;
!}
Note that this implementation does not fully implement the 'Cancelable_lock'
semantics but it is useful to get things started. The corresponding 'lib/mk/lock.mk'
can be based on another platform's variant:
!SRC_CC = lock.cc
!vpath lock.cc $(REP_DIR)/src/base/lock
The OKL4-specific _signal library_ can be taken almost unmodified from
'base-pistachio/'. The _pager library_ is a bit more complicated because
it depends on 'ipc_pager.h' and the corresponding part of the ipc library,
which we have not yet implemented yet. However, based on the knowledge
gained from the 'okl4_06_pager' test, the adaption of another platform's
implementation of 'src/base/ipc/pager.cc' becomes straight-forward. For now,
it actually suffices to leave the functions in 'pager.cc' blank.
Once, we get the skeleton of core linked, we can work on the OKL4-specific
code, starting with core's platform initialization in 'platform.cc'.
Configuring core's memory allocators:
:'region_alloc': This is the allocator containing the virtual address
regions that are usable within core. The boot-info parser reports these
regions via the callbacks 'init_mem' and 'add_virt_mem'.
:'ram_alloc': This is the allocator containing the available physical
memory pages. It must be initialized with the physical-memory ranges
provided via the 'init_mem' and 'add_phys_mem' callbacks.
:'core_mem_alloc': This is an allocator for available virtual address
ranges within core. In contrast to 'region_alloc' and 'ram_alloc', which
both are operating at page-granularity, 'core_mem_alloc' can be used to
allocate arbitrarily-sized memory objects. The implementation uses
'region_alloc' and 'ram_alloc' as back ends. The core-local mapping
of physical memory pages to core's virtual address space is done in a
similar way as practiced in the 'okl4_06_pager' test program.
For implementing the allocators, special care must be taken to make their
interfaces thread safe as they may be used concurrently by different core
threads. With the memory configuration in place, core will pass the first
initialization steps and tries to initialize 'Core_env', which is a
core-specific variant of the Genode environment. A part of 'Core_env' is a
server-activation, which is indeed a thread. Upon the creation of this thread,
the main thread of core will stop executing until the new thread's startup
protocol is finished. So we have to implement core's thread-creating facility,
which is 'platform_thread.cc'.
After core successfully creates its secondary threads (called 'activation' and
'pager'), and finishes the initialization of 'Core_env()', it starts executing
the 'main' function, which uses plain Genode APIs such as the 'env()->heap()'.
The heap however relies on a working 'env()->rm_session()' and
'env()->ram_session()'. To make 'env()->rm_session()' functional, we need to
provide a working implementation of the 'Core_rm_session::attach()' function,
which maps the content of a dataspace to core's local address space. Once,
core starts using its 'Env', it will try to use 'env()->rm_session()' to attach
dataspaces into its local address space. Therefore, we need an implementation
of a core version of the 'Rm_session' interface, which we call
'Core_rm_session'. This implementation uses the OKL4 kernel API to map the
physical pages of a dataspace into core's local address space. With the
working core environment, core will look for the binary of the init process.
Init is supplied to core as a boot module via the elfweaver mechanism we
just explored with the 'okl4_07_boot_info' test. Within core, all boot modules
are registered to an instance of the 'Rom_fs' class. Hence, we will need to
call OKL4's boot-info parser with the right callback functions supplied and put
the collected information into 'Rom_fs'. It is useful to take the other
platforms as reference.
Starting init
#############
To enable core to successfully load and start the init process, we first need
to build the init binary. For compiling 'init' we have to implement the still
missing functionality of determining the parent capability at the startup code.
The needed function is called 'parent_cap()' and should be implemented in the
'_main' function. For OKL4, the implementation looks exactly like the Pistachio
version. On both kernels, the parent capability is supplied at predefined
locations declared in the linker script. The corresponding symbols are called
'_parent_cap_thread_id' and '_parent_cap_local_name'.
After successfully having started init, we can proceed with starting further
instances of init as a children of the first instance. This can be achieved by the
following config file:
!<config>
! <parent-provides>
! <service name="ROM"/>
! <service name="RAM"/>
! <service name="CAP"/>
! <service name="PD"/>
! <service name="RM"/>
! <service name="CPU"/>
! <service name="LOG"/>
! </parent-provides>
! <default-route>
! <any-service> <parent/> </any-service>
! </default-route>
! <start name="init.1">
! <binary name="init"/>
! <resource name="RAM" quantum="5M"/>
! </start>
! <start name="init.2">
! <binary name="init"/>
! <resource name="RAM" quantum="5M"/>
! <config>
! <parent-provides>
! <service name="ROM"/>
! <service name="RAM"/>
! <service name="CAP"/>
! <service name="PD"/>
! <service name="RM"/>
! <service name="CPU"/>
! <service name="LOG"/>
! </parent-provides>
! <default-route>
! <any-service> <parent/> </any-service>
! </default-route>
! <start name="init.2.1">
! <binary name="init"/>
! <resource name="RAM" quantum="2M"/>
! </start>
! <start name="init.2.2">
! <binary name="init"/>
! <resource name="RAM" quantum="2M"/>
! </start>
! </config>
! </start>
!</config>
To successfully execute the creation of this nested process tree, we need
a correct implementation of 'unmap' functionality within core.
Furthermore, if starting multiple processes, we will soon run into the problem
of starting too many threads in core. This is caused by the default
implementation of Genode's signal API.
Within core, each 'Rm_session_component' within core is a signal transmitter,
used for signalling address-space faults.
With the default implementation, each signal transmitter employs one thread.
Because OKL4's roottask is limited to 8 threads, the number of RM sessions
becomes quite limited. Therefore, we disable signal support on OKL4 for now
by the means of a dummy implementation of the signal interface. Later, we can
create a OKL4-specific signal implementation, which will hopefully be able to
utilize OKL4's asynchronous notification mechanism.
Hardware access and the Genode demo scenario
############################################
The default demo scenario of Genode requires hardware access performed by the
following components:
* The timer driver needs access to a hardware timer. On x86, the programmable
interval timer (PIT) is available for this use case.
However, for the first version of Genode on OKL4, we can use a simple dummy
driver that ignores the argument of 'msleep' and just returns.
* The PS/2 driver and the timer driver rely on interrupts. We already exercised
interrupt handling in 'okl4_08_timer_pit'. So it is relatively straight-forward
to implement the IRQ service in core. (taking the other platforms such as
Pistachio as reference)
* The VESA driver requires several hardware facilities, in particular access
to the VGA registers via I/O ports, the frame buffer via memory-mapped I/O
and other resources such as the PIC (at least some VESA BIOSes rely on the
PIT to implement proper delays during the PLL initialization).
However, with a working implementation of the I/O-port service and
I/O-memory service in core, these requirements become satisfied.
If all the hardware-access services within core are in place, we should be able
to start 'fb_drv', 'ps2_drv', 'nitpicker', 'launchpad'. Furthermore starting
and killing of an additional 'testnit' process via the launchpad should work.
However, we will observe that starting another instance of testnit after
killing it will not work. In order to fully support restartable components,
we have to implement thread destruction, and the cancel-blocking mechanism within core.
The interesting bits about thread destruction are 'Platform_thread::unbind' and
'Platform_pd::_destroy_pd'. For implementing the cancel-blocking mechanism, we
have to revisit core's 'Platform_thread::cancel_blocking', the IPC framework
('src/base/ipc/ipc.cc') and the lock implementation ('src/base/lock/lock.cc').
With this work done, we are able to run the full Genode demonstration scenario
including the Scout tutorial browser, user-level device drivers for PS/2
input and video, and the dynamic creation and destruction of process trees.
Outlook
#######
We consider the result of the porting work as described in this article as the
first working version of Genode on OKL4. Of course, there are several areas
for possible improvements, which we will address in a demand-driven way.
The following list gives some hints:
* Exploring OKL4's kernel mutex for Genode's lock implementation,
paying special attention to the cancel-blocking semantics
* Increasing the flexibility of the UTCB allocator in core. Right now, the UTCB
area of each PD is equally sized, defined by the 'THREAD_BITS' definition.
In the future, we could support differently sized UTCB areas to tailor the
number of threads per protection domain.
* Checking the privileges of non-core tasks
* Supporting RM faults and nested region-manager sessions
* Replacing the dummy timer implementation with a proper PIT-based
timer
* Virtualizing the PIT in the VESA frame-buffer driver, otherwise
the PIT-based timer service won't be usable because of both
components needing access to the PIT. Fortunately, the VESA BIOS of Qemu
does not access the PIT but we are aware that other BIOSes do.
* Eventually optimize I/O port access. Right now, we perform an RPC call
to core for each I/O port access, which is ok for the other platforms
because I/O ports are rarely used (mostly for the PS/2 driver, but at
a low rate). On OKL4 however, we provide the user-level time source
via the timer driver that accesses the PIT via I/O ports. We could
optimize these accesses by lazily mapping the I/O ports from core to
the timer driver the first time, an RPC call to the I/O service is
performed.

View File

@ -39,27 +39,25 @@ a look at this page:
Downloading and building the OKL4 kernel
########################################
To download the OKL4 source code, issue
To download the OKL4 source code, issue the following command:
! make prepare
! <genode-dir>/tool/ports/prepare_port okl4
from within the 'base-okl4' directory. The Makefile within this directory will
take care of downloading the kernel's source code and applying the patches
found at 'base-okl4/patches'.
It will take care of downloading the kernel's source code and applying the
patches found at 'base-okl4/patches'.
For the vesa driver on x86 the x86emu library is required and can be downloaded
and prepared by invoking the following command from within the 'libports'
directory:
For the VESA driver on x86, the x86emu library is required and can be downloaded
and prepared by invoking the following command:
! make prepare PKG=x86emu
! <genode-dir>/tool/ports/prepare_port x86emu
To create a build directory for Genode running on OKL4, use the 'create_builddir'
tool:
! <genode-dir>/tool/create_builddir okl4_x86 BUILD_DIR=<build-dir>
! <genode-dir>/tool/create_builddir okl4_x86
Once, you have created the build directory, the OKL4 kernel can be built from
within '<build-dir>' via
Once you have created the build directory at '<genode-dir>/build/okl4_x86',
the OKL4 kernel can be built from within the build directory via
! make kernel
@ -67,8 +65,8 @@ within '<build-dir>' via
Running the Genode demonstration scenario
#########################################
For a quick test drive of the OKL4 kernel, issue 'make run/demo' from the build
directory.
For a quick test drive of the OKL4 kernel, issue 'make run/demo' from the
build directory.
Manually building a boot image
@ -76,7 +74,7 @@ Manually building a boot image
This section is not needed when using Genode's run-script mechanism. The manual
steps described below are automatically executed via the OKL4 run environment
as found at 'base-okl4/run/env'.
as found at 'tool/run/boot_dir/okl4'.
To practically use the OKL4 kernel and applications running on top of it, Open
Kernel Labs provide a tool called 'elfweaver', that is used to merge different
@ -97,7 +95,7 @@ The corresponding line in your weaver.xml should look like this:
Before creating the image, we need to supply a Genode config file as well.
For a quick start, you can copy and rename the template provided 'os/config/demo'
to '<builddir>/bin/config'. Alternatively, you can assign another file to the
to '<buil-ddir>/bin/config'. Alternatively, you can assign another file to the
'filename' of the 'memsection' declaration for the config file in 'weaver.xml'.
Now, we can use 'elfweaver' to create the image. Go to the 'bin' directory in
the Genode build directory that contains all the binaries and invoke the
@ -117,7 +115,7 @@ Apparently, elfweaver has a problem with calculating the size of the boot info
section. As a quick fix, you can increase the value of 'BOOTINFO_GUESS_OPS' in
'<okl4-dir>/tools/pyelf/weaver/bootinfo.py'.
The resulting elf image can be loaded by Grub now.
The resulting elf image can be loaded by GRUB now.
Further Information
@ -125,13 +123,6 @@ Further Information
:[http://genode.org/documentation/articles/genode-on-okl4]:
Article about the porting work of Genode to OKL4, featuring many technical
insights that are useful to understand the peculiarities of this base platform.
insights that are useful to understand the peculiarities of this base
platform.
:okl4_2.1.1-patch.9/README:
OKL4 building guide
:[http://wiki.ok-labs.com]:
OKL4 developer wiki
:[http://wiki.ok-labs.com/downloads/release-3.0/elfweaver-ref-manual-3.0.pdf]:
Elfweaver reference manual

View File

@ -1,144 +0,0 @@
/*
* \brief Thread creation on OKL4
* \author Norman Feske
* \date 2009-03-26
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CREATE_THREAD_H_
#define _CREATE_THREAD_H_
/* OKL4 includes */
namespace Okl4 { extern "C" {
#include <l4/types.h>
#include <l4/utcb.h>
#include <l4/config.h>
#include <l4/thread.h>
#include <l4/ipc.h>
#include <l4/message.h>
#include <l4/schedule.h>
} }
namespace Okl4 {
extern L4_Word_t copy_uregister_to_utcb(void);
}
/* Genode includes */
#include <base/printf.h>
#include <base/native_types.h>
enum { DEFAULT_PRIORITY = 100 };
/**
* Create and start new thread
*
* \param thread_no designated thread number of new thread
* \param space_no space ID in which the new thread will be executed
* \param sp initial stack pointer
* \param ip initial instruction pointer
* \return native thread ID
*/
static inline Okl4::L4_ThreadId_t create_thread(int thread_no, int space_no,
void *sp, void (*ip)(void))
{
using namespace Okl4;
/* activate local thread by assigning a UTCB address and thread ID */
L4_ThreadId_t new_thread_id = L4_GlobalId(thread_no, 1);
L4_SpaceId_t roottask_space_id = L4_SpaceId(space_no);
L4_ThreadId_t scheduler = L4_rootserver;
L4_ThreadId_t pager = L4_rootserver;
L4_ThreadId_t exception_handler = L4_rootserver;
L4_Word_t resources = 0;
L4_Word_t utcb_location = (L4_Word_t)utcb_base_get()
+ space_no*L4_GetUtcbAreaSize()
+ thread_no*L4_GetUtcbSize();
#ifdef NO_UTCB_RELOCATE
utcb_location = ~0; /* UTCB allocation is handled by the kernel */
#endif
int ret = L4_ThreadControl(new_thread_id,
roottask_space_id,
scheduler, pager, exception_handler,
resources, (void *)utcb_location);
if (ret != 1) {
PERR("L4_ThreadControl returned %d, error code=%d",
ret, (int)L4_ErrorCode());
for (;;);
return L4_nilthread;
}
/* let the new thread know its global thread id */
L4_Set_UserDefinedHandleOf(new_thread_id, new_thread_id.raw);
/* start thread */
L4_Start_SpIp(new_thread_id, (L4_Word_t)sp, (L4_Word_t)ip);
/* set default priority */
L4_Set_Priority(new_thread_id, DEFAULT_PRIORITY);
return new_thread_id;
}
/**
* Perform thread startup protocol to make global ID known to the ourself
*
* This function must be executed by a newly created thread to make
* 'thread_get_my_global_id' to work.
*/
static inline void thread_init_myself()
{
using namespace Okl4;
/*
* Read global thread ID from user-defined handle and store it
* into a designated UTCB entry.
*/
L4_Word_t my_global_id = L4_UserDefinedHandle();
__L4_TCR_Set_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF, my_global_id);
}
/**
* Register the rootserver's thread ID at our UTCB
*
* This function must be executed at the startup of the rootserver main
* thread to make 'thread_get_my_gloal_id' to work.
*/
static inline void roottask_init_myself()
{
using namespace Okl4;
/*
* On Genode, the user-defined handle get initialized with the thread's
* global ID by core when creating the new thread. For the main thread,
* we do this manually.
*/
__L4_TCR_Set_UserDefinedHandle(L4_rootserver.raw);
copy_uregister_to_utcb();
}
/**
* Return the global thread ID of the calling thread
*
* On OKL4 we cannot use 'L4_Myself()' to determine our own thread's
* identity. By convention, each thread stores its global ID in a
* defined entry of its UTCB.
*/
static inline Okl4::L4_ThreadId_t thread_get_my_global_id()
{
Okl4::L4_ThreadId_t myself;
myself.raw = Okl4::__L4_TCR_ThreadWord(Genode::UTCB_TCR_THREAD_WORD_MYSELF);
return myself;
}
#endif /* _CREATE_THREAD_H_ */

View File

@ -1,36 +0,0 @@
/*
* \brief I/O port access function for x86
* \author Norman Feske
* \date 2009-03-31
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _IO_PORT_H_
#define _IO_PORT_H_
/**
* Read byte from I/O port
*/
inline unsigned char inb(unsigned short port)
{
unsigned char res;
asm volatile ("inb %%dx, %0" :"=a"(res) :"Nd"(port));
return res;
}
/**
* Write byte to I/O port
*/
inline void outb(unsigned short port, unsigned char val)
{
asm volatile ("outb %b0, %w1" : : "a" (val), "Nd" (port));
}
#endif /* _IO_PORT_H_ */

View File

@ -1,87 +0,0 @@
/*
* \brief Minimalistic environment used for test steps
* \author Norman Feske
* \date 2009-03-25
*
* This file is not an interface but an implementation snippet. It should
* be included only once per program because it contains the implementation
* of the global 'env()' function.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _MINI_ENV_H_
#define _MINI_ENV_H_
#include <base/allocator_avl.h>
#include <base/env.h>
/**
* Minimalistic environment
*
* \param HEAP_SIZE size of static heap in bytes
*
* This implementation of the Genode environment does only
* provide a heap. It suffices to successfully initialize
* the C++ exception handling.
*/
template <unsigned HEAP_SIZE>
class Minimal_env : public Genode::Env
{
private:
char _heap[HEAP_SIZE];
Genode::Allocator_avl _alloc;
public:
/**
* Constructor
*/
Minimal_env() : _alloc(0) {
_alloc.add_range((Genode::addr_t)_heap, HEAP_SIZE); }
Genode::Allocator *heap() { return &_alloc; }
/***********************************************
** Dummy implementation of the Env interface **
***********************************************/
Genode::Parent *parent() { return 0; }
Genode::Ram_session *ram_session() { return 0; }
Genode::Cpu_session *cpu_session() { return 0; }
Genode::Rm_session *rm_session() { return 0; }
Genode::Pd_session *pd_session() { return 0; }
Genode::Ram_session_capability ram_session_cap() {
return Genode::Ram_session_capability(); }
Genode::Cpu_session_capability cpu_session_cap() {
return Genode::Cpu_session_capability(); }
void reload_parent_cap(Genode::Capability<Genode::Parent>::Dst, long) { }
};
/**
* Instance of the minimal environment
*/
namespace Genode {
/**
* Instance of minimalistic Genode environment providing a
* static heap of 64KB
*/
Env *env()
{
static Minimal_env<64*1024> env;
return &env;
}
}
#endif /* _MINI_ENV_H_ */

View File

@ -1,27 +0,0 @@
OKL4_DIR = ../okl4_2.1.1-patch.9
CXXFLAGS = -I$(OKL4_DIR)/build/pistachio/include -nostdlib -nostdinc
#CXXFLAGS += -DCONFIG_MAX_THREAD_BITS=10
#CXXFLAGS += -DCONFIG_CPU_IA32_I586
#CXXFLAGS += -DCONFIG_KDB_CONS
LD_SCRIPT = genode.ld
LDFLAGS = -Wl,-Ttext=0x01000000 -Wl,-T$(LD_SCRIPT)
all: image
hello: hello.cc
$(CXX) $(CXXFLAGS) $(LDFLAGS) -static crt0.s hello.cc -o $@
#
# Merge kernel and hello program into a single elf image
#
# We need to strip the elf image to make the image loadable by grub.
# Otherwise GRUB complaints:
#
# "Error 28: Selected item cannot fit into memory"
#
image: hello weaver.xml
$(OKL4_DIR)/tools/pyelf/elfweaver merge -o $@ weaver.xml
strip $@
clean:
rm -f hello image

View File

@ -1,54 +0,0 @@
/**
* \brief Startup code for Genode applications
* \author Christian Helmuth
* \date 2006-07-06
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/*--- .text (program code) -------------------------*/
.text
.globl _start
_start:
/* XXX Switch to our own stack. */
mov $_stack_high,%esp
/* Clear the base pointer so that stack backtraces will work. */
xor %ebp,%ebp
/* Jump into init C code */
call _main
/* We should never get here since _main does not return */
1: int $3
jmp 2f
.ascii "_main() returned."
2: jmp 1b
/*--------------------------------------------------*/
.globl __dso_handle
__dso_handle:
.long 0
/*--- .eh_frame (exception frames) -----------------*/
/*
.section .eh_frame,"aw"
.globl __EH_FRAME_BEGIN__
__EH_FRAME_BEGIN__:
*/
/*--- .bss (non-initialized data) ------------------*/
.bss
.p2align 4
.globl _stack_low
_stack_low:
.space 64*1024
.globl _stack_high
_stack_high:

View File

@ -1,89 +0,0 @@
/*
* \brief Linker script for GENODE programs
* \author Christian Helmuth
* \date 2006-04-12
*/
/*
* Copyright (C) 2006-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
ENTRY(_start)
PHDRS
{
ro PT_LOAD;
rw PT_LOAD;
}
SECTIONS
{
.text : {
/* begin of program image (link address) */
_prog_img_beg = .;
*(.init)
*(.text .text.* .gnu.linkonce.t.*)
*(.fini)
*(.rodata .rodata.* .gnu.linkonce.r.*)
. = ALIGN(0x04);
_ctors_start = .;
KEEP (*(.ctors))
KEEP (*(SORT(.ctors.*)))
_ctors_end = .;
_dtors_start = .;
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
_dtors_end = .;
} : ro = 0x0
/* Linux: exception section for uaccess mechanism */
__ex_table : { *(__ex_table) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
. = ALIGN(0x1000);
_prog_img_data = .;
.data : {
/* Leave space for parent capability parameters at start of data
* section. The protection domain creator is reponsible for storing
* sane values here.
*/
_parent_cap_thread_id = .;
LONG(0xffffffff);
_parent_cap_local_name = .;
LONG(0xffffffff);
*(.data .data.* .gnu.linkonce.d.*)
} : rw
/* exception frames for C++ */
.eh_frame : {
__eh_frame_start__ = .;
KEEP (*(.eh_frame))
LONG(0)
} : rw
.gcc_except_table : { KEEP(*(.gcc_except_table)) }
.dynamic : { *(.dynamic) }
.bss : {
*(.bss .bss.* .gnu.linkonce.b.* COMMON)
}
/* end of program image -- must be after last section */
_prog_img_end = .;
/DISCARD/ : {
*(.note)
*(.note.ABI-tag)
*(.comment)
*(.eh_frame)
}
}

View File

@ -1,76 +0,0 @@
/*
* \brief Simple roottask replacement for OKL4 that just prints some text
* \author Norman Feske
* \date 2008-09-01
*/
/*
* Copyright (C) 2008-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
typedef unsigned char uint8_t;
/**
* Read byte from I/O port
*/
inline uint8_t inb(unsigned short port)
{
uint8_t res;
asm volatile ("inb %%dx, %0" :"=a"(res) :"Nd"(port));
return res;
}
/**
* Write byte to I/O port
*/
inline void outb(unsigned short port, uint8_t val)
{
asm volatile ("outb %b0, %w1" : : "a" (val), "Nd" (port));
}
/**
* Definitions of PC serial ports
*/
enum Comport { COMPORT_0, COMPORT_1, COMPORT_2, COMPORT_3 };
/**
* Output character to serial port
*/
inline void serial_out_char(Comport comport, uint8_t c)
{
static int io_port[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
/* wait until serial port is ready */
while (!(inb(io_port[comport] + 5) & 0x60));
/* output character */
outb(io_port[comport], c);
}
/**
* Print null-terminated string to serial port
*/
inline void serial_out_string(Comport comport, const char *str)
{
while (*str)
serial_out_char(comport, *str++);
}
/**
* Main program, called by the startup code in crt0.s
*/
extern "C" int _main(void)
{
serial_out_string(COMPORT_0, "Hallo, this is some code running on OKL4.\n");
serial_out_string(COMPORT_0, "Returning from main...\n");
return 0;
}

View File

@ -1,61 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE image SYSTEM "weaver-1.1.dtd">
<image>
<machine>
<word_size size="0x20" />
<virtual_memory name="virtual">
<region base="0x200000" size="0x3fe00000" />
</virtual_memory>
<physical_memory name="system_dram">
<region base="0x0" size="0xa000" type="dedicated" />
</physical_memory>
<physical_memory name="bios">
<region base="0xf0000" size="0x10000" type="dedicated" />
</physical_memory>
<physical_memory name="rom_expansion">
<region base="0xc0000" size="0x30000" type="dedicated" />
</physical_memory>
<physical_memory name="physical">
<region base="0x100000" size="0x7f00000" type="conventional" />
</physical_memory>
<phys_device name="timer_dev">
<interrupt name="int_timer0" number="0" />
</phys_device>
<phys_device name="serial_dev">
<interrupt name="int_serial0" number="4" />
</phys_device>
<phys_device name="rtc_dev">
</phys_device>
<page_size size="0x1000" />
<page_size size="0x400000" />
</machine>
<physical_pool name="system_dram" direct="true">
<memory src="system_dram" />
</physical_pool>
<virtual_pool name="virtual">
<memory src="virtual" />
</virtual_pool>
<physical_pool name="bios" direct="true">
<memory src="bios" />
</physical_pool>
<physical_pool name="rom_expansion" direct="true">
<memory src="rom_expansion" />
</physical_pool>
<physical_pool name="physical" direct="true">
<memory src="physical" />
</physical_pool>
<kernel file="../okl4_2.1.1-patch.9/build/pistachio/bin/kernel" xip="false" >
<dynamic max_threads="0x400" />
<config>
<option key="root_caps" value="4096"/>
</config>
</kernel>
<rootprogram file="hello" virtpool="virtual" physpool="physical" >
</rootprogram>
</image>

View File

@ -1,37 +0,0 @@
/*
* \brief Test for the Genode console and exception handling
* \author Norman Feske
* \date 2009-03-24
*
* This program can be started as roottask replacement directly on
* the OKL4 kernel.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
/* local includes */
#include "../mini_env.h"
/**
* Main program
*/
int main()
{
Genode::printf("Hello, this is Genode's printf.\n");
try {
throw 1;
} catch (...) {
Genode::printf("Successfully caught an exception.\n");
}
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = hello
REQUIRES = okl4
LIBS = cxx core_printf ipc
SRC_CC = hello.cc

View File

@ -1,76 +0,0 @@
/*
* \brief Test for using OKL4 system-call bindings for thread creation
* \author Norman Feske
* \date 2009-03-25
*
* This program can be started as roottask replacement directly on the
* OKL4 kernel.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* local includes */
#include "../mini_env.h"
#include "../create_thread.h"
/**
* Global variable, modified by the thread, observed by the main thread
*/
static volatile int counter;
/**
* Thread entry function
*/
static void thread_entry(void)
{
/* infinite busy loop incrementing a global variable */
while (1)
counter++;
}
/**
* Main program
*/
int main()
{
using namespace Genode;
using namespace Okl4;
/* set default priority for ourself to make round-robin scheduling work */
L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY);
/* stack used for the new thread */
enum { THREAD_STACK_SIZE = 4096 };
static int thread_stack[THREAD_STACK_SIZE];
/* stack grows from top, hence we specify the end of the stack */
create_thread(1, L4_rootserverno,
(void *)(&thread_stack[THREAD_STACK_SIZE]),
thread_entry);
/* observe the work done by the new thread */
enum { COUNT_MAX = 10*1000*1000 };
printf("main thread: let new thread count to %d\n", (int)COUNT_MAX);
while (counter < COUNT_MAX) {
printf("main thread: counter=%d\n", counter);
/*
* Yield the remaining time slice to the new thread to
* avoid printing the same counter value again and again.
*/
L4_Yield();
}
printf("exiting main()\n");
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = test-okl4_thread
REQUIRES = okl4
LIBS = cxx core_printf ipc
SRC_CC = main.cc

View File

@ -1,79 +0,0 @@
/*
* \brief Test for IPC send and wait via Genode's IPC framework
* \author Norman Feske
* \date 2009-03-26
*
* This program can be started as roottask replacement directly on the
* OKL4 kernel.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/ipc.h>
/* local includes */
#include "../mini_env.h"
#include "../create_thread.h"
using namespace Genode;
using namespace Okl4;
static Untyped_capability receiver_cap;
/**
* Sender thread, must not be started before 'receiver_cap' is initialized
*/
static void sender_thread_entry()
{
thread_init_myself();
static Msgbuf<256> sndbuf;
static Ipc_ostream os(receiver_cap, &sndbuf);
int a = 1, b = 2, c = 3;
printf("sending a=%d, b=%d, c=%d\n", a, b, c);
os << a << b << c << IPC_SEND;
for (;;) L4_Yield();
}
/**
* Main program
*/
int main()
{
roottask_init_myself();
/* set default priority for ourself to make round-robin scheduling work */
L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY);
static Msgbuf<256> rcvbuf;
static Ipc_istream is(&rcvbuf);
/* make input stream capability known */
receiver_cap = is;
/* create sender thread, sending to destination (us) */
enum { THREAD_STACK_SIZE = 4096 };
static int thread_stack[THREAD_STACK_SIZE];
create_thread(1, L4_rootserverno,
(void *)(&thread_stack[THREAD_STACK_SIZE]),
sender_thread_entry);
/* wait for incoming IPC */
int a = 0, b = 0, c = 0;
is >> IPC_WAIT >> a >> b >> c;
printf("received a=%d, b=%d, c=%d\n", a, b, c);
printf("exiting main()\n");
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = test-ipc_send_wait
REQUIRES = okl4
LIBS = cxx core_printf ipc
SRC_CC = main.cc

View File

@ -1,90 +0,0 @@
/*
* \brief Test for IPC call via Genode's IPC framework
* \author Norman Feske
* \date 2009-03-26
*
* This program can be started as roottask replacement directly on the
* OKL4 kernel. The main program plays the role of a server. It starts
* a thread that acts as a client and performs an IPC call to the server.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/ipc.h>
/* local includes */
#include "../mini_env.h"
#include "../create_thread.h"
using namespace Genode;
using namespace Okl4;
static Untyped_capability server_cap;
/**
* Client thread, must not be started before 'destination' is initialized
*/
static void client_thread_entry()
{
thread_init_myself();
Msgbuf<256> client_rcvbuf, client_sndbuf;
Ipc_client client(server_cap, &client_sndbuf, &client_rcvbuf);
printf("client sends call(11, 12, 13)\n");
int res, d = 0, e = 0;
res = (client << 11 << 12 << 13 << IPC_CALL >> d >> e).result();
printf("client received reply d=%d, e=%d, res=%d\n", d, e, res);
printf("client sends call(14, 15, 16)\n");
res = (client << 14 << 15 << 16 << IPC_CALL >> d >> e).result();
printf("client received reply d=%d, e=%d, res=%d\n", d, e, res);
for (;;) L4_Yield();
}
/**
* Main program
*/
int main()
{
roottask_init_myself();
/* set default priority for ourself to make round-robin scheduling work */
L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY);
Msgbuf<256> server_rcvbuf, server_sndbuf;
Ipc_server server(&server_sndbuf, &server_rcvbuf);
/* make server capability known */
server_cap = server;
/* create client thread, making a call to the server (us) */
enum { THREAD_STACK_SIZE = 4096 };
static int thread_stack[THREAD_STACK_SIZE];
create_thread(1, L4_rootserverno,
(void *)(&thread_stack[THREAD_STACK_SIZE]),
client_thread_entry);
/* infinite server loop */
int a = 0, b = 0, c = 0;
for (;;) {
printf("server: reply_wait\n");
server >> IPC_REPLY_WAIT >> a >> b >> c;
printf("server: received a=%d, b=%d, c=%d, send reply %d, %d, res=33\n",
a, b, c, a + b + c, a*b*c);
server << a + b + c << a*b*c;
server.ret(33);
}
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = test-ipc_call
REQUIRES = okl4
LIBS = cxx core_printf ipc
SRC_CC = main.cc

View File

@ -1,142 +0,0 @@
/*
* \brief Test for creating and paging address spaces
* \author Norman Feske
* \date 2009-03-28
*
* This program can be started as roottask replacement directly on the
* OKL4 kernel.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
namespace Okl4 { extern "C" {
#include <l4/space.h>
} }
/* local includes */
#include "../mini_env.h"
#include "../create_thread.h"
using namespace Genode;
using namespace Okl4;
/**
* Entry of child address space
*/
static void subspace_thread_entry()
{
static char read_area[4096*2];
static char write_area[4096*2];
thread_init_myself();
int a = 0;
for (unsigned i = 0; i < sizeof(read_area); i++)
a += read_area[i];
for (unsigned i = 0; i < sizeof(write_area); i++)
write_area[i] = a;
for (;;) L4_Yield();
}
/**
* Print page-fault information in a human-readable form
*/
static inline void print_page_fault(L4_Word_t type, L4_Word_t addr, L4_Word_t ip)
{
printf("page (%s%s%s) fault at pf_addr=%lx, pf_ip=%lx\n",
type & L4_Readable ? "r" : "-",
type & L4_Writable ? "w" : "-",
type & L4_eXecutable ? "x" : "-",
addr, ip);
}
/**
* Main program
*/
int main()
{
roottask_init_myself();
/* set default priority for ourself to make round-robin scheduling work */
L4_Set_Priority(L4_Myself(), DEFAULT_PRIORITY);
enum { NEW_SPACE_ID = 1 };
enum { FPAGE_LOG2_SIZE_4K = 12 };
/* create address space */
L4_SpaceId_t space = L4_SpaceId(NEW_SPACE_ID);
L4_Word_t control = L4_SpaceCtrl_new;
L4_ClistId_t cap_list = L4_rootclist;
L4_Fpage_t utcb_area = L4_FpageLog2((L4_Word_t)utcb_base_get() +
NEW_SPACE_ID*L4_GetUtcbAreaSize(),
FPAGE_LOG2_SIZE_4K);
#ifdef NO_UTCB_RELOCATE
utcb_area = L4_Nilpage; /* UTCB allocation is handled by the kernel */
#endif
L4_Word_t resources = 0;
L4_Word_t old_resources = 0;
int ret = L4_SpaceControl(space, control, cap_list, utcb_area,
resources, &old_resources);
if (ret != 1)
PERR("L4_SpaceControl returned %d, error code=%d",
ret, (int)L4_ErrorCode());
/* create main thread for new address space */
enum { THREAD_STACK_SIZE = 4096 };
static int thread_stack[THREAD_STACK_SIZE];
create_thread(1, NEW_SPACE_ID,
(void *)(&thread_stack[THREAD_STACK_SIZE]),
subspace_thread_entry);
printf("entering pager loop\n");
for (;;) {
L4_ThreadId_t faulter;
/* wait for page fault */
L4_MsgTag_t faulter_tag = L4_Wait(&faulter);
/* read fault information */
L4_Word_t pf_type, pf_ip, pf_addr;
L4_StoreMR(1, &pf_addr);
L4_StoreMR(2, &pf_ip);
pf_type = L4_Label(faulter_tag) & 7;
print_page_fault(pf_type, pf_addr, pf_ip);
/* determine corresponding page in our own address space */
pf_addr &= ~(4096 - 1);
L4_Fpage_t fpage = L4_FpageLog2(pf_addr, 12);
fpage.X.rwx = 7;
/* request physical address of page */
L4_MapItem_t map_item;
L4_PhysDesc_t phys_desc;
L4_ReadFpage(L4_SpaceId(0), fpage, &phys_desc, &map_item);
/* map page to faulting space */
int ret = L4_MapFpage(L4_SenderSpace(), fpage, phys_desc);
if (ret != 1)
PERR("L4_MapFpage returned %d, error_code=%d",
ret, (int)L4_ErrorCode());
/* reply to page-fault message to resume the faulting thread */
L4_LoadMR(0, 0);
L4_Send(faulter);
}
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = test-pager
REQUIRES = okl4
LIBS = cxx core_printf ipc
SRC_CC = main.cc

View File

@ -1,103 +0,0 @@
/*
* \brief Test for parsing OKL4 boot information
* \author Norman Feske
* \date 2009-03-24
*
* This program can be started as roottask replacement directly on
* the OKL4 kernel. It determines the available memory resources
* and boot-time data modules.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
/* local includes */
#include "../mini_env.h"
/* OKL4 includes */
namespace Okl4 { extern "C" {
#include <l4/utcb.h>
#include <l4/schedule.h>
#include <bootinfo/bootinfo.h>
} }
using namespace Okl4;
using namespace Genode;
static int init_mem(uintptr_t virt_base, uintptr_t virt_end,
uintptr_t phys_base, uintptr_t phys_end,
const bi_user_data_t * data)
{
printf("init_mem: virt=[%lx,%lx), phys=[%lx,%lx)\n",
virt_base, virt_end, phys_base, phys_end);
return 0;
}
static int add_virt_mem(bi_name_t pool, uintptr_t base, uintptr_t end,
const bi_user_data_t * data)
{
printf("add_virt_mem: pool=%d region=[%lx,%lx]\n", pool, base, end);
return 0;
}
static int add_phys_mem(bi_name_t pool, uintptr_t base, uintptr_t end,
const bi_user_data_t * data)
{
printf("add_phys_mem: pool=%d region=[%lx,%lx]\n", pool, base, end);
return 0;
}
static int export_object(bi_name_t pd, bi_name_t obj,
bi_export_type_t export_type, char *key,
Okl4::size_t key_len,
const bi_user_data_t * data)
{
printf("export_object: pd=%d obj=%d type=%d key=\"%s\"\n",
pd, obj, export_type, key);
return 0;
}
static bi_name_t new_ms(bi_name_t owner, uintptr_t base, uintptr_t size,
uintptr_t flags, uintptr_t attr, bi_name_t physpool,
bi_name_t virtpool, bi_name_t zone,
const bi_user_data_t * data)
{
printf("new_ms: owner=%d region=[%lx,%lx), flags=%lx, attr=%lx, physpool=%d, virtpool=%d, zone=%d\n",
owner, base, base + size - 1, flags, attr, physpool, virtpool, zone);
return 0;
}
/**
* Main program
*/
int main()
{
L4_Word_t boot_info_addr;
L4_StoreMR(1, &boot_info_addr);
printf("boot info at 0x%lx\n", boot_info_addr);
printf("parsing boot info...\n");
static bi_user_data_t user_data;
static bi_callbacks_t callbacks;
callbacks.init_mem = init_mem;
callbacks.add_virt_mem = add_virt_mem;
callbacks.add_phys_mem = add_phys_mem;
callbacks.export_object = export_object;
callbacks.new_ms = new_ms;
int ret = bootinfo_parse((void *)boot_info_addr, &callbacks, &user_data);
printf("finished parsing of boot info with ret=%d, exiting main()\n", ret);
return 0;
}

View File

@ -1,22 +0,0 @@
/*
* \brief Integer types required for using OKL4's boot-info parser
* \author Norman Feske
* \date 2009-04-04
*
* This file is indirectly included by OKL4's 'bootinfo.h'.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _CORE__INCLUDE__STDINT_H_
#define _CORE__INCLUDE__STDINT_H_
typedef unsigned long uintptr_t;
typedef signed long intptr_t;
#endif /* _CORE__INCLUDE__STDINT_H_ */

View File

@ -1,5 +0,0 @@
TARGET = test-boot_info
REQUIRES = okl4
LIBS = cxx core_printf ipc bootinfo
SRC_CC = main.cc
INC_DIR += $(PRG_DIR)

View File

@ -1,135 +0,0 @@
/*
* \brief Test for interrupt handling and timer on OKL4
* \author Norman Feske
* \date 2009-03-31
*
* This program can be started as roottask replacement directly on the OKL4
* kernel. It has two purposes, to test the interrupt handling on OKL4 and to
* provide a user-level time source. The x86 version of the OKL4 kernel uses
* the APIC timer as scheduling timer. So the PIT free to use as user-land time
* source. This is needed because the OKL4 kernel provides no means to access
* the kernel-level time source through IPC timeouts anymore.
*/
/*
* Copyright (C) 2009-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
/* Genode includes */
#include <base/printf.h>
/* local includes */
#include "../mini_env.h"
#include "../io_port.h"
namespace Okl4 { extern "C" {
#include <l4/thread.h>
#include <l4/interrupt.h>
#include <l4/security.h>
#include <l4/ipc.h>
} }
using namespace Okl4;
using namespace Genode;
enum { IRQ_PIT = 0 }; /* timer interrupt line at the PIC */
enum {
PIT_TICKS_PER_SECOND = 1193182,
PIT_MAX_COUNT = 65535,
PIT_DATA_PORT_0 = 0x40, /* data port for PIT channel 0, connected
to the PIC */
PIT_CMD_PORT = 0x43 /* PIT command port */
};
/**
* Bit definitions for accessing the PIT command port
*/
enum {
PIT_CMD_SELECT_CHANNEL_0 = 0 << 6,
PIT_CMD_ACCESS_LO = 1 << 4,
PIT_CMD_ACCESS_LO_HI = 3 << 4,
PIT_CMD_MODE_IRQ = 0 << 1,
PIT_CMD_MODE_RATE = 2 << 1,
};
/**
* Set PIT counter value
*/
static inline void pit_set_counter(uint16_t value)
{
outb(PIT_DATA_PORT_0, value & 0xff);
outb(PIT_DATA_PORT_0, (value >> 8) & 0xff);
}
/**
* Main program
*/
int main()
{
/* operate PIT in one-shot mode */
outb(PIT_CMD_PORT, PIT_CMD_SELECT_CHANNEL_0 |
PIT_CMD_ACCESS_LO_HI |
PIT_CMD_MODE_IRQ);
int irq = IRQ_PIT;
/* allow roottask (ourself) to handle the interrupt */
L4_LoadMR(0, irq);
int ret = L4_AllowInterruptControl(L4_rootspace);
if (ret != 1)
printf("L4_AllowInterruptControl returned %d, error code=%ld\n",
ret, L4_ErrorCode());
/* bit to use for IRQ notifications */
enum { IRQ_NOTIFY_BIT = 13 };
/*
* Note: 'L4_Myself()' does not work for the thread argument of
* 'L4_RegisterInterrupt'. We have to specify our global ID.
*/
L4_LoadMR(0, irq);
ret = L4_RegisterInterrupt(L4_rootserver, IRQ_NOTIFY_BIT, 0, 0);
if (ret != 1)
printf("L4_RegisterInterrupt returned %d, error code=%ld\n",
ret, L4_ErrorCode());
/* prepare ourself to receive asynchronous IRQ notifications */
L4_ThreadId_t partner = L4_nilthread;
L4_Set_NotifyMask(1 << IRQ_NOTIFY_BIT);
L4_Accept(L4_NotifyMsgAcceptor);
int cnt = 0, seconds = 1;
for (;;) {
/* wait for asynchronous interrupt notification */
L4_ReplyWait(partner, &partner);
/*
* Schedule next interrupt
*
* The PIT generates the next interrupt when reaching
* PIT_MAX_COUNT. By initializing the PIT with a higher
* value than 0, we can shorten the time until the next
* interrupt occurs.
*/
pit_set_counter(0);
/* we got an interrupt, acknowledge */
L4_LoadMR(0, irq);
L4_AcknowledgeInterrupt(0, 0);
/* count timer interrupts, print a message each second */
if (cnt++ == PIT_TICKS_PER_SECOND/PIT_MAX_COUNT) {
printf("Second %d\n", seconds++);
cnt = 0;
}
}
return 0;
}

View File

@ -1,4 +0,0 @@
TARGET = test-timer_pit
REQUIRES = okl4 x86
LIBS = cxx core_printf ipc
SRC_CC = main.cc