Tuesday, February 12, 2013

Visions on Genode OS development


Introduction

This documents summarizes my visions of what and how could and should be improved about the Genode Operating System Framework in order to make it usable on a day-to-day basis in various applications.

Please note that some of these opinions may be biased because my main interest in Genode is using it as a virtualization solution for embedded systems (namely, smartphones and tablet computers). Some of the ideas are inspired by an experience of porting Genode to a Samsung Galaxy Nexus phone, so they are correlated with what I'm planning to work on throughout 2013. Norman Feske of Genode Labs has expressed disagreement over some points here so you're welcome to comment and add your ideas and we'll see how it turns out.

And yeah, I want that any mobile phone geek and an embedded hacker out there reading this post joins the effort to make Genode running on at least one commercially available phone. Cmon, XDA crowd, where are you?

While this text may seem overly verbose, I tried to explain the points in a simple language so that my motivation behind each idea is clear to every reader, even those unfamiliar with the area of the Operating System development and without the knowledge of embedded hardware design.
Overall, I think we need to port a lot of functionality common to most OS kernels, like drivers, file system support and networking protocols, but at the same time I would like to avoid reimplementing the whole linux in C++. Perhaps the optimal solution is keeping drivers inside Genode, and letting a paravirtualized linux instance manage networking and serve as the platform for running userland applications.
In fact, Genode on ARM today is like linux was 7-10 years ago. Indeed, you can make it run, but the device driver infrastructure is missing and you'll have to write everything from scratch. And it will be slow and power-hungry.

Security

Currently, the strongest security issue with Genode is the lack of control over mapping the IO memory. Any service having the capability for an IO or connection is able to map arbitrary memory area. Besides, memory mapping is exclusive and multiple services cannot map the same region. This brings both security issues and programming inconvenience.

IO

I propose that a new IO memory mapper be written to provide coarse-grained region mapping. Firstly, it should be initialized with a list of memory regions. Each region should contain its name, physical base and length. Here's an example of how the config would look like:
<start name="New_IO">
 <binary name="New_IO"/>
  <config>
   <region name="I2C_1" start="0x40100000" size="0x1000" />
  </config>
  <provides><service name="IO"/></provides>
</start> 

Each service requiring RM access should use a proxy to limit its access to specific regions. Example:
<start name="IO_limiter_clk">
 <binary name="IO_limiter"/>
  <config>
   <region name="ClockControl"/>
   <region name="PowerDomain"/>
  </config>
 <provides><service name="IO"/></provides>
<start> 

For some SoC (System on Chip, essentially a microprocessor and the logic that comes inside of it like display or memory controllers) IO regions, we could define them inside the service code (platform_drv driver) so that definitions don't have to be duplicated inside the XML configuration file.
Such an approach involving looking up the regions by name eliminates the need to store the region definitions in header files and duplicated them in configs for proxies.
Alternatively, we could write a proxy that would restrict IO access to a specific memory area and create an instance of it for every service that wants to access IO memory. The downside of this approach is that IO region definitions will be scattered around the whole config file and possibly duplicated in both C++ headers and configs.

Hardware - Generic Framework

While techically we could use Genode as a thin virtualization layer and just forward all IO to a virtualized traditional kernel (linux) like Xen does, that would not solve the reliability, security and management issues. Porting device drivers to Genode instead of running them in paravirtualized kernels gives certain advantages:
  • Isolation - each driver is running in a separate virtual memory space
  • Guest (virtualized) kernel can be updated independently of hardware drivers

Therefore, let us take a look at what we need in to have implemented in order to use

I2C

We have currently implemented a basic I2C interface, allowing single- and multi-byte transfers. Among the remaining issues are adding advanced protocols, such as SMBUS and merging the code with Genode upstream.

GPIO Multiplexing

In modern SoCs, GPIO pins are typically multiplexed to several hardware subsystems. Besides, a pin can typically be configured to be pulled either to ground, to voltage source, left floating or in a high-impedance state (tristated). However, the number of available MUX directions, and configuration states differs on various SoCs. For example, TI OMAP has 8 MUX alternative functions and separate configurations for input, output and suspend states, while Qualcomm MSM has only 3 alternatives and one configuration for all states. Currently, we have an implementation for OMAP4, and whether the interface should be made part of GPIO session or kept separate and SoC-specific is under discussion.

