From 8fa8ca9feb7565ae4c40305bb462956fdec0ffbc Mon Sep 17 00:00:00 2001 From: Alexander Kartashov Date: Tue, 22 Jan 2013 22:53:24 +0400 Subject: [PATCH] arm: added ARM-specific files modelled after arch/x86 Signed-off-by: Alexander Kartashov Signed-off-by: Pavel Emelyanov --- Makefile | 7 + arch/arm/Makefile | 57 +++++ arch/arm/crtools.c | 232 ++++++++++++++++++ arch/arm/gen-sys-exec-tbl.pl | 39 +++ arch/arm/gen-syscalls.pl | 104 ++++++++ arch/arm/include/asm/atomic.h | 67 ++++++ arch/arm/include/asm/bitops.h | 111 +++++++++ arch/arm/include/asm/dump.h | 14 ++ arch/arm/include/asm/linkage.h | 24 ++ arch/arm/include/asm/memcpy_64.h | 20 ++ arch/arm/include/asm/parasite-syscall.h | 18 ++ arch/arm/include/asm/parasite.h | 28 +++ arch/arm/include/asm/processor-flags.h | 42 ++++ arch/arm/include/asm/restore.h | 25 ++ arch/arm/include/asm/restorer.h | 147 ++++++++++++ arch/arm/include/asm/syscall-aux.S | 9 + arch/arm/include/asm/syscall-aux.h | 8 + arch/arm/include/asm/types.h | 248 ++++++++++++++++++++ arch/arm/parasite-head.S | 24 ++ arch/arm/restorer.c | 50 ++++ arch/arm/syscall-common.S | 52 ++++ arch/arm/syscall.def | 78 ++++++ arch/arm/uidiv.S | 186 +++++++++++++++ test/zdtm/lib/arch/arm/include/asm/atomic.h | 66 ++++++ 24 files changed, 1656 insertions(+) create mode 100644 arch/arm/Makefile create mode 100644 arch/arm/crtools.c create mode 100755 arch/arm/gen-sys-exec-tbl.pl create mode 100755 arch/arm/gen-syscalls.pl create mode 100644 arch/arm/include/asm/atomic.h create mode 100644 arch/arm/include/asm/bitops.h create mode 100644 arch/arm/include/asm/dump.h create mode 100644 arch/arm/include/asm/linkage.h create mode 100644 arch/arm/include/asm/memcpy_64.h create mode 100644 arch/arm/include/asm/parasite-syscall.h create mode 100644 arch/arm/include/asm/parasite.h create mode 100644 arch/arm/include/asm/processor-flags.h create mode 100644 arch/arm/include/asm/restore.h create mode 100644 arch/arm/include/asm/restorer.h create mode 100644 arch/arm/include/asm/syscall-aux.S create mode 100644 arch/arm/include/asm/syscall-aux.h create mode 100644 arch/arm/include/asm/types.h create mode 100644 arch/arm/parasite-head.S create mode 100644 arch/arm/restorer.c create mode 100644 arch/arm/syscall-common.S create mode 100644 arch/arm/syscall.def create mode 100644 arch/arm/uidiv.S create mode 100644 test/zdtm/lib/arch/arm/include/asm/atomic.h diff --git a/Makefile b/Makefile index d20384501..9c512d5d6 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,13 @@ ifeq ($(uname_M),x86_64) LDARCH := i386:x86-64 endif +ifeq ($(findstring arm,$(uname_M)),arm) + ARCH := arm + ARCH_DEFINES := -DCONFIG_ARM + LDARCH := arm + CFLAGS += -march=armv7-a +endif + SRC_DIR ?= $(shell pwd) ARCH_DIR := $(SRC_DIR)/arch/$(ARCH) diff --git a/arch/arm/Makefile b/arch/arm/Makefile new file mode 100644 index 000000000..847e1061e --- /dev/null +++ b/arch/arm/Makefile @@ -0,0 +1,57 @@ +SYS-DEF := $(ARCH_DIR)/syscall.def +SYS-ASM-COMMON := syscall-common.S +SYS-TYPES := $(SRC_DIR)/include/syscall-types.h + +SYS-CODES := $(SRC_DIR)/include/syscall-codes.h +SYS-PROTO := $(SRC_DIR)/include/syscall.h + +SYS-ASM := syscalls.S +SYS-GEN := $(ARCH_DIR)/gen-syscalls.pl +SYS-GEN-TBL := $(ARCH_DIR)/gen-sys-exec-tbl.pl + +SYS-OBJ := syscalls.o + +SYS-EXEC-TBL := sys-exec-tbl.c + +CFLAGS += -c -fpie -Wstrict-prototypes -Wa,--noexecstack -D__ASSEMBLY__ -nostdlib -fomit-frame-pointer -I$(shell pwd) + +ARCH_BITS := 32 + +.DEFAULT_GOAL := arm + +$(SYS-ASM): $(SYS-GEN) $(SYS-DEF) $(SYS-ASM-COMMON) $(SYS-TYPES) + $(E) " GEN " $@ + $(Q) perl \ + $(SYS-GEN) \ + $(SYS-DEF) \ + $(SYS-CODES) \ + $(SYS-PROTO) \ + $(SYS-ASM) \ + $(SYS-ASM-COMMON) \ + $(SYS-TYPES) \ + $(ARCH_BITS) + +$(SYS-EXEC-TBL): $(SYS-GEN-TBL) $(SYS-DEF) + $(E) " GEN " $@ + $(Q) perl \ + $(SYS-GEN-TBL) \ + $(SYS-DEF) \ + $(SYS-EXEC-TBL) \ + $(ARCH_BITS) + +%.o: %.S + $(E) " CC " $@ + $(Q) $(CC) $(CFLAGS) $^ -o $@ + +arm: $(SYS-OBJ) $(SYS-EXEC-TBL) + +clean: + $(E) " CLEAN SYSCALLS" + $(Q) $(RM) -f $(SYS-ASM) + $(Q) $(RM) -f $(SYS-CODES) + $(Q) $(RM) -f $(SYS-PROTO) + $(Q) $(RM) -f $(SYS-OBJ) + $(Q) $(RM) -f $(SYS-EXEC-TBL) + $(Q) $(RM) -f *.o *.d + +.PHONY: clean arm diff --git a/arch/arm/crtools.c b/arch/arm/crtools.c new file mode 100644 index 000000000..c1ee5dc1d --- /dev/null +++ b/arch/arm/crtools.c @@ -0,0 +1,232 @@ +#include +#include + +#include "asm/types.h" +#include "compiler.h" +#include "ptrace.h" +#include "asm/processor-flags.h" +#include "protobuf.h" +#include "../protobuf/core.pb-c.h" +#include "../protobuf/creds.pb-c.h" +#include "parasite-syscall.h" +#include "syscall.h" +#include "log.h" +#include "util.h" +#include "cpu.h" +#include "fpu.h" +#include "elf.h" +#include "parasite-syscall.h" +#include "restorer.h" + + +/* + * Injected syscall instruction + */ +const char code_syscall[] = { + 0x00, 0x00, 0x00, 0xef, /* SVC #0 */ + 0xf0, 0x01, 0xf0, 0xe7 /* UDF #32 */ +}; + +const int code_syscall_size = round_up(sizeof(code_syscall), sizeof(long)); + +static inline void __check_code_syscall(void) +{ + BUILD_BUG_ON(sizeof(code_syscall) != BUILTIN_SYSCALL_SIZE); + BUILD_BUG_ON(!is_log2(sizeof(code_syscall))); +} + + +void parasite_setup_regs(unsigned long new_ip, user_regs_struct_t *regs) +{ + regs->ARM_pc = new_ip; + + /* Avoid end of syscall processing */ + regs->ARM_ORIG_r0 = -1; + + /* Make sure flags are in known state */ + regs->ARM_cpsr &= PSR_f | PSR_s | PSR_x | PSR_T_BIT | MODE32_BIT; +} + + +int syscall_seized(struct parasite_ctl *ctl, int nr, unsigned long *ret, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long arg5, + unsigned long arg6) +{ + user_regs_struct_t regs = ctl->regs_orig; + int err; + + regs.ARM_r7 = (unsigned long)nr; + regs.ARM_r0 = arg1; + regs.ARM_r1 = arg2; + regs.ARM_r2 = arg3; + regs.ARM_r3 = arg4; + regs.ARM_r4 = arg5; + regs.ARM_r5 = arg6; + + parasite_setup_regs(ctl->syscall_ip, ®s); + err = __parasite_execute(ctl, ctl->pid, ®s); + if (err) + return err; + + *ret = regs.ARM_r0; + return 0; +} + +#define assign_reg(dst, src, e) dst->e = (__typeof__(dst->e))src.ARM_##e + +int get_task_regs(pid_t pid, CoreEntry *core, const struct parasite_ctl *ctl) +{ + user_regs_struct_t regs = {{-1}}; + struct user_vfp vfp; + int ret = -1; + + pr_info("Dumping GP/FPU registers ... "); + + if (ctl) + regs = ctl->regs_orig; + else { + if (ptrace(PTRACE_GETREGS, pid, NULL, ®s)) { + pr_err("Can't obtain GP registers for %d\n", pid); + goto err; + } + } + + if (ptrace(PTRACE_GETFPREGS, pid, NULL, &vfp)) { + pr_err("Can't obtain FPU registers for %d\n", pid); + goto err; + } + + /* Did we come from a system call? */ + if ((int)regs.ARM_ORIG_r0 >= 0) { + /* Restart the system call */ + switch ((long)(int)regs.ARM_r0) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + regs.ARM_r0 = regs.ARM_ORIG_r0; + regs.ARM_pc -= 4; + break; + case -ERESTART_RESTARTBLOCK: + regs.ARM_r0 = __NR_restart_syscall; + regs.ARM_pc -= 4; + break; + } + } + + + // Save the ARM CPU state + + assign_reg(core->ti_arm->gpregs, regs, r0); + assign_reg(core->ti_arm->gpregs, regs, r1); + assign_reg(core->ti_arm->gpregs, regs, r2); + assign_reg(core->ti_arm->gpregs, regs, r3); + assign_reg(core->ti_arm->gpregs, regs, r4); + assign_reg(core->ti_arm->gpregs, regs, r5); + assign_reg(core->ti_arm->gpregs, regs, r6); + assign_reg(core->ti_arm->gpregs, regs, r7); + assign_reg(core->ti_arm->gpregs, regs, r8); + assign_reg(core->ti_arm->gpregs, regs, r9); + assign_reg(core->ti_arm->gpregs, regs, r10); + assign_reg(core->ti_arm->gpregs, regs, fp); + assign_reg(core->ti_arm->gpregs, regs, ip); + assign_reg(core->ti_arm->gpregs, regs, sp); + assign_reg(core->ti_arm->gpregs, regs, lr); + assign_reg(core->ti_arm->gpregs, regs, pc); + assign_reg(core->ti_arm->gpregs, regs, cpsr); + core->ti_arm->gpregs->orig_r0 = regs.ARM_ORIG_r0; + + + // Save the VFP state + + memcpy(CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs, &vfp.fpregs, sizeof(vfp.fpregs)); + CORE_THREAD_ARCH_INFO(core)->fpstate->fpscr = vfp.fpscr; + + ret = 0; + +err: + return ret; +} + +int arch_alloc_thread_info(CoreEntry *core) +{ + ThreadInfoArm *ti_arm; + UserArmRegsEntry *gpregs; + UserArmVfpstateEntry *fpstate; + ThreadCoreEntry *thread_core; + + ti_arm = xmalloc(sizeof(*ti_arm)); + if (!ti_arm) + goto err; + thread_info_arm__init(ti_arm); + + gpregs = xmalloc(sizeof(*gpregs)); + user_arm_regs_entry__init(gpregs); + ti_arm->gpregs = gpregs; + + fpstate = xmalloc(sizeof(*fpstate)); + user_arm_vfpstate_entry__init(fpstate); + fpstate->vfp_regs = xmalloc(32*sizeof(unsigned long long)); + fpstate->n_vfp_regs = 32; + ti_arm->fpstate = fpstate; + + core->ti_arm = ti_arm; + + + thread_core = xmalloc(sizeof(*thread_core)); + if (!thread_core) + goto err; + thread_core_entry__init(thread_core); + core->thread_core = thread_core; + +err: + return 0; +} + +void core_entry_free(CoreEntry *core) +{ + if (core) { + if (CORE_THREAD_ARCH_INFO(core)) { + if (CORE_THREAD_ARCH_INFO(core)->fpstate) { + xfree(CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs); + xfree(CORE_THREAD_ARCH_INFO(core)->fpstate); + } + xfree(CORE_THREAD_ARCH_INFO(core)->gpregs); + } + xfree(CORE_THREAD_ARCH_INFO(core)); + xfree(core->thread_core); + xfree(core->tc); + xfree(core->ids); + } + +} + +int sigreturn_prep_fpu_frame(struct thread_restore_args *args, CoreEntry *core) +{ + memcpy(args->fpu_state.ufp.fpregs, CORE_THREAD_ARCH_INFO(core)->fpstate->vfp_regs, + sizeof(args->fpu_state.ufp.fpregs)); + args->fpu_state.ufp.fpscr = CORE_THREAD_ARCH_INFO(core)->fpstate->fpscr; + + return 0; +} + +void *mmap_seized(struct parasite_ctl *ctl, + void *addr, size_t length, int prot, + int flags, int fd, off_t offset) +{ + unsigned long map; + int err; + + if (offset & ~PAGE_MASK) + return 0; + + err = syscall_seized(ctl, __NR_mmap2, &map, + (unsigned long)addr, length, prot, flags, fd, offset >> 12); + if (err < 0 || map > TASK_SIZE) + map = 0; + + return (void *)map; +} diff --git a/arch/arm/gen-sys-exec-tbl.pl b/arch/arm/gen-sys-exec-tbl.pl new file mode 100755 index 000000000..1746a0c75 --- /dev/null +++ b/arch/arm/gen-sys-exec-tbl.pl @@ -0,0 +1,39 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $in = $ARGV[0]; +my $tblout = $ARGV[1]; +my $bits = $ARGV[2]; + +my $code = "code$bits"; + +open TBLOUT, ">", $tblout or die $!; +open IN, "<", $in or die $!; + +print TBLOUT "/* Autogenerated, don't edit */\n"; + +for () { + if ($_ =~ /\#/) { + next; + } + + my $sys_name; + my $sys_num; + + if (/(?\S+)\s+(?\S+)\s+(?\d+)\s+(?(?:\d+|\!))\s+\((?.+)\)/) { + $sys_name = $+{alias}; + } elsif (/(?\S+)\s+(?\d+)\s+(?(?:\d+|\!))\s+\((?.+)\)/) { + $sys_name = $+{name}; + } else { + unlink $tblout; + die "Invalid syscall definition file: invalid entry $_\n"; + } + + $sys_num = $+{$code}; + + if ($sys_num ne "!") { + print TBLOUT "SYSCALL($sys_name, $sys_num)\n"; + } +} diff --git a/arch/arm/gen-syscalls.pl b/arch/arm/gen-syscalls.pl new file mode 100755 index 000000000..4b65621bc --- /dev/null +++ b/arch/arm/gen-syscalls.pl @@ -0,0 +1,104 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $in = $ARGV[0]; +my $codesout = $ARGV[1]; +my $codes = $ARGV[1] =~ s/.*include\///gr; +my $protosout = $ARGV[2]; +my $protos = $ARGV[2] =~ s/.*include\///gr; +my $asmout = $ARGV[3]; +my $asmcommon = $ARGV[4]; +my $prototypes = $ARGV[5] =~ s/.*include\///gr; +my $bits = $ARGV[6]; + +my $codesdef = $codes =~ tr/.-/_/r; +my $protosdef = $protos =~ tr/.-/_/r; +my $code = "code$bits"; +my $need_aux = 0; + +unlink $codesout; +unlink $protosout; +unlink $asmout; + +open CODESOUT, ">", $codesout or die $!; +open PROTOSOUT, ">", $protosout or die $!; +open ASMOUT, ">", $asmout or die $!; +open IN, "<", $in or die $!; + +print CODESOUT <<"END"; +/* Autogenerated, don't edit */ +#ifndef $codesdef +#define $codesdef +END + +print PROTOSOUT <<"END"; +/* Autogenerated, don't edit */ +#ifndef $protosdef +#define $protosdef +#include "$prototypes" +#include "$codes" +END + +print ASMOUT <<"END"; +/* Autogenerated, don't edit */ +#include "$codes" +#include "$asmcommon" +END + + +for () { + if ($_ =~ /\#/) { + next; + } + + my $code_macro; + my $sys_name; + + if (/(?\S+)\s+(?\S+)\s+(?\d+)\s+(?(?:\d+|\!))\s+\((?.+)\)/) { + $code_macro = "__NR_$+{name}"; + $sys_name = "sys_$+{alias}"; + } elsif (/(?\S+)\s+(?\d+)\s+(?(?:\d+|\!))\s+\((?.+)\)/) { + $code_macro = "__NR_$+{name}"; + $sys_name = "sys_$+{name}"; + } else { + unlink $codesout; + unlink $protosout; + unlink $asmout; + + die "Invalid syscall definition file: invalid entry $_\n"; + } + + if ($+{$code} ne "!") { + print CODESOUT "#define $code_macro $+{$code}\n"; + + my $nargs; + + if ($+{args} eq "void") { + $nargs = 0; + } else { + my $tmp = $+{args}; + $nargs = 1 + ($tmp =~ tr/\,/\,/); + + if ($nargs <= 4) { + $nargs = 0; + } + } + + print ASMOUT "syscall$nargs $sys_name, $code_macro\n"; + + } else { + $need_aux = 1; + } + + print PROTOSOUT "extern long $sys_name($+{args});\n"; +} + +if ($need_aux == 1) { + print ASMOUT "#include \"asm/syscall-aux.S\"\n"; + print CODESOUT "#include \"asm/syscall-aux.h\"\n"; +} + +print CODESOUT "#endif /* $codesdef */"; +print PROTOSOUT "#endif /* $protosdef */"; diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h new file mode 100644 index 000000000..ccf6a7dc4 --- /dev/null +++ b/arch/arm/include/asm/atomic.h @@ -0,0 +1,67 @@ +#ifndef __CR_ATOMIC_H__ +#define __CR_ATOMIC_H__ + +typedef struct { + u32 counter; +} atomic_t; + + +/* Copied from the Linux kernel header arch/arm/include/asm/atomic.h */ + +#define smp_mb() __asm__ __volatile__ ("dmb" : : : "memory") + +#define atomic_set(mem,v) ((mem)->counter = (v)) +#define atomic_get(v) (*(volatile u32 *)&(v)->counter) + +static inline unsigned int atomic_add_return(int i, atomic_t *v) +{ + unsigned long tmp; + unsigned int result; + + smp_mb(); + + __asm__ __volatile__("@ atomic_add_return\n" +"1: ldrex %0, [%3]\n" +" add %0, %0, %4\n" +" strex %1, %0, [%3]\n" +" teq %1, #0\n" +" bne 1b\n" + : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) + : "r" (&v->counter), "Ir" (i) + : "cc"); + + smp_mb(); + + return result; +} + +static inline unsigned int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long tmp; + int result; + + smp_mb(); + + __asm__ __volatile__("@ atomic_sub_return\n" +"1: ldrex %0, [%3]\n" +" sub %0, %0, %4\n" +" strex %1, %0, [%3]\n" +" teq %1, #0\n" +" bne 1b\n" + : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) + : "r" (&v->counter), "Ir" (i) + : "cc"); + + smp_mb(); + + return result; +} + +static inline unsigned int atomic_inc(atomic_t *v) { return atomic_add_return(1, v) - 1; } + +static inline unsigned int atomic_dec(atomic_t *v) { return atomic_sub_return(1, v) + 1; } + +/* true if the result is 0, or false for all other cases. */ +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) + +#endif /* __CR_ATOMIC_H__ */ diff --git a/arch/arm/include/asm/bitops.h b/arch/arm/include/asm/bitops.h new file mode 100644 index 000000000..5aa218d11 --- /dev/null +++ b/arch/arm/include/asm/bitops.h @@ -0,0 +1,111 @@ +#ifndef __CR_BITOPS_H__ +#define __CR_BITOPS_H__ + +#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d)) +#define BITS_PER_LONG (8 * sizeof(long)) +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_LONG) + +#define DECLARE_BITMAP(name, bits) \ + unsigned long name[BITS_TO_LONGS(bits)] + +#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1) +/* Technically wrong, but this avoids compilation errors on some gcc + versions. */ +#define BITOP_ADDR(x) "=m" (*(volatile long *) (x)) +#else +#define BITOP_ADDR(x) "+m" (*(volatile long *) (x)) +#endif + +#define ADDR BITOP_ADDR(addr) + +static inline void set_bit(int nr, volatile unsigned long *addr) { + *addr |= (1 << nr); +} + +static inline void change_bit(int nr, volatile unsigned long *addr) +{ + *addr ^= (1 << nr); +} + +static inline int test_bit(int nr, volatile const unsigned long *addr) +{ + return (*addr & (1 << nr)) ? -1 : 0; +} + +static inline void clear_bit(int nr, volatile unsigned long *addr) +{ + *addr &= ~(1 << nr); +} + +/** + * __ffs - find first set bit in word + * @word: The word to search + * + * Undefined if no bit exists, so code should check against 0 first. + */ +static inline unsigned long __ffs(unsigned long word) +{ + int p = 0; + + for (; p < 8*sizeof(word); ++p) { + if (word & 1) { + break; + } + + word >>= 1; + } + + return p; +} + +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/* + * Find the next set bit in a memory region. + */ +static inline +unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + return size; + size -= result; + offset %= BITS_PER_LONG; + if (offset) { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + goto found_first; + if (tmp) + goto found_middle; + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + while (size & ~(BITS_PER_LONG-1)) { + if ((tmp = *(p++))) + goto found_middle; + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + if (!size) + return result; + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + return result + size; /* Nope. */ +found_middle: + return result + __ffs(tmp); +} + +#define for_each_bit(i, bitmask) \ + for (i = find_next_bit(bitmask, sizeof(bitmask), 0); \ + i < sizeof(bitmask); \ + i = find_next_bit(bitmask, sizeof(bitmask), i + 1)) + +#endif /* __CR_BITOPS_H__ */ diff --git a/arch/arm/include/asm/dump.h b/arch/arm/include/asm/dump.h new file mode 100644 index 000000000..d8d0ae134 --- /dev/null +++ b/arch/arm/include/asm/dump.h @@ -0,0 +1,14 @@ +#ifndef __CR_ASM_DUMP_H__ +#define __CR_ASM_DUMP_H__ + +extern int get_task_regs(pid_t pid, CoreEntry *core, const struct parasite_ctl *ctl); +extern int arch_alloc_thread_info(CoreEntry *core); +extern void core_entry_free(CoreEntry *core); + + +static inline void core_put_tls(CoreEntry *core, u32 tls) +{ + core->ti_arm->tls = tls; +} + +#endif diff --git a/arch/arm/include/asm/linkage.h b/arch/arm/include/asm/linkage.h new file mode 100644 index 000000000..738064233 --- /dev/null +++ b/arch/arm/include/asm/linkage.h @@ -0,0 +1,24 @@ +#ifndef __CR_LINKAGE_H__ +#define __CR_LINKAGE_H__ + +#ifdef __ASSEMBLY__ + +#define __ALIGN .align 4, 0x00 +#define __ALIGN_STR ".align 4, 0x00" + +#define GLOBAL(name) \ + .globl name; \ + name: + +#define ENTRY(name) \ + .globl name; \ + .type name, #function; \ + __ALIGN; \ + name: + +#define END(sym) \ + .size sym, . - sym + +#endif /* __ASSEMBLY__ */ + +#endif /* __CR_LINKAGE_H__ */ diff --git a/arch/arm/include/asm/memcpy_64.h b/arch/arm/include/asm/memcpy_64.h new file mode 100644 index 000000000..3dc8f3423 --- /dev/null +++ b/arch/arm/include/asm/memcpy_64.h @@ -0,0 +1,20 @@ +#ifndef __MEMCPY_ARM_H__ +#define __MEMCPY_ARM_H__ + +#include "compiler.h" +#include "types.h" + +static always_inline void *builtin_memcpy(void *to, const void *from, unsigned int n) +{ + int i; + unsigned char *cto = to; + const unsigned char *cfrom = from; + + for (i = 0; i < n; ++i, ++cto, ++cfrom) { + *cto = *cfrom; + } + + return to; +} + +#endif diff --git a/arch/arm/include/asm/parasite-syscall.h b/arch/arm/include/asm/parasite-syscall.h new file mode 100644 index 000000000..897127717 --- /dev/null +++ b/arch/arm/include/asm/parasite-syscall.h @@ -0,0 +1,18 @@ +#ifndef __CR_ASM_PARASITE_SYSCALL_H__ +#define __CR_ASM_PARASITE_SYSCALL_H__ + + +#define ARCH_SI_TRAP TRAP_BRKPT + + +extern const char code_syscall[]; +extern const int code_syscall_size; + + +void parasite_setup_regs(unsigned long new_ip, user_regs_struct_t *regs); + +void *mmap_seized(struct parasite_ctl *ctl, + void *addr, size_t length, int prot, + int flags, int fd, off_t offset); + +#endif diff --git a/arch/arm/include/asm/parasite.h b/arch/arm/include/asm/parasite.h new file mode 100644 index 000000000..3ea6e3434 --- /dev/null +++ b/arch/arm/include/asm/parasite.h @@ -0,0 +1,28 @@ +#ifndef __ASM_PARASITE_H__ +#define __ASM_PARASITE_H__ + +static inline u32 arch_get_tls(void) { + uint32_t res; + + asm ( + "adr %%r1, 1f \n" + "ldr %%r1, [%%r1] \n" + "push { %%r7, %%lr } \n" + "blx %%r1 \n" + "pop { %%r7, %%lr } \n" + "mov %0, %%r0 \n" + "b 2f \n" + + "1: \n" + ".word 0xffff0fe0 \n" + + "2: \n" + :"=r"(res) + : + : "r0", "r1", "memory" + ); + + return res; +} + +#endif diff --git a/arch/arm/include/asm/processor-flags.h b/arch/arm/include/asm/processor-flags.h new file mode 100644 index 000000000..fc00a9e64 --- /dev/null +++ b/arch/arm/include/asm/processor-flags.h @@ -0,0 +1,42 @@ +#ifndef __CR_PROCESSOR_FLAGS_H__ +#define __CR_PROCESSOR_FLAGS_H__ + +/* Copied from the Linux kernel header arch/arm/include/uapi/asm/ptrace.h */ + +/* + * PSR bits + */ +#define USR26_MODE 0x00000000 +#define FIQ26_MODE 0x00000001 +#define IRQ26_MODE 0x00000002 +#define SVC26_MODE 0x00000003 +#define USR_MODE 0x00000010 +#define FIQ_MODE 0x00000011 +#define IRQ_MODE 0x00000012 +#define SVC_MODE 0x00000013 +#define ABT_MODE 0x00000017 +#define UND_MODE 0x0000001b +#define SYSTEM_MODE 0x0000001f +#define MODE32_BIT 0x00000010 +#define MODE_MASK 0x0000001f +#define PSR_T_BIT 0x00000020 +#define PSR_F_BIT 0x00000040 +#define PSR_I_BIT 0x00000080 +#define PSR_A_BIT 0x00000100 +#define PSR_E_BIT 0x00000200 +#define PSR_J_BIT 0x01000000 +#define PSR_Q_BIT 0x08000000 +#define PSR_V_BIT 0x10000000 +#define PSR_C_BIT 0x20000000 +#define PSR_Z_BIT 0x40000000 +#define PSR_N_BIT 0x80000000 + +/* + * Groups of PSR bits + */ +#define PSR_f 0xff000000 /* Flags */ +#define PSR_s 0x00ff0000 /* Status */ +#define PSR_x 0x0000ff00 /* Extension */ +#define PSR_c 0x000000ff /* Control */ + +#endif diff --git a/arch/arm/include/asm/restore.h b/arch/arm/include/asm/restore.h new file mode 100644 index 000000000..c30d91a1d --- /dev/null +++ b/arch/arm/include/asm/restore.h @@ -0,0 +1,25 @@ +#ifndef __CR_ASM_RESTORE_H__ +#define __CR_ASM_RESTORE_H__ + +#define JUMP_TO_RESTORER_BLOB(new_sp, restore_task_exec_start, \ + task_args) \ + asm volatile( \ + "mov %%sp, %%%0 \n" \ + "mov %%r1, %%%1 \n" \ + "mov %%r0, %%%2 \n" \ + "bx %%r1 \n" \ + : \ + : "r"(new_sp), \ + "r"(restore_task_exec_start), \ + "r"(task_args) \ + : "sp", "r0", "r1", "memory") + +static inline void core_get_tls(CoreEntry *pcore, u32 *ptls) +{ + *ptls = pcore->ti_arm->tls; +} + + +int sigreturn_prep_fpu_frame(struct thread_restore_args *args, CoreEntry *core); + +#endif diff --git a/arch/arm/include/asm/restorer.h b/arch/arm/include/asm/restorer.h new file mode 100644 index 000000000..ea9325581 --- /dev/null +++ b/arch/arm/include/asm/restorer.h @@ -0,0 +1,147 @@ +#ifndef __CR_ASM_RESTORER_H__ +#define __CR_ASM_RESTORER_H__ + +#include "asm/types.h" +#include "../protobuf/core.pb-c.h" + +/* Copied from the Linux kernel header arch/arm/include/asm/sigcontext.h */ + +struct rt_sigcontext { + unsigned long trap_no; + unsigned long error_code; + unsigned long oldmask; + unsigned long arm_r0; + unsigned long arm_r1; + unsigned long arm_r2; + unsigned long arm_r3; + unsigned long arm_r4; + unsigned long arm_r5; + unsigned long arm_r6; + unsigned long arm_r7; + unsigned long arm_r8; + unsigned long arm_r9; + unsigned long arm_r10; + unsigned long arm_fp; + unsigned long arm_ip; + unsigned long arm_sp; + unsigned long arm_lr; + unsigned long arm_pc; + unsigned long arm_cpsr; + unsigned long fault_address; +}; + +/* Copied from the Linux kernel header arch/arm/include/asm/ucontext.h */ + +#define VFP_MAGIC 0x56465001 +#define VFP_STORAGE_SIZE sizeof(struct vfp_sigframe) + +struct vfp_sigframe { + unsigned long magic; + unsigned long size; + struct user_vfp ufp; + struct user_vfp_exc ufp_exc; +}; + +struct aux_sigframe { + /* + struct crunch_sigframe crunch; + struct iwmmxt_sigframe iwmmxt; + */ + + struct vfp_sigframe vfp; + unsigned long end_magic; +} __attribute__((__aligned__(8))); + +#include "sigframe.h" + +struct sigframe { + struct rt_ucontext uc; + unsigned long retcode[2]; +}; + +struct rt_sigframe { + struct rt_siginfo info; + struct sigframe sig; +}; + + +#define ARCH_RT_SIGRETURN(new_sp) \ + asm volatile( \ + "mov %%sp, %0 \n" \ + "mov %%r7, #"__stringify(__NR_rt_sigreturn)" \n" \ + "svc #0 \n" \ + : \ + : "r"(new_sp) \ + : "sp","memory") + +#define RUN_CLONE_RESTORE_FN(ret, clone_flags, new_sp, parent_tid, \ + thread_args, clone_restore_fn) \ + asm volatile( \ + "clone_emul: \n" \ + "ldr %%r1, %2 \n" \ + "sub %%r1, #16 \n" \ + "mov %%r0, %%%6 \n" \ + "str %%r0, [%%r1, #4] \n" \ + "mov %%r0, %%%5 \n" \ + "str %%r0, [%%r1] \n" \ + "mov %%r0, %%%1 \n" \ + "mov %%r2, %%%3 \n" \ + "mov %%r3, %%%4 \n" \ + "mov %%r7, #"__stringify(__NR_clone)" \n" \ + "svc #0 \n" \ + \ + "cmp %%r0, #0 \n" \ + "beq thread_run \n" \ + \ + "mov %%%0, %%r0 \n" \ + "b clone_end \n" \ + \ + "thread_run: \n" \ + "pop { %%r1 } \n" \ + "pop { %%r0 } \n" \ + "bx %%r1 \n" \ + \ + "clone_end: \n" \ + : "=r"(ret) \ + : "r"(clone_flags), \ + "m"(new_sp), \ + "r"(&parent_tid), \ + "r"(&thread_args[i].pid), \ + "r"(clone_restore_fn), \ + "r"(&thread_args[i]) \ + : "r0", "r1", "r2", "r3", "r7", "memory") + +#define ARCH_FAIL_CORE_RESTORE \ + asm volatile( \ + "mov %%sp, %0 \n" \ + "mov %%r0, #0 \n" \ + "bx %%r0 \n" \ + : \ + : "r"(ret) \ + : "memory") + + +#define RT_SIGFRAME_UC(rt_sigframe) rt_sigframe->sig.uc + +#define SIGFRAME_OFFSET 0 + + +int restore_gpregs(struct rt_sigframe *f, UserArmRegsEntry *r); + +int restore_fpu(struct rt_sigframe *sigframe, struct thread_restore_args *args); + +static inline void restore_tls(u32 tls) { + asm ( + "mov %%r7, #15 \n" + "lsl %%r7, #16 \n" + "mov %%r0, #5 \n" + "add %%r7, %%r0 \n" /* r7 = 0xF005 */ + "mov %%r0, %0 \n" + "svc #0 \n" + : + : "r"(tls) + : "r0", "r7" + ); +} + +#endif diff --git a/arch/arm/include/asm/syscall-aux.S b/arch/arm/include/asm/syscall-aux.S new file mode 100644 index 000000000..77e13979a --- /dev/null +++ b/arch/arm/include/asm/syscall-aux.S @@ -0,0 +1,9 @@ + ENTRY(sys_mmap) + push { %r4, %r5, %r7 } + ldr %r4, [%sp, #12] + ldr %r5, [%sp, #16] + lsr %r5, #12 + do_sys 192 + pop { %r4, %r5, %r7 } + bx %lr + END(sys_mmap) \ No newline at end of file diff --git a/arch/arm/include/asm/syscall-aux.h b/arch/arm/include/asm/syscall-aux.h new file mode 100644 index 000000000..ec8c2d383 --- /dev/null +++ b/arch/arm/include/asm/syscall-aux.h @@ -0,0 +1,8 @@ +#define __NR_mmap2 192 + +#define __ARM_NR_BASE 0x0f0000 +#define __ARM_NR_breakpoint (__ARM_NR_BASE+1) +#define __ARM_NR_cacheflush (__ARM_NR_BASE+2) +#define __ARM_NR_usr26 (__ARM_NR_BASE+3) +#define __ARM_NR_usr32 (__ARM_NR_BASE+4) +#define __ARM_NR_set_tls (__ARM_NR_BASE+5) diff --git a/arch/arm/include/asm/types.h b/arch/arm/include/asm/types.h new file mode 100644 index 000000000..d068e51c1 --- /dev/null +++ b/arch/arm/include/asm/types.h @@ -0,0 +1,248 @@ +#ifndef __CR_ASM_TYPES_H__ +#define __CR_ASM_TYPES_H__ + +#include +#include +#include +#include "../protobuf/core.pb-c.h" + +#include "asm/bitops.h" + +/* prctl.h */ +#define PR_SET_NAME 15 +#define PR_GET_NAME 16 + +#define PR_CAPBSET_DROP 24 +#define PR_GET_SECUREBITS 27 +#define PR_SET_SECUREBITS 28 + +#define SECURE_NO_SETUID_FIXUP 2 + +#define PR_SET_MM 35 +# define PR_SET_MM_START_CODE 1 +# define PR_SET_MM_END_CODE 2 +# define PR_SET_MM_START_DATA 3 +# define PR_SET_MM_END_DATA 4 +# define PR_SET_MM_START_STACK 5 +# define PR_SET_MM_START_BRK 6 +# define PR_SET_MM_BRK 7 +# define PR_SET_MM_ARG_START 8 +# define PR_SET_MM_ARG_END 9 +# define PR_SET_MM_ENV_START 10 +# define PR_SET_MM_ENV_END 11 +# define PR_SET_MM_AUXV 12 +# define PR_SET_MM_EXE_FILE 13 + +#define PR_GET_TID_ADDRESS 40 + +/* fcntl */ +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif +#ifndef F_SETPIPE_SZ +# define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) +#endif +#ifndef F_GETPIPE_SZ +# define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) +#endif + +#ifndef F_GETOWNER_UIDS +#define F_GETOWNER_UIDS 17 +#endif + +#define CLONE_CHILD_USEPID 0x02000000 +#define CLONE_VFORK 0x00004000 + +#define SIGMAX 64 +#define SIGMAX_OLD 31 + +#define ERESTARTSYS 512 +#define ERESTARTNOINTR 513 +#define ERESTARTNOHAND 514 +#define ERESTART_RESTARTBLOCK 516 + +typedef uint64_t u64; +typedef int64_t s64; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned char u8; +typedef signed char s8; + +#define MAJOR(dev) ((dev)>>8) +#define MINOR(dev) ((dev) & 0xff) + +#define _LINUX_CAPABILITY_VERSION_3 0x20080522 +#define _LINUX_CAPABILITY_U32S_3 2 + + +typedef struct { + unsigned long sig[2]; +} rt_sigset_t; + +typedef void rt_signalfn_t(int, siginfo_t *, void *); +typedef rt_signalfn_t *rt_sighandler_t; + +typedef void rt_restorefn_t(void); +typedef rt_restorefn_t *rt_sigrestore_t; + +typedef struct { + rt_sighandler_t rt_sa_handler; + unsigned long rt_sa_flags; + rt_sigrestore_t rt_sa_restorer; + rt_sigset_t rt_sa_mask; +} rt_sigaction_t; + +#define _KNSIG 64 +#define _NSIG_BPW 32 + +#define _KNSIG_WORDS (_KNSIG / _NSIG_BPW) + +typedef struct { + unsigned long sig[_KNSIG_WORDS]; +} k_rtsigset_t; + +static inline void ksigfillset(k_rtsigset_t *set) +{ + int i; + for (i = 0; i < _KNSIG_WORDS; i++) + set->sig[i] = (unsigned long)-1; +} + + +/* + * Copied from the Linux kernel header arch/arm/include/asm/ptrace.h + * + * A thread ARM CPU context + */ + +typedef struct { + long uregs[18]; +} user_regs_struct_t; + +#define ARM_cpsr uregs[16] +#define ARM_pc uregs[15] +#define ARM_lr uregs[14] +#define ARM_sp uregs[13] +#define ARM_ip uregs[12] +#define ARM_fp uregs[11] +#define ARM_r10 uregs[10] +#define ARM_r9 uregs[9] +#define ARM_r8 uregs[8] +#define ARM_r7 uregs[7] +#define ARM_r6 uregs[6] +#define ARM_r5 uregs[5] +#define ARM_r4 uregs[4] +#define ARM_r3 uregs[3] +#define ARM_r2 uregs[2] +#define ARM_r1 uregs[1] +#define ARM_r0 uregs[0] +#define ARM_ORIG_r0 uregs[17] + + +/* Copied from arch/arm/include/asm/user.h */ + +struct user_vfp { + unsigned long long fpregs[32]; + unsigned long fpscr; +}; + +struct user_vfp_exc { + unsigned long fpexc; + unsigned long fpinst; + unsigned long fpinst2; +}; + +#define ASSIGN_TYPED(a, b) do { a = (typeof(a))b; } while (0) +#define ASSIGN_MEMBER(a,b,m) do { ASSIGN_TYPED((a)->m, (b)->m); } while (0) + +#ifndef PAGE_SIZE +# define PAGE_SIZE 4096 +#endif + +#ifndef PAGE_MASK +# define PAGE_MASK (~(PAGE_SIZE - 1)) +#endif + +/* For sys_kcmp */ +enum kcmp_type { + KCMP_FILE, + KCMP_VM, + KCMP_FILES, + KCMP_FS, + KCMP_SIGHAND, + KCMP_IO, + KCMP_SYSVSEM, + + KCMP_TYPES, +}; + +/* For UNIX sockets data */ +#ifndef SCM_MAX_FD +# define SCM_MAX_FD 253 +#endif + +#include + +#ifndef F_SETOWN_EX +#define F_SETOWN_EX 15 +#define F_GETOWN_EX 16 + +struct f_owner_ex { + int type; + pid_t pid; +}; +#endif + +/* File handle */ +typedef struct { + u32 bytes; + u32 type; + u64 __handle[16]; +} fh_t; + +#ifndef MAP_HUGETLB +# define MAP_HUGETLB 0x40000 +#endif + +#ifndef MADV_HUGEPAGE +# define MADV_HUGEPAGE 14 +#endif + +#ifndef MADV_NOHUGEPAGE +# define MADV_NOHUGEPAGE 15 +#endif + +#ifndef MADV_DONTDUMP +# define MADV_DONTDUMP 16 +#endif + +#define REG_RES(regs) ((regs).ARM_r0) +#define REG_IP(regs) ((regs).ARM_pc) + +#define TASK_SIZE 0xbf000000 + +#define AT_VECTOR_SIZE 40 + +typedef UserArmRegsEntry UserRegsEntry; + +#define CORE_ENTRY__MARCH CORE_ENTRY__MARCH__ARM + +#define CORE_THREAD_ARCH_INFO(core) core->ti_arm + +#define TI_SP(core) ((core)->ti_arm->gpregs->sp) + +typedef uint32_t auxv_t; + +static inline void *decode_pointer(uint64_t v) { return (void*)(uint32_t)v; } +static inline uint64_t encode_pointer(void *p) { return (uint32_t)p; } + +#define BITS_PER_ULONG 32 + +typedef struct { + struct user_vfp ufp; + struct user_vfp_exc ufp_exc; +} fpu_state_t; + +#endif /* __CR_ASM_TYPES_H__ */ diff --git a/arch/arm/parasite-head.S b/arch/arm/parasite-head.S new file mode 100644 index 000000000..b4bffe626 --- /dev/null +++ b/arch/arm/parasite-head.S @@ -0,0 +1,24 @@ +#include "asm/linkage.h" +#include "parasite.h" + + .section .head.text, "ax" +ENTRY(__export_parasite_head_start) + adr %sp, __export_parasite_stack + adr %r0, __export_parasite_cmd + ldr %r0, [%r0] + adr %r1, __export_parasite_args + bl parasite_service + .byte 0xf0, 0x01, 0xf0, 0xe7 @ the instruction UDF #32 generates the signal SIGTRAP in Linux + +__export_parasite_cmd: + .long 0 +__export_parasite_args: + .long 0 + .space PARASITE_ARG_SIZE,0 + .space PARASITE_STACK_SIZE,0 + + .space 228, 0 + +__export_parasite_stack: + .long 0 +END(__export_parasite_head_start) diff --git a/arch/arm/restorer.c b/arch/arm/restorer.c new file mode 100644 index 000000000..bcef36cdf --- /dev/null +++ b/arch/arm/restorer.c @@ -0,0 +1,50 @@ +#include + +#include "restorer.h" +#include "asm/restorer.h" +#include "asm/memcpy_64.h" + +#include "syscall.h" +#include "log.h" +#include "fpu.h" +#include "cpu.h" + +int restore_gpregs(struct rt_sigframe *f, UserArmRegsEntry *r) +{ +#define CPREG1(d) f->sig.uc.uc_mcontext.arm_##d = r->d +#define CPREG2(d, s) f->sig.uc.uc_mcontext.arm_##d = r->s + + CPREG1(r0); + CPREG1(r1); + CPREG1(r2); + CPREG1(r3); + CPREG1(r4); + CPREG1(r5); + CPREG1(r6); + CPREG1(r7); + CPREG1(r8); + CPREG1(r9); + CPREG1(r10); + CPREG1(fp); + CPREG1(ip); + CPREG1(sp); + CPREG1(lr); + CPREG1(pc); + CPREG1(cpsr); + +#undef CPREG1 +#undef CPREG2 + + return 0; +} + +int restore_fpu(struct rt_sigframe *sigframe, struct thread_restore_args *args) +{ + struct aux_sigframe *aux = (struct aux_sigframe *)&sigframe->sig.uc.uc_regspace; + + aux->vfp.magic = VFP_MAGIC; + aux->vfp.size = VFP_STORAGE_SIZE; + builtin_memcpy(&aux->vfp.ufp, &args->fpu_state.ufp, sizeof(aux->vfp.ufp)); + + return 0; +} diff --git a/arch/arm/syscall-common.S b/arch/arm/syscall-common.S new file mode 100644 index 000000000..d5dd18bdc --- /dev/null +++ b/arch/arm/syscall-common.S @@ -0,0 +1,52 @@ +#include "asm/linkage.h" + + .macro mov_r7 imm + mov %r7, #\imm + .endm + + + // Call the kernel + + .macro do_sys opcode + movw %r7, #\opcode + svc #0 + .endm + + + // a syscall with 0-4 arguments + + .macro syscall0 name, opcode + ENTRY(\name) + push { %r7 } + do_sys \opcode + pop { %r7 } + bx %lr + END(\name) + .endm + + + // a syscall with 5 arguments + + .macro syscall5 name, opcode + ENTRY(\name) + push { %r4, %r7 } + ldr %r4, [%sp, #8] + do_sys \opcode + pop { %r4, %r7 } + bx %lr + END(\name) + .endm + + + // a syscall with 6 arguments + + .macro syscall6 name, opcode + ENTRY(\name) + push { %r4, %r5, %r7 } + ldr %r4, [%sp, #12] + ldr %r5, [%sp, #16] + do_sys \opcode + pop { %r4, %r5, %r7 } + bx %lr + END(\name) + .endm diff --git a/arch/arm/syscall.def b/arch/arm/syscall.def new file mode 100644 index 000000000..ce175f357 --- /dev/null +++ b/arch/arm/syscall.def @@ -0,0 +1,78 @@ +# +# System calls table, please make sure the table consist only the syscalls +# really used somewhere in project. +# +# The template is (name and arguments are optinal if you need only __NR_x +# defined, but no realy entry point in syscalls lib). +# +# name/alias code code32 arguments +# ----------------------------------------------------------------------- +# +read 0 3 (int fd, void *buf, unsigned long count) +write 1 4 (int fd, const void *buf, unsigned long count) +open 2 5 (const char *filename, unsigned long flags, unsigned long mode) +close 3 6 (int fd) +lseek 8 19 (int fd, unsigned long offset, unsigned long origin) +mmap 9 ! (void *addr, unsigned long len, unsigned long prot, unsigned long flags, unsigned long fd, unsigned long offset) +mprotect 10 125 (const void *addr, unsigned long len, unsigned long prot) +munmap 11 91 (void *addr, unsigned long len) +brk 12 45 (void *addr) +rt_sigaction sigaction 13 174 (int signum, const rt_sigaction_t *act, rt_sigaction_t *oldact, size_t sigsetsize) +rt_sigprocmask sigprocmask 14 175 (int how, k_rtsigset_t *set, k_rtsigset_t *old, size_t sigsetsize) +rt_sigreturn 15 173 (void) +mincore 27 219 (void *addr, unsigned long size, unsigned char *vec) +ioctl 16 54 (unsigned int fd, unsigned int cmd, unsigned long arg) +mremap 25 163 (unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flag, unsigned long new_addr) +madvise 28 220 (unsigned long start, size_t len, int behavior) +shmat 30 305 (int shmid, void *shmaddr, int shmflag) +pause 34 29 (void) +nanosleep 35 162 (struct timespec *req, struct timespec *rem) +getitimer 36 105 (int which, const struct itimerval *val) +setitimer 38 104 (int which, const struct itimerval *val, struct itimerval *old) +getpid 39 20 (void) +socket 41 281 (int domain, int type, int protocol) +connect 42 283 (int sockfd, struct sockaddr *addr, int addrlen) +sendmsg 46 296 (int sockfd, const struct msghdr *msg, int flags) +recvmsg 47 297 (int sockfd, struct msghdr *msg, int flags) +bind 49 282 (int sockfd, const struct sockaddr *addr, int addrlen) +setsockopt 54 294 (int sockfd, int level, int optname, const void *optval, socklen_t optlen) +getsockopt 55 295 (int sockfd, int level, int optname, const void *optval, socklen_t *optlen) +clone 56 120 (unsigned long flags, void *child_stack, void *parent_tid, void *child_tid) +exit 60 1 (unsigned long error_code) +wait4 61 114 (int pid, int *status, int options, struct rusage *ru) +kill 62 37 (long pid, int sig) +fcntl 72 55 (int fd, int type, long arg) +flock 73 143 (int fd, unsigned long cmd) +mkdir 83 39 (const char *name, int mode) +rmdir 84 40 (const char *name) +unlink 87 10 (char *pathname) +readlink 89 85 (const char *path, char *buf, int bufsize) +umask 95 60 (int mask) +getgroups 115 205 (int gsize, unsigned int *groups) +setresuid 117 164 (int uid, int euid, int suid) +setresgid 119 170 (int gid, int egid, int sgid) +getpgid 121 132 (pid_t pid) +setfsuid 122 138 (int fsuid) +setfsgid 123 139 (int fsgid) +getsid 124 147 (void) +capset 126 185 (struct cap_header *h, struct cap_data *d) +setpriority 141 97 (int which, int who, int nice) +sched_setscheduler 144 156 (int pid, int policy, struct sched_param *p) +personality 135 136 (unsigned int personality) +prctl 157 172 (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) +arch_prctl 158 17 (int option, unsigned long addr) +setrlimit 160 75 (int resource, struct krlimit *rlim) +mount 165 21 (char *dev_nmae, char *dir_name, char *type, unsigned long flags, void *data) +umount2 166 52 (char *name, int flags) +gettid 186 224 (void) +futex 202 240 (u32 *uaddr, int op, u32 val, struct timespec *utime, u32 *uaddr2, u32 val3) +set_tid_address 218 256 (int *tid_addr) +restart_syscall 219 0 (void) +exit_group 231 248 (int error_code) +set_robust_list 273 338 (struct robust_list_head *head, size_t len) +get_robust_list 274 339 (int pid, struct robust_list_head **head_ptr, size_t *len_ptr) +fanotify_init 300 367 (unsigned int flags, unsigned int event_f_flags) +fanotify_mark 301 368 (int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname) +open_by_handle_at 304 371 (int mountdirfd, struct file_handle *handle, int flags) +setns 308 375 (int fd, int nstype) +kcmp 312 378 (pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2) diff --git a/arch/arm/uidiv.S b/arch/arm/uidiv.S new file mode 100644 index 000000000..e77f6100c --- /dev/null +++ b/arch/arm/uidiv.S @@ -0,0 +1,186 @@ +.globl __aeabi_uidiv + +work .req r4 @ XXXX is this safe ? +dividend .req r0 +divisor .req r1 +overdone .req r2 +result .req r2 +curbit .req r3 + +#define LSYM(x) x + +.macro THUMB_DIV_MOD_BODY modulo + @ Load the constant 0x10000000 into our work register. + mov work, #1 + lsl work, #28 +LSYM(Loop1): + @ Unless the divisor is very big, shift it up in multiples of + @ four bits, since this is the amount of unwinding in the main + @ division loop. Continue shifting until the divisor is + @ larger than the dividend. + cmp divisor, work + bhs LSYM(Lbignum) + cmp divisor, dividend + bhs LSYM(Lbignum) + lsl divisor, #4 + lsl curbit, #4 + b LSYM(Loop1) +LSYM(Lbignum): + @ Set work to 0x80000000 + lsl work, #3 +LSYM(Loop2): + @ For very big divisors, we must shift it a bit at a time, or + @ we will be in danger of overflowing. + cmp divisor, work + bhs LSYM(Loop3) + cmp divisor, dividend + bhs LSYM(Loop3) + lsl divisor, #1 + lsl curbit, #1 + b LSYM(Loop2) +LSYM(Loop3): + @ Test for possible subtractions ... + .if \modulo + @ ... On the final pass, this may subtract too much from the dividend, + @ so keep track of which subtractions are done, we can fix them up + @ afterwards. + mov overdone, #0 + cmp dividend, divisor + blo LSYM(Lover1) + sub dividend, dividend, divisor +LSYM(Lover1): + lsr work, divisor, #1 + cmp dividend, work + blo LSYM(Lover2) + sub dividend, dividend, work + mov ip, curbit + mov work, #1 + ror curbit, work + orr overdone, curbit + mov curbit, ip +LSYM(Lover2): + lsr work, divisor, #2 + cmp dividend, work + blo LSYM(Lover3) + sub dividend, dividend, work + mov ip, curbit + mov work, #2 + ror curbit, work + orr overdone, curbit + mov curbit, ip +LSYM(Lover3): + lsr work, divisor, #3 + cmp dividend, work + blo LSYM(Lover4) + sub dividend, dividend, work + mov ip, curbit + mov work, #3 + ror curbit, work + orr overdone, curbit + mov curbit, ip +LSYM(Lover4): + mov ip, curbit + .else + @ ... and note which bits are done in the result. On the final pass, + @ this may subtract too much from the dividend, but the result will be ok, + @ since the "bit" will have been shifted out at the bottom. + cmp dividend, divisor + blo LSYM(Lover1) + sub dividend, dividend, divisor + orr result, result, curbit +LSYM(Lover1): + lsr work, divisor, #1 + cmp dividend, work + blo LSYM(Lover2) + sub dividend, dividend, work + lsr work, curbit, #1 + orr result, work +LSYM(Lover2): + lsr work, divisor, #2 + cmp dividend, work + blo LSYM(Lover3) + sub dividend, dividend, work + lsr work, curbit, #2 + orr result, work +LSYM(Lover3): + lsr work, divisor, #3 + cmp dividend, work + blo LSYM(Lover4) + sub dividend, dividend, work + lsr work, curbit, #3 + orr result, work +LSYM(Lover4): + .endif + + cmp dividend, #0 @ Early termination? + beq LSYM(Lover5) + lsr curbit, #4 @ No, any more bits to do? + beq LSYM(Lover5) + lsr divisor, #4 + b LSYM(Loop3) +LSYM(Lover5): + .if \modulo + @ Any subtractions that we should not have done will be recorded in + @ the top three bits of "overdone". Exactly which were not needed + @ are governed by the position of the bit, stored in ip. + mov work, #0xe + lsl work, #28 + and overdone, work + beq LSYM(Lgot_result) + + @ If we terminated early, because dividend became zero, then the + @ bit in ip will not be in the bottom nibble, and we should not + @ perform the additions below. We must test for this though + @ (rather relying upon the TSTs to prevent the additions) since + @ the bit in ip could be in the top two bits which might then match + @ with one of the smaller RORs. + mov curbit, ip + mov work, #0x7 + tst curbit, work + beq LSYM(Lgot_result) + + mov curbit, ip + mov work, #3 + ror curbit, work + tst overdone, curbit + beq LSYM(Lover6) + lsr work, divisor, #3 + add dividend, work +LSYM(Lover6): + mov curbit, ip + mov work, #2 + ror curbit, work + tst overdone, curbit + beq LSYM(Lover7) + lsr work, divisor, #2 + add dividend, work +LSYM(Lover7): + mov curbit, ip + mov work, #1 + ror curbit, work + tst overdone, curbit + beq LSYM(Lgot_result) + lsr work, divisor, #1 + add dividend, work + .endif +LSYM(Lgot_result): +.endm + + + .thumb + .text + +__aeabi_uidiv: + mov curbit, #1 + mov result, #0 + + push { work } + cmp dividend, divisor + blo LSYM(Lgot_result) + + THUMB_DIV_MOD_BODY 0 + + mov r0, result + pop { work } + + bx lr diff --git a/test/zdtm/lib/arch/arm/include/asm/atomic.h b/test/zdtm/lib/arch/arm/include/asm/atomic.h new file mode 100644 index 000000000..6d91ceb16 --- /dev/null +++ b/test/zdtm/lib/arch/arm/include/asm/atomic.h @@ -0,0 +1,66 @@ +#ifndef __CR_ATOMIC_H__ +#define __CR_ATOMIC_H__ + + +typedef uint32_t atomic_t; + + +/* Copied from the Linux kernel header arch/arm/include/asm/atomic.h */ + +#define smp_mb() __asm__ __volatile__ ("dmb" : : : "memory") + +#define atomic_set(mem,v) (*(mem) = (v)) +#define atomic_get(v) (*(volatile uint32_t *)v) + +static inline unsigned int atomic_add_return(int i, atomic_t *v) +{ + unsigned long tmp; + unsigned int result; + + smp_mb(); + + __asm__ __volatile__("@ atomic_add_return\n" +"1: ldrex %0, [%3]\n" +" add %0, %0, %4\n" +" strex %1, %0, [%3]\n" +" teq %1, #0\n" +" bne 1b\n" + : "=&r" (result), "=&r" (tmp), "+Qo" (v) + : "r" (&v), "Ir" (i) + : "cc"); + + smp_mb(); + + return result; +} + +static inline unsigned int atomic_sub_return(int i, atomic_t *v) +{ + unsigned long tmp; + int result; + + smp_mb(); + + __asm__ __volatile__("@ atomic_sub_return\n" +"1: ldrex %0, [%3]\n" +" sub %0, %0, %4\n" +" strex %1, %0, [%3]\n" +" teq %1, #0\n" +" bne 1b\n" + : "=&r" (result), "=&r" (tmp), "+Qo" (v) + : "r" (&v), "Ir" (i) + : "cc"); + + smp_mb(); + + return result; +} + +static inline unsigned int atomic_inc(atomic_t *v) { return atomic_add_return(1, v) - 1; } + +static inline unsigned int atomic_dec(atomic_t *v) { return atomic_sub_return(1, v) + 1; } + +/* true if the result is 0, or false for all other cases. */ +#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0) + +#endif /* __CR_ATOMIC_H__ */