📜 ⬆️ ⬇️

Porting the Genode OS Framework to a new hardware platform

Recently, more and more companies have shown interest in microkernel operating systems. These systems from the category of academic begin to move into the category of operating systems for use in real products. For example, the recently introduced Samsung Knox platform was built using OKL4 Microvisor. It is very likely that there are much more such solutions, but not all manufacturers advertise the technologies used, since at the moment micronuclear systems are mainly used in the field of information security.

A brief history of microkernel OS and a description of the most well-known projects were recently described by my colleague sartakov in the article " Microkernels and FOSDEM'13 ". I want to tell in more detail about the Genode OS Framework and the process of porting it to a new hardware platform based on the ARM architecture processor. As a core for Genode used Fiasco.OC .



And so, what is the Genode OS Framework? This is a framework for building an operating system based on a microkernel. Genode provides a single API that allows you to use different microkernels to build the OS. The following microkernels are currently supported: Codezero, Fiasco, Fiasco.OC, Nova, OKL4, Pistachio. Work on the Linux kernel is also supported. In addition, it is possible to run without the use of third-party cores (base-hw) on some platforms based on the ARM architecture.
These cores support different processor architectures and may use some hardware features. For example, the Fiasco.OC core can work on a large number of architectures, such as: x86, amd64, ARM (other architectures are available, but their support is not used in the Genode at the moment), and the Nova core is a micro-hypervisor for the x86 architecture and allows using hardware virtualization. Through the use of the Genode framework, we can compile the application without any changes to the source code for use on any of the supported kernels.
More information about Genode can be obtained from the documentation posted on the project website, as well as from materials read by Norman Feske for the " Summer School of System Programming Ksys labs "
')
Currently Genode + Fiasco.OC for ARM architecture supports the following platforms: Realview PBXA9, Versatile Express A9X4, Pandaboard (TI OMAP4), Freescale iMX53, Arndale (Samsung Exynos 5). All of these debug boards are quite expensive, and some are generally very difficult to purchase. Now there are many single-board PCs with a price of less than $ 100, which are used by enthusiasts both as debugging boards for learning about the operating system, and as a basis for prototyping and manufacturing their devices. Linux is usually ported to these devices, and some even have documentation in open sources. A good article with an overview of the available devices " Choosing a mini computer for home creativity (DIY) ". Some platforms of the described boards are already supported by the Fiasco.OC core and do not require porting.

As you may have guessed from the photo at the beginning of the post, I used the Cubieboard mini-PC based on the SoC Allwinner A10 as a platform for porting.
This platform is interesting for several reasons:
- not too outdated architecture Cortex-A8;
- availability of the source code of the U-Boot loader, the Linux kernel and the presence of documentation “leaked” to the network in the form of a user manual;
- a large set of peripherals (SATA, HDMI, etc.);
- the presence of a large number of inexpensive “hackable” devices on this chip (Cubieboard, Mele A1000 / A2000, and others).

Consider this SoC in more detail.

CPU: ARM Cortex-A8 up to 1Ghz with support for NEON, VFPv3, Trustzone
GPU: Mali 400 MP with support for Open GL ES 2.0
VPU: CedarX with FullHD video support.
Periphery: 4xUSB Host, USB OTG, 4xSD / MMC, 8xUART, 4xSPI, 3xI2C, GPIO, SATA, HDMI, LCD-interface and others

The core of Fiasco.OC supports the architecture of Cortex-A8 processors. This means that in order to port it to a new platform, you only need to add support for the platform, the so-called Board Support Package (BSP). The BSP source code is located in the kernel / fiasco / src / kern / arm / bsp directory.
The BSP for ARM architecture in Fiasco.OC includes:
- driver interrupt controller;
- driver timer;
- UART driver;
- implementation of a reset.
In addition, the BSP includes a memory allocation configuration.
The memory in A10, according to the user manual, is distributed as follows:

In order for the OS to use memory, it must know the physical addresses. These parameters are set in the Mem_layout class, mem_layout-arm-sun4i.cpp file:
EXTENSION class Mem_layout { public: enum Virt_layout_sun4i : Address { Timer_map_base = Devices1_map_base + 0x020C00, Intc_map_base = Devices1_map_base + 0x020400, Uart_base = Devices1_map_base + 0x028000, Watchdog_map_base = Timer_map_base + 0x90, }; enum Phys_layout_sun4i : Address { Devices1_phys_base = 0x01c00000, Sdram_phys_base = 0x40000000, Flush_area_phys_base = 0xe0000000, }; }; 

An interrupt controller is required to handle events from various sources. The driver of the interrupt controller is used to control the controller, performing such operations as: controller configuration, interrupt masking, processing function. The driver code in the pic-arm-sun4i.cpp file.

