aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>2006-03-31 05:30:22 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:18:52 -0500
commitaa6758d4867cd07bd76105ade6177fe6148e559a (patch)
tree0053f855c885a6dc322337468a2c45b6b2f8ddd0
parent972410b0232e97609fcefc8e408fe3037fcd607b (diff)
[PATCH] uml: implement {get,set}_thread_area for i386
Implement sys_[gs]et_thread_area and the corresponding ptrace operations for UML. This is the main chunk, additional parts follow. This implementation is now well tested and has run reliably for some time, and we've understood all the previously existing problems. Their implementation saves the new GDT content and then forwards the call to the host when appropriate, i.e. immediately when the target process is running or on context switch otherwise (i.e. on fork and on ptrace() calls). In SKAS mode, we must switch registers on each context switch (because SKAS does not switches tls_array together with current->mm). Also, added get_cpu() locking; this has been done for SKAS mode, since TT does not need it (it does not use smp_processor_id()). Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Acked-by: Jeff Dike <jdike@addtoit.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--arch/um/include/os.h4
-rw-r--r--arch/um/kernel/exec_kern.c12
-rw-r--r--arch/um/kernel/process_kern.c20
-rw-r--r--arch/um/kernel/ptrace.c10
-rw-r--r--arch/um/kernel/skas/process_kern.c2
-rw-r--r--arch/um/os-Linux/Makefile4
-rw-r--r--arch/um/os-Linux/tls.c77
-rw-r--r--arch/um/sys-i386/Makefile3
-rw-r--r--arch/um/sys-i386/ptrace.c2
-rw-r--r--arch/um/sys-i386/sys_call_table.S2
-rw-r--r--arch/um/sys-i386/syscalls.c14
-rw-r--r--arch/um/sys-i386/tls.c326
-rw-r--r--arch/um/sys-x86_64/Makefile3
-rw-r--r--arch/um/sys-x86_64/tls.c14
-rw-r--r--include/asm-um/desc.h12
-rw-r--r--include/asm-um/processor-i386.h35
-rw-r--r--include/asm-um/processor-x86_64.h9
-rw-r--r--include/asm-um/ptrace-generic.h14
-rw-r--r--include/asm-um/ptrace-i386.h41
-rw-r--r--include/asm-um/ptrace-x86_64.h35
-rw-r--r--include/asm-um/segment.h2
21 files changed, 580 insertions, 61 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 5fb84e889b2b..e9e1db1e08d0 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -236,6 +236,10 @@ extern int run_helper_thread(int (*proc)(void *), void *arg,
236 int stack_order); 236 int stack_order);
237extern int helper_wait(int pid); 237extern int helper_wait(int pid);
238 238
239
240/* tls.c */
241extern int os_set_thread_area(void *data, int pid);
242extern int os_get_thread_area(void *data, int pid);
239/* umid.c */ 243/* umid.c */
240 244
241extern int umid_file_name(char *name, char *buf, int len); 245extern int umid_file_name(char *name, char *buf, int len);
diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c
index f9b346e05b00..c0cb627bf594 100644
--- a/arch/um/kernel/exec_kern.c
+++ b/arch/um/kernel/exec_kern.c
@@ -22,6 +22,7 @@
22 22
23void flush_thread(void) 23void flush_thread(void)
24{ 24{
25 arch_flush_thread(&current->thread.arch);
25 CHOOSE_MODE(flush_thread_tt(), flush_thread_skas()); 26 CHOOSE_MODE(flush_thread_tt(), flush_thread_skas());
26} 27}
27 28
@@ -74,14 +75,3 @@ long sys_execve(char __user *file, char __user *__user *argv,
74 unlock_kernel(); 75 unlock_kernel();
75 return(error); 76 return(error);
76} 77}
77
78/*
79 * Overrides for Emacs so that we follow Linus's tabbing style.
80 * Emacs will notice this stuff at the end of the file and automatically
81 * adjust the settings for this buffer only. This must remain at the end
82 * of the file.
83 * ---------------------------------------------------------------------------
84 * Local variables:
85 * c-file-style: "linux"
86 * End:
87 */
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c
index ba8a52c1f7ae..f6a5a502120b 100644
--- a/arch/um/kernel/process_kern.c
+++ b/arch/um/kernel/process_kern.c
@@ -156,9 +156,25 @@ int copy_thread(int nr, unsigned long clone_flags, unsigned long sp,
156 unsigned long stack_top, struct task_struct * p, 156 unsigned long stack_top, struct task_struct * p,
157 struct pt_regs *regs) 157 struct pt_regs *regs)
158{ 158{
159 int ret;
160
159 p->thread = (struct thread_struct) INIT_THREAD; 161 p->thread = (struct thread_struct) INIT_THREAD;
160 return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, 162 ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
161 clone_flags, sp, stack_top, p, regs)); 163 clone_flags, sp, stack_top, p, regs);
164
165 if (ret || !current->thread.forking)
166 goto out;
167
168 clear_flushed_tls(p);
169
170 /*
171 * Set a new TLS for the child thread?
172 */
173 if (clone_flags & CLONE_SETTLS)
174 ret = arch_copy_tls(p);
175
176out:
177 return ret;
162} 178}
163 179
164void initial_thread_cb(void (*proc)(void *), void *arg) 180void initial_thread_cb(void (*proc)(void *), void *arg)
diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c
index 394582202ce6..60d2eda995c1 100644
--- a/arch/um/kernel/ptrace.c
+++ b/arch/um/kernel/ptrace.c
@@ -185,6 +185,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
185 ret = set_fpxregs(data, child); 185 ret = set_fpxregs(data, child);
186 break; 186 break;
187#endif 187#endif
188 case PTRACE_GET_THREAD_AREA:
189 ret = ptrace_get_thread_area(child, addr,
190 (struct user_desc __user *) data);
191 break;
192
193 case PTRACE_SET_THREAD_AREA:
194 ret = ptrace_set_thread_area(child, addr,
195 (struct user_desc __user *) data);
196 break;
197
188 case PTRACE_FAULTINFO: { 198 case PTRACE_FAULTINFO: {
189 /* Take the info from thread->arch->faultinfo, 199 /* Take the info from thread->arch->faultinfo,
190 * but transfer max. sizeof(struct ptrace_faultinfo). 200 * but transfer max. sizeof(struct ptrace_faultinfo).
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c
index 14360ac17f02..38b185370c42 100644
--- a/arch/um/kernel/skas/process_kern.c
+++ b/arch/um/kernel/skas/process_kern.c
@@ -111,6 +111,8 @@ int copy_thread_skas(int nr, unsigned long clone_flags, unsigned long sp,
111 if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp; 111 if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp;
112 112
113 handler = fork_handler; 113 handler = fork_handler;
114
115 arch_copy_thread(&current->thread.arch, &p->thread.arch);
114 } 116 }
115 else { 117 else {
116 init_thread_registers(&p->thread.regs.regs); 118 init_thread_registers(&p->thread.regs.regs);
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
index 9e0788cf3aec..f4bfc4c7ccac 100644
--- a/arch/um/os-Linux/Makefile
+++ b/arch/um/os-Linux/Makefile
@@ -4,7 +4,7 @@
4# 4#
5 5
6obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \ 6obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \
7 signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o \ 7 signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o tls.o \
8 user_syms.o util.o drivers/ sys-$(SUBARCH)/ 8 user_syms.o util.o drivers/ sys-$(SUBARCH)/
9 9
10obj-$(CONFIG_MODE_SKAS) += skas/ 10obj-$(CONFIG_MODE_SKAS) += skas/
@@ -12,7 +12,7 @@ obj-$(CONFIG_TTY_LOG) += tty_log.o
12user-objs-$(CONFIG_TTY_LOG) += tty_log.o 12user-objs-$(CONFIG_TTY_LOG) += tty_log.o
13 13
14USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \ 14USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \
15 process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o \ 15 process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o tls.o \
16 uaccess.o umid.o util.o 16 uaccess.o umid.o util.o
17 17
18CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH) 18CFLAGS_user_syms.o += -DSUBARCH_$(SUBARCH)
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c
new file mode 100644
index 000000000000..63dfcf709377
--- /dev/null
+++ b/arch/um/os-Linux/tls.c
@@ -0,0 +1,77 @@
1#include <errno.h>
2#include <sys/ptrace.h>
3#include <asm/ldt.h>
4#include "uml-config.h"
5
6/* TLS support - we basically rely on the host's one.*/
7
8/* In TT mode, this should be called only by the tracing thread, and makes sense
9 * only for PTRACE_SET_THREAD_AREA. In SKAS mode, it's used normally.
10 *
11 */
12
13#ifndef PTRACE_GET_THREAD_AREA
14#define PTRACE_GET_THREAD_AREA 25
15#endif
16
17#ifndef PTRACE_SET_THREAD_AREA
18#define PTRACE_SET_THREAD_AREA 26
19#endif
20
21int os_set_thread_area(void *data, int pid)
22{
23 struct user_desc *info = data;
24 int ret;
25
26 ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
27 (unsigned long) info);
28 if (ret < 0)
29 ret = -errno;
30 return ret;
31}
32
33#ifdef UML_CONFIG_MODE_SKAS
34
35int os_get_thread_area(void *data, int pid)
36{
37 struct user_desc *info = data;
38 int ret;
39
40 ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
41 (unsigned long) info);
42 if (ret < 0)
43 ret = -errno;
44 return ret;
45}
46
47#endif
48
49#ifdef UML_CONFIG_MODE_TT
50#include "linux/unistd.h"
51
52_syscall1(int, get_thread_area, struct user_desc *, u_info);
53_syscall1(int, set_thread_area, struct user_desc *, u_info);
54
55int do_set_thread_area_tt(struct user_desc *info)
56{
57 int ret;
58
59 ret = set_thread_area(info);
60 if (ret < 0) {
61 ret = -errno;
62 }
63 return ret;
64}
65
66int do_get_thread_area_tt(struct user_desc *info)
67{
68 int ret;
69
70 ret = get_thread_area(info);
71 if (ret < 0) {
72 ret = -errno;
73 }
74 return ret;
75}
76
77#endif /* UML_CONFIG_MODE_TT */
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile
index 2c06a07a1bdb..98b20b7bba4f 100644
--- a/arch/um/sys-i386/Makefile
+++ b/arch/um/sys-i386/Makefile
@@ -1,5 +1,6 @@
1obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ 1obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \
2 ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o sys_call_table.o 2 ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o \
3 sys_call_table.o tls.o
3 4
4obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o 5obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
5 6
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c
index 927fcd955651..6a23cc6947c3 100644
--- a/arch/um/sys-i386/ptrace.c
+++ b/arch/um/sys-i386/ptrace.c
@@ -18,10 +18,12 @@
18void arch_switch_to_tt(struct task_struct *from, struct task_struct *to) 18void arch_switch_to_tt(struct task_struct *from, struct task_struct *to)
19{ 19{
20 update_debugregs(to->thread.arch.debugregs_seq); 20 update_debugregs(to->thread.arch.debugregs_seq);
21 arch_switch_tls_tt(from, to);
21} 22}
22 23
23void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) 24void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
24{ 25{
26 arch_switch_tls_skas(from, to);
25} 27}
26 28
27int is_syscall(unsigned long addr) 29int is_syscall(unsigned long addr)
diff --git a/arch/um/sys-i386/sys_call_table.S b/arch/um/sys-i386/sys_call_table.S
index ad75c27afe38..1ff61474b25c 100644
--- a/arch/um/sys-i386/sys_call_table.S
+++ b/arch/um/sys-i386/sys_call_table.S
@@ -6,8 +6,6 @@
6 6
7#define sys_vm86old sys_ni_syscall 7#define sys_vm86old sys_ni_syscall
8#define sys_vm86 sys_ni_syscall 8#define sys_vm86 sys_ni_syscall
9#define sys_set_thread_area sys_ni_syscall
10#define sys_get_thread_area sys_ni_syscall
11 9
12#define sys_stime um_stime 10#define sys_stime um_stime
13#define sys_time um_time 11#define sys_time um_time
diff --git a/arch/um/sys-i386/syscalls.c b/arch/um/sys-i386/syscalls.c
index 1845123ed124..749dd1bfe60f 100644
--- a/arch/um/sys-i386/syscalls.c
+++ b/arch/um/sys-i386/syscalls.c
@@ -61,21 +61,27 @@ long old_select(struct sel_arg_struct __user *arg)
61 return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); 61 return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
62} 62}
63 63
64/* The i386 version skips reading from %esi, the fourth argument. So we must do 64/*
65 * this, too. 65 * The prototype on i386 is:
66 *
67 * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr)
68 *
69 * and the "newtls" arg. on i386 is read by copy_thread directly from the
70 * register saved on the stack.
66 */ 71 */
67long sys_clone(unsigned long clone_flags, unsigned long newsp, 72long sys_clone(unsigned long clone_flags, unsigned long newsp,
68 int __user *parent_tid, int unused, int __user *child_tid) 73 int __user *parent_tid, void *newtls, int __user *child_tid)
69{ 74{
70 long ret; 75 long ret;
71 76
72 if (!newsp) 77 if (!newsp)
73 newsp = UPT_SP(&current->thread.regs.regs); 78 newsp = UPT_SP(&current->thread.regs.regs);
79
74 current->thread.forking = 1; 80 current->thread.forking = 1;
75 ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid, 81 ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
76 child_tid); 82 child_tid);
77 current->thread.forking = 0; 83 current->thread.forking = 0;
78 return(ret); 84 return ret;
79} 85}
80 86
81/* 87/*
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c
new file mode 100644
index 000000000000..e3c5bc593fae
--- /dev/null
+++ b/arch/um/sys-i386/tls.c
@@ -0,0 +1,326 @@
1/*
2 * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
3 * Licensed under the GPL
4 */
5
6#include "linux/config.h"
7#include "linux/kernel.h"
8#include "linux/sched.h"
9#include "linux/slab.h"
10#include "linux/types.h"
11#include "asm/uaccess.h"
12#include "asm/ptrace.h"
13#include "asm/segment.h"
14#include "asm/smp.h"
15#include "asm/desc.h"
16#include "choose-mode.h"
17#include "kern.h"
18#include "kern_util.h"
19#include "mode_kern.h"
20#include "os.h"
21#include "mode.h"
22
23#ifdef CONFIG_MODE_SKAS
24#include "skas.h"
25#endif
26
27#ifdef CONFIG_MODE_SKAS
28int do_set_thread_area_skas(struct user_desc *info)
29{
30 int ret;
31 u32 cpu;
32
33 cpu = get_cpu();
34 ret = os_set_thread_area(info, userspace_pid[cpu]);
35 put_cpu();
36 return ret;
37}
38
39int do_get_thread_area_skas(struct user_desc *info)
40{
41 int ret;
42 u32 cpu;
43
44 cpu = get_cpu();
45 ret = os_get_thread_area(info, userspace_pid[cpu]);
46 put_cpu();
47 return ret;
48}
49#endif
50
51/*
52 * sys_get_thread_area: get a yet unused TLS descriptor index.
53 * XXX: Consider leaving one free slot for glibc usage at first place. This must
54 * be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else.
55 *
56 * Also, this must be tested when compiling in SKAS mode with dinamic linking
57 * and running against NPTL.
58 */
59static int get_free_idx(struct task_struct* task)
60{
61 struct thread_struct *t = &task->thread;
62 int idx;
63
64 if (!t->arch.tls_array)
65 return GDT_ENTRY_TLS_MIN;
66
67 for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++)
68 if (!t->arch.tls_array[idx].present)
69 return idx + GDT_ENTRY_TLS_MIN;
70 return -ESRCH;
71}
72
73#define O_FORCE 1
74
75static inline void clear_user_desc(struct user_desc* info)
76{
77 /* Postcondition: LDT_empty(info) returns true. */
78 memset(info, 0, sizeof(*info));
79
80 /* Check the LDT_empty or the i386 sys_get_thread_area code - we obtain
81 * indeed an empty user_desc.
82 */
83 info->read_exec_only = 1;
84 info->seg_not_present = 1;
85}
86
87static int load_TLS(int flags, struct task_struct *to)
88{
89 int ret = 0;
90 int idx;
91
92 for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) {
93 struct uml_tls_struct* curr = &to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN];
94
95 /* Actually, now if it wasn't flushed it gets cleared and
96 * flushed to the host, which will clear it.*/
97 if (!curr->present) {
98 if (!curr->flushed) {
99 clear_user_desc(&curr->tls);
100 curr->tls.entry_number = idx;
101 } else {
102 WARN_ON(!LDT_empty(&curr->tls));
103 continue;
104 }
105 }
106
107 if (!(flags & O_FORCE) && curr->flushed)
108 continue;
109
110 ret = do_set_thread_area(&curr->tls);
111 if (ret)
112 goto out;
113
114 curr->flushed = 1;
115 }
116out:
117 return ret;
118}
119
120/* Verify if we need to do a flush for the new process, i.e. if there are any
121 * present desc's, only if they haven't been flushed.
122 */
123static inline int needs_TLS_update(struct task_struct *task)
124{
125 int i;
126 int ret = 0;
127
128 for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
129 struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
130
131 /* Can't test curr->present, we may need to clear a descriptor
132 * which had a value. */
133 if (curr->flushed)
134 continue;
135 ret = 1;
136 break;
137 }
138 return ret;
139}
140
141/* On a newly forked process, the TLS descriptors haven't yet been flushed. So
142 * we mark them as such and the first switch_to will do the job.
143 */
144void clear_flushed_tls(struct task_struct *task)
145{
146 int i;
147
148 for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) {
149 struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN];
150
151 /* Still correct to do this, if it wasn't present on the host it
152 * will remain as flushed as it was. */
153 if (!curr->present)
154 continue;
155
156 curr->flushed = 0;
157 }
158}
159
160/* This in SKAS0 does not need to be used, since we have different host
161 * processes. Nor will this need to be used when we'll add support to the host
162 * SKAS patch. */
163int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to)
164{
165 return load_TLS(O_FORCE, to);
166}
167
168int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to)
169{
170 if (needs_TLS_update(to))
171 return load_TLS(0, to);
172
173 return 0;
174}
175
176static int set_tls_entry(struct task_struct* task, struct user_desc *info,
177 int idx, int flushed)
178{
179 struct thread_struct *t = &task->thread;
180
181 if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
182 return -EINVAL;
183
184 t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info;
185 t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1;
186 t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed;
187
188 return 0;
189}
190
191int arch_copy_tls(struct task_struct *new)
192{
193 struct user_desc info;
194 int idx, ret = -EFAULT;
195
196 if (copy_from_user(&info,
197 (void __user *) UPT_ESI(&new->thread.regs.regs),
198 sizeof(info)))
199 goto out;
200
201 ret = -EINVAL;
202 if (LDT_empty(&info))
203 goto out;
204
205 idx = info.entry_number;
206
207 ret = set_tls_entry(new, &info, idx, 0);
208out:
209 return ret;
210}
211
212/* XXX: use do_get_thread_area to read the host value? I'm not at all sure! */
213static int get_tls_entry(struct task_struct* task, struct user_desc *info, int idx)
214{
215 struct thread_struct *t = &task->thread;
216
217 if (!t->arch.tls_array)
218 goto clear;
219
220 if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX)
221 return -EINVAL;
222
223 if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present)
224 goto clear;
225
226 *info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls;
227
228out:
229 /* Temporary debugging check, to make sure that things have been
230 * flushed. This could be triggered if load_TLS() failed.
231 */
232 if (unlikely(task == current && !t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) {
233 printk(KERN_ERR "get_tls_entry: task with pid %d got here "
234 "without flushed TLS.", current->pid);
235 }
236
237 return 0;
238clear:
239 /* When the TLS entry has not been set, the values read to user in the
240 * tls_array are 0 (because it's cleared at boot, see
241 * arch/i386/kernel/head.S:cpu_gdt_table). Emulate that.
242 */
243 clear_user_desc(info);
244 info->entry_number = idx;
245 goto out;
246}
247
248asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc)
249{
250 struct user_desc info;
251 int idx, ret;
252
253 if (copy_from_user(&info, user_desc, sizeof(info)))
254 return -EFAULT;
255
256 idx = info.entry_number;
257
258 if (idx == -1) {
259 idx = get_free_idx(current);
260 if (idx < 0)
261 return idx;
262 info.entry_number = idx;
263 /* Tell the user which slot we chose for him.*/
264 if (put_user(idx, &user_desc->entry_number))
265 return -EFAULT;
266 }
267
268 ret = CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, &info);
269 if (ret)
270 return ret;
271 return set_tls_entry(current, &info, idx, 1);
272}
273
274/*
275 * Perform set_thread_area on behalf of the traced child.
276 * Note: error handling is not done on the deferred load, and this differ from
277 * i386. However the only possible error are caused by bugs.
278 */
279int ptrace_set_thread_area(struct task_struct *child, int idx,
280 struct user_desc __user *user_desc)
281{
282 struct user_desc info;
283
284 if (copy_from_user(&info, user_desc, sizeof(info)))
285 return -EFAULT;
286
287 return set_tls_entry(child, &info, idx, 0);
288}
289
290asmlinkage int sys_get_thread_area(struct user_desc __user *user_desc)
291{
292 struct user_desc info;
293 int idx, ret;
294
295 if (get_user(idx, &user_desc->entry_number))
296 return -EFAULT;
297
298 ret = get_tls_entry(current, &info, idx);
299 if (ret < 0)
300 goto out;
301
302 if (copy_to_user(user_desc, &info, sizeof(info)))
303 ret = -EFAULT;
304
305out:
306 return ret;
307}
308
309/*
310 * Perform get_thread_area on behalf of the traced child.
311 */
312int ptrace_get_thread_area(struct task_struct *child, int idx,
313 struct user_desc __user *user_desc)
314{
315 struct user_desc info;
316 int ret;
317
318 ret = get_tls_entry(child, &info, idx);
319 if (ret < 0)
320 goto out;
321
322 if (copy_to_user(user_desc, &info, sizeof(info)))
323 ret = -EFAULT;
324out:
325 return ret;
326}
diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile
index 1ee2ed1599be..b5fc22babddf 100644
--- a/arch/um/sys-x86_64/Makefile
+++ b/arch/um/sys-x86_64/Makefile
@@ -5,7 +5,8 @@
5# 5#
6 6
7obj-y = bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \ 7obj-y = bugs.o delay.o fault.o ldt.o mem.o ptrace.o ptrace_user.o \
8 sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o ksyms.o 8 sigcontext.o signal.o syscalls.o syscall_table.o sysrq.o ksyms.o \
9 tls.o
9 10
10obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o 11obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
11obj-$(CONFIG_MODULES) += um_module.o 12obj-$(CONFIG_MODULES) += um_module.o
diff --git a/arch/um/sys-x86_64/tls.c b/arch/um/sys-x86_64/tls.c
new file mode 100644
index 000000000000..ce1bf1b81c43
--- /dev/null
+++ b/arch/um/sys-x86_64/tls.c
@@ -0,0 +1,14 @@
1#include "linux/sched.h"
2
3void debug_arch_force_load_TLS(void)
4{
5}
6
7void clear_flushed_tls(struct task_struct *task)
8{
9}
10
11int arch_copy_tls(struct task_struct *t)
12{
13 return 0;
14}
diff --git a/include/asm-um/desc.h b/include/asm-um/desc.h
index ac1d2a20d178..4ec34a51b62c 100644
--- a/include/asm-um/desc.h
+++ b/include/asm-um/desc.h
@@ -1,6 +1,16 @@
1#ifndef __UM_DESC_H 1#ifndef __UM_DESC_H
2#define __UM_DESC_H 2#define __UM_DESC_H
3 3
4#include "asm/arch/desc.h" 4/* Taken from asm-i386/desc.h, it's the only thing we need. The rest wouldn't
5 * compile, and has never been used. */
6#define LDT_empty(info) (\
7 (info)->base_addr == 0 && \
8 (info)->limit == 0 && \
9 (info)->contents == 0 && \
10 (info)->read_exec_only == 1 && \
11 (info)->seg_32bit == 0 && \
12 (info)->limit_in_pages == 0 && \
13 (info)->seg_not_present == 1 && \
14 (info)->useable == 0 )
5 15
6#endif 16#endif
diff --git a/include/asm-um/processor-i386.h b/include/asm-um/processor-i386.h
index 4108a579eb92..595f1c3e1e40 100644
--- a/include/asm-um/processor-i386.h
+++ b/include/asm-um/processor-i386.h
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) 2 * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
3 * Licensed under the GPL 3 * Licensed under the GPL
4 */ 4 */
@@ -6,21 +6,48 @@
6#ifndef __UM_PROCESSOR_I386_H 6#ifndef __UM_PROCESSOR_I386_H
7#define __UM_PROCESSOR_I386_H 7#define __UM_PROCESSOR_I386_H
8 8
9#include "linux/string.h"
10#include "asm/host_ldt.h"
11#include "asm/segment.h"
12
9extern int host_has_xmm; 13extern int host_has_xmm;
10extern int host_has_cmov; 14extern int host_has_cmov;
11 15
12/* include faultinfo structure */ 16/* include faultinfo structure */
13#include "sysdep/faultinfo.h" 17#include "sysdep/faultinfo.h"
14 18
19struct uml_tls_struct {
20 struct user_desc tls;
21 unsigned flushed:1;
22 unsigned present:1;
23};
24
15struct arch_thread { 25struct arch_thread {
26 struct uml_tls_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
16 unsigned long debugregs[8]; 27 unsigned long debugregs[8];
17 int debugregs_seq; 28 int debugregs_seq;
18 struct faultinfo faultinfo; 29 struct faultinfo faultinfo;
19}; 30};
20 31
21#define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \ 32#define INIT_ARCH_THREAD { \
22 .debugregs_seq = 0, \ 33 .tls_array = { [ 0 ... GDT_ENTRY_TLS_ENTRIES - 1 ] = \
23 .faultinfo = { 0, 0, 0 } } 34 { .present = 0, .flushed = 0 } }, \
35 .debugregs = { [ 0 ... 7 ] = 0 }, \
36 .debugregs_seq = 0, \
37 .faultinfo = { 0, 0, 0 } \
38}
39
40static inline void arch_flush_thread(struct arch_thread *thread)
41{
42 /* Clear any TLS still hanging */
43 memset(&thread->tls_array, 0, sizeof(thread->tls_array));
44}
45
46static inline void arch_copy_thread(struct arch_thread *from,
47 struct arch_thread *to)
48{
49 memcpy(&to->tls_array, &from->tls_array, sizeof(from->tls_array));
50}
24 51
25#include "asm/arch/user.h" 52#include "asm/arch/user.h"
26 53
diff --git a/include/asm-um/processor-x86_64.h b/include/asm-um/processor-x86_64.h
index e1e1255a1d36..10609af376c0 100644
--- a/include/asm-um/processor-x86_64.h
+++ b/include/asm-um/processor-x86_64.h
@@ -28,6 +28,15 @@ extern inline void rep_nop(void)
28 .debugregs_seq = 0, \ 28 .debugregs_seq = 0, \
29 .faultinfo = { 0, 0, 0 } } 29 .faultinfo = { 0, 0, 0 } }
30 30
31static inline void arch_flush_thread(struct arch_thread *thread)
32{
33}
34
35static inline void arch_copy_thread(struct arch_thread *from,
36 struct arch_thread *to)
37{
38}
39
31#include "asm/arch/user.h" 40#include "asm/arch/user.h"
32 41
33#define current_text_addr() \ 42#define current_text_addr() \
diff --git a/include/asm-um/ptrace-generic.h b/include/asm-um/ptrace-generic.h
index 8c57e384cb8a..503484305e67 100644
--- a/include/asm-um/ptrace-generic.h
+++ b/include/asm-um/ptrace-generic.h
@@ -60,17 +60,9 @@ extern void show_regs(struct pt_regs *regs);
60extern void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs, 60extern void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
61 int error_code); 61 int error_code);
62 62
63#endif 63extern int arch_copy_tls(struct task_struct *new);
64extern void clear_flushed_tls(struct task_struct *task);
64 65
65#endif 66#endif
66 67
67/* 68#endif
68 * Overrides for Emacs so that we follow Linus's tabbing style.
69 * Emacs will notice this stuff at the end of the file and automatically
70 * adjust the settings for this buffer only. This must remain at the end
71 * of the file.
72 * ---------------------------------------------------------------------------
73 * Local variables:
74 * c-file-style: "linux"
75 * End:
76 */
diff --git a/include/asm-um/ptrace-i386.h b/include/asm-um/ptrace-i386.h
index fe882b9d917e..30656c962d74 100644
--- a/include/asm-um/ptrace-i386.h
+++ b/include/asm-um/ptrace-i386.h
@@ -8,8 +8,11 @@
8 8
9#define HOST_AUDIT_ARCH AUDIT_ARCH_I386 9#define HOST_AUDIT_ARCH AUDIT_ARCH_I386
10 10
11#include "linux/compiler.h"
11#include "sysdep/ptrace.h" 12#include "sysdep/ptrace.h"
12#include "asm/ptrace-generic.h" 13#include "asm/ptrace-generic.h"
14#include "asm/host_ldt.h"
15#include "choose-mode.h"
13 16
14#define PT_REGS_EAX(r) UPT_EAX(&(r)->regs) 17#define PT_REGS_EAX(r) UPT_EAX(&(r)->regs)
15#define PT_REGS_EBX(r) UPT_EBX(&(r)->regs) 18#define PT_REGS_EBX(r) UPT_EBX(&(r)->regs)
@@ -38,15 +41,31 @@
38 41
39#define user_mode(r) UPT_IS_USER(&(r)->regs) 42#define user_mode(r) UPT_IS_USER(&(r)->regs)
40 43
41#endif 44extern int ptrace_get_thread_area(struct task_struct *child, int idx,
45 struct user_desc __user *user_desc);
42 46
43/* 47extern int ptrace_set_thread_area(struct task_struct *child, int idx,
44 * Overrides for Emacs so that we follow Linus's tabbing style. 48 struct user_desc __user *user_desc);
45 * Emacs will notice this stuff at the end of the file and automatically 49
46 * adjust the settings for this buffer only. This must remain at the end 50extern int do_set_thread_area_skas(struct user_desc *info);
47 * of the file. 51extern int do_get_thread_area_skas(struct user_desc *info);
48 * --------------------------------------------------------------------------- 52
49 * Local variables: 53extern int do_set_thread_area_tt(struct user_desc *info);
50 * c-file-style: "linux" 54extern int do_get_thread_area_tt(struct user_desc *info);
51 * End: 55
52 */ 56extern int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to);
57extern int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to);
58
59static inline int do_get_thread_area(struct user_desc *info)
60{
61 return CHOOSE_MODE_PROC(do_get_thread_area_tt, do_get_thread_area_skas, info);
62}
63
64static inline int do_set_thread_area(struct user_desc *info)
65{
66 return CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, info);
67}
68
69struct task_struct;
70
71#endif
diff --git a/include/asm-um/ptrace-x86_64.h b/include/asm-um/ptrace-x86_64.h
index be51219a8ffe..c894e68b1f96 100644
--- a/include/asm-um/ptrace-x86_64.h
+++ b/include/asm-um/ptrace-x86_64.h
@@ -8,6 +8,8 @@
8#define __UM_PTRACE_X86_64_H 8#define __UM_PTRACE_X86_64_H
9 9
10#include "linux/compiler.h" 10#include "linux/compiler.h"
11#include "asm/errno.h"
12#include "asm/host_ldt.h"
11 13
12#define signal_fault signal_fault_x86_64 14#define signal_fault signal_fault_x86_64
13#define __FRAME_OFFSETS /* Needed to get the R* macros */ 15#define __FRAME_OFFSETS /* Needed to get the R* macros */
@@ -63,15 +65,26 @@ void signal_fault(struct pt_regs_subarch *regs, void *frame, char *where);
63 65
64#define profile_pc(regs) PT_REGS_IP(regs) 66#define profile_pc(regs) PT_REGS_IP(regs)
65 67
66#endif 68static inline int ptrace_get_thread_area(struct task_struct *child, int idx,
69 struct user_desc __user *user_desc)
70{
71 return -ENOSYS;
72}
67 73
68/* 74static inline int ptrace_set_thread_area(struct task_struct *child, int idx,
69 * Overrides for Emacs so that we follow Linus's tabbing style. 75 struct user_desc __user *user_desc)
70 * Emacs will notice this stuff at the end of the file and automatically 76{
71 * adjust the settings for this buffer only. This must remain at the end 77 return -ENOSYS;
72 * of the file. 78}
73 * --------------------------------------------------------------------------- 79
74 * Local variables: 80static inline void arch_switch_to_tt(struct task_struct *from,
75 * c-file-style: "linux" 81 struct task_struct *to)
76 * End: 82{
77 */ 83}
84
85static inline void arch_switch_to_skas(struct task_struct *from,
86 struct task_struct *to)
87{
88}
89
90#endif
diff --git a/include/asm-um/segment.h b/include/asm-um/segment.h
index 55e40301f625..48775452e2cc 100644
--- a/include/asm-um/segment.h
+++ b/include/asm-um/segment.h
@@ -1,4 +1,6 @@
1#ifndef __UM_SEGMENT_H 1#ifndef __UM_SEGMENT_H
2#define __UM_SEGMENT_H 2#define __UM_SEGMENT_H
3 3
4#include "asm/arch/segment.h"
5
4#endif 6#endif