]> nv-tegra.nvidia Code Review - linux-2.6.git/commitdiff
[PATCH] uml: x86_64 thread fixes
authorJeff Dike <jdike@addtoit.com>
Sat, 10 Feb 2007 09:44:29 +0000 (01:44 -0800)
committerLinus Torvalds <torvalds@woody.linux-foundation.org>
Sun, 11 Feb 2007 18:51:24 +0000 (10:51 -0800)
x86_64 needs some TLS fixes.  What was missing was remembering the child
thread id during clone and stuffing it into the child during each context
switch.

The %fs value is stored separately in the thread structure since the host
controls what effect it has on the actual register file.  The host also needs
to store it in its own thread struct, so we need the value kept outside the
register file.

arch_prctl_skas was fixed to call PTRACE_ARCH_PRCTL appropriately.  There is
some saving and restoring of registers in the ARCH_SET_* cases so that the
correct set of registers are changed on the host and restored to the process
when it runs again.

Signed-off-by: Jeff Dike <jdike@addtoit.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
arch/um/include/os.h
arch/um/os-Linux/sys-x86_64/Makefile
arch/um/os-Linux/sys-x86_64/prctl.c [new file with mode: 0644]
arch/um/sys-x86_64/syscalls.c
arch/um/sys-x86_64/tls.c
include/asm-um/processor-x86_64.h
include/asm-um/ptrace-x86_64.h

index 8f602667296d7b4eca716f2bdfd4337140d1f9b2..8629bd1914925aba5d78f4348221c796fc323920 100644 (file)
@@ -340,4 +340,6 @@ extern void maybe_sigio_broken(int fd, int read);
 extern void sig_handler_common_skas(int sig, void *sc_ptr);
 extern void user_signal(int sig, union uml_pt_regs *regs, int pid);
 
+extern int os_arch_prctl(int pid, int code, unsigned long *addr);
+
 #endif
index f67842a7735b80b09b6a7f1fbb13a1f98861a630..7955e061a678aaf3d7f29143b3a6ed65257ad391 100644 (file)
@@ -3,7 +3,7 @@
 # Licensed under the GPL
 #
 
-obj-$(CONFIG_MODE_SKAS) = registers.o signal.o
+obj-$(CONFIG_MODE_SKAS) = registers.o prctl.o signal.o
 
 USER_OBJS := $(obj-y)
 
diff --git a/arch/um/os-Linux/sys-x86_64/prctl.c b/arch/um/os-Linux/sys-x86_64/prctl.c
new file mode 100644 (file)
index 0000000..9d34edd
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
+ * Licensed under the GPL
+ */
+
+#include <sys/ptrace.h>
+#include <linux/ptrace.h>
+
+int os_arch_prctl(int pid, int code, unsigned long *addr)
+{
+        return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code);
+}
index 73ce4463f70c6f96b2cd94f000b69df5a0da95ff..f309fa9bc23225ce43d4ce2bcb69dc3d48f75da9 100644 (file)
@@ -16,6 +16,7 @@
 #include "asm/prctl.h" /* XXX This should get the constants from libc */
 #include "choose-mode.h"
 #include "kern.h"