The timer is required to generate events, such as: end of time-slot or IPC timeout. A10 has 6 timers. As a timer for periodic interrupts, Timer0 is used. In addition to general-purpose timers, the SoC also includes Watchdog and an RTC with an alarm clock. To use the timer in the system mode, it must generate an interrupt every 1 ms. Initialization of the timer and the necessary functions are implemented in the file timer-arm-sun4i.cpp.

The UART driver is used by the kernel to display debug messages and access the JDB kernel debugger. There is no initialization of the UART module in Fiasco.OC; it is believed that the bootloader has already configured it, in our case U-Boot. The UART driver code is in the uart-arm-sun4i.cpp and kernel / fiasco / src / lib / uart / uart_sun4i.cc files.

To perform a full reset of the processor, the Watchdog timer is used, the purpose of which is to reset the system when the code loops. The implementation is in the reset-arm-sun4i.cpp file.

After performing these steps, we get the BSP for the selected processor. But the kernel still needs to be loaded. Consider the Genode + Fiasco.OC boot process on the ARM architecture:

1. When the ROM-boot starts, it searches for the bootloader on the SD card or boots from NAND when there is no SD card.
2. U-Boot, executing startup scripts, loads the Genode image as an ELF or u-boot-image.
3. The loaded module contains the Fiasco.OC kernel and all other executable files, such as: sigma0, root task (in Genode it is the core module) and user programs.
4. The first bootstrap starts, which performs part of the operations necessary for the start of the kernel, such as:
- scanning of available memory (not performed for all architectures, on ARM, the value of available memory is set in the configuration on the platform);
- relocation of modules in memory (sigma0 and root tasks must be located in certain regions so that the kernel at launch can start them);
- and directly launching the kernel.
7. The kernel performs the necessary system initialization, starts the sigma0 and root task.
8. The core module (which is the root task) starts up, which initializes the Genode services and allows you to run the applications we need using the configuration file.

Correspondingly, to launch bootstrap on Cubieboard, he needs to know about this platform, so we add the required configuration l4 / mk / platforms / cubieboard.conf and l4 / pkg / bootstrap / server / src / platform / sun4i.cc. In addition, you must implement the UART driver to display information about the download to the console.

The final step in porting is to add the necessary configuration files to the Genode build system. The process is simple, I will not describe, the changes can be viewed in the corresponding commit in the repository. A good description of the build system was discussed in the " Genode OS Framework Programming Environment " lecture.

Source codes are on Github https://github.com/Ksys-labs/genode in the tutorials branch. A modified version of Fiasco.OC in the https://github.com/Ksys-labs/foc repository in the r47-sun4i branch, these source codes already contain the necessary patches and are downloaded using the Genode build system.

To build, you need to clone the source code:
 git clone git://github.com/Ksys-labs/genode.git git checkout tutorials cd genode 

Assemble the toolchain for ARM:
 ./tools/tool_chain arm 

And download the core of Fiasco.OC:
 make prepare -C base-foc 

Now everything is ready for launch.
1. Create a build directory using the command:
 ./tools/create_builddir foc_cubieboard BUILD_DIR=_build.foc_cubieboard cd _build.foc_cubieboard 

2. Add the hello_tutorial repository to build the simplest script that does not require drivers that are not yet available.
 echo 'REPOSITORIES += $(GENODE_DIR)/hello_tutorial' >> etc/build.conf 

3. Turn on the generation of the file u-boot-image
 echo 'SPECS += uboot' >> etc/spec.conf 

4. We collect an image
 make run/hello 

