# Implement sys_[gs]et_thread_area and the corresponding ptrace operations for
# UML. Their implementation simply defers the calls to the host support.
# 
# Still an early version, the code is already in good shape, but the testing
# always fails, while I don't know obvious reasons for that (i.e. the
# implementation should be complete, from what I see).
# 
# In SKAS mode, we must switch registers on each context switch (because SKAS
# does not switches tls_array together with current->mm). In SKAS0, this
# wouldn't be needed.
# 
# On TT mode, I've implemented deferred loading to implement
# PTRACE_SET_THREAD_AREA when executed in the guest, doing it in
# arch_switch_tls(). The ->flushed flag is used for this purpose.
# 
# Also, we must implement TLS copying on clone(), if this is part of the
# specification. It seems yes, since dup_task_struct() copies the whole
# thread_struct; in fact, it is implemented (through clear_flushed_tls()).
# 
# In copy_thread() we clear the "flushed" attribute and then load it in
# arch_switch(), via the above "deferred" loading.
# 
# 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()).
# 
# Also, yet to decide: *) whether to leave an empty TLS slot to glibc - it is
# difficult, however, on anything else than the set_thread_area with -1 as
# index, because the userspace prog must use the real gdt entry number in the
# segment selector (i.e. the value of the TLS register). Any testing on this
# needs to be done while actually linking to NPTL glibc, i.e. compiling in
# SKAS-only mode (and probably, except on some Gentoo systems and on RHEL 4,
# with dynamic linking).
# 
# *) whether we must use the real get_thread_area() (executed on the host) at
# all. The current situation is that the wrappers are implemented but not used.
# And any value set by something else than UML wouldn't be useful for the guest
# userspace. XXX: I'd like to use the wrappers for consistency verification.
# 
# *) whether tls_array must be an array or a pointer - I just switched to an
# array (wasting only 15 bytes in arch_thread) to avoid -ENOMEM errors on
# set_thread_area which are disallowed. I think it should be an array now.
# 
# Signed-off-by: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Index: linux-2.6.16/arch/um/include/os.h
===================================================================
--- linux-2.6.16.orig/arch/um/include/os.h	2006-03-23 18:55:38.000000000 -0500
+++ linux-2.6.16/arch/um/include/os.h	2006-03-23 20:28:46.000000000 -0500
@@ -331,4 +331,8 @@ extern int ignore_sigio_fd(int fd);
 extern void sig_handler_common_skas(int sig, void *sc_ptr);
 extern void user_signal(int sig, union uml_pt_regs *regs, int pid);
 
+
+/* tls.c */
+extern int os_set_thread_area(void *data, int pid);
+extern int os_get_thread_area(void *data, int pid);
 #endif
Index: linux-2.6.16/arch/um/kernel/exec_kern.c
===================================================================
--- linux-2.6.16.orig/arch/um/kernel/exec_kern.c	2006-03-23 18:55:38.000000000 -0500
+++ linux-2.6.16/arch/um/kernel/exec_kern.c	2006-03-23 20:28:46.000000000 -0500
@@ -22,6 +22,7 @@
 
 void flush_thread(void)
 {
+	arch_flush_thread(&current->thread.arch);
 	CHOOSE_MODE(flush_thread_tt(), flush_thread_skas());
 }
 
