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 | 60 | ||||
-rw-r--r-- | arch/um/sys-i386/ptrace_user.c | 10 | ||||
-rw-r--r-- | arch/um/sys-i386/signal.c | 107 | ||||
-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 | ||||
-rw-r--r-- | arch/um/sys-i386/user-offsets.c | 10 |
8 files changed, 531 insertions, 81 deletions
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile index f5fd5b0156d..98b20b7bba4 100644 --- a/arch/um/sys-i386/Makefile +++ b/arch/um/sys-i386/Makefile @@ -1,23 +1,18 @@ -obj-y := bitops.o bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ - ptrace_user.o semaphore.o signal.o sigcontext.o syscalls.o sysrq.o \ - sys_call_table.o +obj-y = bugs.o checksum.o delay.o fault.o ksyms.o ldt.o ptrace.o \ + ptrace_user.o signal.o sigcontext.o syscalls.o sysrq.o \ + sys_call_table.o tls.o obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o -obj-$(CONFIG_HIGHMEM) += highmem.o -obj-$(CONFIG_MODULES) += module.o +subarch-obj-y = lib/bitops.o kernel/semaphore.o +subarch-obj-$(CONFIG_HIGHMEM) += mm/highmem.o +subarch-obj-$(CONFIG_MODULES) += kernel/module.o USER_OBJS := bugs.o ptrace_user.o sigcontext.o fault.o stub_segv.o -SYMLINKS = bitops.c semaphore.c highmem.c module.c - include arch/um/scripts/Makefile.rules -bitops.c-dir = lib -semaphore.c-dir = kernel -highmem.c-dir = mm -module.c-dir = kernel - -$(obj)/stub_segv.o : _c_flags = $(call unprofile,$(CFLAGS)) +extra-$(CONFIG_MODE_TT) += unmap.o -include arch/um/scripts/Makefile.unmap +$(obj)/stub_segv.o $(obj)/unmap.o: \ + _c_flags = $(call unprofile,$(CFLAGS)) diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c index e839ce65ad2..6028bc7cc01 100644 --- a/arch/um/sys-i386/ptrace.c +++ b/arch/um/sys-i386/ptrace.c @@ -6,6 +6,7 @@ #include <linux/config.h> #include <linux/compiler.h> #include "linux/sched.h" +#include "linux/mm.h" #include "asm/elf.h" #include "asm/ptrace.h" #include "asm/uaccess.h" @@ -14,9 +15,22 @@ #include "sysdep/sigcontext.h" #include "sysdep/sc.h" -void arch_switch(void) +void arch_switch_to_tt(struct task_struct *from, struct task_struct *to) { - update_debugregs(current->thread.arch.debugregs_seq); + update_debugregs(to->thread.arch.debugregs_seq); + arch_switch_tls_tt(from, to); +} + +void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) +{ + int err = arch_switch_tls_skas(from, to); + if (!err) + return; + + if (err != -EINVAL) + printk(KERN_WARNING "arch_switch_tls_skas failed, errno %d, not EINVAL\n", -err); + else + printk(KERN_WARNING "arch_switch_tls_skas failed, errno = EINVAL\n"); } int is_syscall(unsigned long addr) @@ -26,9 +40,17 @@ int is_syscall(unsigned long addr) n = copy_from_user(&instr, (void __user *) addr, sizeof(instr)); if(n){ - printk("is_syscall : failed to read instruction from 0x%lx\n", - addr); - return(0); + /* access_process_vm() grants access to vsyscall and stub, + * while copy_from_user doesn't. Maybe access_process_vm is + * slow, but that doesn't matter, since it will be called only + * in case of singlestepping, if copy_from_user failed. + */ + n = access_process_vm(current, addr, &instr, sizeof(instr), 0); + if(n != sizeof(instr)) { + printk("is_syscall : failed to read instruction from " + "0x%lx\n", addr); + return(1); + } } /* int 0x80 or sysenter */ return((instr == 0x80cd) || (instr == 0x340f)); @@ -115,22 +137,22 @@ unsigned long getreg(struct task_struct *child, int regno) int peek_user(struct task_struct *child, long addr, long data) { /* read the word at location addr in the USER area. */ - unsigned long tmp; + unsigned long tmp; - if ((addr & 3) || addr < 0) - return -EIO; + if ((addr & 3) || addr < 0) + return -EIO; - tmp = 0; /* Default return condition */ - if(addr < MAX_REG_OFFSET){ - tmp = getreg(child, addr); - } - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - tmp = child->thread.arch.debugregs[addr]; - } - return put_user(tmp, (unsigned long *) data); + tmp = 0; /* Default return condition */ + if(addr < MAX_REG_OFFSET){ + tmp = getreg(child, addr); + } + else if((addr >= offsetof(struct user, u_debugreg[0])) && + (addr <= offsetof(struct user, u_debugreg[7]))){ + addr -= offsetof(struct user, u_debugreg[0]); + addr = addr >> 2; + tmp = child->thread.arch.debugregs[addr]; + } + return put_user(tmp, (unsigned long __user *) data); } struct i387_fxsave_struct { diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c index 7c376c95de5..9f3bd8ed78f 100644 --- a/arch/um/sys-i386/ptrace_user.c +++ b/arch/um/sys-i386/ptrace_user.c @@ -14,6 +14,7 @@ #include "sysdep/thread.h" #include "user.h" #include "os.h" +#include "uml-config.h" int ptrace_getregs(long pid, unsigned long *regs_out) { @@ -43,6 +44,7 @@ int ptrace_setfpregs(long pid, unsigned long *regs) return 0; } +/* All the below stuff is of interest for TT mode only */ static void write_debugregs(int pid, unsigned long *regs) { struct user *dummy; @@ -75,7 +77,6 @@ static void read_debugregs(int pid, unsigned long *regs) /* Accessed only by the tracing thread */ static unsigned long kernel_debugregs[8] = { [ 0 ... 7 ] = 0 }; -static int debugregs_seq = 0; void arch_enter_kernel(void *task, int pid) { @@ -89,6 +90,11 @@ void arch_leave_kernel(void *task, int pid) write_debugregs(pid, TASK_DEBUGREGS(task)); } +#ifdef UML_CONFIG_PT_PROXY +/* Accessed only by the tracing thread */ +static int debugregs_seq; + +/* Only called by the ptrace proxy */ void ptrace_pokeuser(unsigned long addr, unsigned long data) { if((addr < offsetof(struct user, u_debugreg[0])) || @@ -109,6 +115,7 @@ static void update_debugregs_cb(void *arg) write_debugregs(pid, kernel_debugregs); } +/* Optimized out in its header when not defined */ void update_debugregs(int seq) { int me; @@ -118,6 +125,7 @@ void update_debugregs(int seq) me = os_getpid(); initial_thread_cb(update_debugregs_cb, &me); } +#endif /* * 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 7cd1a82dc8c..f5d0e1c37ea 100644 --- a/arch/um/sys-i386/signal.c +++ b/arch/um/sys-i386/signal.c @@ -19,7 +19,7 @@ #include "skas.h" static int copy_sc_from_user_skas(struct pt_regs *regs, - struct sigcontext *from) + struct sigcontext __user *from) { struct sigcontext sc; unsigned long fpregs[HOST_FP_SIZE]; @@ -57,8 +57,8 @@ static int copy_sc_from_user_skas(struct pt_regs *regs, return(0); } -int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, - struct pt_regs *regs) +int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate __user *to_fp, + struct pt_regs *regs, unsigned long sp) { struct sigcontext sc; unsigned long fpregs[HOST_FP_SIZE]; @@ -72,7 +72,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, sc.edi = REGS_EDI(regs->regs.skas.regs); sc.esi = REGS_ESI(regs->regs.skas.regs); sc.ebp = REGS_EBP(regs->regs.skas.regs); - sc.esp = REGS_SP(regs->regs.skas.regs); + sc.esp = sp; sc.ebx = REGS_EBX(regs->regs.skas.regs); sc.edx = REGS_EDX(regs->regs.skas.regs); sc.ecx = REGS_ECX(regs->regs.skas.regs); @@ -92,7 +92,7 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, "errno = %d\n", err); return(1); } - to_fp = (to_fp ? to_fp : (struct _fpstate *) (to + 1)); + to_fp = (to_fp ? to_fp : (struct _fpstate __user *) (to + 1)); sc.fpstate = to_fp; if(err) @@ -113,10 +113,11 @@ int copy_sc_to_user_skas(struct sigcontext *to, struct _fpstate *to_fp, * saved pointer is in the kernel, but the sigcontext is in userspace, so we * copy_to_user it. */ -int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, +int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext __user *from, int fpsize) { - struct _fpstate *to_fp, *from_fp; + struct _fpstate *to_fp; + struct _fpstate __user *from_fp; unsigned long sigs; int err; @@ -131,20 +132,28 @@ int copy_sc_from_user_tt(struct sigcontext *to, struct sigcontext *from, return(err); } -int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate *fp, - struct sigcontext *from, int fpsize) +int copy_sc_to_user_tt(struct sigcontext *to, struct _fpstate __user *fp, + struct sigcontext *from, int fpsize, unsigned long sp) { - struct _fpstate *to_fp, *from_fp; + struct _fpstate __user *to_fp; + struct _fpstate *from_fp; int err; - to_fp = (fp ? fp : (struct _fpstate *) (to + 1)); + to_fp = (fp ? fp : (struct _fpstate __user *) (to + 1)); from_fp = from->fpstate; err = copy_to_user(to, from, sizeof(*to)); + + /* The SP in the sigcontext is the updated one for the signal + * delivery. The sp passed in is the original, and this needs + * to be restored, so we stick it in separately. + */ + err |= copy_to_user(&SC_SP(to), sp, sizeof(sp)); + if(from_fp != NULL){ err |= copy_to_user(&to->fpstate, &to_fp, sizeof(to->fpstate)); err |= copy_to_user(to_fp, from_fp, fpsize); } - return(err); + return err; } #endif @@ -158,15 +167,15 @@ static int copy_sc_from_user(struct pt_regs *to, void __user *from) return(ret); } -static int copy_sc_to_user(struct sigcontext *to, struct _fpstate *fp, - struct pt_regs *from) +static int copy_sc_to_user(struct sigcontext *to, struct _fpstate __user *fp, + struct pt_regs *from, unsigned long sp) { return(CHOOSE_MODE(copy_sc_to_user_tt(to, fp, UPT_SC(&from->regs), - sizeof(*fp)), - copy_sc_to_user_skas(to, fp, from))); + sizeof(*fp), sp), + copy_sc_to_user_skas(to, fp, from, sp))); } -static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, +static int copy_ucontext_to_user(struct ucontext __user *uc, struct _fpstate __user *fp, sigset_t *set, unsigned long sp) { int err = 0; @@ -174,14 +183,14 @@ static int copy_ucontext_to_user(struct ucontext *uc, struct _fpstate *fp, err |= put_user(current->sas_ss_sp, &uc->uc_stack.ss_sp); err |= put_user(sas_ss_flags(sp), &uc->uc_stack.ss_flags); err |= put_user(current->sas_ss_size, &uc->uc_stack.ss_size); - err |= copy_sc_to_user(&uc->uc_mcontext, fp, ¤t->thread.regs); + err |= copy_sc_to_user(&uc->uc_mcontext, fp, ¤t->thread.regs, sp); err |= copy_to_user(&uc->uc_sigmask, set, sizeof(*set)); return(err); } struct sigframe { - char *pretcode; + char __user *pretcode; int sig; struct sigcontext sc; struct _fpstate fpstate; @@ -191,10 +200,10 @@ struct sigframe struct rt_sigframe { - char *pretcode; + char __user *pretcode; int sig; - struct siginfo *pinfo; - void *puc; + struct siginfo __user *pinfo; + void __user *puc; struct siginfo info; struct ucontext uc; struct _fpstate fpstate; @@ -206,21 +215,32 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, sigset_t *mask) { struct sigframe __user *frame; - void *restorer; + void __user *restorer; + unsigned long save_sp = PT_REGS_SP(regs); int err = 0; stack_top &= -8UL; - frame = (struct sigframe *) stack_top - 1; + frame = (struct sigframe __user *) stack_top - 1; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return 1; - restorer = (void *) frame->retcode; + restorer = frame->retcode; if(ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; + /* Update SP now because the page fault handler refuses to extend + * the stack if the faulting address is too far below the current + * SP, which frame now certainly is. If there's an error, the original + * value is restored on the way out. + * When writing the sigcontext to the stack, we have to write the + * original value, so that's passed to copy_sc_to_user, which does + * the right thing with it. + */ + PT_REGS_SP(regs) = (unsigned long) frame; + err |= __put_user(restorer, &frame->pretcode); err |= __put_user(sig, &frame->sig); - err |= copy_sc_to_user(&frame->sc, NULL, regs); + err |= copy_sc_to_user(&frame->sc, NULL, regs, save_sp); err |= __put_user(mask->sig[0], &frame->sc.oldmask); if (_NSIG_WORDS > 1) err |= __copy_to_user(&frame->extramask, &mask->sig[1], @@ -238,7 +258,7 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, err |= __put_user(0x80cd, (short __user *)(frame->retcode+6)); if(err) - return(err); + goto err; PT_REGS_SP(regs) = (unsigned long) frame; PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; @@ -248,7 +268,11 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig, if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) ptrace_notify(SIGTRAP); - return(0); + return 0; + +err: + PT_REGS_SP(regs) = save_sp; + return err; } int setup_signal_stack_si(unsigned long stack_top, int sig, @@ -256,25 +280,29 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, siginfo_t *info, sigset_t *mask) { struct rt_sigframe __user *frame; - void *restorer; + void __user *restorer; + unsigned long save_sp = PT_REGS_SP(regs); int err = 0; stack_top &= -8UL; - frame = (struct rt_sigframe *) stack_top - 1; + frame = (struct rt_sigframe __user *) stack_top - 1; if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return 1; - restorer = (void *) frame->retcode; + restorer = frame->retcode; if(ka->sa.sa_flags & SA_RESTORER) restorer = ka->sa.sa_restorer; + /* See comment above about why this is here */ + PT_REGS_SP(regs) = (unsigned long) frame; + err |= __put_user(restorer, &frame->pretcode); err |= __put_user(sig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, info); err |= copy_ucontext_to_user(&frame->uc, &frame->fpstate, mask, - PT_REGS_SP(regs)); + save_sp); /* * This is movl $,%eax ; int $0x80 @@ -288,9 +316,8 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, err |= __put_user(0x80cd, (short __user *)(frame->retcode+5)); if(err) - return(err); + goto err; - PT_REGS_SP(regs) = (unsigned long) frame; PT_REGS_IP(regs) = (unsigned long) ka->sa.sa_handler; PT_REGS_EAX(regs) = (unsigned long) sig; PT_REGS_EDX(regs) = (unsigned long) &frame->info; @@ -298,13 +325,17 @@ int setup_signal_stack_si(unsigned long stack_top, int sig, if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED)) ptrace_notify(SIGTRAP); - return(0); + return 0; + +err: + PT_REGS_SP(regs) = save_sp; + return err; } long sys_sigreturn(struct pt_regs regs) { unsigned long sp = PT_REGS_SP(¤t->thread.regs); - struct sigframe __user *frame = (struct sigframe *)(sp - 8); + struct sigframe __user *frame = (struct sigframe __user *)(sp - 8); sigset_t set; struct sigcontext __user *sc = &frame->sc; unsigned long __user *oldmask = &sc->oldmask; @@ -336,8 +367,8 @@ long sys_sigreturn(struct pt_regs regs) long sys_rt_sigreturn(struct pt_regs regs) { - unsigned long __user sp = PT_REGS_SP(¤t->thread.regs); - struct rt_sigframe __user *frame = (struct rt_sigframe *) (sp - 4); + unsigned long sp = PT_REGS_SP(¤t->thread.regs); + struct rt_sigframe __user *frame = (struct rt_sigframe __user *) (sp - 4); sigset_t set; struct ucontext __user *uc = &frame->uc; 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 ad75c27afe3..1ff61474b25 100644 --- a/arch/um/sys-i386/sys_call_table.S +++ b/arch/um/sys-i386/sys_call_table.S @@ -6,8 +6,6 @@ #define sys_vm86old sys_ni_syscall #define sys_vm86 sys_ni_syscall -#define sys_set_thread_area sys_ni_syscall -#define sys_get_thread_area sys_ni_syscall #define sys_stime um_stime #define sys_time um_time diff --git a/arch/um/sys-i386/syscalls.c b/arch/um/sys-i386/syscalls.c index 83e9be820a8..749dd1bfe60 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) return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); } -/* The i386 version skips reading from %esi, the fourth argument. So we must do - * this, too. +/* + * The prototype on i386 is: + * + * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr) + * + * and the "newtls" arg. on i386 is read by copy_thread directly from the + * register saved on the stack. */ long sys_clone(unsigned long clone_flags, unsigned long newsp, - int __user *parent_tid, int unused, int __user *child_tid) + int __user *parent_tid, void *newtls, int __user *child_tid) { long ret; if (!newsp) newsp = UPT_SP(¤t->thread.regs.regs); + current->thread.forking = 1; ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, child_tid); current->thread.forking = 0; - return(ret); + return ret; } /* @@ -104,7 +110,7 @@ long sys_ipc (uint call, int first, int second, union semun fourth; if (!ptr) return -EINVAL; - if (get_user(fourth.__pad, (void **) ptr)) + if (get_user(fourth.__pad, (void __user * __user *) ptr)) return -EFAULT; return sys_semctl (first, second, third, fourth); } diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c new file mode 100644 index 00000000000..a3188e861cc --- /dev/null +++ b/arch/um/sys-i386/tls.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> + * Licensed under the GPL + */ + +#include "linux/config.h" +#include "linux/kernel.h" +#include "linux/sched.h" +#include "linux/slab.h" +#include "linux/types.h" +#include "asm/uaccess.h" +#include "asm/ptrace.h" +#include "asm/segment.h" +#include "asm/smp.h" +#include "asm/desc.h" +#include "choose-mode.h" +#include "kern.h" +#include "kern_util.h" +#include "mode_kern.h" +#include "os.h" +#include "mode.h" + +#ifdef CONFIG_MODE_SKAS +#include "skas.h" +#endif + +/* If needed we can detect when it's uninitialized. */ +static int host_supports_tls = -1; +int host_gdt_entry_tls_min = -1; + +#ifdef CONFIG_MODE_SKAS +int do_set_thread_area_skas(struct user_desc *info) +{ + int ret; + u32 cpu; + + cpu = get_cpu(); + ret = os_set_thread_area(info, userspace_pid[cpu]); + put_cpu(); + return ret; +} + +int do_get_thread_area_skas(struct user_desc *info) +{ + int ret; + u32 cpu; + + cpu = get_cpu(); + ret = os_get_thread_area(info, userspace_pid[cpu]); + put_cpu(); + return ret; +} +#endif + +/* + * sys_get_thread_area: get a yet unused TLS descriptor index. + * XXX: Consider leaving one free slot for glibc usage at first place. This must + * be done here (and by changing GDT_ENTRY_TLS_* macros) and nowhere else. + * + * Also, this must be tested when compiling in SKAS mode with dinamic linking + * and running against NPTL. + */ +static int get_free_idx(struct task_struct* task) +{ + struct thread_struct *t = &task->thread; + int idx; + + if (!t->arch.tls_array) + return GDT_ENTRY_TLS_MIN; + + for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) + if (!t->arch.tls_array[idx].present) + return idx + GDT_ENTRY_TLS_MIN; + return -ESRCH; +} + +static inline void clear_user_desc(struct user_desc* info) +{ + /* Postcondition: LDT_empty(info) returns true. */ + memset(info, 0, sizeof(*info)); + + /* Check the LDT_empty or the i386 sys_get_thread_area code - we obtain + * indeed an empty user_desc. + */ + info->read_exec_only = 1; + info->seg_not_present = 1; +} + +#define O_FORCE 1 + +static int load_TLS(int flags, struct task_struct *to) +{ + int ret = 0; + int idx; + + for (idx = GDT_ENTRY_TLS_MIN; idx < GDT_ENTRY_TLS_MAX; idx++) { + struct uml_tls_struct* curr = &to->thread.arch.tls_array[idx - GDT_ENTRY_TLS_MIN]; + + /* Actually, now if it wasn't flushed it gets cleared and + * flushed to the host, which will clear it.*/ + if (!curr->present) { + if (!curr->flushed) { + clear_user_desc(&curr->tls); + curr->tls.entry_number = idx; + } else { + WARN_ON(!LDT_empty(&curr->tls)); + continue; + } + } + + if (!(flags & O_FORCE) && curr->flushed) + continue; + + ret = do_set_thread_area(&curr->tls); + if (ret) + goto out; + + curr->flushed = 1; + } +out: + return ret; +} + +/* Verify if we need to do a flush for the new process, i.e. if there are any + * present desc's, only if they haven't been flushed. + */ +static inline int needs_TLS_update(struct task_struct *task) +{ + int i; + int ret = 0; + + for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { + struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; + + /* Can't test curr->present, we may need to clear a descriptor + * which had a value. */ + if (curr->flushed) + continue; + ret = 1; + break; + } + return ret; +} + +/* On a newly forked process, the TLS descriptors haven't yet been flushed. So + * we mark them as such and the first switch_to will do the job. + */ +void clear_flushed_tls(struct task_struct *task) +{ + int i; + + for (i = GDT_ENTRY_TLS_MIN; i < GDT_ENTRY_TLS_MAX; i++) { + struct uml_tls_struct* curr = &task->thread.arch.tls_array[i - GDT_ENTRY_TLS_MIN]; + + /* Still correct to do this, if it wasn't present on the host it + * will remain as flushed as it was. */ + if (!curr->present) + continue; + + curr->flushed = 0; + } +} + +/* In SKAS0 mode, currently, multiple guest threads sharing the same ->mm have a + * common host process. So this is needed in SKAS0 too. + * + * However, if each thread had a different host process (and this was discussed + * for SMP support) this won't be needed. + * + * And this will not need be used when (and if) we'll add support to the host + * SKAS patch. */ + +int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to) +{ + if (!host_supports_tls) + return 0; + + /* We have no need whatsoever to switch TLS for kernel threads; beyond + * that, that would also result in us calling os_set_thread_area with + * userspace_pid[cpu] == 0, which gives an error. */ + if (likely(to->mm)) + return load_TLS(O_FORCE, to); + + return 0; +} + +int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to) +{ + if (!host_supports_tls) + return 0; + + if (needs_TLS_update(to)) + return load_TLS(0, to); + + return 0; +} + +static int set_tls_entry(struct task_struct* task, struct user_desc *info, + int idx, int flushed) +{ + struct thread_struct *t = &task->thread; + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls = *info; + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present = 1; + t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed = flushed; + + return 0; +} + +int arch_copy_tls(struct task_struct *new) +{ + struct user_desc info; + int idx, ret = -EFAULT; + + if (copy_from_user(&info, + (void __user *) UPT_ESI(&new->thread.regs.regs), + sizeof(info))) + goto out; + + ret = -EINVAL; + if (LDT_empty(&info)) + goto out; + + idx = info.entry_number; + + ret = set_tls_entry(new, &info, idx, 0); +out: + return ret; +} + +/* XXX: use do_get_thread_area to read the host value? I'm not at all sure! */ +static int get_tls_entry(struct task_struct* task, struct user_desc *info, int idx) +{ + struct thread_struct *t = &task->thread; + + if (!t->arch.tls_array) + goto clear; + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + if (!t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].present) + goto clear; + + *info = t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].tls; + +out: + /* Temporary debugging check, to make sure that things have been + * flushed. This could be triggered if load_TLS() failed. + */ + if (unlikely(task == current && !t->arch.tls_array[idx - GDT_ENTRY_TLS_MIN].flushed)) { + printk(KERN_ERR "get_tls_entry: task with pid %d got here " + "without flushed TLS.", current->pid); + } + + return 0; +clear: + /* When the TLS entry has not been set, the values read to user in the + * tls_array are 0 (because it's cleared at boot, see + * arch/i386/kernel/head.S:cpu_gdt_table). Emulate that. + */ + clear_user_desc(info); + info->entry_number = idx; + goto out; +} + +asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc) +{ + struct user_desc info; + int idx, ret; + + if (!host_supports_tls) + return -ENOSYS; + + if (copy_from_user(&info, user_desc, sizeof(info))) + return -EFAULT; + + idx = info.entry_number; + + if (idx == -1) { + idx = get_free_idx(current); + if (idx < 0) + return idx; + info.entry_number = idx; + /* Tell the user which slot we chose for him.*/ + if (put_user(idx, &user_desc->entry_number)) + return -EFAULT; + } + + ret = CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, &info); + if (ret) + return ret; + return set_tls_entry(current, &info, idx, 1); +} + +/* + * Perform set_thread_area on behalf of the traced child. + * Note: error handling is not done on the deferred load, and this differ from + * i386. However the only possible error are caused by bugs. + */ +int ptrace_set_thread_area(struct task_struct *child, int idx, + struct user_desc __user *user_desc) +{ + struct user_desc info; + + if (!host_supports_tls) + return -EIO; + + if (copy_from_user(&info, user_desc, sizeof(info))) + return -EFAULT; + + return set_tls_entry(child, &info, idx, 0); +} + +asmlinkage int sys_get_thread_area(struct user_desc __user *user_desc) +{ + struct user_desc info; + int idx, ret; + + if (!host_supports_tls) + return -ENOSYS; + + if (get_user(idx, &user_desc->entry_number)) + return -EFAULT; + + ret = get_tls_entry(current, &info, idx); + if (ret < 0) + goto out; + + if (copy_to_user(user_desc, &info, sizeof(info))) + ret = -EFAULT; + +out: + return ret; +} + +/* + * Perform get_thread_area on behalf of the traced child. + */ +int ptrace_get_thread_area(struct task_struct *child, int idx, + struct user_desc __user *user_desc) +{ + struct user_desc info; + int ret; + + if (!host_supports_tls) + return -EIO; + + ret = get_tls_entry(child, &info, idx); + if (ret < 0) + goto out; + + if (copy_to_user(user_desc, &info, sizeof(info))) + ret = -EFAULT; +out: + return ret; +} + + +/* XXX: This part is probably common to i386 and x86-64. Don't create a common + * file for now, do that when implementing x86-64 support.*/ +static int __init __setup_host_supports_tls(void) { + check_host_supports_tls(&host_supports_tls, &host_gdt_entry_tls_min); + if (host_supports_tls) { + printk(KERN_INFO "Host TLS support detected\n"); + printk(KERN_INFO "Detected host type: "); + switch (host_gdt_entry_tls_min) { + case GDT_ENTRY_TLS_MIN_I386: + printk("i386\n"); + break; + case GDT_ENTRY_TLS_MIN_X86_64: + printk("x86_64\n"); + break; + } + } else + printk(KERN_ERR " Host TLS support NOT detected! " + "TLS support inside UML will not work\n"); + return 1; +} + +__initcall(__setup_host_supports_tls); diff --git a/arch/um/sys-i386/user-offsets.c b/arch/um/sys-i386/user-offsets.c index 26b68675053..6f4ef2b7fa4 100644 --- a/arch/um/sys-i386/user-offsets.c +++ b/arch/um/sys-i386/user-offsets.c @@ -3,12 +3,13 @@ #include <asm/ptrace.h> #include <asm/user.h> #include <linux/stddef.h> +#include <sys/poll.h> #define DEFINE(sym, val) \ - asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) #define DEFINE_LONGS(sym, val) \ - asm volatile("\n->" #sym " %0 " #val : : "i" (val/sizeof(unsigned long))) + asm volatile("\n->" #sym " %0 " #val : : "i" (val/sizeof(unsigned long))) #define OFFSET(sym, str, mem) \ DEFINE(sym, offsetof(struct str, mem)); @@ -67,4 +68,9 @@ void foo(void) DEFINE(HOST_ES, ES); DEFINE(HOST_GS, GS); DEFINE(UM_FRAME_SIZE, sizeof(struct user_regs_struct)); + + /* XXX Duplicated between i386 and x86_64 */ + DEFINE(UM_POLLIN, POLLIN); + DEFINE(UM_POLLPRI, POLLPRI); + DEFINE(UM_POLLOUT, POLLOUT); } |