All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alexander Graf <agraf@suse.de>
To: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH] Fix TLS support on x86
Date: Mon, 02 Jul 2007 12:01:32 +0200	[thread overview]
Message-ID: <4688CCFC.3000100@suse.de> (raw)
In-Reply-To: <20070621231612.GC25967@networkno.de>

[-- Attachment #1: Type: text/plain, Size: 916 bytes --]

Hi,

these are the updated patches for TLS support:

qemu-cvs-futex.patch

This patches futex support into qemu-user. It is basically done by David
Woodhouse and I implemented FUTEX_WAKE_OP because actually one
application did not work without (I don't really remember which one
though). If FUTEX_WAKE_OP gets triggered qemu throws a warning though so
if anyone experiences problems with it and it works without we should
disable it then.

qemu-cvs-sched_getaffinity.patch

Flash9 needs sys_get_getaffinity to work properly. As far as I can tell
there should be no need for endianness-conversion, because the
information is written bit-wise.

qemu-cvs-tls.patch

implements set_thread_area for x86 and modifies the do_clone function,
so TLS is evaluated. This is 90% done by David Woodhouse, I only changed
it so it works for me (TID setters, proper segment register setters,
fork() fix, made clone() work).


Alex


[-- Attachment #2: qemu-cvs-futex.patch --]
[-- Type: text/x-patch, Size: 4172 bytes --]

Index: qemu/linux-user/syscall.c
===================================================================
--- qemu.orig/linux-user/syscall.c
+++ qemu/linux-user/syscall.c
@@ -17,6 +17,8 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
+
+#define __user
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdarg.h>
@@ -60,6 +62,7 @@
 #define tchars host_tchars /* same as target */
 #define ltchars host_ltchars /* same as target */
 
+#include <linux/futex.h>
 #include <linux/termios.h>
 #include <linux/unistd.h>
 #include <linux/utsname.h>
@@ -2554,6 +2557,91 @@ static inline void host_to_target_timesp
     unlock_user_struct(target_ts, target_addr, 1);
 }
 
+#ifdef BSWAP_NEEDED
+static int futex_op(int oldval, int op, int oparg)
+{
+	int retval = oparg;
+	switch(op) {
+	    case FUTEX_OP_SET: break;
+	    case FUTEX_OP_ADD: retval += oparg; break;
+	    case FUTEX_OP_OR: retval |= oparg; break;
+	    case FUTEX_OP_ANDN: retval &= oparg; break;
+	    case FUTEX_OP_XOR: retval ^= oparg; break;
+	}
+	return retval;
+}
+
+static int futex_cmp(int oldval, int cmp, int cmparg)
+{
+	switch(cmp) {
+	    case FUTEX_OP_CMP_EQ: return oldval == cmparg;
+	    case FUTEX_OP_CMP_NE: return oldval != cmparg;
+	    case FUTEX_OP_CMP_LT: return oldval <  cmparg;
+	    case FUTEX_OP_CMP_LE: return oldval <= cmparg;
+	    case FUTEX_OP_CMP_GT: return oldval >  cmparg;
+	    case FUTEX_OP_CMP_GE: return oldval >= cmparg;
+	}
+	return -1;
+}
+#endif
+
+static long do_futex(target_ulong uaddr, int op, uint32_t val,
+                    target_ulong utime, target_ulong uaddr2,
+                    uint32_t val3)
+{
+       struct timespec host_utime;
+       unsigned long val2 = utime;
+       long retval;
+
+       if (utime && (op == FUTEX_WAIT || op == FUTEX_LOCK_PI)) {
+               target_to_host_timespec(&host_utime, utime);
+               val2 = (unsigned long)&host_utime;
+       }
+ 
+#ifdef BSWAP_NEEDED
+       switch(op) {
+       case FUTEX_CMP_REQUEUE:
+               val3 = tswap32(val3);
+       case FUTEX_REQUEUE:
+               val2 = tswap32(val2);
+       case FUTEX_WAIT:
+       case FUTEX_WAKE:
+       case FUTEX_WAKE_OP:
+               val = tswap32(val);
+       case FUTEX_LOCK_PI: /* This one's icky, but comes out OK */
+       case FUTEX_UNLOCK_PI:
+               break;
+       default: 
+               gemu_log("qemu: Unsupported futex op %d\n", op);
+               return -ENOSYS;
+       } 
+       if (op == FUTEX_WAKE_OP) {
+               /* Need to munge the secondary operation (val3) */
+	       gemu_log("qemu: Tricky FUTEX_WAKE_OP - trying to emulate it\n");
+               val3 = tswap32(val3);
+               int op2 = (val3 >> 28) & 0xf;
+               int cmp = (val3 >> 24) & 0xf;
+               int oparg = (val3 >> 12) & 0xfff;
+               int cmparg = val3 & 0xfff;
+               int shift = val3 & (FUTEX_OP_OPARG_SHIFT << 28);
+               int oldval = tget32(uaddr2);
+               if (shift)
+                   oparg = 1 << oparg;
+
+              tput32(uaddr2,futex_op(oldval, op2, oparg));
+              retval = syscall(__NR_futex, g2h(uaddr), FUTEX_WAKE, val, 0, 0, 0);
+              if(futex_cmp(oldval, cmp, cmparg)) {
+                  retval = syscall(__NR_futex, g2h(uaddr2), FUTEX_WAKE, val2, 0, 0, 0);
+              }
+       } else {
+              retval = syscall(__NR_futex, g2h(uaddr), op, val, val2, g2h(uaddr2), val3);
+       }
+#else
+       retval = syscall(__NR_futex, g2h(uaddr), op, val, val2, g2h(uaddr2), val3);
+#endif
+       return retval;
+}
+
 long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, 
                 long arg4, long arg5, long arg6)
 {
@@ -4713,6 +4801,11 @@ long do_syscall(void *cpu_env, int num, 
     }
 #endif
 
+#ifdef TARGET_NR_futex
+    case TARGET_NR_futex:
+        ret = get_errno(do_futex(arg1, arg2, arg3, arg4, arg5, arg6));
+        break;
+#endif
 #if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address)
     case TARGET_NR_set_tid_address:
       ret = get_errno(set_tid_address((int *) arg1));


[-- Attachment #3: qemu-cvs-sched_getaffinity.patch --]
[-- Type: text/x-patch, Size: 1664 bytes --]

Index: qemu/linux-user/syscall.c
===================================================================
--- qemu.orig/linux-user/syscall.c
+++ qemu/linux-user/syscall.c
@@ -149,6 +149,7 @@ type name (type1 arg1,type2 arg2,type3 a
 #define __NR_sys_syslog __NR_syslog
 #define __NR_sys_tgkill __NR_tgkill
 #define __NR_sys_clone __NR_clone
+#define __NR_sys_sched_getaffinity __NR_sched_getaffinity
 
 #if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
 #define __NR__llseek __NR_lseek
@@ -171,6 +172,7 @@ _syscall3(int,sys_rt_sigqueueinfo,int,pi
 _syscall3(int,sys_syslog,int,type,char*,bufp,int,len)
 _syscall3(int,sys_tgkill,int,tgid,int,pid,int,sig)
 _syscall5(int,sys_clone, int, flags, void *, child_stack, int *, parent_tidptr, void *, newtls, int *, child_tidptr)
+_syscall3(int,sys_sched_getaffinity,pid_t,pid,unsigned int,cpusetsize,void*,mask)
 #ifdef __NR_exit_group
 _syscall1(int,exit_group,int,error_code)
 #endif
@@ -4823,6 +4825,17 @@ long do_syscall(void *cpu_env, int num, 
 	goto unimplemented_nowarn;
 #endif
 
+#ifdef TARGET_NR_sched_getaffinity
+    case TARGET_NR_sched_getaffinity:
+    {
+        cpu_set_t *mask;
+        lock_user_struct(mask, arg3, 1);                                                                                                          
+        ret = get_errno(sys_sched_getaffinity((pid_t)arg1, (unsigned int)arg2, mask));
+        unlock_user_struct(mask, arg3, 0);                                                                                                        
+        break;
+    }
+#endif
+
     default:
     unimplemented:
         gemu_log("qemu: Unsupported syscall: %d\n", num);


[-- Attachment #4: qemu-cvs-tls.patch --]
[-- Type: text/x-patch, Size: 9356 bytes --]

Index: qemu/linux-user/main.c
===================================================================
--- qemu.orig/linux-user/main.c
+++ qemu/linux-user/main.c
@@ -156,7 +156,7 @@ static void set_gate(void *ptr, unsigned
     p[1] = tswapl(e2);
 }
 
-uint64_t gdt_table[6];
+uint64_t gdt_table[9];
 uint64_t idt_table[256];
 
 /* only dpl matters as we do only user space emulation */
Index: qemu/linux-user/syscall.c
===================================================================
--- qemu.orig/linux-user/syscall.c
+++ qemu/linux-user/syscall.c
@@ -145,6 +145,7 @@ type name (type1 arg1,type2 arg2,type3 a
 #define __NR_sys_rt_sigqueueinfo __NR_rt_sigqueueinfo
 #define __NR_sys_syslog __NR_syslog
 #define __NR_sys_tgkill __NR_tgkill
+#define __NR_sys_clone __NR_clone
 
 #if defined(__alpha__) || defined (__ia64__) || defined(__x86_64__)
 #define __NR__llseek __NR_lseek
@@ -166,6 +167,7 @@ _syscall5(int, _llseek,  uint,  fd, ulon
 _syscall3(int,sys_rt_sigqueueinfo,int,pid,int,sig,siginfo_t *,uinfo)
 _syscall3(int,sys_syslog,int,type,char*,bufp,int,len)
 _syscall3(int,sys_tgkill,int,tgid,int,pid,int,sig)
+_syscall5(int,sys_clone, int, flags, void *, child_stack, int *, parent_tidptr, void *, newtls, int *, child_tidptr)
 #ifdef __NR_exit_group
 _syscall1(int,exit_group,int,error_code)
 #endif
@@ -2115,29 +2117,107 @@ int do_modify_ldt(CPUX86State *env, int 
     return ret;
 }
 
+int do_set_thread_area(CPUX86State *env, target_ulong ptr)
+{
+    uint64_t *gdt_table = g2h(env->gdt.base);
+    struct target_modify_ldt_ldt_s ldt_info;
+    struct target_modify_ldt_ldt_s *target_ldt_info;
+    int seg_32bit, contents, read_exec_only, limit_in_pages;
+    int seg_not_present, useable;
+    uint32_t *lp, entry_1, entry_2;
+    int i;
+
+    lock_user_struct(target_ldt_info, ptr, 1);
+    ldt_info.entry_number = tswap32(target_ldt_info->entry_number);
+    ldt_info.base_addr = tswapl(target_ldt_info->base_addr);
+    ldt_info.limit = tswap32(target_ldt_info->limit);
+    ldt_info.flags = tswap32(target_ldt_info->flags);
+    if (ldt_info.entry_number == -1) {
+           for (i=6; i<8; i++)
+                   if (gdt_table[i] == 0) {
+                           ldt_info.entry_number = i;
+                           target_ldt_info->entry_number = tswap32(i);
+                           break;
+                   }
+    }
+    unlock_user_struct(target_ldt_info, ptr, 0);
+    
+    if (ldt_info.entry_number < 6 || ldt_info.entry_number > 8)
+           return -EINVAL;
+    seg_32bit = ldt_info.flags & 1;
+    contents = (ldt_info.flags >> 1) & 3;
+    read_exec_only = (ldt_info.flags >> 3) & 1;
+    limit_in_pages = (ldt_info.flags >> 4) & 1;
+    seg_not_present = (ldt_info.flags >> 5) & 1;
+    useable = (ldt_info.flags >> 6) & 1;
+
+    if (contents == 3) {
+        if (seg_not_present == 0)
+            return -EINVAL;
+    }
+
+    /* NOTE: same code as Linux kernel */
+    /* Allow LDTs to be cleared by the user. */
+    if (ldt_info.base_addr == 0 && ldt_info.limit == 0) {
+        if ((contents == 0             &&
+             read_exec_only == 1       &&
+             seg_32bit == 0            &&
+             limit_in_pages == 0       &&
+             seg_not_present == 1      &&
+             useable == 0 )) {
+            entry_1 = 0;
+            entry_2 = 0;
+            goto install;
+        }
+    }
+    
+    entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) |
+        (ldt_info.limit & 0x0ffff);
+    entry_2 = (ldt_info.base_addr & 0xff000000) |
+        ((ldt_info.base_addr & 0x00ff0000) >> 16) |
+        (ldt_info.limit & 0xf0000) |
+        ((read_exec_only ^ 1) << 9) |
+        (contents << 10) |
+        ((seg_not_present ^ 1) << 15) |
+        (seg_32bit << 22) |
+        (limit_in_pages << 23) |
+       (useable << 20) |
+       0x7000;
+
+    /* Install the new entry ...  */
+install:
+    lp = (uint32_t *)(gdt_table + ldt_info.entry_number);
+    lp[0] = tswap32(entry_1);
+    lp[1] = tswap32(entry_2);
+    return 0;
+}
 #endif /* defined(TARGET_I386) */
 
 /* this stack is the equivalent of the kernel stack associated with a
    thread/process */
 #define NEW_STACK_SIZE 8192
 
-static int clone_func(void *arg)
+static int clone_func(void *cloneenv)
 {
-    CPUState *env = arg;
-    cpu_loop(env);
+    cpu_loop((CPUState *)cloneenv);
     /* never exits */
     return 0;
 }
 
-int do_fork(CPUState *env, unsigned int flags, unsigned long newsp)
+int do_fork(CPUState *env, unsigned int flags, target_ulong newsp, target_ulong parent_tidptr, target_ulong newtls, target_ulong child_tidptr)
 {
     int ret;
+    unsigned long parent_tid=gettid();
     TaskState *ts;
     uint8_t *new_stack;
     CPUState *new_env;
-    
+#if defined(TARGET_I386)
+    uint64_t *new_gdt_table;
+#endif
     if (flags & CLONE_VM) {
         ts = malloc(sizeof(TaskState) + NEW_STACK_SIZE);
+        if (!ts)
+                return -ENOMEM;
         memset(ts, 0, sizeof(TaskState));
         new_stack = ts->stack;
         ts->used = 1;
@@ -2149,6 +2229,27 @@ int do_fork(CPUState *env, unsigned int 
 #if defined(TARGET_I386)
         if (!newsp)
             newsp = env->regs[R_ESP];
+        new_gdt_table = malloc(9 * 8);
+        if (!new_gdt_table) {
+                free(new_env);
+                return -ENOMEM;
+        }
+        /* Copy main GDT table from parent, but clear TLS entries */
+        memcpy(new_gdt_table, g2h(env->gdt.base), 6 * 8);
+        memset(&new_gdt_table[6], 0, 3 * 8); 
+        new_env->gdt.base = h2g(new_gdt_table);
+        if (flags & CLONE_SETTLS) {
+               ret = do_set_thread_area(new_env, newtls);
+               if (ret) {
+                       free(new_gdt_table);
+                       free(new_env);
+                       return ret;
+               }
+        }
+	
+        cpu_x86_load_seg(new_env, R_FS, new_env->segs[R_FS].selector);
+        cpu_x86_load_seg(new_env, R_GS, new_env->segs[R_GS].selector);
+	
         new_env->regs[R_ESP] = newsp;
         new_env->regs[R_EAX] = 0;
 #elif defined(TARGET_ARM)
@@ -2202,15 +2303,27 @@ int do_fork(CPUState *env, unsigned int 
 #endif
         new_env->opaque = ts;
 #ifdef __ia64__
-        ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+	ret = __clone2(clone_func, new_stack + NEW_STACK_SIZE, flags & ~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), new_env);
 #else
-	ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+	ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags & ~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), new_env);
 #endif
     } else {
         /* if no CLONE_VM, we consider it is a fork */
-        if ((flags & ~CSIGNAL) != 0)
-            return -EINVAL;
-        ret = fork();
+	ret = sys_clone(flags & ~(CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID), 0, g2h(parent_tidptr), NULL, g2h(child_tidptr));
+    }
+    /* Store child thread ID at location parent_tidptr in parent and child memory. 
+       Currently this is only done in client memory */
+     if(flags & CLONE_PARENT_SETTID) {
+        tput32(parent_tidptr, parent_tid);
+     }
+ 
+    /* Store child thread ID at location child_tidptr in child memory. */
+    if(flags & CLONE_CHILD_SETTID) {
+      if(ret==0) { /* only in client memory for fork() */
+        tput32(child_tidptr, gettid());
+      } else if(flags & CLONE_VM) { /* real threads need it too */
+        tput32(child_tidptr, ret);
+      }
     }
     return ret;
 }
@@ -2458,7 +2571,7 @@ long do_syscall(void *cpu_env, int num, 
         _mcleanup();
 #endif
         gdb_exit(cpu_env, arg1);
-        /* XXX: should free thread stack and CPU env */
+        /* XXX: should free thread stack, GDT and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
         break;
@@ -2487,7 +2600,7 @@ long do_syscall(void *cpu_env, int num, 
         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:
@@ -3651,7 +3764,7 @@ long do_syscall(void *cpu_env, int num, 
         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 */
@@ -4039,7 +4152,7 @@ long do_syscall(void *cpu_env, int num, 
 #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
@@ -4561,12 +4674,12 @@ long do_syscall(void *cpu_env, int num, 
 #ifdef TARGET_NR_set_thread_area
     case TARGET_NR_set_thread_area:
 #ifdef TARGET_MIPS
-      ((CPUMIPSState *) cpu_env)->tls_value = arg1;
-      ret = 0;
-      break;
+        ((CPUMIPSState *) cpu_env)->tls_value = arg1;
+        ret = 0;
 #else
-      goto unimplemented_nowarn;
+        ret = get_errno(do_set_thread_area(cpu_env, arg1));
 #endif
+        break;
 #endif
 #ifdef TARGET_NR_get_thread_area
     case TARGET_NR_get_thread_area:


  parent reply	other threads:[~2007-07-02  9:59 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-06-08 14:17 [Qemu-devel] [PATCH] Fix TLS support on x86 Alexander Graf
2007-06-18 19:21 ` Thiemo Seufer
2007-06-20 16:42   ` Alexander Graf
2007-06-21  5:31     ` David Woodhouse
2007-06-21  6:01       ` Jakub Jelinek
2007-06-21  7:11       ` Alexander Graf
     [not found]     ` <20070621225550.GB25967@networkno.de>
     [not found]       ` <1801E7EE-FF0A-4A43-88ED-4503DEBBC831@suse.de>
     [not found]         ` <20070621231612.GC25967@networkno.de>
2007-07-02 10:01           ` Alexander Graf [this message]
2007-11-13 18:44             ` Stefan Weil
2007-11-13 18:46               ` Thayne Harbaugh
2007-11-13 22:13                 ` Fabrice Bellard
2007-11-14 20:02                   ` Stefan Weil

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4688CCFC.3000100@suse.de \
    --to=agraf@suse.de \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.