* [Qemu-devel] [PATCH] arm eabi TLS @ 2007-12-12 0:44 Thayne Harbaugh 2007-12-12 1:29 ` Paul Brook 2007-12-12 23:03 ` Fabrice Bellard 0 siblings, 2 replies; 11+ messages in thread From: Thayne Harbaugh @ 2007-12-12 0:44 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: text/plain, Size: 336 bytes --] I believe Paul Brook did the original patch for arm eabi TLS. The patch has bounced around for a bit but hasn't been applied. We've been using this patch for a while and have tweaked it to be a bit more correct as far as code organization. Please let me know what else should be improved for this so that it can be applied. Thanks. [-- Attachment #2: 09_arm_eabitls.patch --] [-- Type: text/x-patch, Size: 27912 bytes --] Index: qemu/configure =================================================================== --- qemu.orig/configure 2007-12-11 10:16:49.000000000 -0700 +++ qemu/configure 2007-12-11 10:16:50.000000000 -0700 @@ -105,6 +105,7 @@ darwin_user="no" build_docs="no" uname_release="" +nptl="yes" # OS specific targetos=`uname -s` @@ -318,6 +319,8 @@ ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; + --disable-nptl) nptl="no" + ;; esac done @@ -413,6 +416,7 @@ echo " --disable-linux-user disable all linux usermode emulation targets" echo " --enable-darwin-user enable all darwin usermode emulation targets" echo " --disable-darwin-user disable all darwin usermode emulation targets" +echo " --disable-nptl disable usermode NPTL guest support" echo " --fmod-lib path to FMOD library" echo " --fmod-inc path to FMOD includes" echo " --enable-uname-release=R Return R for uname -r in usermode emulation" @@ -579,6 +583,23 @@ } EOF +# check NPTL support +cat > $TMPC <<EOF +#include <sched.h> +void foo() +{ +#ifndef CLONE_SETTLS +#error bork +#endif +} +EOF + +if $cc -c -o $TMPO $TMPC 2> /dev/null ; then + : +else + nptl="no" +fi + ########################################## # SDL probe @@ -743,6 +764,7 @@ echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ echo "uname -r $uname_release" +echo "NPTL support $nptl" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -948,6 +970,7 @@ echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak fi fi + if test "$cocoa" = "yes" ; then echo "#define CONFIG_COCOA 1" >> $config_h echo "CONFIG_COCOA=yes" >> $config_mak @@ -1183,6 +1206,15 @@ echo "#define TARGET_HAS_ELFLOAD32 1" >> $config_h fi +if test "$nptl" = "yes" ; then + case "$target_cpu" in + arm | armeb | ppc | ppc64) + echo "USE_NPTL=yes" >> $config_mak + echo "#define USE_NPTL 1" >> $config_h + ;; + esac +fi + test -f ${config_h}~ && cmp -s $config_h ${config_h}~ && mv ${config_h}~ $config_h done # for target in $targets Index: qemu/exec-all.h =================================================================== --- qemu.orig/exec-all.h 2007-12-11 10:16:49.000000000 -0700 +++ qemu/exec-all.h 2007-12-11 10:16:50.000000000 -0700 @@ -340,170 +340,7 @@ extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; -#if defined(__powerpc__) -static inline int testandset (int *p) -{ - int ret; - __asm__ __volatile__ ( - "0: lwarx %0,0,%1\n" - " xor. %0,%3,%0\n" - " bne 1f\n" - " stwcx. %2,0,%1\n" - " bne- 0b\n" - "1: " - : "=&r" (ret) - : "r" (p), "r" (1), "r" (0) - : "cr0", "memory"); - return ret; -} -#elif defined(__i386__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__x86_64__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__s390__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" - " jl 0b" - : "=&d" (ret) - : "r" (1), "a" (p), "0" (*p) - : "cc", "memory" ); - return ret; -} -#elif defined(__alpha__) -static inline int testandset (int *p) -{ - int ret; - unsigned long one; - - __asm__ __volatile__ ("0: mov 1,%2\n" - " ldl_l %0,%1\n" - " stl_c %2,%1\n" - " beq %2,1f\n" - ".subsection 2\n" - "1: br 0b\n" - ".previous" - : "=r" (ret), "=m" (*p), "=r" (one) - : "m" (*p)); - return ret; -} -#elif defined(__sparc__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__("ldstub [%1], %0" - : "=r" (ret) - : "r" (p) - : "memory"); - - return (ret ? 1 : 0); -} -#elif defined(__arm__) -static inline int testandset (int *spinlock) -{ - register unsigned int ret; - __asm__ __volatile__("swp %0, %1, [%2]" - : "=r"(ret) - : "0"(1), "r"(spinlock)); - - return ret; -} -#elif defined(__mc68000) -static inline int testandset (int *p) -{ - char ret; - __asm__ __volatile__("tas %1; sne %0" - : "=r" (ret) - : "m" (p) - : "cc","memory"); - return ret; -} -#elif defined(__ia64) - -#include <ia64intrin.h> - -static inline int testandset (int *p) -{ - return __sync_lock_test_and_set (p, 1); -} -#elif defined(__mips__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ( - " .set push \n" - " .set noat \n" - " .set mips2 \n" - "1: li $1, 1 \n" - " ll %0, %1 \n" - " sc $1, %1 \n" - " beqz $1, 1b \n" - " .set pop " - : "=r" (ret), "+R" (*p) - : - : "memory"); - - return ret; -} -#else -#error unimplemented CPU support -#endif - -typedef int spinlock_t; - -#define SPIN_LOCK_UNLOCKED 0 - -#if defined(CONFIG_USER_ONLY) -static inline void spin_lock(spinlock_t *lock) -{ - while (testandset(lock)); -} - -static inline void spin_unlock(spinlock_t *lock) -{ - *lock = 0; -} - -static inline int spin_trylock(spinlock_t *lock) -{ - return !testandset(lock); -} -#else -static inline void spin_lock(spinlock_t *lock) -{ -} - -static inline void spin_unlock(spinlock_t *lock) -{ -} - -static inline int spin_trylock(spinlock_t *lock) -{ - return 1; -} -#endif +#include "qspinlock.h" extern spinlock_t tb_lock; Index: qemu/linux-user/arm/syscall.h =================================================================== --- qemu.orig/linux-user/arm/syscall.h 2007-12-11 10:16:49.000000000 -0700 +++ qemu/linux-user/arm/syscall.h 2007-12-11 10:16:50.000000000 -0700 @@ -28,7 +28,9 @@ #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 -#define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2) +#define ARM_NR_BASE 0xf0000 +#define ARM_NR_cacheflush (ARM_NR_BASE + 2) +#define ARM_NR_set_tls (ARM_NR_BASE + 5) #define ARM_NR_semihosting 0x123456 #define ARM_NR_thumb_semihosting 0xAB Index: qemu/linux-user/main.c =================================================================== --- qemu.orig/linux-user/main.c 2007-12-11 10:16:49.000000000 -0700 +++ qemu/linux-user/main.c 2007-12-11 10:16:50.000000000 -0700 @@ -363,6 +363,50 @@ } } +/* Handle a jump to the kernel code page. */ +static int +do_kernel_trap(CPUARMState *env) +{ + uint32_t addr; + uint32_t *ptr; + uint32_t cpsr; + + switch (env->regs[15]) { + case 0xffff0fc0: /* __kernel_cmpxchg */ + /* XXX: This only works between threads, not between processes. + Use native atomic operations. */ + /* ??? This probably breaks horribly if the access segfaults. */ + cpu_lock(); + ptr = (uint32_t *)env->regs[2]; + cpsr = cpsr_read(env); + if (*ptr == env->regs[0]) { + *ptr = env->regs[1]; + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C); + cpu_unlock(); + break; + case 0xffff0fe0: /* __kernel_get_tls */ + env->regs[0] = env->cp15.c13_tls3; + break; + default: + return 1; + } + /* Jump back to the caller. */ + addr = env->regs[14]; + if (addr & 1) { + env->thumb = 1; + addr &= ~1; + } + env->regs[15] = addr; + + return 0; +} + void cpu_loop(CPUARMState *env) { int trapnr; @@ -424,10 +468,8 @@ } } - if (n == ARM_NR_cacheflush) { - arm_cache_flush(env->regs[0], env->regs[1]); - } else if (n == ARM_NR_semihosting - || n == ARM_NR_thumb_semihosting) { + if (n == ARM_NR_semihosting + || n == ARM_NR_thumb_semihosting) { env->regs[0] = do_arm_semihosting (env); } else if (n == 0 || n >= ARM_SYSCALL_BASE || (env->thumb && n == ARM_THUMB_SYSCALL)) { @@ -438,6 +480,26 @@ n -= ARM_SYSCALL_BASE; env->eabi = 0; } + if (n > ARM_NR_BASE) { + switch (n) + { + case ARM_NR_cacheflush: + arm_cache_flush(env->regs[0], env->regs[1]); + break; +#ifdef USE_NPTL + case ARM_NR_set_tls: + cpu_set_tls(env, env->regs[0]); + env->regs[0] = 0; + break; +#endif + default: + printf ("Error: Bad syscall: %x\n", n); + env->regs[0] = -TARGET_ENOSYS; + goto error; + } + } + else + { env->regs[0] = do_syscall(env, n, env->regs[0], @@ -446,7 +508,9 @@ env->regs[3], env->regs[4], env->regs[5]); + } } else { + printf ("Error: Bad syscall: %x\n", n); goto error; } } @@ -484,6 +548,10 @@ } } break; + case EXCP_KERNEL_TRAP: + if (do_kernel_trap(env)) + goto error; + break; default: error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", @@ -2348,6 +2416,10 @@ ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; + /* Register the magic kernel code page. The cpu will generate a + special exception when it tries to execute code here. We can't + put real code here because it may be in use by the host kernel. */ + page_set_flags(0xffff0000, 0xffff0fff, 0); #endif if (gdbstub_port) { Index: qemu/linux-user/qemu.h =================================================================== --- qemu.orig/linux-user/qemu.h 2007-12-11 10:16:49.000000000 -0700 +++ qemu/linux-user/qemu.h 2007-12-11 10:16:50.000000000 -0700 @@ -104,6 +104,9 @@ #endif int used; /* non zero if used */ struct image_info *info; +#ifdef USE_NPTL + abi_ulong child_tid_addr; +#endif uint8_t stack[0]; } __attribute__((aligned(16))) TaskState; Index: qemu/linux-user/syscall.c =================================================================== --- qemu.orig/linux-user/syscall.c 2007-12-11 10:16:49.000000000 -0700 +++ qemu/linux-user/syscall.c 2007-12-11 10:16:50.000000000 -0700 @@ -71,9 +71,18 @@ #include <linux/kd.h> #include "qemu.h" +#include "qspinlock.h" //#define DEBUG +#ifdef USE_NPTL +#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ + CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) +#else +/* XXX: Hardcode the above values. */ +#define CLONE_NPTL_FLAGS2 0 +#endif + #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) /* 16 bit uid wrappers emulation */ @@ -2797,9 +2806,19 @@ thread/process */ #define NEW_STACK_SIZE 8192 +#ifdef USE_NPTL +static spinlock_t nptl_lock = SPIN_LOCK_UNLOCKED; +#endif + static int clone_func(void *arg) { CPUState *env = arg; +#ifdef USE_NPTL + /* Wait until the parent has finshed initializing the tls state. */ + while (!spin_trylock(&nptl_lock)) + usleep(1); + spin_unlock(&nptl_lock); +#endif cpu_loop(env); /* never exits */ return 0; @@ -2807,12 +2826,24 @@ /* do_fork() Must return host values and target errnos (unlike most do_*() functions). */ -int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) +int do_fork(CPUState *env, + unsigned int flags, + abi_ulong new_stk_addr, + abi_ulong parent_tid_addr, + abi_ulong newtls_addr, + abi_ulong child_tid_addr) { int ret; TaskState *ts; uint8_t *new_stack; CPUState *new_env; +#ifdef USE_NPTL + unsigned int nptl_flags; + + if (flags & CLONE_PARENT_SETTID) + if (put_user_s32(gettid(), parent_tid_addr)) + return -TARGET_EFAULT; +#endif if (flags & CLONE_VM) { ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); @@ -2824,51 +2855,37 @@ first_task_state = ts; /* we create a new CPU instance. */ new_env = cpu_copy(env); + if (!new_stk_addr) + new_stk_addr = get_sp_from_cpustate(env); #if defined(TARGET_I386) - if (!newsp) - newsp = env->regs[R_ESP]; - new_env->regs[R_ESP] = newsp; + new_env->regs[R_ESP] = new_stk_addr; new_env->regs[R_EAX] = 0; #elif defined(TARGET_ARM) - if (!newsp) - newsp = env->regs[13]; - new_env->regs[13] = newsp; + new_env->regs[13] = new_stk_addr; new_env->regs[0] = 0; #elif defined(TARGET_SPARC) - if (!newsp) - newsp = env->regwptr[22]; - new_env->regwptr[22] = newsp; + new_env->regwptr[22] = new_stk_addr; new_env->regwptr[0] = 0; /* XXXXX */ printf ("HELPME: %s:%d\n", __FILE__, __LINE__); #elif defined(TARGET_M68K) - if (!newsp) - newsp = env->aregs[7]; - new_env->aregs[7] = newsp; + new_env->aregs[7] = new_stk_addr; new_env->dregs[0] = 0; /* ??? is this sufficient? */ #elif defined(TARGET_MIPS) - if (!newsp) - newsp = env->gpr[29][env->current_tc]; - new_env->gpr[29][env->current_tc] = newsp; + new_env->gpr[29][env->current_tc] = new_stk_addr; #elif defined(TARGET_PPC) - if (!newsp) - newsp = env->gpr[1]; - new_env->gpr[1] = newsp; + new_env->gpr[1] = new_stk_addr; { int i; for (i = 7; i < 32; i++) new_env->gpr[i] = 0; } #elif defined(TARGET_SH4) - if (!newsp) - newsp = env->gregs[15]; - new_env->gregs[15] = newsp; + new_env->gregs[15] = new_stk_addr; /* XXXXX */ #elif defined(TARGET_ALPHA) - if (!newsp) - newsp = env->ir[30]; - new_env->ir[30] = newsp; + new_env->ir[30] = new_stk_addr; /* ? */ { int i; @@ -2876,23 +2893,71 @@ new_env->ir[i] = 0; } #elif defined(TARGET_CRIS) - if (!newsp) - newsp = env->regs[14]; - new_env->regs[14] = newsp; + new_env->regs[14] = new_stk_addr; #else #error unsupported target CPU #endif new_env->opaque = ts; +#ifdef USE_NPTL + nptl_flags = flags; + flags &= ~CLONE_NPTL_FLAGS2; + + if (nptl_flags & CLONE_CHILD_CLEARTID) { + ts->child_tid_addr = child_tid_addr; + } + + if (nptl_flags & CLONE_SETTLS) + cpu_set_tls(new_env, newtls_addr); + + /* Grab the global cpu lock so that the thread setup appears + atomic. */ + if (nptl_flags & CLONE_CHILD_SETTID) + spin_lock(&nptl_lock); + +#else + if (flags & CLONE_NPTL_FLAGS2) + return -EINVAL; +#endif #ifdef __ia64__ ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); #else ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); #endif +#ifdef USE_NPTL + if (ret != -1) { + if (nptl_flags & CLONE_CHILD_SETTID) + if (put_user_s32(ret, child_tid_addr)) + return -TARGET_EFAULT; + } + + /* Allow the child to continue. */ + if (nptl_flags & CLONE_CHILD_SETTID) + spin_unlock(&nptl_lock); +#endif } else { /* if no CLONE_VM, we consider it is a fork */ - if ((flags & ~CSIGNAL) != 0) + if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) return -EINVAL; ret = fork(); +#ifdef USE_NPTL + /* There is a race condition here. The parent process could + theoretically read the TID in the child process before the child + tid is set. This would require using either ptrace + (not implemented) or having *_tidptr to point at a shared memory + mapping. We can't repeat the spinlock hack used above because + the child process gets its own copy of the lock. */ + if (ret == 0) { + /* Child Process. */ + if (flags & CLONE_CHILD_SETTID) + if (put_user_s32(gettid(), child_tid_addr)) + return -TARGET_EFAULT; + ts = (TaskState *)env->opaque; + if (flags & CLONE_CHILD_CLEARTID) + ts->child_tid_addr = child_tid_addr; + if (flags & CLONE_SETTLS) + cpu_set_tls(env, newtls_addr); + } +#endif } return ret; } @@ -3370,7 +3435,7 @@ ret = do_brk(arg1); break; case TARGET_NR_fork: - ret = get_errno(do_fork(cpu_env, SIGCHLD, 0)); + ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0)); break; #ifdef TARGET_NR_waitpid case TARGET_NR_waitpid: @@ -4726,7 +4791,7 @@ ret = get_errno(fsync(arg1)); break; case TARGET_NR_clone: - ret = get_errno(do_fork(cpu_env, arg1, arg2)); + ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5)); break; #ifdef __NR_exit_group /* new thread calls */ @@ -5152,7 +5217,9 @@ #endif #ifdef TARGET_NR_vfork case TARGET_NR_vfork: - ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0)); + ret = get_errno(do_fork(cpu_env, + CLONE_VFORK | CLONE_VM | SIGCHLD, + 0, 0, 0, 0)); break; #endif #ifdef TARGET_NR_ugetrlimit Index: qemu/qspinlock.h =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/qspinlock.h 2007-12-11 10:16:50.000000000 -0700 @@ -0,0 +1,188 @@ +/* + * Atomic operation helper include + * + * Copyright (c) 2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef QSPINLOCK_H +#define QSPINLOCK_H + +#if defined(__powerpc__) +static inline int testandset (int *p) +{ + int ret; + __asm__ __volatile__ ( + "0: lwarx %0,0,%1\n" + " xor. %0,%3,%0\n" + " bne 1f\n" + " stwcx. %2,0,%1\n" + " bne- 0b\n" + "1: " + : "=&r" (ret) + : "r" (p), "r" (1), "r" (0) + : "cr0", "memory"); + return ret; +} +#elif defined(__i386__) +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#elif defined(__x86_64__) +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#elif defined(__s390__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (ret) + : "r" (1), "a" (p), "0" (*p) + : "cc", "memory" ); + return ret; +} +#elif defined(__alpha__) +static inline int testandset (int *p) +{ + int ret; + unsigned long one; + + __asm__ __volatile__ ("0: mov 1,%2\n" + " ldl_l %0,%1\n" + " stl_c %2,%1\n" + " beq %2,1f\n" + ".subsection 2\n" + "1: br 0b\n" + ".previous" + : "=r" (ret), "=m" (*p), "=r" (one) + : "m" (*p)); + return ret; +} +#elif defined(__sparc__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__("ldstub [%1], %0" + : "=r" (ret) + : "r" (p) + : "memory"); + + return (ret ? 1 : 0); +} +#elif defined(__arm__) +static inline int testandset (int *spinlock) +{ + register unsigned int ret; + __asm__ __volatile__("swp %0, %1, [%2]" + : "=r"(ret) + : "0"(1), "r"(spinlock)); + + return ret; +} +#elif defined(__mc68000) +static inline int testandset (int *p) +{ + char ret; + __asm__ __volatile__("tas %1; sne %0" + : "=r" (ret) + : "m" (p) + : "cc","memory"); + return ret; +} +#elif defined(__ia64) + +#include <ia64intrin.h> + +static inline int testandset (int *p) +{ + return __sync_lock_test_and_set (p, 1); +} +#elif defined(__mips__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__ ( + " .set push \n" + " .set noat \n" + " .set mips2 \n" + "1: li $1, 1 \n" + " ll %0, %1 \n" + " sc $1, %1 \n" + " beqz $1, 1b \n" + " .set pop " + : "=r" (ret), "+R" (*p) + : + : "memory"); + + return ret; +} +#else +#error unimplemented CPU support +#endif + +typedef int spinlock_t; + +#define SPIN_LOCK_UNLOCKED 0 + +#if defined(CONFIG_USER_ONLY) +static inline void spin_lock(spinlock_t *lock) +{ + while (testandset(lock)); +} + +static inline void spin_unlock(spinlock_t *lock) +{ + *lock = 0; +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return !testandset(lock); +} +#else +static inline void spin_lock(spinlock_t *lock) +{ +} + +static inline void spin_unlock(spinlock_t *lock) +{ +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return 1; +} +#endif + +#endif /* QSPINLOCK_H */ Index: qemu/target-arm/cpu.h =================================================================== --- qemu.orig/target-arm/cpu.h 2007-12-11 10:16:49.000000000 -0700 +++ qemu/target-arm/cpu.h 2007-12-11 10:16:50.000000000 -0700 @@ -37,7 +37,11 @@ #define EXCP_IRQ 5 #define EXCP_FIQ 6 #define EXCP_BKPT 7 +#if defined(CONFIG_USER_ONLY) +#define EXCP_KERNEL_TRAP 8 /* Jumped to kernel code page. */ +#else #define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */ +#endif #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -221,6 +225,12 @@ void cpu_lock(void); void cpu_unlock(void); +#if defined(USE_NPTL) +static inline void cpu_set_tls(CPUARMState *env, uint32_t newtls_addr) +{ + env->cp15.c13_tls3 = newtls_addr; +} +#endif #define CPSR_M (0x1f) #define CPSR_T (1 << 5) Index: qemu/target-arm/op.c =================================================================== --- qemu.orig/target-arm/op.c 2007-12-11 10:16:49.000000000 -0700 +++ qemu/target-arm/op.c 2007-12-11 10:16:50.000000000 -0700 @@ -1003,11 +1003,19 @@ cpu_loop_exit(); } +#if !defined(CONFIG_USER_ONLY) void OPPROTO op_exception_exit(void) { env->exception_index = EXCP_EXCEPTION_EXIT; cpu_loop_exit(); } +#else +void OPPROTO op_kernel_trap(void) +{ + env->exception_index = EXCP_KERNEL_TRAP; + cpu_loop_exit(); +} +#endif /* defined(CONFIG_USER_ONLY) */ /* VFP support. We follow the convention used for VFP instrunctions: Single precition routines have a "s" suffix, double precision a Index: qemu/target-arm/translate.c =================================================================== --- qemu.orig/target-arm/translate.c 2007-12-11 10:16:49.000000000 -0700 +++ qemu/target-arm/translate.c 2007-12-11 10:16:50.000000000 -0700 @@ -7513,13 +7513,20 @@ if (env->condexec_bits) gen_op_set_condexec(0); do { -#ifndef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) + /* Intercept jump to the magic kernel page. */ + if (dc->pc > 0xffff0000) { + gen_op_kernel_trap(); + dc->is_jmp = DISAS_UPDATE; + break; + } +#else /* !defined(CONFIG_USER_ONLY) */ if (dc->pc >= 0xfffffff0 && IS_M(env)) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_op_exception_exit(); } -#endif +#endif /* !defined(CONFIG_USER_ONLY) */ if (env->nb_breakpoints > 0) { for(j = 0; j < env->nb_breakpoints; j++) { Index: qemu/arm.ld =================================================================== --- qemu.orig/arm.ld 2007-12-11 10:16:49.000000000 -0700 +++ qemu/arm.ld 2007-12-11 10:16:50.000000000 -0700 @@ -26,6 +26,10 @@ { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } .rela.rodata : { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } .rel.got : { *(.rel.got) } .rela.got : { *(.rela.got) } .rel.ctors : { *(.rel.ctors) } Index: qemu/target-ppc/cpu.h =================================================================== --- qemu.orig/target-ppc/cpu.h 2007-12-11 10:16:49.000000000 -0700 +++ qemu/target-ppc/cpu.h 2007-12-11 10:16:50.000000000 -0700 @@ -712,6 +712,12 @@ void do_interrupt (CPUPPCState *env); void ppc_hw_interrupt (CPUPPCState *env); void cpu_loop_exit (void); +#if defined(USE_NPTL) +static inline void cpu_set_tls(CPUPPCState *env, ppc_gpr_t newtls_addr) +{ + env->gpr[2] = newtls_addr; +} +#endif void dump_stack (CPUPPCState *env); ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-12 0:44 [Qemu-devel] [PATCH] arm eabi TLS Thayne Harbaugh @ 2007-12-12 1:29 ` Paul Brook 2007-12-12 23:03 ` Fabrice Bellard 1 sibling, 0 replies; 11+ messages in thread From: Paul Brook @ 2007-12-12 1:29 UTC (permalink / raw) To: qemu-devel, thayne On Wednesday 12 December 2007, Thayne Harbaugh wrote: > I believe Paul Brook did the original patch for arm eabi TLS. The patch > has bounced around for a bit but hasn't been applied. We've been using > this patch for a while and have tweaked it to be a bit more correct as > far as code organization. > > Please let me know what else should be improved for this so that it can > be applied. Implementing clone(CLONE_VM) is a bad idea. We should only do that after we've fixed everything else that breaks with multiple threads You're also missing some of the ARM kernel helper routines. Paul ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-12 0:44 [Qemu-devel] [PATCH] arm eabi TLS Thayne Harbaugh 2007-12-12 1:29 ` Paul Brook @ 2007-12-12 23:03 ` Fabrice Bellard 2007-12-13 1:21 ` Paul Brook 1 sibling, 1 reply; 11+ messages in thread From: Fabrice Bellard @ 2007-12-12 23:03 UTC (permalink / raw) To: thayne, qemu-devel Thayne Harbaugh wrote: > I believe Paul Brook did the original patch for arm eabi TLS. The patch > has bounced around for a bit but hasn't been applied. We've been using > this patch for a while and have tweaked it to be a bit more correct as > far as code organization. > > Please let me know what else should be improved for this so that it can > be applied. - the clone() syscall must be disabled when it is used to create a thread because it cannot work reliably in its current state. - the system to intercept calls to the syscall page must be made more generic to be used at least by arm user and x86_64 user. - It would be good to limit the changes in the CPU emulation code to handle the TLS. For example, on MIPS, the TLS register must not be stored in the CPU state. Same for ARM. Regards, Fabrice. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-12 23:03 ` Fabrice Bellard @ 2007-12-13 1:21 ` Paul Brook 2007-12-13 1:15 ` Thayne Harbaugh ` (2 more replies) 0 siblings, 3 replies; 11+ messages in thread From: Paul Brook @ 2007-12-13 1:21 UTC (permalink / raw) To: qemu-devel; +Cc: thayne > - It would be good to limit the changes in the CPU emulation code to > handle the TLS. For example, on MIPS, the TLS register must not be > stored in the CPU state. Same for ARM. I disagree. The TLS register is part of the CPU state. On many machines (including ARMv6 CPUs) it's an actual CPU register. I'm fairly sure the same is true for recent MIPS revisions. Paul ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-13 1:21 ` Paul Brook @ 2007-12-13 1:15 ` Thayne Harbaugh 2008-01-28 18:27 ` Felipe Contreras 2007-12-13 8:57 ` Fabrice Bellard 2007-12-13 13:28 ` Daniel Jacobowitz 2 siblings, 1 reply; 11+ messages in thread From: Thayne Harbaugh @ 2007-12-13 1:15 UTC (permalink / raw) To: Paul Brook; +Cc: qemu-devel On Thu, 2007-12-13 at 01:21 +0000, Paul Brook wrote: > > - It would be good to limit the changes in the CPU emulation code to > > handle the TLS. For example, on MIPS, the TLS register must not be > > stored in the CPU state. Same for ARM. > > I disagree. The TLS register is part of the CPU state. On many machines > (including ARMv6 CPUs) it's an actual CPU register. I'm fairly sure the same > is true for recent MIPS revisions. I agree with Paul. Some archs actually use a CPU register and require the kernel to help manage TLS. Other archs can manage TLS completely in user space. It's been a while since I've investigated all the details for each arch but I'll go review it. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-13 1:15 ` Thayne Harbaugh @ 2008-01-28 18:27 ` Felipe Contreras 2008-04-04 15:03 ` Marc-André Lureau 0 siblings, 1 reply; 11+ messages in thread From: Felipe Contreras @ 2008-01-28 18:27 UTC (permalink / raw) To: thayne, qemu-devel; +Cc: Paul Brook On Dec 13, 2007 3:15 AM, Thayne Harbaugh <thayne@c2.net> wrote: > > > On Thu, 2007-12-13 at 01:21 +0000, Paul Brook wrote: > > > - It would be good to limit the changes in the CPU emulation code to > > > handle the TLS. For example, on MIPS, the TLS register must not be > > > stored in the CPU state. Same for ARM. > > > > I disagree. The TLS register is part of the CPU state. On many machines > > (including ARMv6 CPUs) it's an actual CPU register. I'm fairly sure the same > > is true for recent MIPS revisions. > > I agree with Paul. Some archs actually use a CPU register and require > the kernel to help manage TLS. Other archs can manage TLS completely in > user space. It's been a while since I've investigated all the details > for each arch but I'll go review it. How is this going? The patch works pretty well for me. -- Felipe Contreras ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2008-01-28 18:27 ` Felipe Contreras @ 2008-04-04 15:03 ` Marc-André Lureau 2008-04-04 17:48 ` Stuart Anderson 2008-05-05 15:12 ` Felipe Contreras 0 siblings, 2 replies; 11+ messages in thread From: Marc-André Lureau @ 2008-04-04 15:03 UTC (permalink / raw) To: qemu-devel; +Cc: Paul Brook, thayne Hi, On Mon, Jan 28, 2008 at 9:27 PM, Felipe Contreras <felipe.contreras@gmail.com> wrote: > > How is this going? > > The patch works pretty well for me. > Unfortunately, the patch does not apply cleanly anymore. Is there an updated version somewhere? regards, -- Marc-André Lureau ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2008-04-04 15:03 ` Marc-André Lureau @ 2008-04-04 17:48 ` Stuart Anderson 2008-05-05 15:12 ` Felipe Contreras 1 sibling, 0 replies; 11+ messages in thread From: Stuart Anderson @ 2008-04-04 17:48 UTC (permalink / raw) To: qemu-devel [-- Attachment #1: Type: TEXT/PLAIN, Size: 1291 bytes --] On Fri, 4 Apr 2008, Marc-André Lureau wrote: > Hi, > > On Mon, Jan 28, 2008 at 9:27 PM, Felipe Contreras > <felipe.contreras@gmail.com> wrote: >> >> How is this going? >> >> The patch works pretty well for me. >> > > Unfortunately, the patch does not apply cleanly anymore. > > Is there an updated version somewhere? Not yet. Thayne (who started this thread) has moved on to other opportunities and is no longer working on it. I am still working on it, but due to a lack of time haven't touched it recently. I am however planning to spend some time on it hopefully in the next week or two, once I finish up with the tasks I'm currently working on. One of the first things to do will of course be to pull the patches up to the CVS head again. For anyone that is interested, all of our work on this can be found at http://qemu.olmeclinux.com/ . This includes our full patch set, test suites, and complete userspaces. Stuart Stuart R. Anderson anderson@netsweng.com Network & Software Engineering http://www.netsweng.com/ 1024D/37A79149: 0791 D3B8 9A4C 2CDC A31F BD03 0A62 E534 37A7 9149 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2008-04-04 15:03 ` Marc-André Lureau 2008-04-04 17:48 ` Stuart Anderson @ 2008-05-05 15:12 ` Felipe Contreras 1 sibling, 0 replies; 11+ messages in thread From: Felipe Contreras @ 2008-05-05 15:12 UTC (permalink / raw) To: qemu-devel; +Cc: Marc-André Lureau, Paul Brook, thayne [-- Attachment #1: Type: text/plain, Size: 944 bytes --] Hi, On Fri, Apr 4, 2008 at 6:03 PM, Marc-André Lureau <marcandre.lureau@gmail.com> wrote: > Hi, > > > On Mon, Jan 28, 2008 at 9:27 PM, Felipe Contreras > <felipe.contreras@gmail.com> wrote: > > > > How is this going? > > > > The patch works pretty well for me. > > > > Unfortunately, the patch does not apply cleanly anymore. > > Is there an updated version somewhere? > > regards, I just updated the patch to current CVS (actually git ;) Also, I found other similar patches: http://dev.openbossa.org/trac/mamona/browser/packages/qemu/qemu-0.9.1/qemu-0.9.0-nptl-update.patch http://dev.openbossa.org/trac/mamona/browser/packages/qemu/qemu-0.9.1/qemu-0.9.0-nptl.patch And, Paul Brook is supposed to be working on this. http://lists.gnu.org/archive/html/qemu-devel/2008-04/msg00685.html Would be nice to know what is the actual status of this, I've updated the patch multiple times already. Best regards. -- Felipe Contreras [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 09_arm_eabitls_felipec.patch --] [-- Type: text/x-patch; name=09_arm_eabitls_felipec.patch, Size: 29721 bytes --] commit 84135872f57ed0bbae528c589bca51ddf161663f Author: Felipe Contreras <felipe.contreras@gmail.com> Date: Mon May 5 17:35:52 2008 +0300 Add NPTL support. Original patch by Thayne Harbaugh and Paul Brook. diff --git a/arm.ld b/arm.ld index e216cbf..08c87a1 100644 --- a/arm.ld +++ b/arm.ld @@ -26,6 +26,10 @@ SECTIONS { *(.rel.rodata) *(.rel.gnu.linkonce.r*) } .rela.rodata : { *(.rela.rodata) *(.rela.gnu.linkonce.r*) } + .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) } + .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) } + .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) } + .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) } .rel.got : { *(.rel.got) } .rela.got : { *(.rela.got) } .rel.ctors : { *(.rel.ctors) } diff --git a/configure b/configure index a2f54b8..912cf5e 100755 --- a/configure +++ b/configure @@ -112,6 +112,7 @@ darwin_user="no" build_docs="no" uname_release="" curses="yes" +nptl="yes" # OS specific targetos=`uname -s` @@ -333,6 +334,8 @@ for opt do ;; *) echo "ERROR: unknown option $opt"; show_help="yes" ;; + --disable-nptl) nptl="no" + ;; esac done @@ -430,6 +433,7 @@ echo " --enable-linux-user enable all linux usermode emulation targets" echo " --disable-linux-user disable all linux usermode emulation targets" echo " --enable-darwin-user enable all darwin usermode emulation targets" echo " --disable-darwin-user disable all darwin usermode emulation targets" +echo " --disable-nptl disable usermode NPTL guest support" echo " --fmod-lib path to FMOD library" echo " --fmod-inc path to FMOD includes" echo " --enable-uname-release=R Return R for uname -r in usermode emulation" @@ -607,6 +611,23 @@ int main(void) { } EOF +# check NPTL support +cat > $TMPC <<EOF +#include <sched.h> +void foo() +{ +#ifndef CLONE_SETTLS +#error bork +#endif +} +EOF + +if $cc -c -o $TMPO $TMPC 2> /dev/null ; then + : +else + nptl="no" +fi + ########################################## # SDL probe @@ -805,6 +826,7 @@ echo "brlapi support $brlapi" echo "Documentation $build_docs" [ ! -z "$uname_release" ] && \ echo "uname -r $uname_release" +echo "NPTL support $nptl" if test $sdl_too_old = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" @@ -1027,6 +1049,7 @@ if test "$sdl1" = "yes" ; then echo "SDL_CFLAGS=`$sdl_config --cflags`" >> $config_mak fi fi + if test "$cocoa" = "yes" ; then echo "#define CONFIG_COCOA 1" >> $config_h echo "CONFIG_COCOA=yes" >> $config_mak @@ -1289,6 +1312,15 @@ if test "$target_user_only" = "yes" -a "$elfload32" = "yes"; then echo "#define TARGET_HAS_ELFLOAD32 1" >> $config_h fi +if test "$nptl" = "yes" ; then + case "$target_cpu" in + arm | armeb | ppc | ppc64) + echo "USE_NPTL=yes" >> $config_mak + echo "#define USE_NPTL 1" >> $config_h + ;; + esac +fi + test -f ${config_h}~ && cmp -s $config_h ${config_h}~ && mv ${config_h}~ $config_h done # for target in $targets diff --git a/exec-all.h b/exec-all.h index 898cf68..22110e9 100644 --- a/exec-all.h +++ b/exec-all.h @@ -303,218 +303,8 @@ extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; extern void *io_mem_opaque[IO_MEM_NB_ENTRIES]; -#if defined(__hppa__) - -typedef int spinlock_t[4]; - -#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } - -static inline void resetlock (spinlock_t *p) -{ - (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; -} - -#else - -typedef int spinlock_t; - -#define SPIN_LOCK_UNLOCKED 0 - -static inline void resetlock (spinlock_t *p) -{ - *p = SPIN_LOCK_UNLOCKED; -} - -#endif - -#if defined(__powerpc__) -static inline int testandset (int *p) -{ - int ret; - __asm__ __volatile__ ( - "0: lwarx %0,0,%1\n" - " xor. %0,%3,%0\n" - " bne 1f\n" - " stwcx. %2,0,%1\n" - " bne- 0b\n" - "1: " - : "=&r" (ret) - : "r" (p), "r" (1), "r" (0) - : "cr0", "memory"); - return ret; -} -#elif defined(__i386__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__x86_64__) -static inline int testandset (int *p) -{ - long int readval = 0; - - __asm__ __volatile__ ("lock; cmpxchgl %2, %0" - : "+m" (*p), "+a" (readval) - : "r" (1) - : "cc"); - return readval; -} -#elif defined(__s390__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" - " jl 0b" - : "=&d" (ret) - : "r" (1), "a" (p), "0" (*p) - : "cc", "memory" ); - return ret; -} -#elif defined(__alpha__) -static inline int testandset (int *p) -{ - int ret; - unsigned long one; - - __asm__ __volatile__ ("0: mov 1,%2\n" - " ldl_l %0,%1\n" - " stl_c %2,%1\n" - " beq %2,1f\n" - ".subsection 2\n" - "1: br 0b\n" - ".previous" - : "=r" (ret), "=m" (*p), "=r" (one) - : "m" (*p)); - return ret; -} -#elif defined(__sparc__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__("ldstub [%1], %0" - : "=r" (ret) - : "r" (p) - : "memory"); - - return (ret ? 1 : 0); -} -#elif defined(__arm__) -static inline int testandset (int *spinlock) -{ - register unsigned int ret; - __asm__ __volatile__("swp %0, %1, [%2]" - : "=r"(ret) - : "0"(1), "r"(spinlock)); - - return ret; -} -#elif defined(__mc68000) -static inline int testandset (int *p) -{ - char ret; - __asm__ __volatile__("tas %1; sne %0" - : "=r" (ret) - : "m" (p) - : "cc","memory"); - return ret; -} -#elif defined(__hppa__) - -/* Because malloc only guarantees 8-byte alignment for malloc'd data, - and GCC only guarantees 8-byte alignment for stack locals, we can't - be assured of 16-byte alignment for atomic lock data even if we - specify "__attribute ((aligned(16)))" in the type declaration. So, - we use a struct containing an array of four ints for the atomic lock - type and dynamically select the 16-byte aligned int from the array - for the semaphore. */ -#define __PA_LDCW_ALIGNMENT 16 -static inline void *ldcw_align (void *p) { - unsigned long a = (unsigned long)p; - a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); - return (void *)a; -} - -static inline int testandset (spinlock_t *p) -{ - unsigned int ret; - p = ldcw_align(p); - __asm__ __volatile__("ldcw 0(%1),%0" - : "=r" (ret) - : "r" (p) - : "memory" ); - return !ret; -} - -#elif defined(__ia64) - -#include <ia64intrin.h> - -static inline int testandset (int *p) -{ - return __sync_lock_test_and_set (p, 1); -} -#elif defined(__mips__) -static inline int testandset (int *p) -{ - int ret; - - __asm__ __volatile__ ( - " .set push \n" - " .set noat \n" - " .set mips2 \n" - "1: li $1, 1 \n" - " ll %0, %1 \n" - " sc $1, %1 \n" - " beqz $1, 1b \n" - " .set pop " - : "=r" (ret), "+R" (*p) - : - : "memory"); - - return ret; -} -#else -#error unimplemented CPU support -#endif - -#if defined(CONFIG_USER_ONLY) -static inline void spin_lock(spinlock_t *lock) -{ - while (testandset(lock)); -} - -static inline void spin_unlock(spinlock_t *lock) -{ - resetlock(lock); -} - -static inline int spin_trylock(spinlock_t *lock) -{ - return !testandset(lock); -} -#else -static inline void spin_lock(spinlock_t *lock) -{ -} - -static inline void spin_unlock(spinlock_t *lock) -{ -} - -static inline int spin_trylock(spinlock_t *lock) -{ - return 1; -} -#endif - +#include "qspinlock.h" + extern spinlock_t tb_lock; extern int tb_invalidated_flag; diff --git a/linux-user/arm/syscall.h b/linux-user/arm/syscall.h index e7f2e8d..be21cbe 100644 --- a/linux-user/arm/syscall.h +++ b/linux-user/arm/syscall.h @@ -28,7 +28,9 @@ struct target_pt_regs { #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 -#define ARM_NR_cacheflush (ARM_SYSCALL_BASE + 0xf0000 + 2) +#define ARM_NR_BASE 0xf0000 +#define ARM_NR_cacheflush (ARM_NR_BASE + 2) +#define ARM_NR_set_tls (ARM_NR_BASE + 5) #define ARM_NR_semihosting 0x123456 #define ARM_NR_thumb_semihosting 0xAB diff --git a/linux-user/main.c b/linux-user/main.c index 2125aa5..c85e484 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -365,6 +365,50 @@ static void arm_cache_flush(abi_ulong start, abi_ulong last) } } +/* Handle a jump to the kernel code page. */ +static int +do_kernel_trap(CPUARMState *env) +{ + uint32_t addr; + uint32_t *ptr; + uint32_t cpsr; + + switch (env->regs[15]) { + case 0xffff0fc0: /* __kernel_cmpxchg */ + /* XXX: This only works between threads, not between processes. + Use native atomic operations. */ + /* ??? This probably breaks horribly if the access segfaults. */ + cpu_lock(); + ptr = (uint32_t *)env->regs[2]; + cpsr = cpsr_read(env); + if (*ptr == env->regs[0]) { + *ptr = env->regs[1]; + env->regs[0] = 0; + cpsr |= CPSR_C; + } else { + env->regs[0] = -1; + cpsr &= ~CPSR_C; + } + cpsr_write(env, cpsr, CPSR_C); + cpu_unlock(); + break; + case 0xffff0fe0: /* __kernel_get_tls */ + env->regs[0] = env->cp15.c13_tls3; + break; + default: + return 1; + } + /* Jump back to the caller. */ + addr = env->regs[14]; + if (addr & 1) { + env->thumb = 1; + addr &= ~1; + } + env->regs[15] = addr; + + return 0; +} + void cpu_loop(CPUARMState *env) { int trapnr; @@ -475,10 +519,8 @@ void cpu_loop(CPUARMState *env) } } - if (n == ARM_NR_cacheflush) { - arm_cache_flush(env->regs[0], env->regs[1]); - } else if (n == ARM_NR_semihosting - || n == ARM_NR_thumb_semihosting) { + if (n == ARM_NR_semihosting + || n == ARM_NR_thumb_semihosting) { env->regs[0] = do_arm_semihosting (env); } else if (n == 0 || n >= ARM_SYSCALL_BASE || (env->thumb && n == ARM_THUMB_SYSCALL)) { @@ -489,6 +531,26 @@ void cpu_loop(CPUARMState *env) n -= ARM_SYSCALL_BASE; env->eabi = 0; } + if (n > ARM_NR_BASE) { + switch (n) + { + case ARM_NR_cacheflush: + arm_cache_flush(env->regs[0], env->regs[1]); + break; +#ifdef USE_NPTL + case ARM_NR_set_tls: + cpu_set_tls(env, env->regs[0]); + env->regs[0] = 0; + break; +#endif + default: + printf ("Error: Bad syscall: %x\n", n); + env->regs[0] = -TARGET_ENOSYS; + goto error; + } + } + else + { env->regs[0] = do_syscall(env, n, env->regs[0], @@ -497,7 +559,9 @@ void cpu_loop(CPUARMState *env) env->regs[3], env->regs[4], env->regs[5]); + } } else { + printf ("Error: Bad syscall: %x\n", n); goto error; } } @@ -535,6 +599,10 @@ void cpu_loop(CPUARMState *env) } } break; + case EXCP_KERNEL_TRAP: + if (do_kernel_trap(env)) + goto error; + break; default: error: fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", @@ -2403,6 +2471,10 @@ int main(int argc, char **argv) ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; + /* Register the magic kernel code page. The cpu will generate a + special exception when it tries to execute code here. We can't + put real code here because it may be in use by the host kernel. */ + page_set_flags(0xffff0000, 0xffff0fff, 0); #endif if (gdbstub_port) { diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b33ad89..5b54c29 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -109,6 +109,9 @@ typedef struct TaskState { #endif int used; /* non zero if used */ struct image_info *info; +#ifdef USE_NPTL + abi_ulong child_tid_addr; +#endif uint8_t stack[0]; } __attribute__((aligned(16))) TaskState; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b1dc365..34a0647 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -70,9 +70,18 @@ #include <linux/kd.h> #include "qemu.h" +#include "qspinlock.h" //#define DEBUG +#ifdef USE_NPTL +#define CLONE_NPTL_FLAGS2 (CLONE_SETTLS | \ + CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID) +#else +/* XXX: Hardcode the above values. */ +#define CLONE_NPTL_FLAGS2 0 +#endif + #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_SPARC) \ || defined(TARGET_M68K) || defined(TARGET_SH4) || defined(TARGET_CRIS) /* 16 bit uid wrappers emulation */ @@ -2700,9 +2709,19 @@ abi_long do_arch_prctl(CPUX86State *env, int code, abi_ulong addr) thread/process */ #define NEW_STACK_SIZE 8192 +#ifdef USE_NPTL +static spinlock_t nptl_lock = SPIN_LOCK_UNLOCKED; +#endif + static int clone_func(void *arg) { CPUState *env = arg; +#ifdef USE_NPTL + /* Wait until the parent has finshed initializing the tls state. */ + while (!spin_trylock(&nptl_lock)) + usleep(1); + spin_unlock(&nptl_lock); +#endif cpu_loop(env); /* never exits */ return 0; @@ -2710,12 +2729,24 @@ static int clone_func(void *arg) /* do_fork() Must return host values and target errnos (unlike most do_*() functions). */ -int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) +int do_fork(CPUState *env, + unsigned int flags, + abi_ulong new_stk_addr, + abi_ulong parent_tid_addr, + abi_ulong newtls_addr, + abi_ulong child_tid_addr) { int ret; TaskState *ts; uint8_t *new_stack; CPUState *new_env; +#ifdef USE_NPTL + unsigned int nptl_flags; + + if (flags & CLONE_PARENT_SETTID) + if (put_user_s32(gettid(), parent_tid_addr)) + return -TARGET_EFAULT; +#endif if (flags & CLONE_VM) { ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE); @@ -2727,34 +2758,27 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) first_task_state = ts; /* we create a new CPU instance. */ new_env = cpu_copy(env); + if (!new_stk_addr) + new_stk_addr = get_sp_from_cpustate(env); #if defined(TARGET_I386) - if (!newsp) - newsp = env->regs[R_ESP]; - new_env->regs[R_ESP] = newsp; + new_env->regs[R_ESP] = new_stk_addr; new_env->regs[R_EAX] = 0; #elif defined(TARGET_ARM) - if (!newsp) - newsp = env->regs[13]; - new_env->regs[13] = newsp; + new_env->regs[13] = new_stk_addr; new_env->regs[0] = 0; #elif defined(TARGET_SPARC) - if (!newsp) - newsp = env->regwptr[22]; - new_env->regwptr[22] = newsp; + new_env->regwptr[22] = new_stk_addr; new_env->regwptr[0] = 0; /* XXXXX */ printf ("HELPME: %s:%d\n", __FILE__, __LINE__); #elif defined(TARGET_M68K) - if (!newsp) - newsp = env->aregs[7]; - new_env->aregs[7] = newsp; + new_env->aregs[7] = new_stk_addr; new_env->dregs[0] = 0; /* ??? is this sufficient? */ #elif defined(TARGET_MIPS) - if (!newsp) - newsp = env->gpr[env->current_tc][29]; - new_env->gpr[env->current_tc][29] = newsp; + new_env->gpr[29][env->current_tc] = new_stk_addr; #elif defined(TARGET_PPC) + new_env->gpr[1] = new_stk_addr; if (!newsp) newsp = env->gpr[1]; new_env->gpr[1] = newsp; @@ -2764,14 +2788,10 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) new_env->gpr[i] = 0; } #elif defined(TARGET_SH4) - if (!newsp) - newsp = env->gregs[15]; - new_env->gregs[15] = newsp; + new_env->gregs[15] = new_stk_addr; /* XXXXX */ #elif defined(TARGET_ALPHA) - if (!newsp) - newsp = env->ir[30]; - new_env->ir[30] = newsp; + new_env->ir[30] = new_stk_addr; /* ? */ { int i; @@ -2779,23 +2799,71 @@ int do_fork(CPUState *env, unsigned int flags, abi_ulong newsp) new_env->ir[i] = 0; } #elif defined(TARGET_CRIS) - if (!newsp) - newsp = env->regs[14]; - new_env->regs[14] = newsp; + new_env->regs[14] = new_stk_addr; #else #error unsupported target CPU #endif new_env->opaque = ts; +#ifdef USE_NPTL + nptl_flags = flags; + flags &= ~CLONE_NPTL_FLAGS2; + + if (nptl_flags & CLONE_CHILD_CLEARTID) { + ts->child_tid_addr = child_tid_addr; + } + + if (nptl_flags & CLONE_SETTLS) + cpu_set_tls(new_env, newtls_addr); + + /* Grab the global cpu lock so that the thread setup appears + atomic. */ + if (nptl_flags & CLONE_CHILD_SETTID) + spin_lock(&nptl_lock); + +#else + if (flags & CLONE_NPTL_FLAGS2) + return -EINVAL; +#endif #ifdef __ia64__ ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); #else ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env); #endif +#ifdef USE_NPTL + if (ret != -1) { + if (nptl_flags & CLONE_CHILD_SETTID) + if (put_user_s32(ret, child_tid_addr)) + return -TARGET_EFAULT; + } + + /* Allow the child to continue. */ + if (nptl_flags & CLONE_CHILD_SETTID) + spin_unlock(&nptl_lock); +#endif } else { /* if no CLONE_VM, we consider it is a fork */ - if ((flags & ~CSIGNAL) != 0) + if ((flags & ~(CSIGNAL | CLONE_NPTL_FLAGS2)) != 0) return -EINVAL; ret = fork(); +#ifdef USE_NPTL + /* There is a race condition here. The parent process could + theoretically read the TID in the child process before the child + tid is set. This would require using either ptrace + (not implemented) or having *_tidptr to point at a shared memory + mapping. We can't repeat the spinlock hack used above because + the child process gets its own copy of the lock. */ + if (ret == 0) { + /* Child Process. */ + if (flags & CLONE_CHILD_SETTID) + if (put_user_s32(gettid(), child_tid_addr)) + return -TARGET_EFAULT; + ts = (TaskState *)env->opaque; + if (flags & CLONE_CHILD_CLEARTID) + ts->child_tid_addr = child_tid_addr; + if (flags & CLONE_SETTLS) + cpu_set_tls(env, newtls_addr); + } +#endif } return ret; } @@ -3120,7 +3188,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_brk(arg1); break; case TARGET_NR_fork: - ret = get_errno(do_fork(cpu_env, SIGCHLD, 0)); + ret = get_errno(do_fork(cpu_env, SIGCHLD, 0, 0, 0, 0)); break; #ifdef TARGET_NR_waitpid case TARGET_NR_waitpid: @@ -4483,7 +4551,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(fsync(arg1)); break; case TARGET_NR_clone: - ret = get_errno(do_fork(cpu_env, arg1, arg2)); + ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg4, arg5)); break; #ifdef __NR_exit_group /* new thread calls */ @@ -4919,7 +4987,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #endif #ifdef TARGET_NR_vfork case TARGET_NR_vfork: - ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0)); + ret = get_errno(do_fork(cpu_env, + CLONE_VFORK | CLONE_VM | SIGCHLD, + 0, 0, 0, 0)); break; #endif #ifdef TARGET_NR_ugetrlimit diff --git a/qspinlock.h b/qspinlock.h new file mode 100644 index 0000000..2332eba --- /dev/null +++ b/qspinlock.h @@ -0,0 +1,235 @@ +/* + * Atomic operation helper include + * + * Copyright (c) 2005 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef QSPINLOCK_H +#define QSPINLOCK_H + +#if defined(__hppa__) + +typedef int spinlock_t[4]; + +#define SPIN_LOCK_UNLOCKED { 1, 1, 1, 1 } + +static inline void resetlock (spinlock_t *p) +{ + (*p)[0] = (*p)[1] = (*p)[2] = (*p)[3] = 1; +} + +#else + +typedef int spinlock_t; + +#define SPIN_LOCK_UNLOCKED 0 + +static inline void resetlock (spinlock_t *p) +{ + *p = SPIN_LOCK_UNLOCKED; +} + +#endif + +#if defined(__powerpc__) +static inline int testandset (int *p) +{ + int ret; + __asm__ __volatile__ ( + "0: lwarx %0,0,%1\n" + " xor. %0,%3,%0\n" + " bne 1f\n" + " stwcx. %2,0,%1\n" + " bne- 0b\n" + "1: " + : "=&r" (ret) + : "r" (p), "r" (1), "r" (0) + : "cr0", "memory"); + return ret; +} +#elif defined(__i386__) +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#elif defined(__x86_64__) +static inline int testandset (int *p) +{ + long int readval = 0; + + __asm__ __volatile__ ("lock; cmpxchgl %2, %0" + : "+m" (*p), "+a" (readval) + : "r" (1) + : "cc"); + return readval; +} +#elif defined(__s390__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__ ("0: cs %0,%1,0(%2)\n" + " jl 0b" + : "=&d" (ret) + : "r" (1), "a" (p), "0" (*p) + : "cc", "memory" ); + return ret; +} +#elif defined(__alpha__) +static inline int testandset (int *p) +{ + int ret; + unsigned long one; + + __asm__ __volatile__ ("0: mov 1,%2\n" + " ldl_l %0,%1\n" + " stl_c %2,%1\n" + " beq %2,1f\n" + ".subsection 2\n" + "1: br 0b\n" + ".previous" + : "=r" (ret), "=m" (*p), "=r" (one) + : "m" (*p)); + return ret; +} +#elif defined(__sparc__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__("ldstub [%1], %0" + : "=r" (ret) + : "r" (p) + : "memory"); + + return (ret ? 1 : 0); +} +#elif defined(__arm__) +static inline int testandset (int *spinlock) +{ + register unsigned int ret; + __asm__ __volatile__("swp %0, %1, [%2]" + : "=r"(ret) + : "0"(1), "r"(spinlock)); + + return ret; +} +#elif defined(__mc68000) +static inline int testandset (int *p) +{ + char ret; + __asm__ __volatile__("tas %1; sne %0" + : "=r" (ret) + : "m" (p) + : "cc","memory"); + return ret; +} +#elif defined(__hppa__) + +/* Because malloc only guarantees 8-byte alignment for malloc'd data, + and GCC only guarantees 8-byte alignment for stack locals, we can't + be assured of 16-byte alignment for atomic lock data even if we + specify "__attribute ((aligned(16)))" in the type declaration. So, + we use a struct containing an array of four ints for the atomic lock + type and dynamically select the 16-byte aligned int from the array + for the semaphore. */ +#define __PA_LDCW_ALIGNMENT 16 +static inline void *ldcw_align (void *p) { + unsigned long a = (unsigned long)p; + a = (a + __PA_LDCW_ALIGNMENT - 1) & ~(__PA_LDCW_ALIGNMENT - 1); + return (void *)a; +} + +static inline int testandset (spinlock_t *p) +{ + unsigned int ret; + p = ldcw_align(p); + __asm__ __volatile__("ldcw 0(%1),%0" + : "=r" (ret) + : "r" (p) + : "memory" ); + return !ret; +} + +#elif defined(__ia64) + +#include <ia64intrin.h> + +static inline int testandset (int *p) +{ + return __sync_lock_test_and_set (p, 1); +} +#elif defined(__mips__) +static inline int testandset (int *p) +{ + int ret; + + __asm__ __volatile__ ( + " .set push \n" + " .set noat \n" + " .set mips2 \n" + "1: li $1, 1 \n" + " ll %0, %1 \n" + " sc $1, %1 \n" + " beqz $1, 1b \n" + " .set pop " + : "=r" (ret), "+R" (*p) + : + : "memory"); + + return ret; +} +#else +#error unimplemented CPU support +#endif + +#if defined(CONFIG_USER_ONLY) +static inline void spin_lock(spinlock_t *lock) +{ + while (testandset(lock)); +} + +static inline void spin_unlock(spinlock_t *lock) +{ + resetlock(lock); +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return !testandset(lock); +} +#else +static inline void spin_lock(spinlock_t *lock) +{ +} + +static inline void spin_unlock(spinlock_t *lock) +{ +} + +static inline int spin_trylock(spinlock_t *lock) +{ + return 1; +} +#endif + +#endif /* QSPINLOCK_H */ diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 2ff25a5..ddae750 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -37,7 +37,11 @@ #define EXCP_IRQ 5 #define EXCP_FIQ 6 #define EXCP_BKPT 7 +#if defined(CONFIG_USER_ONLY) +#define EXCP_KERNEL_TRAP 8 /* Jumped to kernel code page. */ +#else #define EXCP_EXCEPTION_EXIT 8 /* Return from v7M exception. */ +#endif #define ARMV7M_EXCP_RESET 1 #define ARMV7M_EXCP_NMI 2 @@ -217,6 +221,12 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo, void cpu_lock(void); void cpu_unlock(void); +#if defined(USE_NPTL) +static inline void cpu_set_tls(CPUARMState *env, uint32_t newtls_addr) +{ + env->cp15.c13_tls3 = newtls_addr; +} +#endif #define CPSR_M (0x1f) #define CPSR_T (1 << 5) diff --git a/target-arm/translate.c b/target-arm/translate.c index 4678586..6cece6e 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -8599,13 +8599,20 @@ static inline int gen_intermediate_code_internal(CPUState *env, store_cpu_field(tmp, condexec_bits); } do { -#ifndef CONFIG_USER_ONLY +#if defined(CONFIG_USER_ONLY) + /* Intercept jump to the magic kernel page. */ + if (dc->pc > 0xffff0000) { + gen_exception(EXCP_KERNEL_TRAP); + dc->is_jmp = DISAS_UPDATE; + break; + } +#else /* !defined(CONFIG_USER_ONLY) */ if (dc->pc >= 0xfffffff0 && IS_M(env)) { /* We always get here via a jump, so know we are not in a conditional execution block. */ gen_exception(EXCP_EXCEPTION_EXIT); } -#endif +#endif /* !defined(CONFIG_USER_ONLY) */ if (env->nb_breakpoints > 0) { for(j = 0; j < env->nb_breakpoints; j++) { diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 8812975..30fae1c 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -712,6 +712,12 @@ int cpu_ppc_signal_handler (int host_signum, void *pinfo, void do_interrupt (CPUPPCState *env); void ppc_hw_interrupt (CPUPPCState *env); void cpu_loop_exit (void); +#if defined(USE_NPTL) +static inline void cpu_set_tls(CPUPPCState *env, ppc_gpr_t newtls_addr) +{ + env->gpr[2] = newtls_addr; +} +#endif void dump_stack (CPUPPCState *env); ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-13 1:21 ` Paul Brook 2007-12-13 1:15 ` Thayne Harbaugh @ 2007-12-13 8:57 ` Fabrice Bellard 2007-12-13 13:28 ` Daniel Jacobowitz 2 siblings, 0 replies; 11+ messages in thread From: Fabrice Bellard @ 2007-12-13 8:57 UTC (permalink / raw) To: Paul Brook; +Cc: qemu-devel, thayne Paul Brook wrote: >> - It would be good to limit the changes in the CPU emulation code to >> handle the TLS. For example, on MIPS, the TLS register must not be >> stored in the CPU state. Same for ARM. > > I disagree. The TLS register is part of the CPU state. On many machines > (including ARMv6 CPUs) it's an actual CPU register. I'm fairly sure the same > is true for recent MIPS revisions. If some CPUs implement it in hardware, then I agree. Fabrice. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [Qemu-devel] [PATCH] arm eabi TLS 2007-12-13 1:21 ` Paul Brook 2007-12-13 1:15 ` Thayne Harbaugh 2007-12-13 8:57 ` Fabrice Bellard @ 2007-12-13 13:28 ` Daniel Jacobowitz 2 siblings, 0 replies; 11+ messages in thread From: Daniel Jacobowitz @ 2007-12-13 13:28 UTC (permalink / raw) To: qemu-devel On Thu, Dec 13, 2007 at 01:21:03AM +0000, Paul Brook wrote: > I disagree. The TLS register is part of the CPU state. On many machines > (including ARMv6 CPUs) it's an actual CPU register. I'm fairly sure the same > is true for recent MIPS revisions. That's correct, though I don't know if there is silicon to match yet. -- Daniel Jacobowitz CodeSourcery ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2008-05-05 15:12 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-12-12 0:44 [Qemu-devel] [PATCH] arm eabi TLS Thayne Harbaugh 2007-12-12 1:29 ` Paul Brook 2007-12-12 23:03 ` Fabrice Bellard 2007-12-13 1:21 ` Paul Brook 2007-12-13 1:15 ` Thayne Harbaugh 2008-01-28 18:27 ` Felipe Contreras 2008-04-04 15:03 ` Marc-André Lureau 2008-04-04 17:48 ` Stuart Anderson 2008-05-05 15:12 ` Felipe Contreras 2007-12-13 8:57 ` Fabrice Bellard 2007-12-13 13:28 ` Daniel Jacobowitz
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).