From 4a9b1c6aabec6f3acfca96b70a8bffdbe08fb85a Mon Sep 17 00:00:00 2001 From: Christian Prochaska Date: Fri, 24 May 2013 11:04:42 +0200 Subject: [PATCH] base-linux: add ARM support This patch introduces a new platform 'linux_arm' for building and running Genode/Linux on an ARM device. Known limitations: - libc 'setjmp()'/'longjmp()' doesn't currently save/restore floating point registers Fixes #746. --- base-linux/etc/specs.conf | 8 ++ base-linux/lib/import/import-lx_hybrid.mk | 4 + base-linux/lib/import/import-syscall.mk | 4 +- base-linux/lib/mk/arm/base.mk | 8 ++ base-linux/lib/mk/arm/startup.mk | 5 ++ base-linux/lib/mk/arm/syscall.mk | 5 ++ base-linux/mk/spec-linux_arm.mk | 25 ++++++ base-linux/src/base/process/process.cc | 8 +- base-linux/src/platform/arm/crt0.s | 66 ++++++++++++++++ base-linux/src/platform/arm/lx_clone.S | 94 +++++++++++++++++++++++ base-linux/src/platform/arm/lx_syscall.S | 29 +++++++ base/src/platform/arm/crt0.s | 14 +++- libports/lib/mk/arm/libc-gen.mk | 3 - libports/lib/mk/arm/libc-setjmp.mk | 7 ++ os/lib/mk/arm/ldso_crt0_lx.mk | 3 + os/src/lib/ldso/arm/crt0.s | 8 +- os/src/lib/ldso/arm/linux/crt0.s | 58 ++++++++++++++ os/src/lib/ldso/include/arm/call_main.h | 10 ++- tool/builddir/etc/build.conf.linux_arm | 1 + tool/create_builddir | 1 + 20 files changed, 350 insertions(+), 11 deletions(-) create mode 100644 base-linux/lib/mk/arm/base.mk create mode 100644 base-linux/lib/mk/arm/startup.mk create mode 100644 base-linux/lib/mk/arm/syscall.mk create mode 100644 base-linux/mk/spec-linux_arm.mk create mode 100644 base-linux/src/platform/arm/crt0.s create mode 100644 base-linux/src/platform/arm/lx_clone.S create mode 100644 base-linux/src/platform/arm/lx_syscall.S create mode 100644 os/lib/mk/arm/ldso_crt0_lx.mk create mode 100644 os/src/lib/ldso/arm/linux/crt0.s create mode 100644 tool/builddir/etc/build.conf.linux_arm diff --git a/base-linux/etc/specs.conf b/base-linux/etc/specs.conf index 7574aad19..c5cb82c97 100644 --- a/base-linux/etc/specs.conf +++ b/base-linux/etc/specs.conf @@ -9,8 +9,16 @@ ifeq ($(shell uname -m),x86_64) SPECS ?= genode linux_x86_64 sdl else +ifeq ($(shell uname -m),armv6l) +SPECS ?= genode linux_arm sdl arm_v6 +else +ifeq ($(shell uname -m),armv7l) +SPECS ?= genode linux_arm sdl arm_v7a +else SPECS ?= genode linux_x86_32 sdl endif +endif +endif # # If you want to build for the host platform, diff --git a/base-linux/lib/import/import-lx_hybrid.mk b/base-linux/lib/import/import-lx_hybrid.mk index 2bc5d2f6f..9dfeeccca 100644 --- a/base-linux/lib/import/import-lx_hybrid.mk +++ b/base-linux/lib/import/import-lx_hybrid.mk @@ -97,8 +97,12 @@ USE_HOST_LD_SCRIPT = yes ifeq (x86_64,$(findstring x86_64,$(SPECS))) CXX_LINK_OPT += -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 else +ifeq (arm,$(findstring arm,$(SPECS))) +CXX_LINK_OPT += -Wl,--dynamic-linker=/lib/ld-linux.so.3 +else CXX_LINK_OPT += -Wl,--dynamic-linker=/lib/ld-linux.so.2 endif +endif # because we use the host compiler's libgcc, omit the Genode toolchain's version LD_LIBGCC = diff --git a/base-linux/lib/import/import-syscall.mk b/base-linux/lib/import/import-syscall.mk index 3d27ee978..8a8889653 100644 --- a/base-linux/lib/import/import-syscall.mk +++ b/base-linux/lib/import/import-syscall.mk @@ -1,9 +1,9 @@ HOST_INC_DIR += $(dir $(call select_from_repositories,src/platform/linux_syscalls.h)) HOST_INC_DIR += /usr/include -# needed for Ubuntu 11.04 +# needed for Ubuntu >= 11.04 +HOST_INC_DIR += /usr/include/$(shell gcc -dumpmachine) HOST_INC_DIR += /usr/include/i386-linux-gnu -HOST_INC_DIR += /usr/include/x86_64-linux-gnu # # Some header files installed on GNU/Linux test for the GNU compiler. For diff --git a/base-linux/lib/mk/arm/base.mk b/base-linux/lib/mk/arm/base.mk new file mode 100644 index 000000000..aae6655f0 --- /dev/null +++ b/base-linux/lib/mk/arm/base.mk @@ -0,0 +1,8 @@ +include $(REP_DIR)/lib/mk/base.inc + +LIBS += startup cxx + +SRC_CC += thread.cc thread_linux.cc + +vpath thread.cc $(BASE_DIR)/src/base/thread +vpath thread_linux.cc $(REP_DIR)/src/base/thread diff --git a/base-linux/lib/mk/arm/startup.mk b/base-linux/lib/mk/arm/startup.mk new file mode 100644 index 000000000..fdcb7c4fd --- /dev/null +++ b/base-linux/lib/mk/arm/startup.mk @@ -0,0 +1,5 @@ +LIBS += syscall + +include $(BASE_DIR)/lib/mk/startup.inc + +vpath crt0.s $(REP_DIR)/src/platform/arm diff --git a/base-linux/lib/mk/arm/syscall.mk b/base-linux/lib/mk/arm/syscall.mk new file mode 100644 index 000000000..9fc83d589 --- /dev/null +++ b/base-linux/lib/mk/arm/syscall.mk @@ -0,0 +1,5 @@ +REQUIRES = linux arm +SRC_S += lx_clone.S lx_syscall.S + +vpath lx_clone.S $(REP_DIR)/../base-linux/src/platform/arm +vpath lx_syscall.S $(REP_DIR)/../base-linux/src/platform/arm diff --git a/base-linux/mk/spec-linux_arm.mk b/base-linux/mk/spec-linux_arm.mk new file mode 100644 index 000000000..945dd7f79 --- /dev/null +++ b/base-linux/mk/spec-linux_arm.mk @@ -0,0 +1,25 @@ +# +# Specifics for Linux on ARM +# +SPECS += linux arm + +REP_INC_DIR += src/platform/arm + +ifeq ($(shell gcc -dumpmachine),arm-linux-gnueabihf) +CC_MARCH += -mfloat-abi=hard +endif + +# +# We need to manually add the default linker script on the command line in case +# of standard library use. Otherwise, we were not able to extend it by the +# context area section. +# +ifeq ($(USE_HOST_LD_SCRIPT),yes) +LD_SCRIPT_STATIC = ldscripts/armelf_linux_eabi.xc +endif + +# +# Include less-specific configuration +# +include $(call select_from_repositories,mk/spec-arm.mk) +include $(call select_from_repositories,mk/spec-linux.mk) diff --git a/base-linux/src/base/process/process.cc b/base-linux/src/base/process/process.cc index 06866b380..88d0425bc 100644 --- a/base-linux/src/base/process/process.cc +++ b/base-linux/src/base/process/process.cc @@ -70,7 +70,13 @@ Process::Process(Dataspace_capability elf_data_ds_cap, PERR("Dynamically linked file found, but no dynamic linker binary present"); return; } - elf_data_ds_cap = _dynamic_linker_cap; + /* + * Starting the dynamic linker directly may cause it to be loaded at the + * wrong address on ARM-Linux. But since the dynamically linked + * application has a dynamic linker (by default ld.lib.so) defined as its + * interpreter in the ELF image, it's okay to just start the application + * directly on Linux. + */ } /* diff --git a/base-linux/src/platform/arm/crt0.s b/base-linux/src/platform/arm/crt0.s new file mode 100644 index 000000000..ce570f4ab --- /dev/null +++ b/base-linux/src/platform/arm/crt0.s @@ -0,0 +1,66 @@ +/* + * \brief Startup code for Genode applications + * \author Christian Helmuth + * \author Christian Prochaska + * \date 2006-07-06 + */ + +/* + * Copyright (C) 2006-2012 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: + + ldr r1,=__initial_sp + str sp,[r1] + + /* + * environ = &argv[argc + 1] + * in Genode argc is always 1 + */ + add sp,sp,#12 + ldr r1,=lx_environ + str sp,[r1] + + /* XXX Switch to our own stack. */ + ldr sp,=_stack_high + + /* Clear the frame pointer and the link register so that stack backtraces will work. */ + mov fp,#0 + mov lr,#0 + + /* Jump into init C code */ + b _main + +/*--------------------------------------------------*/ + .data + .globl __dso_handle +__dso_handle: + .long 0 + + .globl __initial_sp +__initial_sp: + .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: diff --git a/base-linux/src/platform/arm/lx_clone.S b/base-linux/src/platform/arm/lx_clone.S new file mode 100644 index 000000000..29f025a17 --- /dev/null +++ b/base-linux/src/platform/arm/lx_clone.S @@ -0,0 +1,94 @@ +/* + * \brief Linux clone() binding + * \author Christian Prochaska + * \date 2012-05-05 + * + * based on eglibc-2.11.3/ports/sysdeps/unix/sysv/linux/arm/clone.S + */ + +#define SYS_clone 120 +#define SYS_exit 1 +#define SYS_getpid 20 + +#define __ARM_EABI__ 1 + +#define CLONE_VM 0x00000100 +#define CLONE_THREAD 0x00010000 + + .text + .globl lx_clone + .type lx_clone, #function +lx_clone: + + @ insert the args onto the new stack + str r3, [r1, #-4]! + str r0, [r1, #-4]! + + @ do the system call + @ get flags + mov r0, r2 +#ifdef RESET_PID + mov ip, r2 +#endif + @ new sp is already in r1 +#ifdef __ARM_EABI__ + stmfd sp!, {r4, r7} +#else + str r4, [sp, #-8]! +#endif + ldr r2, [sp, #8] + ldr r3, [sp, #12] + ldr r4, [sp, #16] +#ifdef __ARM_EABI__ + ldr r7, =SYS_clone + swi 0x0 +#else + swi SYS_clone +#endif + cmp r0, #0 + beq 1f +#ifdef __ARM_EABI__ + ldmfd sp!, {r4, r7} +#else + ldr r4, [sp], #8 +#endif + #blt PLTJMP(C_SYMBOL_NAME(__syscall_error)) + bx lr + +1: +#ifdef RESET_PID + tst ip, #CLONE_THREAD + bne 3f + mov r0, #0xffff0fff + mov lr, pc + sub pc, r0, #31 + mov r1, r0 + tst ip, #CLONE_VM + movne r0, #-1 +#ifdef __ARM_EABI__ + ldr r7, =SYS_getpid + swieq 0x0 +#else + swieq SYS_getpid +#endif + str r0, [r1, #PID_OFFSET] + str r0, [r1, #TID_OFFSET] +3: +#endif + @ pick the function arg and call address off the stack and execute + ldr r0, [sp, #4] +#if defined(__ARM_ARCH_4T__) && defined(__THUMB_INTERWORK__) + ldr ip, [sp], #8 + mov lr, pc + bx ip +#else + mov lr, pc + ldr pc, [sp], #8 +#endif + + @ and we are done, passing the return value through r0 + ldr r7, =SYS_exit + swi 0x0 + +/* tell the linker that this code does not need an executable stack */ +.section .note.GNU-stack, "", %progbits diff --git a/base-linux/src/platform/arm/lx_syscall.S b/base-linux/src/platform/arm/lx_syscall.S new file mode 100644 index 000000000..0cee0f211 --- /dev/null +++ b/base-linux/src/platform/arm/lx_syscall.S @@ -0,0 +1,29 @@ +/* + * \brief Linux syscall() binding + * \author Christian Prochaska + * \date 2012-05-05 + * + * based on eglibc-2.11.3/ports/sysdeps/unix/sysv/linux/arm/syscall.S + * + * error case: + * glibc's syscall() function returns -1 and sets errno + * lx_syscall() returns -errno + */ + + .text + .globl lx_syscall + .type lx_syscall, #function +lx_syscall: + mov ip, sp + stmfd sp!, {r4, r5, r6, r7} + mov r7, r0 + mov r0, r1 + mov r1, r2 + mov r2, r3 + ldmfd ip, {r3, r4, r5, r6} + swi 0x0 + ldmfd sp!, {r4, r5, r6, r7} + bx lr + +/* tell the linker that this code does not need an executable stack */ +.section .note.GNU-stack, "", %progbits diff --git a/base/src/platform/arm/crt0.s b/base/src/platform/arm/crt0.s index 3232e5f06..c260e259c 100644 --- a/base/src/platform/arm/crt0.s +++ b/base/src/platform/arm/crt0.s @@ -17,15 +17,25 @@ .globl _start _start: - ldr sp, .initial_sp + ldr r4, .initial_sp + str sp, [r4] + + ldr sp, .stack_high b _main -.initial_sp: .word _stack_high +.initial_sp: .word __initial_sp +.stack_high: .word _stack_high +/*--------------------------------------------------*/ + .data .globl __dso_handle __dso_handle: .long 0 + .globl __initial_sp +__initial_sp: + .long 0 + /*--- .bss (non-initialized data) ------------------*/ .section ".bss" diff --git a/libports/lib/mk/arm/libc-gen.mk b/libports/lib/mk/arm/libc-gen.mk index ed0b1a95a..5daac7206 100644 --- a/libports/lib/mk/arm/libc-gen.mk +++ b/libports/lib/mk/arm/libc-gen.mk @@ -12,7 +12,4 @@ SRC_C += $(filter-out $(FILTER_OUT_C),$(notdir $(wildcard $(LIBC_GEN_ARM_DIR)/* # fix missing include prefix for 'ucontext.h', should be 'sys/ucontext.h' CC_OPT_makecontext = -I$(REP_DIR)/include/libc/sys -# needed to compile setjmp.S -CC_OPT += -DSOFTFLOAT - vpath % $(LIBC_GEN_ARM_DIR) diff --git a/libports/lib/mk/arm/libc-setjmp.mk b/libports/lib/mk/arm/libc-setjmp.mk index dfe0944d4..9ade9cc8a 100644 --- a/libports/lib/mk/arm/libc-setjmp.mk +++ b/libports/lib/mk/arm/libc-setjmp.mk @@ -7,6 +7,13 @@ SRC_S = _setjmp.S setjmp.S # CC_OPT += -D_STANDALONE +# +# Needed to compile on hard-float Linux +# FIXME: Floating point registers don't get saved/restored +# when using this definition! +# +CC_OPT += -D__SOFTFP__ + include $(REP_DIR)/lib/mk/libc-common.inc vpath %.S $(LIBC_GEN_ARM_DIR) diff --git a/os/lib/mk/arm/ldso_crt0_lx.mk b/os/lib/mk/arm/ldso_crt0_lx.mk new file mode 100644 index 000000000..39c497073 --- /dev/null +++ b/os/lib/mk/arm/ldso_crt0_lx.mk @@ -0,0 +1,3 @@ +SRC_S = crt0.s + +vpath crt0.s $(REP_DIR)/src/lib/ldso/arm/linux diff --git a/os/src/lib/ldso/arm/crt0.s b/os/src/lib/ldso/arm/crt0.s index 682b3a56e..529a1da5e 100644 --- a/os/src/lib/ldso/arm/crt0.s +++ b/os/src/lib/ldso/arm/crt0.s @@ -20,10 +20,14 @@ _start_ldso: ldr r2, .initial_utcb str r0, [r2] - ldr sp, .initial_sp + ldr r2, .initial_sp + str sp, [r2] + + ldr sp, .stack_high bl init_rtld b _main - .initial_sp: .word _stack_high + .initial_sp: .word __initial_sp + .stack_high: .word _stack_high .initial_utcb: .word _main_utcb diff --git a/os/src/lib/ldso/arm/linux/crt0.s b/os/src/lib/ldso/arm/linux/crt0.s new file mode 100644 index 000000000..8ac1d1439 --- /dev/null +++ b/os/src/lib/ldso/arm/linux/crt0.s @@ -0,0 +1,58 @@ +/* + * \brief Startup code for ld.lib.so (linux_arm) + * \author Christian Prochaska + * \date 2012-07-06 + */ + +/* + * Copyright (C) 2012 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) -------------------------*/ +.section ".text.crt0" + + .globl _start_ldso +_start_ldso: + + ldr sl, .L_GOT +.L_GOT_OFF: + add sl, pc, sl + + ldr r1, .initial_sp + ldr r1, [sl, r1] + str sp, [r1] + + /* + * environ = &argv[argc + 1] + * in Genode argc is always 1 + */ + add sp, sp,#12 + ldr r1, .lx_environ + ldr r1, [sl, r1] + str sp, [r1] + + /* XXX Switch to our own stack. */ + ldr r1, .stack_high + ldr sp, [sl, r1] + + /* relocate ldso */ + mov r1, #0 + bl init_rtld + + /* + * Clear the frame pointer and the link register so that stack + * backtraces will work. + */ + mov fp, #0 + mov lr, #0 + + /* Jump into init C code */ + b _main + + .L_GOT: .word _GLOBAL_OFFSET_TABLE_ - (.L_GOT_OFF + 8) + .initial_sp: .word __initial_sp(GOT) + .lx_environ: .word lx_environ(GOT) + .stack_high: .word _stack_high(GOT) diff --git a/os/src/lib/ldso/include/arm/call_main.h b/os/src/lib/ldso/include/arm/call_main.h index a3ea952b7..2af320382 100644 --- a/os/src/lib/ldso/include/arm/call_main.h +++ b/os/src/lib/ldso/include/arm/call_main.h @@ -18,7 +18,15 @@ */ void call_main(void (*func)(void)) { - func(); + extern long __initial_sp; + + asm volatile ("mov %%sp, %0;" + "bx %1;" + : + : "r" (__initial_sp), + "r" (func) + : "memory" + ); } #endif /* _ARM__CALL_MAIN_H_ */ diff --git a/tool/builddir/etc/build.conf.linux_arm b/tool/builddir/etc/build.conf.linux_arm new file mode 100644 index 000000000..06b8af675 --- /dev/null +++ b/tool/builddir/etc/build.conf.linux_arm @@ -0,0 +1 @@ +REPOSITORIES = $(GENODE_DIR)/base-linux diff --git a/tool/create_builddir b/tool/create_builddir index 6d01bb6b9..568724ca4 100755 --- a/tool/create_builddir +++ b/tool/create_builddir @@ -20,6 +20,7 @@ usage: @echo @echo " can be:" @echo " 'linux_x86'" + @echo " 'linux_arm'" @echo " 'fiasco_x86'" @echo " 'pistachio_x86'" @echo " 'okl4_x86'"