IRQ Multiplexing

For some tasks, it is required to have a service that would either combine multiple interrupt sources into a single virtual source or, vice versa, provide multiple virtual interrupts from a single hardware signal. That would be useful for example for I2C port expanders. This could be based on current implementations in Linux.

Voltage Regulators

A framework similiar to the one used in linux should be implemented, including reference counting and disabling the unused power sources.

Framebuffer / Graphics

Currently, Genode supports only a basic fixed framebuffer configuration. Here is a list of features that would be nice to have in no particular order.
  • Tiling WM with resizable windows
  • Overlay support. If we divert each app to a separate overlay, that would improve performance by using HW blitting capabilities
  • HW-accelerated blitting, scaling and other transformations
  • Different color spaces support
If this is all implemented, it would be possible to make a generic driver for L4Linux guests that would allow the Guest OS to utilize all hardware capabilities while actual implementation details are hidden in the particular Genode driver.

Another hot topic is OpenGL isolation. The problem with embedded hardware is that the drivers almost often come in the binary form, and running them atop Genode would require writing a custom ELF loader and carefully porting linux kernel-mode drivers. As an experiment, it could be possible to make a small linux ramdisk containing the minimal set of libraries to run a proprietary driver. Also, the linux kernel driver could be used to provide the framebuffer interface to the Genode services. This "linux-as-a-driver" would provide the generic OpenGL interface that could be utilized by other L4Linux instances and Genode services. In general, I think building small instances of linux with the bare minimum of drivers and userspace libraries is a viable alternative to reverse-engineering and porting complicated drivers.

MMC

Not much to say here. Currently there is only one implementation, for OMAP4 ported from u-boot, and it currently only works on pandaboard. It only supports one mmc controller out of five, and only block access, so no fun with wifi. Probably the SDIO stack should be ported from linux as it is quite mature and has several nice properties:
  • Supports block, sdio interfaces
  • Has quircks and workarounds for broken controllers
  • Power Management support
  • Cleanly separated bus code from SoC-specific implementation

USB

Currently the only way to use USB is through the Linux DDE (Device Driver Environment) package. This has multiple problems. One is that all the drivers related to usb are linked into a huge blob providing all the interfaces as NIC, Input etc. This clearly violates the microkernel server separation principle.

Another problem is that developing quickly turns into writing quirky hacks to make linux drivers run. USB is quite a complex protocol and porting all drivers to Genode is a complicated task. Therefore, I suggest that for both Host and Client (OTG) modes, only hardware drivers that are able to set up endpoints are written, and then this endpoints are passed to a generic driver inside the virtualized guest OS (linux/BSD) and the guest USB stack takes care of everything. This is not a solution for the long-term goal of using Genode as a general-purpose OS, but would quite suffice for virtualization purposes.

Sound

Sound is a complex issue. I don't currently have a clear vision on this point, here are just some ideas.
  • Port alsa-driver to enable porting codec drivers from linux
  • Provide a mixer interface and export controls to virtual linuxes
  • Alternatively, run PulseAudio on Genode

RTC

This should be pretty straightforward. A driver should be able to:
  • Query current date/time
  • Store date/time
  • Provide Guest L4Linux with date/time via a virtual driver

PM: DVFS

Power Management is a sad topic. There's currently no PM at all which is unsuitable for embedded applications. First step would be implementing voltage and frequency scaling to reduce power consumption when the CPU load is low. For this, the scheduler should send some notifications about the CPU utilization to the policy driver which will in turn decide what to do. Maybe cpufreq from linux should be used for inspiration.

PM: IDLE

For some hardware, especially embedded ARM, it is possible to save tremendous amounts of energy by entering low power states (like WFI - Wait For Interrupt) or CPU-specific ones (like LPA on Samsung Exynos). This task includes adding the support for "mild" power saving states in which caches and devices are not turned off, so we don't have to deal with saving device power state and restoring it on wakeup.

