Site logo
Stories around the Genode Operating System RSS feed
Sebastian Sumpf avatar

Enablement of Genode's ARMv8 support

ARMv8 introduces a new 64 bit instruction set architecture (ISA). This implies that there exists a different general purpose register set as well as new instructions and opcodes that are not compatible with ARMv7 or ARMv6. While for high level languages like C or C++ the burden of opcode generation is transparently handled by the compiler, there are always cases (e.g, assembly, application binary interface, ELF format) that have to be handled specifically for each ISA. In this article I will describe the most important adaptions required to execute Genode on the ARMv8 architecture.

Application startup

The first thing a Genode component does at application startup is to load a temporary stack pointer from its BSS segment. Stack pointer loading cannot be expressed in C/C++, and therefore, has to be implemented in assembly for each architecture. On Genode these operation are implemented within the crt0.s file. Because most Genode components are linked as dynamic binaries, the stack pointer has to be loaded global offset table relative.

At the end of each crt0.s implementation there is a call (or branch on ARM) to Genode's _main function, in case there are arguments to this function, they must be provided in registers x0-x8 as defined in the Procedure Call Standard for the ARM 64-bit Architecture.

Compare and exchange

Since Genode supports multithreading, a lock implementation for synchronizing access to critical sections becomes necessary. While most of Genode's lock implementation is generic, atomic compare and exchange remains architecture dependent, because atomic functions are offered by most hardware. On ARMv8 compare and exchange can be implemented using the Load-Exclusive (ldxr) and Store-Exclusive (stxr) instructions (See: ARMĀ® Architecture Reference Manual ARMv8).

Dynamic linking

Dynamic linking also contains processor specific portions. Shared libraries may be loaded at arbitrary locations, which requires the dynamic linker to adjust (relocate) any absolute addresses within a library. In order to find the locations that need to be adjusted, the static linker generates relocations into shared libraries and the binary itself. These relocations and the actions to be taken are architecture dependent and are described in ELF supplementals. For ARMv8 the relocation types are defined in ELF for the ARMĀ® 64-bit Architecture and implemented in Genode's dynamic linker.

Lazy binding is when the linker resolves function addresses upon the first call to a function. For this the static linker generates special code in the so called procedure-linkage table (PLT), that calls an address provided by the dynamic linker at runtime and passes the symbol number of the function that has just been called. For this to work, the dynamic linker has to save and restore registers that are not caller safe (i.e., x0-x8, lr, fr on ARMv8), resolve and call the function. This of course is also specific for ARMv8 and our implementation can be found here.

Libc and Libm

The basic C libraries of course contain ARMv8 specific code. Our port of the FreeBSD libc was outdated and did not contain ARMv8 support. Therefore, we updated Genode's libc to FreeBSD-12.0 with Genode release 19.05.

The ARMv8 specific libc part consists of platform headers for plain old data types (POD) and some structured types, atomic instructions, setjmp/longjmp support, and floating-point unit (FPU) specific code.

setjmp/longjmp has to save and restore all general purpose and floating point registers to function properly and is used by Genode's libc and device-driver environment to implement cooperative scheduling.

Floating-point units also vary with each ARM generation and especially libm heavily depends on them. On the one hand the kernel is responsible for FPU save/restore operations during context switches, but also applications need to make sure to respect the calling convention and to implement there assembly functions using code for the correct FPU.

Current state

Having libc and libm in place allows for building a lot of native and ported Genode components already. As the time of writing we started to incorporate ARMv8 into our nightly testing infrastructure.

There are still some parts missing, like Genode's port of Rump Kernels or Gcov support, which we will address in the future.