diff options
Diffstat (limited to 'arch/um/sys-i386')
-rw-r--r-- | arch/um/sys-i386/Makefile | 23 | ||||
-rw-r--r-- | arch/um/sys-i386/ptrace.c | 45 | ||||
-rw-r--r-- | arch/um/sys-i386/ptrace_user.c | 10 | ||||
-rw-r--r-- | arch/um/sys-i386/signal.c | 48 | ||||
-rw-r--r-- | arch/um/sys-i386/sys_call_table.S | 2 | ||||
-rw-r--r-- | arch/um/sys-i386/syscalls.c | 16 | ||||
-rw-r--r-- | arch/um/sys-i386/tls.c | 384 |
7 files changed, 467 insertions, 61 deletions
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index f5fd5b0156d0..98b20b7bba4f 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile | |||
@@ -1,23 +1,18 @@ | |||
1 | obj-y := bitops.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ | 1 | obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ |
2 | ptrace_user.o semaphore.o signal.o sigcontext.o syscalls.o sysrq.o \ | 2 | ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o \ |
3 | sys_call_table.o | 3 | sys_call_table.o tls.o |
4 | 4 | ||
5 | obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o | 5 | obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o |
6 | 6 | ||
7 | obj-$(CONFIG_HIGHMEM) += highmem.o | 7 | subarch-obj-y = lib/bitops.o kernel/semaphore.o |
8 | obj-$(CONFIG_MODULES) += module.o | 8 | subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem.o |
9 | subarch-obj-$(CONFIG_MODULES) += kernel/module.o | ||
9 | 10 | ||
10 | USER_OBJS := bugs.o ptrace_user.o sigcontext.o fault.o stub_segv.o | 11 | USER_OBJS := bugs.o ptrace_user.o sigcontext.o fault.o stub_segv.o |
11 | 12 | ||
12 | SYMLINKS = bitops.c semaphore.c highmem.c module.c | ||
13 | |||
14 | include arch/um/scripts/Makefile.rules | 13 | include arch/um/scripts/Makefile.rules |
15 | 14 | ||
16 | bitops.c-dir = lib | 15 | extra-$(CONFIG_MODE_TT) += unmap.o |
17 | semaphore.c-dir = kernel | ||
18 | highmem.c-dir = mm | ||
19 | module.c-dir = kernel | ||
20 | |||
21 | $(obj)/stub_segv.o : _c_flags = $(call unprofile,$(CFLAGS)) | ||
22 | 16 | ||
23 | include arch/um/scripts/Makefile.unmap | 17 | $(obj)/stub_segv.o $(obj)/unmap.o: \ |
18 | _c_flags = $(call unprofile,$(CFLAGS)) | ||
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c index 8032a105949a..6028bc7cc01b 100644 --- a/arch/um/sys-i386/ptrace.c +++ b/arch/um/sys-i386/ptrace.c | |||
@@ -15,9 +15,22 @@ | |||
15 | #include "sysdep/sigcontext.h" | 15 | #include "sysdep/sigcontext.h" |
16 | #include "sysdep/sc.h" | 16 | #include "sysdep/sc.h" |
17 | 17 | ||
18 | void arch_switch(void) | 18 | void arch_switch_to_tt(struct task_struct *from, struct task_struct *to) |
19 | { | 19 | { |
20 | update_debugregs(current->thread.arch.debugregs_seq); | 20 | update_debugregs(to->thread.arch.debugregs_seq); |
21 | arch_switch_tls_tt(from, to); | ||
22 | } | ||
23 | |||
24 | void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) | ||
25 | { | ||
26 | int err = arch_switch_tls_skas(from, to); | ||
27 | if (!err) | ||
28 | return; | ||
29 | |||
30 | if (err != -EINVAL) | ||
31 | printk(KERN_WARNING "arch_switch_tls_skas failed, errno %d, not EINVAL\n", -err); | ||
32 | else | ||
33 | printk(KERN_WARNING "arch_switch_tls_skas failed, errno = EINVAL\n"); | ||
21 | } | 34 | } |
22 | 35 | ||
23 | int is_syscall(unsigned long addr) | 36 | int is_syscall(unsigned long addr) |
@@ -124,22 +137,22 @@ unsigned long getreg(struct task_struct *child, int regno) | |||
124 | int peek_user(struct task_struct *child, long addr, long data) | 137 | int peek_user(struct task_struct *child, long addr, long data) |
125 | { | 138 | { |
126 | /* read the word at location addr in the USER area. */ | 139 | /* read the word at location addr in the USER area. */ |
127 | unsigned long tmp; | 140 | unsigned long tmp; |
128 | 141 | ||
129 | if ((addr & 3) || addr < 0) | 142 | if ((addr & 3) || addr < 0) |
130 | return -EIO; | 143 | return -EIO; |
131 | 144 | ||
132 | tmp = 0; /* Default return condition */ | 145 | tmp = 0; /* Default return condition */ |
133 | if(addr < MAX_REG_OFFSET){ | 146 | if(addr < MAX_REG_OFFSET){ |
134 | tmp = getreg(child, addr); | 147 | tmp = getreg(child, addr); |
135 | } | 148 | } |
136 | else if((addr >= offsetof(struct user, u_debugreg[0])) && | 149 | else if((addr >= offsetof(struct user, u_debugreg[0])) && |
137 | (addr <= offsetof(struct user, u_debugreg[7]))){ | 150 | (addr <= offsetof(struct user, u_debugreg[7]))){ |
138 | addr -= offsetof(struct user, u_debugreg[0]); | 151 | addr -= offsetof(struct user, u_debugreg[0]); |
139 | addr = addr >> 2; | 152 | addr = addr >> 2; |
140 | tmp = child->thread.arch.debugregs[addr]; | 153 | tmp = child->thread.arch.debugregs[addr]; |
141 | } | 154 | } |
142 | return put_user(tmp, (unsigned long *) data); | 155 | return put_user(tmp, (unsigned long __user *) data); |
143 | } | 156 | } |
144 | 157 | ||
145 | struct i387_fxsave_struct { | 158 | struct i387_fxsave_struct { |
diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c index 7c376c95de50..9f3bd8ed78f5 100644 --- a/arch/um/sys-i386/ptrace_user.c +++ b/arch/um/sys-i386/ptrace_user.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include "sysdep/thread.h" | 14 | #include "sysdep/thread.h" |
15 | #include "user.h" | 15 | #include "user.h" |
16 | #include "os.h" | 16 | #include "os.h" |
17 | #include "uml-config.h" | ||
17 | 18 | ||
18 | int ptrace_getregs(long pid, unsigned long *regs_out) | 19 | int ptrace_getregs(long pid, unsigned long *regs_out) |
19 | { | 20 | { |
@@ -43,6 +44,7 @@ int ptrace_setfpregs(long pid, unsigned long *regs) | |||
43 | return 0; | 44 | return 0; |
44 | } | 45 | } |
45 | 46 | ||
47 | /* All the below stuff is of interest for TT mode only */ | ||
46 | static void write_debugregs(int pid, unsigned long *regs) | 48 | static void write_debugregs(int pid, unsigned long *regs) |
47 | { | 49 | { |
48 | struct user *dummy; | 50 | struct user *dummy; |
@@ -75,7 +77,6 @@ static void read_debugregs(int pid, unsigned long *regs) | |||
75 | 77 | ||
76 | /* Accessed only by the tracing thread */ | 78 | /* Accessed only by the tracing thread */ |
77 | static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; | 79 | static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; |
78 | static int debugregs_seq = 0; | ||
79 | 80 | ||
80 | void arch_enter_kernel(void *task, int pid) | 81 | void arch_enter_kernel(void *task, int pid) |
81 | { | 82 | { |
@@ -89,6 +90,11 @@ void arch_leave_kernel(void *task, int pid) | |||
89 | write_debugregs(pid, TASK_DEBUGREGS(task)); | 90 | write_debugregs(pid, TASK_DEBUGREGS(task)); |
90 | } | 91 | } |
91 | 92 | ||
93 | #ifdef UML_CONFIG_PT_PROXY | ||
94 | /* Accessed only by the tracing thread */ | ||
95 | static int debugregs_seq; | ||
96 | |||
97 | /* Only called by the ptrace proxy */ | ||
92 | void ptrace_pokeuser(unsigned long addr, unsigned long data) | 98 | void ptrace_pokeuser(unsigned long addr, unsigned long data) |
93 | { | 99 | { |
94 | if((addr < offsetof(struct user, u_debugreg[0])) || | 100 | if((addr < offsetof(struct user, u_debugreg[0])) || |
@@ -109,6 +115,7 @@ static void update_debugregs_cb(void *arg) | |||
109 | write_debugregs(pid, kernel_debugregs); | 115 | write_debugregs(pid, kernel_debugregs); |
110 | } | 116 | } |
111 | 117 | ||
118 | /* Optimized out in its header when not defined */ | ||
112 | void update_debugregs(int seq) | 119 | void update_debugregs(int seq) |
113 | { | 120 | { |
114 | int me; | 121 | int me; |
@@ -118,6 +125,7 @@ void update_debugregs(int seq) | |||
118 | me = os_getpid(); | 125 | me = os_getpid(); |
119 | initial_thread_cb(update_debugregs_cb, &me); | 126 | initial_thread_cb(update_debugregs_cb, &me); |
120 | } | 127 | } |
128 | #endif | ||
121 | 129 | ||
122 | /* | 130 | /* |
123 | * Overrides for Emacs so that we follow Linus's tabbing style. | 131 | * Overrides for Emacs so that we follow Linus's tabbing style. |
diff --git a/arch/um/sys-i386/signal.c b/arch/um/sys-i386/signal.c index 33a40f5ef0d2..f5d0e1c37ea2 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include "skas.h" | 19 | #include "skas.h" |
20 | 20 | ||
21 | static int copy_sc_from_user_skas(struct pt_regs *regs, | 21 | static int copy_sc_from_user_skas(struct pt_regs *regs, |
22 | struct sigcontext *from) | 22 | struct sigcontext __user *from) |
23 | { | 23 | { |
24 | struct sigcontext sc; | 24 | struct sigcontext sc; |
25 | unsigned long fpregs[HOST_FP_SIZE]; | 25 | unsigned long fpregs[HOST_FP_SIZE]; |
@@ -57,7 +57,7 @@ static int copy_sc_from_user_skas(struct pt_regs *regs, | |||
57 | return(0); | 57 | return(0); |
58 | } | 58 | } |
59 | 59 | ||
60 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | 60 | int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate __user *to_fp, |
61 | struct pt_regs *regs, unsigned long sp) | 61 | struct pt_regs *regs, unsigned long sp) |
62 | { | 62 | { |
63 | struct sigcontext sc; | 63 | struct sigcontext sc; |
@@ -92,7 +92,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | |||
92 | "errno = %d\n", err); | 92 | "errno = %d\n", err); |
93 | return(1); | 93 | return(1); |
94 | } | 94 | } |
95 | to_fp = (to_fp ? to_fp : (struct _fpstate *) (to + 1)); | 95 | to_fp = (to_fp ? to_fp : (struct _fpstate __user *) (to + 1)); |
96 | sc.fpstate = to_fp; | 96 | sc.fpstate = to_fp; |
97 | 97 | ||
98 | if(err) | 98 | if(err) |
@@ -113,10 +113,11 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, | |||
113 | * saved pointer is in the kernel, but the sigcontext is in userspace, so we | 113 | * saved pointer is in the kernel, but the sigcontext is in userspace, so we |
114 | * copy_to_user it. | 114 | * copy_to_user it. |
115 | */ | 115 | */ |
116 | int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, | 116 | int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext __user *from, |
117 | int fpsize) | 117 | int fpsize) |
118 | { | 118 | { |
119 | struct _fpstate *to_fp, *from_fp; | 119 | struct _fpstate *to_fp; |
120 | struct _fpstate __user *from_fp; | ||
120 | unsigned long sigs; | 121 | unsigned long sigs; |
121 | int err; | 122 | int err; |
122 | 123 | ||
@@ -131,13 +132,14 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, | |||
131 | return(err); | 132 | return(err); |
132 | } | 133 | } |
133 | 134 | ||
134 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, | 135 | int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate __user *fp, |
135 | struct sigcontext *from, int fpsize, unsigned long sp) | 136 | struct sigcontext *from, int fpsize, unsigned long sp) |
136 | { | 137 | { |
137 | struct _fpstate *to_fp, *from_fp; | 138 | struct _fpstate __user *to_fp; |
139 | struct _fpstate *from_fp; | ||
138 | int err; | 140 | int err; |
139 | 141 | ||
140 | to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); | 142 | to_fp = (fp ? fp : (struct _fpstate __user *) (to + 1)); |
141 | from_fp = from->fpstate; | 143 | from_fp = from->fpstate; |
142 | err = copy_to_user(to, from, sizeof(*to)); | 144 | err = copy_to_user(to, from, sizeof(*to)); |
143 | 145 | ||
@@ -165,7 +167,7 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from) | |||
165 | return(ret); | 167 | return(ret); |
166 | } | 168 | } |
167 | 169 | ||
168 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, | 170 | static int copy_sc_to_user(struct sigcontext *to, struct _fpstate __user *fp, |
169 | struct pt_regs *from, unsigned long sp) | 171 | struct pt_regs *from, unsigned long sp) |
170 | { | 172 | { |
171 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), | 173 | return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), |
@@ -173,7 +175,7 @@ static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, | |||
173 | copy_sc_to_user_skas(to, fp, from, sp))); | 175 | copy_sc_to_user_skas(to, fp, from, sp))); |
174 | } | 176 | } |
175 | 177 | ||
176 | static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, | 178 | static int copy_ucontext_to_user(struct ucontext __user *uc, struct _fpstate __user *fp, |
177 | sigset_t *set, unsigned long sp) | 179 | sigset_t *set, unsigned long sp) |
178 | { | 180 | { |
179 | int err = 0; | 181 | int err = 0; |
@@ -188,7 +190,7 @@ static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, | |||
188 | 190 | ||
189 | struct sigframe | 191 | struct sigframe |
190 | { | 192 | { |
191 | char *pretcode; | 193 | char __user *pretcode; |
192 | int sig; | 194 | int sig; |
193 | struct sigcontext sc; | 195 | struct sigcontext sc; |
194 | struct _fpstate fpstate; | 196 | struct _fpstate fpstate; |
@@ -198,10 +200,10 @@ struct sigframe | |||
198 | 200 | ||
199 | struct rt_sigframe | 201 | struct rt_sigframe |
200 | { | 202 | { |
201 | char *pretcode; | 203 | char __user *pretcode; |
202 | int sig; | 204 | int sig; |
203 | struct siginfo *pinfo; | 205 | struct siginfo __user *pinfo; |
204 | void *puc; | 206 | void __user *puc; |
205 | struct siginfo info; | 207 | struct siginfo info; |
206 | struct ucontext uc; | 208 | struct ucontext uc; |
207 | struct _fpstate fpstate; | 209 | struct _fpstate fpstate; |
@@ -213,16 +215,16 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, | |||
213 | sigset_t *mask) | 215 | sigset_t *mask) |
214 | { | 216 | { |
215 | struct sigframe __user *frame; | 217 | struct sigframe __user *frame; |
216 | void *restorer; | 218 | void __user *restorer; |
217 | unsigned long save_sp = PT_REGS_SP(regs); | 219 | unsigned long save_sp = PT_REGS_SP(regs); |
218 | int err = 0; | 220 | int err = 0; |
219 | 221 | ||
220 | stack_top &= -8UL; | 222 | stack_top &= -8UL; |
221 | frame = (struct sigframe *) stack_top - 1; | 223 | frame = (struct sigframe __user *) stack_top - 1; |
222 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 224 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) |
223 | return 1; | 225 | return 1; |
224 | 226 | ||
225 | restorer = (void *) frame->retcode; | 227 | restorer = frame->retcode; |
226 | if(ka->sa.sa_flags & SA_RESTORER) | 228 | if(ka->sa.sa_flags & SA_RESTORER) |
227 | restorer = ka->sa.sa_restorer; | 229 | restorer = ka->sa.sa_restorer; |
228 | 230 | ||
@@ -278,16 +280,16 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, | |||
278 | siginfo_t *info, sigset_t *mask) | 280 | siginfo_t *info, sigset_t *mask) |
279 | { | 281 | { |
280 | struct rt_sigframe __user *frame; | 282 | struct rt_sigframe __user *frame; |
281 | void *restorer; | 283 | void __user *restorer; |
282 | unsigned long save_sp = PT_REGS_SP(regs); | 284 | unsigned long save_sp = PT_REGS_SP(regs); |
283 | int err = 0; | 285 | int err = 0; |
284 | 286 | ||
285 | stack_top &= -8UL; | 287 | stack_top &= -8UL; |
286 | frame = (struct rt_sigframe *) stack_top - 1; | 288 | frame = (struct rt_sigframe __user *) stack_top - 1; |
287 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | 289 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) |
288 | return 1; | 290 | return 1; |
289 | 291 | ||
290 | restorer = (void *) frame->retcode; | 292 | restorer = frame->retcode; |
291 | if(ka->sa.sa_flags & SA_RESTORER) | 293 | if(ka->sa.sa_flags & SA_RESTORER) |
292 | restorer = ka->sa.sa_restorer; | 294 | restorer = ka->sa.sa_restorer; |
293 | 295 | ||
@@ -333,7 +335,7 @@ err: | |||
333 | long sys_sigreturn(struct pt_regs regs) | 335 | long sys_sigreturn(struct pt_regs regs) |
334 | { | 336 | { |
335 | unsigned long sp = PT_REGS_SP(¤t->thread.regs); | 337 | unsigned long sp = PT_REGS_SP(¤t->thread.regs); |
336 | struct sigframe __user *frame = (struct sigframe *)(sp - 8); | 338 | struct sigframe __user *frame = (struct sigframe __user *)(sp - 8); |
337 | sigset_t set; | 339 | sigset_t set; |
338 | struct sigcontext __user *sc = &frame->sc; | 340 | struct sigcontext __user *sc = &frame->sc; |
339 | unsigned long __user *oldmask = &sc->oldmask; | 341 | unsigned long __user *oldmask = &sc->oldmask; |
@@ -365,8 +367,8 @@ long sys_sigreturn(struct pt_regs regs) | |||
365 | 367 | ||
366 | long sys_rt_sigreturn(struct pt_regs regs) | 368 | long sys_rt_sigreturn(struct pt_regs regs) |
367 | { | 369 | { |
368 | unsigned long __user sp = PT_REGS_SP(¤t->thread.regs); | 370 | unsigned long sp = PT_REGS_SP(¤t->thread.regs); |
369 | struct rt_sigframe __user *frame = (struct rt_sigframe *) (sp - 4); | 371 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *) (sp - 4); |
370 | sigset_t set; | 372 | sigset_t set; |
371 | struct ucontext __user *uc = &frame->uc; | 373 | struct ucontext __user *uc = &frame->uc; |
372 | int sig_size = _NSIG_WORDS * sizeof(unsigned long); | 374 | int sig_size = _NSIG_WORDS * sizeof(unsigned long); |
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 83e9be820a86..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 | */ |
67 | long sys_clone(unsigned long clone_flags, unsigned long newsp, | 72 | long 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(¤t->thread.regs.regs); | 78 | newsp = UPT_SP(¤t->thread.regs.regs); |
79 | |||
74 | current->thread.forking = 1; | 80 | current->thread.forking = 1; |
75 | ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, | 81 | ret = do_fork(clone_flags, newsp, ¤t->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 | /* |
@@ -104,7 +110,7 @@ long sys_ipc (uint call, int first, int second, | |||
104 | union semun fourth; | 110 | union semun fourth; |
105 | if (!ptr) | 111 | if (!ptr) |
106 | return -EINVAL; | 112 | return -EINVAL; |
107 | if (get_user(fourth.__pad, (void **) ptr)) | 113 | if (get_user(fourth.__pad, (void __user * __user *) ptr)) |
108 | return -EFAULT; | 114 | return -EFAULT; |
109 | return sys_semctl (first, second, third, fourth); | 115 | return sys_semctl (first, second, third, fourth); |
110 | } | 116 | } |
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c new file mode 100644 index 000000000000..a3188e861cc7 --- /dev/null +++ b/arch/um/sys-i386/tls.c | |||
@@ -0,0 +1,384 @@ | |||
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 | /* If needed we can detect when it's uninitialized. */ | ||
28 | static int host_supports_tls = -1; | ||
29 | int host_gdt_entry_tls_min = -1; | ||
30 | |||
31 | #ifdef CONFIG_MODE_SKAS | ||
32 | int do_set_thread_area_skas(struct user_desc *info) | ||
33 | { | ||
34 | int ret; | ||
35 | u32 cpu; | ||
36 | |||
37 | cpu = get_cpu(); | ||
38 | ret = os_set_thread_area(info, userspace_pid[cpu]); | ||
39 | put_cpu(); | ||
40 | return ret; | ||
41 | } | ||
42 | |||
43 | int do_get_thread_area_skas(struct user_desc *info) | ||
44 | { | ||
45 | int ret; | ||
46 | u32 cpu; | ||
47 | |||
48 | cpu = get_cpu(); | ||
49 | ret = os_get_thread_area(info, userspace_pid[cpu]); | ||
50 | put_cpu(); | ||
51 | return ret; | ||
52 | } | ||
53 | #endif | ||
54 | |||
55 | /* | ||
56 | * sys_get_thread_area: get a yet unused TLS descriptor index. | ||
57 | * XXX: Consider leaving one free slot for glibc usage at first place. This must | ||
58 | * be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else. | ||
59 | * | ||
60 | * Also, this must be tested when compiling in SKAS mode with dinamic linking | ||
61 | * and running against NPTL. | ||
62 | */ | ||
63 | static int get_free_idx(struct task_struct* task) | ||
64 | { | ||
65 | struct thread_struct *t = &task->thread; | ||
66 | int idx; | ||
67 | |||
68 | if (!t->arch.tls_array) | ||
69 | return GDT_ENTRY_TLS_MIN; | ||
70 | |||
71 | for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) | ||
72 | if (!t->arch.tls_array[idx].present) | ||
73 | return idx + GDT_ENTRY_TLS_MIN; | ||
74 | return -ESRCH; | ||
75 | } | ||
76 | |||
77 | static inline void clear_user_desc(struct user_desc* info) | ||
78 | { | ||
79 | /* Postcondition: LDT_empty(info) returns true. */ | ||
80 | memset(info, 0, sizeof(*info)); | ||
81 | |||
82 | /* Check the LDT_empty or the i386 sys_get_thread_area code - we obtain | ||
83 | * indeed an empty user_desc. | ||
84 | */ | ||
85 | info->read_exec_only = 1; | ||
86 | info->seg_not_present = 1; | ||
87 | } | ||
88 | |||
89 | #define O_FORCE 1 | ||
90 | |||
91 | static int load_TLS(int flags, struct task_struct *to) | ||
92 | { | ||
93 | int ret = 0; | ||
94 | int idx; | ||
95 | |||
96 | for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) { | ||
97 | struct uml_tls_struct* curr = &to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN]; | ||
98 | |||
99 | /* Actually, now if it wasn't flushed it gets cleared and | ||
100 | * flushed to the host, which will clear it.*/ | ||
101 | if (!curr->present) { | ||
102 | if (!curr->flushed) { | ||
103 | clear_user_desc(&curr->tls); | ||
104 | curr->tls.entry_number = idx; | ||
105 | } else { | ||
106 | WARN_ON(!LDT_empty(&curr->tls)); | ||
107 | continue; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | if (!(flags & O_FORCE) && curr->flushed) | ||
112 | continue; | ||
113 | |||
114 | ret = do_set_thread_area(&curr->tls); | ||
115 | if (ret) | ||
116 | goto out; | ||
117 | |||
118 | curr->flushed = 1; | ||
119 | } | ||
120 | out: | ||
121 | return ret; | ||
122 | } | ||
123 | |||
124 | /* Verify if we need to do a flush for the new process, i.e. if there are any | ||
125 | * present desc's, only if they haven't been flushed. | ||
126 | */ | ||
127 | static inline int needs_TLS_update(struct task_struct *task) | ||
128 | { | ||
129 | int i; | ||
130 | int ret = 0; | ||
131 | |||
132 | for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { | ||
133 | struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; | ||
134 | |||
135 | /* Can't test curr->present, we may need to clear a descriptor | ||
136 | * which had a value. */ | ||
137 | if (curr->flushed) | ||
138 | continue; | ||
139 | ret = 1; | ||
140 | break; | ||
141 | } | ||
142 | return ret; | ||
143 | } | ||
144 | |||
145 | /* On a newly forked process, the TLS descriptors haven't yet been flushed. So | ||
146 | * we mark them as such and the first switch_to will do the job. | ||
147 | */ | ||
148 | void clear_flushed_tls(struct task_struct *task) | ||
149 | { | ||
150 | int i; | ||
151 | |||
152 | for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { | ||
153 | struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; | ||
154 | |||
155 | /* Still correct to do this, if it wasn't present on the host it | ||
156 | * will remain as flushed as it was. */ | ||
157 | if (!curr->present) | ||
158 | continue; | ||
159 | |||
160 | curr->flushed = 0; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | /* In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a | ||
165 | * common host process. So this is needed in SKAS0 too. | ||
166 | * | ||
167 | * However, if each thread had a different host process (and this was discussed | ||
168 | * for SMP support) this won't be needed. | ||
169 | * | ||
170 | * And this will not need be used when (and if) we'll add support to the host | ||
171 | * SKAS patch. */ | ||
172 | |||
173 | int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) | ||
174 | { | ||
175 | if (!host_supports_tls) | ||
176 | return 0; | ||
177 | |||
178 | /* We have no need whatsoever to switch TLS for kernel threads; beyond | ||
179 | * that, that would also result in us calling os_set_thread_area with | ||
180 | * userspace_pid[cpu] == 0, which gives an error. */ | ||
181 | if (likely(to->mm)) | ||
182 | return load_TLS(O_FORCE, to); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to) | ||
188 | { | ||
189 | if (!host_supports_tls) | ||
190 | return 0; | ||
191 | |||
192 | if (needs_TLS_update(to)) | ||
193 | return load_TLS(0, to); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static int set_tls_entry(struct task_struct* task, struct user_desc *info, | ||
199 | int idx, int flushed) | ||
200 | { | ||
201 | struct thread_struct *t = &task->thread; | ||
202 | |||
203 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
204 | return -EINVAL; | ||
205 | |||
206 | t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info; | ||
207 | t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1; | ||
208 | t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | int arch_copy_tls(struct task_struct *new) | ||
214 | { | ||
215 | struct user_desc info; | ||
216 | int idx, ret = -EFAULT; | ||
217 | |||
218 | if (copy_from_user(&info, | ||
219 | (void __user *) UPT_ESI(&new->thread.regs.regs), | ||
220 | sizeof(info))) | ||
221 | goto out; | ||
222 | |||
223 | ret = -EINVAL; | ||
224 | if (LDT_empty(&info)) | ||
225 | goto out; | ||
226 | |||
227 | idx = info.entry_number; | ||
228 | |||
229 | ret = set_tls_entry(new, &info, idx, 0); | ||
230 | out: | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | /* XXX: use do_get_thread_area to read the host value? I'm not at all sure! */ | ||
235 | static int get_tls_entry(struct task_struct* task, struct user_desc *info, int idx) | ||
236 | { | ||
237 | struct thread_struct *t = &task->thread; | ||
238 | |||
239 | if (!t->arch.tls_array) | ||
240 | goto clear; | ||
241 | |||
242 | if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) | ||
243 | return -EINVAL; | ||
244 | |||
245 | if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present) | ||
246 | goto clear; | ||
247 | |||
248 | *info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls; | ||
249 | |||
250 | out: | ||
251 | /* Temporary debugging check, to make sure that things have been | ||
252 | * flushed. This could be triggered if load_TLS() failed. | ||
253 | */ | ||
254 | if (unlikely(task == current && !t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) { | ||
255 | printk(KERN_ERR "get_tls_entry: task with pid %d got here " | ||
256 | "without flushed TLS.", current->pid); | ||
257 | } | ||
258 | |||
259 | return 0; | ||
260 | clear: | ||
261 | /* When the TLS entry has not been set, the values read to user in the | ||
262 | * tls_array are 0 (because it's cleared at boot, see | ||
263 | * arch/i386/kernel/head.S:cpu_gdt_table). Emulate that. | ||
264 | */ | ||
265 | clear_user_desc(info); | ||
266 | info->entry_number = idx; | ||
267 | goto out; | ||
268 | } | ||
269 | |||
270 | asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc) | ||
271 | { | ||
272 | struct user_desc info; | ||
273 | int idx, ret; | ||
274 | |||
275 | if (!host_supports_tls) | ||
276 | return -ENOSYS; | ||
277 | |||
278 | if (copy_from_user(&info, user_desc, sizeof(info))) | ||
279 | return -EFAULT; | ||
280 | |||
281 | idx = info.entry_number; | ||
282 | |||
283 | if (idx == -1) { | ||
284 | idx = get_free_idx(current); | ||
285 | if (idx < 0) | ||
286 | return idx; | ||
287 | info.entry_number = idx; | ||
288 | /* Tell the user which slot we chose for him.*/ | ||
289 | if (put_user(idx, &user_desc->entry_number)) | ||
290 | return -EFAULT; | ||
291 | } | ||
292 | |||
293 | ret = CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, &info); | ||
294 | if (ret) | ||
295 | return ret; | ||
296 | return set_tls_entry(current, &info, idx, 1); | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Perform set_thread_area on behalf of the traced child. | ||
301 | * Note: error handling is not done on the deferred load, and this differ from | ||
302 | * i386. However the only possible error are caused by bugs. | ||
303 | */ | ||
304 | int ptrace_set_thread_area(struct task_struct *child, int idx, | ||
305 | struct user_desc __user *user_desc) | ||
306 | { | ||
307 | struct user_desc info; | ||
308 | |||
309 | if (!host_supports_tls) | ||
310 | return -EIO; | ||
311 | |||
312 | if (copy_from_user(&info, user_desc, sizeof(info))) | ||
313 | return -EFAULT; | ||
314 | |||
315 | return set_tls_entry(child, &info, idx, 0); | ||
316 | } | ||
317 | |||
318 | asmlinkage int sys_get_thread_area(struct user_desc __user *user_desc) | ||
319 | { | ||
320 | struct user_desc info; | ||
321 | int idx, ret; | ||
322 | |||
323 | if (!host_supports_tls) | ||
324 | return -ENOSYS; | ||
325 | |||
326 | if (get_user(idx, &user_desc->entry_number)) | ||
327 | return -EFAULT; | ||
328 | |||
329 | ret = get_tls_entry(current, &info, idx); | ||
330 | if (ret < 0) | ||
331 | goto out; | ||
332 | |||
333 | if (copy_to_user(user_desc, &info, sizeof(info))) | ||
334 | ret = -EFAULT; | ||
335 | |||
336 | out: | ||
337 | return ret; | ||
338 | } | ||
339 | |||
340 | /* | ||
341 | * Perform get_thread_area on behalf of the traced child. | ||
342 | */ | ||
343 | int ptrace_get_thread_area(struct task_struct *child, int idx, | ||
344 | struct user_desc __user *user_desc) | ||
345 | { | ||
346 | struct user_desc info; | ||
347 | int ret; | ||
348 | |||
349 | if (!host_supports_tls) | ||
350 | return -EIO; | ||
351 | |||
352 | ret = get_tls_entry(child, &info, idx); | ||
353 | if (ret < 0) | ||
354 | goto out; | ||
355 | |||
356 | if (copy_to_user(user_desc, &info, sizeof(info))) | ||
357 | ret = -EFAULT; | ||
358 | out: | ||
359 | return ret; | ||
360 | } | ||
361 | |||
362 | |||
363 | /* XXX: This part is probably common to i386 and x86-64. Don't create a common | ||
364 | * file for now, do that when implementing x86-64 support.*/ | ||
365 | static int __init __setup_host_supports_tls(void) { | ||
366 | check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min); | ||
367 | if (host_supports_tls) { | ||
368 | printk(KERN_INFO "Host TLS support detected\n"); | ||
369 | printk(KERN_INFO "Detected host type: "); | ||
370 | switch (host_gdt_entry_tls_min) { | ||
371 | case GDT_ENTRY_TLS_MIN_I386: | ||
372 | printk("i386\n"); | ||
373 | break; | ||
374 | case GDT_ENTRY_TLS_MIN_X86_64: | ||
375 | printk("x86_64\n"); | ||
376 | break; | ||
377 | } | ||
378 | } else | ||
379 | printk(KERN_ERR " Host TLS support NOT detected! " | ||
380 | "TLS support inside UML will not work\n"); | ||
381 | return 1; | ||
382 | } | ||
383 | |||
384 | __initcall(__setup_host_supports_tls); | ||