@@ -74,14 +75,3 @@ long sys_execve(char *file, char __user 
 	unlock_kernel();
 	return(error);
 }
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
Index: linux-2.6.16/arch/um/kernel/process_kern.c
===================================================================
--- linux-2.6.16.orig/arch/um/kernel/process_kern.c	2006-03-23 16:40:20.000000000 -0500
+++ linux-2.6.16/arch/um/kernel/process_kern.c	2006-03-23 20:28:46.000000000 -0500
@@ -156,9 +156,25 @@ int copy_thread(int nr, unsigned long cl
 		unsigned long stack_top, struct task_struct * p, 
 		struct pt_regs *regs)
 {
+	int ret;
+
 	p->thread = (struct thread_struct) INIT_THREAD;
-	return(CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr, 
-				clone_flags, sp, stack_top, p, regs));
+	ret = CHOOSE_MODE_PROC(copy_thread_tt, copy_thread_skas, nr,
+				clone_flags, sp, stack_top, p, regs);
+
+	if (ret || !current->thread.forking)
+		goto out;
+
+	clear_flushed_tls(p);
+
+	/*
+	 * Set a new TLS for the child thread?
+	 */
+	if (clone_flags & CLONE_SETTLS)
+		ret = arch_copy_tls(p);
+
+out:
+	return ret;
 }
 
 void initial_thread_cb(void (*proc)(void *), void *arg)
Index: linux-2.6.16/arch/um/kernel/ptrace.c
===================================================================
--- linux-2.6.16.orig/arch/um/kernel/ptrace.c	2006-01-03 17:39:46.000000000 -0500
+++ linux-2.6.16/arch/um/kernel/ptrace.c	2006-03-23 20:28:46.000000000 -0500
@@ -187,6 +187,16 @@ long arch_ptrace(struct task_struct *chi
 		ret = set_fpxregs(data, child);
 		break;
 #endif
+	case PTRACE_GET_THREAD_AREA:
+		ret = ptrace_get_thread_area(child, addr,
+					     (struct user_desc __user *) data);
+		break;
+
+	case PTRACE_SET_THREAD_AREA:
+		ret = ptrace_set_thread_area(child, addr,
+					     (struct user_desc __user *) data);
+		break;
+
 	case PTRACE_FAULTINFO: {
                 /* Take the info from thread->arch->faultinfo,
                  * but transfer max. sizeof(struct ptrace_faultinfo).
Index: linux-2.6.16/arch/um/kernel/skas/process_kern.c
===================================================================
--- linux-2.6.16.orig/arch/um/kernel/skas/process_kern.c	2006-03-23 20:27:20.000000000 -0500
+++ linux-2.6.16/arch/um/kernel/skas/process_kern.c	2006-03-23 20:31:23.000000000 -0500
@@ -111,6 +111,8 @@ int copy_thread_skas(int nr, unsigned lo
 		if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp;
 
 		handler = fork_handler;
+
+		arch_copy_thread(&current->thread.arch, &p->thread.arch);
 	}
 	else {
 		init_thread_registers(&p->thread.regs.regs);
Index: linux-2.6.16/arch/um/os-Linux/Makefile
===================================================================
--- linux-2.6.16.orig/arch/um/os-Linux/Makefile	2006-03-23 18:55:38.000000000 -0500
+++ linux-2.6.16/arch/um/os-Linux/Makefile	2006-03-23 20:30:53.000000000 -0500
@@ -4,7 +4,7 @@
 #
 
 obj-y = aio.o elf_aux.o file.o helper.o irq.o main.o mem.o process.o sigio.o \
-	signal.o start_up.o time.o trap.o tt.o tty.o uaccess.o umid.o \
+	signal.o start_up.o time.o tls.o trap.o tt.o tty.o uaccess.o umid.o \
 	user_syms.o util.o drivers/ sys-$(SUBARCH)/
 
 obj-$(CONFIG_MODE_SKAS) += skas/
@@ -12,7 +12,7 @@ obj-$(CONFIG_TTY_LOG) += tty_log.o
 user-objs-$(CONFIG_TTY_LOG) += tty_log.o
 
 USER_OBJS := $(user-objs-y) aio.o elf_aux.o file.o helper.o irq.o main.o mem.o \
-	process.o sigio.o signal.o start_up.o time.o trap.o tt.o tty.o \
+	process.o sigio.o signal.o start_up.o time.o tls.o trap.o tt.o tty.o \
 	uaccess.o umid.o util.o
 
 elf_aux.o: $(ARCH_DIR)/kernel-offsets.h
Index: linux-2.6.16/arch/um/os-Linux/tls.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.16/arch/um/os-Linux/tls.c	2006-03-23 20:28:46.000000000 -0500
@@ -0,0 +1,73 @@
+#include <errno.h>
+#include <sys/ptrace.h>
+#include <asm/ldt.h>
+#include "uml-config.h"
+
+/* TLS support - we basically rely on the host's one.*/
+
+/* In TT mode, this should be called only by the tracing thread, and makes sense
+ * only for PTRACE_SET_THREAD_AREA. In SKAS mode, it's used normally.
+ *
+ */
+
+#ifndef PTRACE_SET_THREAD_AREA
+#define PTRACE_SET_THREAD_AREA 26
+#endif
+
+int os_set_thread_area(void *data, int pid)
+{
+	struct modify_ldt_ldt_s *info = data;
+	int ret;
+
+	ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
+		     (unsigned long) info);
+	if (ret < 0)
+		ret = -errno;
+	return ret;
+}
+
+#ifdef UML_CONFIG_MODE_SKAS
+
+int os_get_thread_area(void *data, int pid)
+{
+	struct modify_ldt_ldt_s *info = data;
+	int ret;
+
+	ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
+		     (unsigned long) info);
+	if (ret < 0)
+		ret = -errno;
+	return ret;
+}
+
+#endif
+
+#ifdef UML_CONFIG_MODE_TT
+#include "asm/unistd.h"
+
+_syscall1(int, get_thread_area, struct user_desc *, u_info);
+_syscall1(int, set_thread_area, struct user_desc *, u_info);
+
+int do_set_thread_area_tt(struct user_desc *info)
+{
+	int ret;
+
+	ret = set_thread_area(info);
+	if (ret < 0) {
+		ret = -errno;
+	}
+	return ret;
+}
+
+int do_get_thread_area_tt(struct user_desc *info)
+{
+	int ret;
+
+	ret = get_thread_area(info);
+	if (ret < 0) {
+		ret = -errno;
+	}
+	return ret;
+}
+
+#endif
Index: linux-2.6.16/arch/um/sys-i386/Makefile
===================================================================
--- linux-2.6.16.orig/arch/um/sys-i386/Makefile	2006-01-03 17:39:46.000000000 -0500
+++ linux-2.6.16/arch/um/sys-i386/Makefile	2006-03-23 20:28:46.000000000 -0500
@@ -1,6 +1,6 @@
 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
+	sys_call_table.o tls.o
 
 obj-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
 
Index: linux-2.6.16/arch/um/sys-i386/syscalls.c
===================================================================
--- linux-2.6.16.orig/arch/um/sys-i386/syscalls.c	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/arch/um/sys-i386/syscalls.c	2006-03-23 20:28:46.000000000 -0500
@@ -61,21 +61,27 @@ long old_select(struct sel_arg_struct __
 	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(&current->thread.regs.regs);
+
 	current->thread.forking = 1;
 	ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
 		      child_tid);
 	current->thread.forking = 0;
-	return(ret);
+	return ret;
 }
 
 /*
Index: linux-2.6.16/arch/um/sys-i386/tls.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.16/arch/um/sys-i386/tls.c	2006-03-23 20:28:46.000000000 -0500
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+ * Licensed under the GPL
+ */
+
+#include "linux/config.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 "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
+
+#ifdef CONFIG_MODE_TT
+/* This is for use by the tracing thread in special situations (see below). */
+static int indirect_set_thread_area_tt(struct user_desc *info) {
+	BUG_ON(os_getpid() != tracing_pid);
+	return os_set_thread_area(info, current->thread.mode.tt.extern_pid);
+}
+
+static inline int indirect_set_thread_area(struct user_desc *info) {
+	if (mode_tt)
+		return indirect_set_thread_area_tt(info);
+	else
+		BUG();
+}
+#endif
+
+#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;
+}
+
+#define O_FORCE 1
+//XXX: seems disappearing
+#define O_INDIRECT 2
+
+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;
+}
+
+/* O_INDIRECT is for TT mode only, and only for the tracing thread. */
+static int load_TLS(int flags, struct task_struct *to)
+{
+	int ret = 0;
+	int idx;
+
+	if (flags & O_INDIRECT)
+		BUG_ON(CHOOSE_MODE(os_getpid() != tracing_pid, 1));
+
+	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];
+
+		/* This check is a bit bogus. We won't clear the GDT entry at
+		 * task switch, which means that access through this segment
+		 * descriptor may not get the expected faults.
+		 */
+		if (!curr->present)
+			continue;
+
+		if (!(flags & O_FORCE) && curr->flushed)
+			continue;
+
+		ret = (flags & O_INDIRECT) ? indirect_set_thread_area(&curr->tls): 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; if force_presents = 0, only if they haven't been flushed.
+ * XXX: remove the force_presents param if not needed.
+ */
+static inline int needs_TLS_update(int force_presents, struct task_struct *task)
+{
+	int ret = 0;
+	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];
+
+		if (!curr->present)
+			continue;
+
+		if (!(curr->flushed || force_presents))
+			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];
+
+		if (!curr->present)
+			continue;
+
+		curr->flushed = 0;
+	}
+}
+
+/* This is called from the tracing thread. Can't be called in SKAS mode.*/
+/*static void deferred_host_tls_set(void* p)
+{
+	int* ret = (int*)p;
+	*ret = load_TLS(O_INDIRECT);
+}*/
+
+/* This in SKAS0 does not need to be used, since we have different host
+ * processes. Nor will this need to be used when we'll add support to the host
+ * SKAS patch. */
+int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to)
+{
+	return load_TLS(O_FORCE, to);
+}
+
+int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to)
+{
+	if (needs_TLS_update(0, 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.
+	 */
+	memset(info, 0, sizeof(*info));
+	info->entry_number = idx;
+	/* 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;
+	goto out;
+}
+
+asmlinkage int sys_set_thread_area(struct user_desc __user *user_desc)
+{
+	struct user_desc info;
+	int idx, ret;
+
+	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 (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 (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;
+
+	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;
+}
Index: linux-2.6.16/arch/um/sys-x86_64/Makefile
===================================================================
--- linux-2.6.16.orig/arch/um/sys-x86_64/Makefile	2006-01-03 17:39:46.000000000 -0500
+++ linux-2.6.16/arch/um/sys-x86_64/Makefile	2006-03-23 20:28:46.000000000 -0500
@@ -7,7 +7,7 @@
 #XXX: why into lib-y?
 lib-y = bitops.o bugs.o csum-partial.o delay.o fault.o ldt.o mem.o memcpy.o \
 	ptrace.o ptrace_user.o sigcontext.o signal.o syscalls.o \
-	syscall_table.o sysrq.o thunk.o
+	syscall_table.o sysrq.o tls.o thunk.o
 lib-$(CONFIG_MODE_SKAS) += stub.o stub_segv.o
 
 obj-y := ksyms.o
Index: linux-2.6.16/arch/um/sys-x86_64/tls.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.16/arch/um/sys-x86_64/tls.c	2006-03-23 20:28:46.000000000 -0500
@@ -0,0 +1,14 @@
+#include "linux/sched.h"
+
+void debug_arch_force_load_TLS(void)
+{
+}
+
+void clear_flushed_tls(struct task_struct *task)
+{
+}
+
+int arch_copy_tls(struct task_struct *t)
+{
+        return 0;
+}
Index: linux-2.6.16/include/asm-um/desc.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/desc.h	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/include/asm-um/desc.h	2006-03-23 20:28:46.000000000 -0500
@@ -1,6 +1,16 @@
 #ifndef __UM_DESC_H
 #define __UM_DESC_H
 
-#include "asm/arch/desc.h"
+/* Taken from asm-i386/desc.h, it's the only thing we need. The rest wouldn't
+ * compile, and has never been used. */
+#define LDT_empty(info) (\
+	(info)->base_addr	== 0	&& \
+	(info)->limit		== 0	&& \
+	(info)->contents	== 0	&& \
+	(info)->read_exec_only	== 1	&& \
+	(info)->seg_32bit	== 0	&& \
+	(info)->limit_in_pages	== 0	&& \
+	(info)->seg_not_present	== 1	&& \
+	(info)->useable		== 0	)
 
 #endif
Index: linux-2.6.16/include/asm-um/processor-i386.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/processor-i386.h	2005-10-28 12:58:15.000000000 -0400
+++ linux-2.6.16/include/asm-um/processor-i386.h	2006-03-23 20:28:46.000000000 -0500
@@ -1,4 +1,4 @@
-/* 
+/*
  * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
  */
@@ -6,21 +6,48 @@
 #ifndef __UM_PROCESSOR_I386_H
 #define __UM_PROCESSOR_I386_H
 
+#include "linux/string.h"
+#include "asm/ldt.h"
+#include "asm/segment.h"
+
 extern int host_has_xmm;
 extern int host_has_cmov;
 
 /* include faultinfo structure */
 #include "sysdep/faultinfo.h"
 
+struct uml_tls_struct {
+	struct user_desc tls;
+	unsigned flushed:1;
+	unsigned present:1;
+};
+
 struct arch_thread {
+	struct uml_tls_struct tls_array[GDT_ENTRY_TLS_ENTRIES];
 	unsigned long debugregs[8];
 	int debugregs_seq;
 	struct faultinfo faultinfo;
 };
 
-#define INIT_ARCH_THREAD { .debugregs  		= { [ 0 ... 7 ] = 0 }, \
-                           .debugregs_seq	= 0, \
-                           .faultinfo		= { 0, 0, 0 } }
+#define INIT_ARCH_THREAD { \
+	.tls_array  		= { [ 0 ... GDT_ENTRY_TLS_ENTRIES - 1 ] = \
+				    { .present = 0, .flushed = 0 } }, \
+	.debugregs  		= { [ 0 ... 7 ] = 0 }, \
+	.debugregs_seq		= 0, \
+	.faultinfo		= { 0, 0, 0 } \
+}
+
+static inline void arch_flush_thread(struct arch_thread *thread)
+{
+	/* Clear any TLS still hanging */
+	memset(&thread->tls_array, 0, sizeof(thread->tls_array));
+}
+
+static inline void arch_copy_thread(struct arch_thread *from,
+                                    struct arch_thread *to)
+{
+        memcpy(&to->tls_array, &from->tls_array, sizeof(from->tls_array));
+}
 
 #include "asm/arch/user.h"
 
Index: linux-2.6.16/include/asm-um/processor-x86_64.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/processor-x86_64.h	2005-10-28 12:58:15.000000000 -0400
+++ linux-2.6.16/include/asm-um/processor-x86_64.h	2006-03-23 20:28:46.000000000 -0500
@@ -28,6 +28,15 @@ extern inline void rep_nop(void)
                            .debugregs_seq	= 0, \
                            .faultinfo		= { 0, 0, 0 } }
 
+static inline void arch_flush_thread(struct arch_thread *thread)
+{
+}
+
+static inline void arch_copy_thread(struct arch_thread *from,
+                                    struct arch_thread *to)
+{
+}
+
 #include "asm/arch/user.h"
 
 #define current_text_addr() \
Index: linux-2.6.16/include/asm-um/ptrace-generic.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/ptrace-generic.h	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/include/asm-um/ptrace-generic.h	2006-03-23 20:28:46.000000000 -0500
@@ -60,17 +60,9 @@ extern void show_regs(struct pt_regs *re
 extern void send_sigtrap(struct task_struct *tsk, union uml_pt_regs *regs,
 			 int error_code);
 
-#endif
+extern int arch_copy_tls(struct task_struct *new);
+extern void clear_flushed_tls(struct task_struct *task);
 
 #endif
 
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
+#endif
Index: linux-2.6.16/include/asm-um/ptrace-i386.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/ptrace-i386.h	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/include/asm-um/ptrace-i386.h	2006-03-23 20:28:46.000000000 -0500
@@ -8,8 +8,11 @@
 
 #define HOST_AUDIT_ARCH AUDIT_ARCH_I386
 
+#include "linux/compiler.h"
 #include "sysdep/ptrace.h"
 #include "asm/ptrace-generic.h"
+#include "asm/ldt.h"
+#include "choose-mode.h"
 
 #define PT_REGS_EAX(r) UPT_EAX(&(r)->regs)
 #define PT_REGS_EBX(r) UPT_EBX(&(r)->regs)
@@ -38,15 +41,31 @@
 
 #define user_mode(r) UPT_IS_USER(&(r)->regs)
 
-#endif
+extern int ptrace_get_thread_area(struct task_struct *child, int idx,
+                                  struct user_desc __user *user_desc);
+
+extern int ptrace_set_thread_area(struct task_struct *child, int idx,
+                                  struct user_desc __user *user_desc);
+
+extern int do_set_thread_area_skas(struct user_desc *info);
+extern int do_get_thread_area_skas(struct user_desc *info);
+
+extern int do_set_thread_area_tt(struct user_desc *info);
+extern int do_get_thread_area_tt(struct user_desc *info);
+
+extern int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to);
+extern int arch_switch_tls_tt(struct task_struct *from, struct task_struct *to);
 
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
+static inline int do_get_thread_area(struct user_desc *info)
+{
+	return CHOOSE_MODE_PROC(do_get_thread_area_tt, do_get_thread_area_skas, info);
+}
+
+static inline int do_set_thread_area(struct user_desc *info)
+{
+	return CHOOSE_MODE_PROC(do_set_thread_area_tt, do_set_thread_area_skas, info);
+}
+
+struct task_struct;
+
+#endif
Index: linux-2.6.16/include/asm-um/ptrace-x86_64.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/ptrace-x86_64.h	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/include/asm-um/ptrace-x86_64.h	2006-03-23 20:28:46.000000000 -0500
@@ -8,6 +8,8 @@
 #define __UM_PTRACE_X86_64_H
 
 #include "linux/compiler.h"
