Commit 8fa8ca9f authored by Alexander Kartashov's avatar Alexander Kartashov Committed by Pavel Emelyanov

arm: added ARM-specific files modelled after arch/x86

Signed-off-by: 's avatarAlexander Kartashov <alekskartashov@parallels.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent a0c86a25
......@@ -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)
......
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
#include <string.h>
#include <unistd.h>
#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, &regs);
err = __parasite_execute(ctl, ctl->pid, &regs);
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, &regs)) {
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;
}
#!/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 (<IN>) {
if ($_ =~ /\#/) {
next;
}
my $sys_name;
my $sys_num;
if (/(?<name>\S+)\s+(?<alias>\S+)\s+(?<code64>\d+)\s+(?<code32>(?:\d+|\!))\s+\((?<args>.+)\)/) {
$sys_name = $+{alias};
} elsif (/(?<name>\S+)\s+(?<code64>\d+)\s+(?<code32>(?:\d+|\!))\s+\((?<args>.+)\)/) {
$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";
}
}
#!/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 (<IN>) {
if ($_ =~ /\#/) {
next;
}
my $code_macro;
my $sys_name;
if (/(?<name>\S+)\s+(?<alias>\S+)\s+(?<code64>\d+)\s+(?<code32>(?:\d+|\!))\s+\((?<args>.+)\)/) {
$code_macro = "__NR_$+{name}";
$sys_name = "sys_$+{alias}";
} elsif (/(?<name>\S+)\s+(?<code64>\d+)\s+(?<code32>(?:\d+|\!))\s+\((?<args>.+)\)/) {
$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 */";
#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__ */
#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__ */
#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
#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__ */
#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
#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
#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
#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
#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
#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
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
#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)
#ifndef __CR_ASM_TYPES_H__
#define __CR_ASM_TYPES_H__
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#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 <fcntl.h>
#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__ */
#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)
#include <unistd.h>
#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;
}
#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
#
# 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)
.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
#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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment