/* * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> * Licensed under the GPL */ #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 0; } __initcall(__setup_host_supports_tls);