+#include "asm/errno.h"
+#include "asm/ldt.h"
 
 #define signal_fault signal_fault_x86_64
 #define __FRAME_OFFSETS /* Needed to get the R* macros */
@@ -63,15 +65,26 @@ void signal_fault(struct pt_regs_subarch
 
 #define profile_pc(regs) PT_REGS_IP(regs)
 
-#endif
+static inline int ptrace_get_thread_area(struct task_struct *child, int idx,
+                                         struct user_desc __user *user_desc)
+{
+        return -ENOSYS;
+}
+
+static inline int ptrace_set_thread_area(struct task_struct *child, int idx,
+                                         struct user_desc __user *user_desc)
+{
+        return -ENOSYS;
+}
+
+static inline void arch_switch_to_tt(struct task_struct *from,
+                                     struct task_struct *to)
+{
+}
+
+static inline void arch_switch_to_skas(struct task_struct *from,
+                                       struct task_struct *to)
+{
+}
 
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-file-style: "linux"
- * End:
- */
+#endif
Index: linux-2.6.16/include/asm-um/segment.h
===================================================================
--- linux-2.6.16.orig/include/asm-um/segment.h	2005-08-28 19:41:01.000000000 -0400
+++ linux-2.6.16/include/asm-um/segment.h	2006-03-23 20:28:46.000000000 -0500
@@ -1,4 +1,6 @@
 #ifndef __UM_SEGMENT_H
 #define __UM_SEGMENT_H
 
+#include "asm/arch/segment.h"
+
 #endif