After a brief build, we get the image in the form: ELF (hello.elf) and u-boot-image (uImage) in the var / run / hello directory.
After running on the device, in the connected console, you can see the boot process and running applications from the hello tutorial:
Log
L4 Bootstrapper
Build: # 4 Thu. Apr 18 22:48:37 MSK 2013, 4.7.2
Scanning up to 1024 MB RAM
Memory size is 1024MB (40,000,000 - 8,000,000,000)
RAM: 0000000040000000 - 000000007fffffff: 1048576kB
Total RAM: 1024MB
mod07: 4117e000-411b8e3c: genode / timer
mod06: 41148000-4117ddc0: genode / hello_server
mod05: 4111c000-41147c28: genode / hello_client
mod04: 410d6000-4111b738: genode / init
mod03: 410d5000-410d51a4: genode / config
mod02: 4106e000-410d431c: genode / core
mod01: 41064000-4106d374: sigma0
mod00: 41015000-41063d20: /home/vanner/projects/genode/_build.foc_cubieboard/kernel/fiasco.oc/fiasco
Moving up to 8 modules behind 41100000
moving module 00 {41015000-41063d1f} -> {412a4000-412f2d1f} [322848]
moving module 01 {41064000-4106d373} -> {412f3000-412fc373} [37748]
moving module 02 {4106e000-410d431b} -> {412fd000-4136331b} [418588]
moving module 03 {410d5000-410d51a3} -> {411b9000-411b91a3} [420]
moving module 04 {410d6000-4111b737} -> {411ba000-411ff737} [284472]
moving module 05 {4111c000-41147c27} -> {41100000-4112bc27} [179240]
moving module 06 {41148000-4117ddbf} -> {4112c000-41161dbf} [220608]
moving module 07 {4117e000-411b8e3b} -> {41162000-4119ce3b} [241212]
moving module 03 {411b9000-411b91a3} -> {4119d000-4119d1a3} [420]
moving module 04 {411ba000-411ff737} -> {4119e000-411e3737} [284472]
Scanning /home/vanner/projects/genode/_build.foc_cubieboard/kernel/fiasco.oc/fiasco -serial_esc
Scanning sigma0
Scanning genode / core
Relocated mbi to [0x4100e000-0x4100e19c]
Loading cts / genode / _build.foc_cubieboard / kernel / fiasco.oc / fiasco
Loading sigma0
Loading genode / core
find kernel info page ...
found kernel info page at 0x40002000
Regions of list 'regions'
[40001000, 400019ff] {a00} Kern cts / genode / _build.foc_cubieboard / kernel / fiasco.oc / fiasco
[40002000, 40060fff] {5f000} Kern cts / genode / _build.foc_cubieboard / kernel / fiasco.oc / fiasco
[40090000, 4009673b] {673c} Sigma0 sigma0
[40098000, 4009e17b] {617c} Sigma0 sigma0
[40100000, 4024743f] {147440} Root genode / core
[41000000, 410143f3] {143f4} Boot bootstrap
[4100e000, 4100e299] {29a} Root Multiboot info
[41100000, 411e3737] {e3738} Root Module
API Version: (87) experimental
Sigma0 config ip: 40090100 sp: 41013d24
Roottask config ip: 4014af84 sp: 00000000
Starting kernel cts / genode / _build.foc_cubieboard / kernel / fiasco.oc / fiasco at 40001198
Hello from Startup :: stage2
Boot_alloc: size = 0x180
Boot_alloc: allocated memory block @ 0xf13e1000 (size = 400)
Boot_alloc: @ 0xf13e1000
Boot_alloc: remaining free block @ 0xf13e1180 (size = 280)
Cache config: ON
ID_PFR [01]: 00001131 00000011 ID_ [DA] FR0: 00000400 00000000
ID_MMFR [04]: 01100003 20000000 01202000 00000211
FPU0: Arch: VFPv3 (3), Part: VFPv3 (30), r: 3, v: c, i: 41, t: hard, p: dbl / sngl
Startup :: stage2 finished
SERIAL ESC: IRQ 1 allocated for serial uart
Not using serial hack in slow timer handler.
Welcome to Fiasco.OC (arm)!
L4 / Fiasco.OC arm microkernel 1998-2013 TU Dresden
Rev: 8991035 compiled with gcc 4.7.2 for sun4i []
Build: # 3 Thu. Apr 18 22:48:33 MSK 2013

Calibrating timer loop ... done.
SIGMA0: Hello!
KIP @ 40002000
allocated 4KB for maintenance structures
SIGMA0: Dump of all resource maps
RAM: ------------------------
[0: 40,000,000; 40,000fff]
[0: 40061000; 4008ffff]
[0: 40097000; 40097fff]
[0: 4009f000; 400fffff]
[4: 40100000; 40247fff]
[0: 40248000; 4100dfff]
[4: 4100e000; 4100efff]
[0: 4100f000; 410fffff]
[4: 41100000; 411e3fff]
[0: 411e4000; 7effffff]
IOMEM: ----------------------
[0: 0; 3fffffff]
[0: 80000000; ffffffff]

