mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
base-okl4: remove unmaintained porting steps
This commit is contained in:
parent
e6729316ff
commit
6de763cb0b
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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_ */
|
@ -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_ */
|
@ -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_ */
|
@ -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
|
@ -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:
|
@ -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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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>
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = hello
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = hello.cc
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = test-okl4_thread
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = main.cc
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = test-ipc_send_wait
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = main.cc
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = test-ipc_call
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = main.cc
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = test-pager
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = main.cc
|
@ -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;
|
||||
}
|
@ -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_ */
|
@ -1,5 +0,0 @@
|
||||
TARGET = test-boot_info
|
||||
REQUIRES = okl4
|
||||
LIBS = cxx core_printf ipc bootinfo
|
||||
SRC_CC = main.cc
|
||||
INC_DIR += $(PRG_DIR)
|
@ -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;
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
TARGET = test-timer_pit
|
||||
REQUIRES = okl4 x86
|
||||
LIBS = cxx core_printf ipc
|
||||
SRC_CC = main.cc
|
Loading…
x
Reference in New Issue
Block a user