PM: Suspend

Adding the support for deep sleep modes (like S3 suspend to RAM on X86) when the CPU core is turned off is the most complex task. It would involve adding a PM_Session interface for devices supporting runtime PM or needing special actions to save/restore power context. We would also need a policy driver which will decide what to do when some service times out or returns an error while trying to switch a power state.

Input Subsystem

Input session should export more information about the input source type and the L4Linux client driver should set up the input and differentiate between a relative mouse and absolute touchscreen movements dynamically at runtime as opposed to a compile-time ifdef. Some other stuff to add:
  • Multitouch support
  • GPIO Keyboard driver

LEDs

Should be easy to make a generic LED session driver and L4Linux client for it. I want that the L4Linux driver has no hardcoded defines in it an looks up the available LEDs dynamically. The only thing that would be hard to support but irrelevant are the programmable controllers which allow to set up custom timing patterns. While this may be useful in some cases and save CPU power, these controllers vary in the capabilities provided and usually lack proper documentation. Therefore, I suggest that in case the support for such hardware is desired, it is handled individually for each driver in future.

Sensors

Modern devices come with a variety of sensors: temperature, orientation, acceleration, light level, proximity. Since they are all essentially a stream of data, I suggest a unified interface for them all. The client in L4Linux or other guest OS should look up the sensor parameters and set itself up dynamically. Here is what data should probably be provided:
  • Sensor Type : string
  • Sensor Data Type : string/enum

Hardware - OMAP4 SoC / Galaxy Nexus phone

To get basic hardware functioning, there remain several "blocker" issues.
Actually, OMAP4 SoC is very complex, so I cannot resist the urge to just map the IO memory and port the linux drivers to Genode "as-is", without rewriting them in a good C++ style and splitting into tiny modules. It seems easier to first create a huge "SOC" driver, and start splitting it into parts when it becomes clear which interfaces and functions are required by other services.

TWL6030 PMIC

Clocks

Power Management

Power Management needs to be implemented not only to conserve power, but also to give access to certain peripherals that are disabled on reboot.
  • Voltage Domains
  • Voltage Channels
  • Power Domains
  • PRCM (Power Reset & Clock Module)

Framebuffer

  • DSS
  • Overlay
  • VCS (simplified I2C over DSS) Interface for panel init
  • S6E8AA0 Panel

MUSB OTG

Currently, all the platform code for initialization and I2C interface is implemented for dde_linux and the linux musb driver initializes the controller and the device gets recognized by the host. There are several issues to fix still remaining before the driver is functional.
  • Timing issues with debugging disabled
  • Data endpoints are set up incorrectly, only configuration endpoints work
  • RNDIS, Serial and EEM gadget drivers need porting (some functions need to be implemented in dde_linux)

HSI Serial

This is the High-Speed transport to the modem. It is similiar to USB and UART in some aspects.

BCM4330 WiFi

This one depends on TWL6030 and MMC

L4Linux Issues

Speaking of our particular case, running L4Linux atop Fiasco.OC kernel and Genode Framework, I would like to mention the problems which make development a very complicated procedure.
L4Re and L4Linux are developed behind closed doors, and only major releases are dropped to the repository. While linux kernel is typically hosted in a git repository, L4Linux uses SVN. These two factors lead to the loss of commit history. You can't see single changes and bugfixes, only one huge diff. This makes updating to a newer kernel tree manually or rebasing nearly impossible.
Another problem is Genode's ports-foc directory. It contains patches that are applied on top of the 'contrib' directory which is just a clone of L4Linux SVN. Working with separate patches quickly becomes unconvenient.
Here's what I propose and will probably implement for our project.
  • Separate L4 architecture patches so that they could be applied on top of any linux kernel version
  • Clean up the unused L4 suppor code (which is irrelevant for Genode-based setup)
  • Apply the patches on top of vanilla or Android kernel to keep revision history
  • Put up a separate kernel repository for L4Linux on Genode
Of course, I would be glad if L4Linux switched to git and made a proper revision history, but convincing the Fiasco.OC crowd is another task, which will probably take a bit more time.