+#include "os.h"
 
 asmlinkage long sys_uname64(struct new_utsname __user * name)
 {
@@ -58,40 +59,67 @@ static long arch_prctl_tt(int code, unsigned long addr)
 
 #ifdef CONFIG_MODE_SKAS
 
-/* XXX: Must also call arch_prctl in the host, beside saving the segment bases! */
-static long arch_prctl_skas(int code, unsigned long addr)
+static long arch_prctl_skas(int code, unsigned long __user *addr)
 {
-       long ret = 0;
+        unsigned long *ptr = addr, tmp;
+       long ret;
+        int pid = current->mm->context.skas.id.u.pid;
 
+       /*
+        * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
+        * be safe), we need to call arch_prctl on the host because
+        * setting %fs may result in something else happening (like a
+        * GDT being set instead).  So, we let the host fiddle the
+        * registers and restore them afterwards.
+        *
+        * So, the saved registers are stored to the process (this
+        * needed because a stub may have been the last thing to run),
+        * arch_prctl is run on the host, then the registers are read
+        * back.
+        */
        switch(code){
        case ARCH_SET_FS:
-               current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr;
-               break;
        case ARCH_SET_GS:
-               current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr;
+                restore_registers(pid, &current->thread.regs.regs);
+                break;
+        case ARCH_GET_FS:
+        case ARCH_GET_GS:
+                /*
+                 * With these two, we read to a local pointer and
+                 * put_user it to the userspace pointer that we were
+                 * given.  If addr isn't valid (because it hasn't been
+                 * faulted in or is just bogus), we want put_user to
+                 * fault it in (or return -EFAULT) instead of having
+                 * the host return -EFAULT.
+                 */
+                ptr = &tmp;
+        }
+
+        ret = os_arch_prctl(pid, code, ptr);
+        if(ret)
+                return ret;
+
+        switch(code){
+       case ARCH_SET_FS:
+       case ARCH_SET_GS:
+                save_registers(pid, &current->thread.regs.regs);
                break;
        case ARCH_GET_FS:
-               ret = put_user(current->thread.regs.regs.skas.
-                               regs[FS_BASE / sizeof(unsigned long)],
-                               (unsigned long __user *)addr);
+               ret = put_user(tmp, addr);
                break;
        case ARCH_GET_GS:
-               ret = put_user(current->thread.regs.regs.skas.
-                               regs[GS_BASE / sizeof(unsigned long)],
-                               (unsigned long __user *)addr);
+               ret = put_user(tmp, addr);
                break;
-       default:
-               ret = -EINVAL;
-               break;
        }
 
-       return(ret);
+       return ret;
 }
 #endif
 
 long sys_arch_prctl(int code, unsigned long addr)
 {
-       return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr));
+       return CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code,
+                                (unsigned long __user *) addr);
 }
 
 long sys_clone(unsigned long clone_flags, unsigned long newsp,
@@ -105,5 +133,14 @@ long sys_clone(unsigned long clone_flags, unsigned long newsp,
        ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
                      child_tid);
        current->thread.forking = 0;
-       return(ret);
+       return ret;
+}
+
+void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
+{
+        if(to->thread.arch.fs == 0)
+                return;
+
+        arch_prctl_skas(ARCH_SET_FS, (void __user *) to->thread.arch.fs);
 }
+
index ce1bf1b81c431dcb859db4f25410a2758c226f19..febbc94be25fd423c890ac8f11d1859ca919217a 100644 (file)
@@ -1,14 +1,17 @@
 #include "linux/sched.h"
 
-void debug_arch_force_load_TLS(void)
-{
-}
-
 void clear_flushed_tls(struct task_struct *task)
 {
 }
 
 int arch_copy_tls(struct task_struct *t)
 {
+       /*
+        * If CLONE_SETTLS is set, we need to save the thread id
+        * (which is argument 5, child_tid, of clone) so it can be set
+        * during context switches.
+        */
+       t->thread.arch.fs = t->thread.regs.regs.skas.regs[R8 / sizeof(long)];
+
         return 0;
 }
index 10609af376c0339af5f22bafe48c918a2103a34f..31c2d4d685bd7692677db195bb4738642dea8459 100644 (file)
@@ -13,6 +13,7 @@
 struct arch_thread {
         unsigned long debugregs[8];
         int debugregs_seq;
+        unsigned long fs;
         struct faultinfo faultinfo;
 };
 
@@ -25,8 +26,9 @@ extern inline void rep_nop(void)
 #define cpu_relax()   rep_nop()
 
 #define INIT_ARCH_THREAD { .debugregs                  = { [ 0 ... 7 ] = 0 }, \
-                           .debugregs_seq      = 0, \
-                           .faultinfo          = { 0, 0, 0 } }
+                           .debugregs_seq      = 0,                           \
+                          .fs                  = 0, \
+                          .faultinfo           = { 0, 0, 0 } }
 
 static inline void arch_flush_thread(struct arch_thread *thread)
 {
index 03b4af4ac09a3c82509e93dc835ee84bb7f7d111..a927450ce4011c460b115b57357801fbb9f6201f 100644 (file)
@@ -81,9 +81,7 @@ static inline void arch_switch_to_tt(struct task_struct *from,
 {
 }
 
-static inline void arch_switch_to_skas(struct task_struct *from,
-                                       struct task_struct *to)
-{
-}
+extern void arch_switch_to_skas(struct task_struct *from,
+                               struct task_struct *to);
 
 #endif