KIP @ 40002000
magic: 4be6344c
version: 87014444
sigma0 esp: 41013d24 eip: 40090100
sigma1 esp: 00000000 eip: 00000000
root esp: 00000000 eip: 4014af84
MBI @ 4100e000
mod [3] [4119d000,4119d1a4) config
mod [4] [4119e000,411e3738) init
mod [5] [41100000,4112bc28) hello_client
mod [6] [4112c000,41161dc0) hello_server
mod [7] [41162000,4119ce3c) timer
: ram_alloc: Allocator 40230784 dump:
Block: [50000000,5000001c) size = 0000001c avail = 00000000 max_avail = 00000000
Block: [5000001c, 50000038) size = 0000001c avail = 00000000 max_avail = 00000000
Block: [50000038,50000054) size = 0000001c avail = 00000000 max_avail = 00000000
Block: [50000054,50000070) size = 0000001c avail = 00000000 max_avail = 2effff58
Block: [50000070,5000008c) size = 0000001c avail = 00000000 max_avail = 00000000
Block: [5000008c, 500000a8) size = 0000001c avail = 00000000 max_avail = 2effff58
Block: [500000a8,7f000000) size = 2effff58 avail = 2effff58 max_avail = 2effff58
=> mem_size = 788529152 (752 MB) / mem_avail = 788528984 (751 MB)
: region_alloc: Allocator 402318f4 dump:
Block: [00001000,40000000) size = 3ffff000 avail = 3ffff000 max_avail = 3ffff000
Block: [7f000000, bfff0000) size = 40ff0000 avail = 40ff0000 max_avail = 40ff0000
Block: [bfff1000, c0000000) size = 0000f000 avail = 0000f000 max_avail = 0000f000
=> mem_size = 2164252672 (2063 MB) / mem_avail = 2164252672 (2063 MB)
: io_mem: Allocator 40230be0 dump:
Block: [00000000,40000000) size = 40000000 avail = 40000000 max_avail = 40000000
Block: [40001000,40002000) size = 00001000 avail = 00001000 max_avail = 40000000
Block: [40003000,40061000) size = 0005e000 avail = 0005e000 max_avail = 0005e000
Block: [40090000,40097000) size = 00007000 avail = 00007000 max_avail = 0005e000
Block: [40098000,4009f000) size = 00007000 avail = 00007000 max_avail = 80ffffff
Block: [7f000000, ffffffff) size = 80ffffff avail = 80ffffff max_avail = 80ffffff
=> mem_size = 3238449151 (3088 MB) / mem_avail = 3238449151 (3088 MB)
: io_port: Allocator 4023103c dump:
: irq: Allocator 40231498 dump:
Block: [00000000,00000100) size = 00000100 avail = 00000100 max_avail = 00000100
=> mem_size = 256 (0 MB) / mem_avail = 256 (0 MB)
: rom_fs: Rom_fs 402321a8 dump:
Rom: [4119e000,411e3738) init
Rom: [41100000,4112bc28) hello_client
Rom: [4119d000,4119d1a4) config
Rom: [4112c000,41161dc0) hello_server
Rom: [40002000.40003000) l4v2_kip
Rom: [40002000.40003000) kip
Rom: [41162000,4119ce3c) timer
: core ranges: Allocator 40233f08 dump:
Block: [40100000,40248000) size = 00148000 avail = 00148000 max_avail = 00148000
Block: [41100000,411e4000) size = 000e4000 avail = 000e4000 max_avail = 2f000000
Block: [50000000.7f000000) size = 2f000000 avail = 2f000000 max_avail = 2f000000
=> mem_size = 790806528 (754 MB) / mem_avail = 790806528 (754 MB)
int main (): - create local services - int main (): - start init - int main (): transferred 751 MB to init
int main (): - init created, waiting for exit condition - [init] Could not open file "ld.lib.so"
[init -> hello_server] Hello :: Root_component :: Root_component (Genode :: Rpc_entrypoint *, Genode :: Allocator *): Creating root component.
[init -> hello_server] virtual Hello :: Session_component * Hello :: Root_component :: _ create_session (const char *): creating hello session.
[init -> hello_client] virtual void Hello :: Session_client :: say_hello (): Saying Hello.
[init -> hello_server] virtual void Hello :: Session_component :: say_hello (): I am here ... Hello.
[init -> hello_client] int main (): Added 2 + 5 = 7
[init -> hello_client] virtual void Hello :: Session_client :: say_hello (): Saying Hello.
[init -> hello_server] virtual void Hello :: Session_component :: say_hello (): I am here ... Hello.
[init -> hello_client] int main (): Added 2 + 5 = 7
...

As you can see, porting a microkernel to a new architecture is not at all difficult. Of course, as I wrote above, to use microkernels in real projects, everything depends on supporting the platform hardware. For example, in the manufacture of prototypes of devices based on the Pandaboard and Gumstix Overo, we had to add a lot of drivers, such as: GPIO, UART, SPI, I2C. These interfaces were required to work with LCD, touchscreen and smartcard reader. To use Cubieboard as a mini-PC with Genode, at a minimum, you need to write (or port) drivers: Framebuffer, USB, Ethernet.

Genode Labs is planning to build a modern OS based on its framework, and they are confidently moving in this direction. This is a very interesting open source project related to alternative OSes. Genode is not very demanding of resources, and in my opinion, single-board PCs on the ARM architecture may well be used as one of the cheapest platforms for this OS.

In addition, Genode is great for studying in Computer Science, and if you are a student and you are interested, you can join us. To do this, look at the first three lectures ( 1 , 2 , 3 ) and complete the tasks from them. You need to put the solution on Github and send a link to edu@ksyslabs.org.

Source: https://habr.com/ru/post/177201/


All Articles