aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2007-02-10 04:44:29 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-11 13:51:24 -0500
commitf355559cf78455ed6be103b020e4b800230c64eb (patch)
tree0de116766d38975f75178e299031d55967eaa9d7 /arch
parent3a150e1da8bc4e840d5a09fc089052011b5b6503 (diff)
[PATCH] uml: x86_64 thread fixes
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>
Diffstat (limited to 'arch')
-rw-r--r--arch/um/include/os.h2
-rw-r--r--arch/um/os-Linux/sys-x86_64/Makefile2
-rw-r--r--arch/um/os-Linux/sys-x86_64/prctl.c12
-rw-r--r--arch/um/sys-x86_64/syscalls.c73
-rw-r--r--arch/um/sys-x86_64/tls.c11
5 files changed, 77 insertions, 23 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 8f602667296d..8629bd191492 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -340,4 +340,6 @@ extern void maybe_sigio_broken(int fd, int read);
340extern void sig_handler_common_skas(int sig, void *sc_ptr); 340extern void sig_handler_common_skas(int sig, void *sc_ptr);
341extern void user_signal(int sig, union uml_pt_regs *regs, int pid); 341extern void user_signal(int sig, union uml_pt_regs *regs, int pid);
342 342
343extern int os_arch_prctl(int pid, int code, unsigned long *addr);
344
343#endif 345#endif
diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile
index f67842a7735b..7955e061a678 100644
--- a/arch/um/os-Linux/sys-x86_64/Makefile
+++ b/arch/um/os-Linux/sys-x86_64/Makefile
@@ -3,7 +3,7 @@
3# Licensed under the GPL 3# Licensed under the GPL
4# 4#
5 5
6obj-$(CONFIG_MODE_SKAS) = registers.o signal.o 6obj-$(CONFIG_MODE_SKAS) = registers.o prctl.o signal.o
7 7
8USER_OBJS := $(obj-y) 8USER_OBJS := $(obj-y)
9 9
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
index 000000000000..9d34eddb517f
--- /dev/null
+++ b/arch/um/os-Linux/sys-x86_64/prctl.c
@@ -0,0 +1,12 @@
1/*
2 * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
3 * Licensed under the GPL
4 */
5
6#include <sys/ptrace.h>
7#include <linux/ptrace.h>
8
9int os_arch_prctl(int pid, int code, unsigned long *addr)
10{
11 return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code);
12}
diff --git a/arch/um/sys-x86_64/syscalls.c b/arch/um/sys-x86_64/syscalls.c
index 73ce4463f70c..f309fa9bc232 100644
--- a/arch/um/sys-x86_64/syscalls.c
+++ b/arch/um/sys-x86_64/syscalls.c
@@ -16,6 +16,7 @@
16#include "asm/prctl.h" /* XXX This should get the constants from libc */ 16#include "asm/prctl.h" /* XXX This should get the constants from libc */
17#include "choose-mode.h" 17#include "choose-mode.h"
18#include "kern.h" 18#include "kern.h"
19#include "os.h"
19 20
20asmlinkage long sys_uname64(struct new_utsname __user * name) 21asmlinkage long sys_uname64(struct new_utsname __user * name)
21{ 22{
@@ -58,40 +59,67 @@ static long arch_prctl_tt(int code, unsigned long addr)
58 59
59#ifdef CONFIG_MODE_SKAS 60#ifdef CONFIG_MODE_SKAS
60 61
61/* XXX: Must also call arch_prctl in the host, beside saving the segment bases! */ 62static long arch_prctl_skas(int code, unsigned long __user *addr)
62static long arch_prctl_skas(int code, unsigned long addr)
63{ 63{
64 long ret = 0; 64 unsigned long *ptr = addr, tmp;
65 long ret;
66 int pid = current->mm->context.skas.id.u.pid;
65 67
68 /*
69 * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
70 * be safe), we need to call arch_prctl on the host because
71 * setting %fs may result in something else happening (like a
72 * GDT being set instead). So, we let the host fiddle the
73 * registers and restore them afterwards.
74 *
75 * So, the saved registers are stored to the process (this
76 * needed because a stub may have been the last thing to run),
77 * arch_prctl is run on the host, then the registers are read
78 * back.
79 */
66 switch(code){ 80 switch(code){
67 case ARCH_SET_FS: 81 case ARCH_SET_FS:
68 current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr;
69 break;
70 case ARCH_SET_GS: 82 case ARCH_SET_GS:
71 current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr; 83 restore_registers(pid, &current->thread.regs.regs);
84 break;
85 case ARCH_GET_FS:
86 case ARCH_GET_GS:
87 /*
88 * With these two, we read to a local pointer and
89 * put_user it to the userspace pointer that we were
90 * given. If addr isn't valid (because it hasn't been
91 * faulted in or is just bogus), we want put_user to
92 * fault it in (or return -EFAULT) instead of having
93 * the host return -EFAULT.
94 */
95 ptr = &tmp;
96 }
97
98 ret = os_arch_prctl(pid, code, ptr);
99 if(ret)
100 return ret;
101
102 switch(code){
103 case ARCH_SET_FS:
104 case ARCH_SET_GS:
105 save_registers(pid, &current->thread.regs.regs);
72 break; 106 break;
73 case ARCH_GET_FS: 107 case ARCH_GET_FS:
74 ret = put_user(current->thread.regs.regs.skas. 108 ret = put_user(tmp, addr);
75 regs[FS_BASE / sizeof(unsigned long)],
76 (unsigned long __user *)addr);
77 break; 109 break;
78 case ARCH_GET_GS: 110 case ARCH_GET_GS:
79 ret = put_user(current->thread.regs.regs.skas. 111 ret = put_user(tmp, addr);
80 regs[GS_BASE / sizeof(unsigned long)],
81 (unsigned long __user *)addr);
82 break; 112 break;
83 default:
84 ret = -EINVAL;
85 break;
86 } 113 }
87 114
88 return(ret); 115 return ret;
89} 116}
90#endif 117#endif
91 118
92long sys_arch_prctl(int code, unsigned long addr) 119long sys_arch_prctl(int code, unsigned long addr)
93{ 120{
94 return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr)); 121 return CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code,
122 (unsigned long __user *) addr);
95} 123}
96 124
97long sys_clone(unsigned long clone_flags, unsigned long newsp, 125long sys_clone(unsigned long clone_flags, unsigned long newsp,
@@ -105,5 +133,14 @@ long sys_clone(unsigned long clone_flags, unsigned long newsp,
105 ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid, 133 ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
106 child_tid); 134 child_tid);
107 current->thread.forking = 0; 135 current->thread.forking = 0;
108 return(ret); 136 return ret;
137}
138
139void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
140{
141 if(to->thread.arch.fs == 0)
142 return;
143
144 arch_prctl_skas(ARCH_SET_FS, (void __user *) to->thread.arch.fs);
109} 145}
146
diff --git a/arch/um/sys-x86_64/tls.c b/arch/um/sys-x86_64/tls.c
index ce1bf1b81c43..febbc94be25f 100644
--- a/arch/um/sys-x86_64/tls.c
+++ b/arch/um/sys-x86_64/tls.c
@@ -1,14 +1,17 @@
1#include "linux/sched.h" 1#include "linux/sched.h"
2 2
3void debug_arch_force_load_TLS(void)
4{
5}
6
7void clear_flushed_tls(struct task_struct *task) 3void clear_flushed_tls(struct task_struct *task)
8{ 4{
9} 5}
10 6
11int arch_copy_tls(struct task_struct *t) 7int arch_copy_tls(struct task_struct *t)
12{ 8{
9 /*
10 * If CLONE_SETTLS is set, we need to save the thread id
11 * (which is argument 5, child_tid, of clone) so it can be set
12 * during context switches.
13 */
14 t->thread.arch.fs = t->thread.regs.regs.skas.regs[R8 / sizeof(long)];
15
13 return 0; 16 return 0;
14} 17}