This is UML support for TLS, which allows one to fully use NPTL glibc,
finally, on a 2.6 host (either x86 or x86_64). This has been happily tested by
many users and by us for some times and we've now fixed all known bugs, and
tested with different glibc's. So this code can IMHO be merged finally.
--
Inform me of my mistakes, so I can keep imitating Homer Simpson's "Doh!".
Paolo Giarrusso, aka Blaisorblade (Skype ID "PaoloGiarrusso", ICQ 215621894)
http://www.user-mode-linux.org/~blaisorblade
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
Comparing this code which is the actual body of the arch-independent cpu_idle(),
it is clear that it's unnecessary to set ->mm and ->active_mm; beyond that, a
kernel thread is not supposed to have ->mm != NULL, only active_mm.
This showed up because I used the assumption (which is IMHO valid) that kernel
thread have their ->mm == NULL, and it failed for this thread.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/kernel/process_kern.c | 4 ----
1 files changed, 0 insertions(+), 4 deletions(-)
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c
index 3113cab..f9948fd 100644
--- a/arch/um/kernel/process_kern.c
+++ b/arch/um/kernel/process_kern.c
@@ -185,10 +185,6 @@ void default_idle(void)
{
CHOOSE_MODE(uml_idle_timer(), (void) 0);
- atomic_inc(&init_mm.mm_count);
- current->mm = &init_mm;
- current->active_mm = &init_mm;
-
while(1){
/* endless idle loop with no priority at all */
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
Newly forked threads have no arch_switch_to_skas() called before their first
run, because when schedule() switches to them they're resumed in the body of
thread_wait() inside fork_handler() rather than in switch_threads() in
switch_to_skas().
Compensate this missing call.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/kernel/skas/process_kern.c | 7 +++++++
arch/um/sys-i386/ptrace.c | 9 ++++++++-
arch/um/sys-i386/tls.c | 13 ++++++++++---
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c
index 38b1853..2135eaf 100644
--- a/arch/um/kernel/skas/process_kern.c
+++ b/arch/um/kernel/skas/process_kern.c
@@ -91,10 +91,17 @@ void fork_handler(int sig)
panic("blech");
schedule_tail(current->thread.prev_sched);
+
+ /* XXX: if interrupt_end() calls schedule, this call to
+ * arch_switch_to_skas isn't needed. We could want to apply this to
+ * improve performance. -bb */
+ arch_switch_to_skas(current->thread.prev_sched, current);
+
current->thread.prev_sched = NULL;
/* Handle any immediate reschedules or signals */
interrupt_end();
+
userspace(¤t->thread.regs.regs);
}
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c
index b587b4b..ce71923 100644
--- a/arch/um/sys-i386/ptrace.c
+++ b/arch/um/sys-i386/ptrace.c
@@ -23,7 +23,14 @@ void arch_switch_to_tt(struct task_struc
void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
{
- arch_switch_tls_skas(from, 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)
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c
index e3c5bc5..2251654 100644
--- a/arch/um/sys-i386/tls.c
+++ b/arch/um/sys-i386/tls.c
@@ -70,8 +70,6 @@ static int get_free_idx(struct task_stru
return -ESRCH;
}
-#define O_FORCE 1
-
static inline void clear_user_desc(struct user_desc* info)
{
/* Postcondition: LDT_empty(info) returns true. */
@@ -84,6 +82,8 @@ static inline void clear_user_desc(struc
info->seg_not_present = 1;
}
+#define O_FORCE 1
+
static int load_TLS(int flags, struct task_struct *to)
{
int ret = 0;
@@ -162,7 +162,13 @@ void clear_flushed_tls(struct task_struc
* SKAS patch. */
int arch_switch_tls_skas(struct task_struct *from, struct task_struct *to)
{
- return load_TLS(O_FORCE, to);
+ /* 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)
@@ -324,3 +330,4 @@ int ptrace_get_thread_area(struct task_s
out:
return ret;
}
+
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
Implement sys_[gs]et_thread_area and the corresponding ptrace operations for
UML. This is the main chunk, additional parts follow. This implementation is now
well tested and has run reliably for some time, and we've understood all the
previously existing problems.
Their implementation saves the new GDT content and then forwards the call to the
host when appropriate, i.e. immediately when the target process is running or on
context switch otherwise (i.e. on fork and on ptrace() calls).
In SKAS mode, we must switch registers on each context switch (because SKAS
does not switches tls_array together with current->mm).
Also, added get_cpu() locking; this has been done for SKAS mode, since TT
does not need it (it does not use smp_processor_id()).
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/include/os.h | 4
arch/um/kernel/exec_kern.c | 12 -
arch/um/kernel/process_kern.c | 20 ++
arch/um/kernel/ptrace.c | 10 +
arch/um/kernel/skas/process_kern.c | 2
arch/um/os-Linux/Makefile | 4
arch/um/os-Linux/tls.c | 77 +++++++++
arch/um/sys-i386/Makefile | 2
arch/um/sys-i386/ptrace.c | 2
arch/um/sys-i386/sys_call_table.S | 2
arch/um/sys-i386/syscalls.c | 14 +-
arch/um/sys-i386/tls.c | 326 ++++++++++++++++++++++++++++++++++++
arch/um/sys-x86_64/Makefile | 2
arch/um/sys-x86_64/tls.c | 14 ++
include/asm-um/desc.h | 12 +
include/asm-um/processor-i386.h | 35 +++-
include/asm-um/processor-x86_64.h | 9 +
include/asm-um/ptrace-generic.h | 14 --
include/asm-um/ptrace-i386.h | 41 +++--
include/asm-um/ptrace-x86_64.h | 35 +++-
include/asm-um/segment.h | 2
21 files changed, 578 insertions(+), 61 deletions(-)
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index d3d1bc6..90869a7 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -234,6 +234,10 @@ extern int run_helper_thread(int (*proc)
int stack_order);
extern int helper_wait(int pid);
+
+/* tls.c */
+extern int os_set_thread_area(void *data, int pid);
+extern int os_get_thread_area(void *data, int pid);
/* umid.c */
extern int umid_file_name(char *name, char *buf, int len);
diff --git a/arch/um/kernel/exec_kern.c b/arch/um/kernel/exec_kern.c
index 1ca8431..a308d3d 100644
--- a/arch/um/kernel/exec_kern.c
+++ b/arch/um/kernel/exec_kern.c
@@ -22,6 +22,7 @@
void flush_thread(void)
{
+ arch_flush_thread(¤t->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:
- */
diff --git a/arch/um/kernel/process_kern.c b/arch/um/kernel/process_kern.c
index f9948fd..877a7ef 100644
--- a/arch/um/kernel/process_kern.c
+++ b/arch/um/kernel/process_kern.c
@@ -156,9 +156,25 @@ int copy_thread(int nr, unsigned long 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)
diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c
index 98e0939..f03a7f5 100644
--- a/arch/um/kernel/ptrace.c
+++ b/arch/um/kernel/ptrace.c
@@ -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).
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c
index 14360ac..38b1853 100644
--- a/arch/um/kernel/skas/process_kern.c
+++ b/arch/um/kernel/skas/process_kern.c
@@ -111,6 +111,8 @@ int copy_thread_skas(int nr, unsigned lo
if(sp != 0) REGS_SP(p->thread.regs.regs.skas.regs) = sp;
handler = fork_handler;
+
+ arch_copy_thread(¤t->thread.arch, &p->thread.arch);
}
else {
init_thread_registers(&p->thread.regs.regs);
diff --git a/arch/um/os-Linux/Makefile b/arch/um/os-Linux/Makefile
index 1659386..e7eb433 100644
--- a/arch/um/os-Linux/Makefile
+++ b/arch/um/os-Linux/Makefile
@@ -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 trap.o tt.o tty.o uaccess.o umid.o tls.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 trap.o tt.o tty.o tls.o \
uaccess.o umid.o util.o
elf_aux.o: $(ARCH_DIR)/kernel-offsets.h
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c
new file mode 100644
index 0000000..63dfcf7
--- /dev/null
+++ b/arch/um/os-Linux/tls.c
@@ -0,0 +1,77 @@
+#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_GET_THREAD_AREA
+#define PTRACE_GET_THREAD_AREA 25
+#endif
+
+#ifndef PTRACE_SET_THREAD_AREA
+#define PTRACE_SET_THREAD_AREA 26
+#endif
+
+int os_set_thread_area(void *data, int pid)
+{
+ struct user_desc *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 user_desc *info = data;
+ int ret;
+
+ ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
+ (unsigned long) info);
+ if (ret < 0)
+ ret = -errno;
+ return ret;
+}
+
+#endif
+
+#ifdef UML_CONFIG_MODE_TT
+#include "linux/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 /* UML_CONFIG_MODE_TT */
diff --git a/arch/um/sys-i386/Makefile b/arch/um/sys-i386/Makefile
index f5fd5b0..90ae78c 100644
--- a/arch/um/sys-i386/Makefile
+++ b/arch/um/sys-i386/Makefile
@@ -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
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c
index bf896b8..b587b4b 100644
--- a/arch/um/sys-i386/ptrace.c
+++ b/arch/um/sys-i386/ptrace.c
@@ -18,10 +18,12 @@
void arch_switch_to_tt(struct task_struct *from, struct task_struct *to)
{
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)
{
+ arch_switch_tls_skas(from, to);
}
int is_syscall(unsigned long addr)
diff --git a/arch/um/sys-i386/sys_call_table.S b/arch/um/sys-i386/sys_call_table.S
index ad75c27..1ff6147 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 83e9be8..8d5fb67 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 __
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;
}
/*
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c
new file mode 100644
index 0000000..e3c5bc5
--- /dev/null
+++ b/arch/um/sys-i386/tls.c
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2005 Paolo 'Blaisorblade' Giarrusso <[email protected]>
+ * 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
+
+#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
+
+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;
+}
+
+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;
+ }
+}
+
+/* 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(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 (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;
+}
diff --git a/arch/um/sys-x86_64/Makefile b/arch/um/sys-x86_64/Makefile
index a351091..fcb01ad 100644
--- a/arch/um/sys-x86_64/Makefile
+++ b/arch/um/sys-x86_64/Makefile
@@ -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
diff --git a/arch/um/sys-x86_64/tls.c b/arch/um/sys-x86_64/tls.c
new file mode 100644
index 0000000..ce1bf1b
--- /dev/null
+++ b/arch/um/sys-x86_64/tls.c
@@ -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;
+}
diff --git a/include/asm-um/desc.h b/include/asm-um/desc.h
index ac1d2a2..4ec34a5 100644
--- a/include/asm-um/desc.h
+++ b/include/asm-um/desc.h
@@ -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
diff --git a/include/asm-um/processor-i386.h b/include/asm-um/processor-i386.h
index 4108a57..595f1c3 100644
--- a/include/asm-um/processor-i386.h
+++ b/include/asm-um/processor-i386.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2002 Jeff Dike ([email protected])
* Licensed under the GPL
*/
@@ -6,21 +6,48 @@
#ifndef __UM_PROCESSOR_I386_H
#define __UM_PROCESSOR_I386_H
+#include "linux/string.h"
+#include "asm/host_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"
diff --git a/include/asm-um/processor-x86_64.h b/include/asm-um/processor-x86_64.h
index e1e1255..10609af 100644
--- a/include/asm-um/processor-x86_64.h
+++ b/include/asm-um/processor-x86_64.h
@@ -28,6 +28,15 @@ extern inline void rep_nop(void)
.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() \
diff --git a/include/asm-um/ptrace-generic.h b/include/asm-um/ptrace-generic.h
index 46599ac..011c356 100644
--- a/include/asm-um/ptrace-generic.h
+++ b/include/asm-um/ptrace-generic.h
@@ -60,17 +60,9 @@ extern void show_regs(struct pt_regs *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
diff --git a/include/asm-um/ptrace-i386.h b/include/asm-um/ptrace-i386.h
index fe882b9..30656c9 100644
--- a/include/asm-um/ptrace-i386.h
+++ b/include/asm-um/ptrace-i386.h
@@ -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/host_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
diff --git a/include/asm-um/ptrace-x86_64.h b/include/asm-um/ptrace-x86_64.h
index be51219..c894e68 100644
--- a/include/asm-um/ptrace-x86_64.h
+++ b/include/asm-um/ptrace-x86_64.h
@@ -8,6 +8,8 @@
#define __UM_PTRACE_X86_64_H
#include "linux/compiler.h"
+#include "asm/errno.h"
+#include "asm/host_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
diff --git a/include/asm-um/segment.h b/include/asm-um/segment.h
index 55e4030..4877545 100644
--- a/include/asm-um/segment.h
+++ b/include/asm-um/segment.h
@@ -1,4 +1,6 @@
#ifndef __UM_SEGMENT_H
#define __UM_SEGMENT_H
+#include "asm/arch/segment.h"
+
#endif
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
ldt-{i386,x86_64}.h is made of two different parts - some code for parsing of
LDT descriptors, which is arch-dependant, and the code to handle uml_ldt_t (an
LDT block inside UML), which is mostly arch-independant (among x86 and x86_64,
at least).
Join the common part in a single file (ldt.h) and split the rest away
(host_ldt-{i386,x86_64}.h).
This is needed because processor.h, with next patches, will start including the
LDT descriptor parsing macros in host_ldt.h, but it can't include ldt.h because
it uses semaphores (and to define semaphores one must first include
processor.h!).
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/Makefile | 2 +
include/asm-um/host_ldt-i386.h | 34 ++++++++++++++++++
include/asm-um/host_ldt-x86_64.h | 38 ++++++++++++++++++++
include/asm-um/ldt-i386.h | 69 ------------------------------------
include/asm-um/ldt-x86_64.h | 73 --------------------------------------
include/asm-um/ldt.h | 41 +++++++++++++++++++++
6 files changed, 114 insertions(+), 143 deletions(-)
diff --git a/arch/um/Makefile b/arch/um/Makefile
index 8d14c7a..b982483 100644
--- a/arch/um/Makefile
+++ b/arch/um/Makefile
@@ -20,7 +20,7 @@ core-y += $(ARCH_DIR)/kernel/ \
# Have to precede the include because the included Makefiles reference them.
SYMLINK_HEADERS := archparam.h system.h sigcontext.h processor.h ptrace.h \
- module.h vm-flags.h elf.h ldt.h
+ module.h vm-flags.h elf.h host_ldt.h
SYMLINK_HEADERS := $(foreach header,$(SYMLINK_HEADERS),include/asm-um/$(header))
# XXX: The "os" symlink is only used by arch/um/include/os.h, which includes
diff --git a/include/asm-um/host_ldt-i386.h b/include/asm-um/host_ldt-i386.h
new file mode 100644
index 0000000..b27cb0a
--- /dev/null
+++ b/include/asm-um/host_ldt-i386.h
@@ -0,0 +1,34 @@
+#ifndef __ASM_HOST_LDT_I386_H
+#define __ASM_HOST_LDT_I386_H
+
+#include "asm/arch/ldt.h"
+
+/*
+ * macros stolen from include/asm-i386/desc.h
+ */
+#define LDT_entry_a(info) \
+ ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
+
+#define LDT_entry_b(info) \
+ (((info)->base_addr & 0xff000000) | \
+ (((info)->base_addr & 0x00ff0000) >> 16) | \
+ ((info)->limit & 0xf0000) | \
+ (((info)->read_exec_only ^ 1) << 9) | \
+ ((info)->contents << 10) | \
+ (((info)->seg_not_present ^ 1) << 15) | \
+ ((info)->seg_32bit << 22) | \
+ ((info)->limit_in_pages << 23) | \
+ ((info)->useable << 20) | \
+ 0x7000)
+
+#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
diff --git a/include/asm-um/host_ldt-x86_64.h b/include/asm-um/host_ldt-x86_64.h
new file mode 100644
index 0000000..74a63f7
--- /dev/null
+++ b/include/asm-um/host_ldt-x86_64.h
@@ -0,0 +1,38 @@
+#ifndef __ASM_HOST_LDT_X86_64_H
+#define __ASM_HOST_LDT_X86_64_H
+
+#include "asm/arch/ldt.h"
+
+/*
+ * macros stolen from include/asm-x86_64/desc.h
+ */
+#define LDT_entry_a(info) \
+ ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
+
+/* Don't allow setting of the lm bit. It is useless anyways because
+ * 64bit system calls require __USER_CS. */
+#define LDT_entry_b(info) \
+ (((info)->base_addr & 0xff000000) | \
+ (((info)->base_addr & 0x00ff0000) >> 16) | \
+ ((info)->limit & 0xf0000) | \
+ (((info)->read_exec_only ^ 1) << 9) | \
+ ((info)->contents << 10) | \
+ (((info)->seg_not_present ^ 1) << 15) | \
+ ((info)->seg_32bit << 22) | \
+ ((info)->limit_in_pages << 23) | \
+ ((info)->useable << 20) | \
+ /* ((info)->lm << 21) | */ \
+ 0x7000)
+
+#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 && \
+ (info)->lm == 0)
+
+#endif
diff --git a/include/asm-um/ldt-i386.h b/include/asm-um/ldt-i386.h
deleted file mode 100644
index 175722a..0000000
--- a/include/asm-um/ldt-i386.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
- * Licensed under the GPL
- *
- * Author: Bodo Stroesser <[email protected]>
- */
-
-#ifndef __ASM_LDT_I386_H
-#define __ASM_LDT_I386_H
-
-#include "asm/semaphore.h"
-#include "asm/arch/ldt.h"
-
-struct mmu_context_skas;
-extern void ldt_host_info(void);
-extern long init_new_ldt(struct mmu_context_skas * to_mm,
- struct mmu_context_skas * from_mm);
-extern void free_ldt(struct mmu_context_skas * mm);
-
-#define LDT_PAGES_MAX \
- ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE)
-#define LDT_ENTRIES_PER_PAGE \
- (PAGE_SIZE/LDT_ENTRY_SIZE)
-#define LDT_DIRECT_ENTRIES \
- ((LDT_PAGES_MAX*sizeof(void *))/LDT_ENTRY_SIZE)
-
-struct ldt_entry {
- __u32 a;
- __u32 b;
-};
-
-typedef struct uml_ldt {
- int entry_count;
- struct semaphore semaphore;
- union {
- struct ldt_entry * pages[LDT_PAGES_MAX];
- struct ldt_entry entries[LDT_DIRECT_ENTRIES];
- } u;
-} uml_ldt_t;
-
-/*
- * macros stolen from include/asm-i386/desc.h
- */
-#define LDT_entry_a(info) \
- ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
-
-#define LDT_entry_b(info) \
- (((info)->base_addr & 0xff000000) | \
- (((info)->base_addr & 0x00ff0000) >> 16) | \
- ((info)->limit & 0xf0000) | \
- (((info)->read_exec_only ^ 1) << 9) | \
- ((info)->contents << 10) | \
- (((info)->seg_not_present ^ 1) << 15) | \
- ((info)->seg_32bit << 22) | \
- ((info)->limit_in_pages << 23) | \
- ((info)->useable << 20) | \
- 0x7000)
-
-#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
diff --git a/include/asm-um/ldt-x86_64.h b/include/asm-um/ldt-x86_64.h
deleted file mode 100644
index 96b35aa..0000000
--- a/include/asm-um/ldt-x86_64.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
- * Licensed under the GPL
- *
- * Author: Bodo Stroesser <[email protected]>
- */
-
-#ifndef __ASM_LDT_X86_64_H
-#define __ASM_LDT_X86_64_H
-
-#include "asm/semaphore.h"
-#include "asm/arch/ldt.h"
-
-struct mmu_context_skas;
-extern void ldt_host_info(void);
-extern long init_new_ldt(struct mmu_context_skas * to_mm,
- struct mmu_context_skas * from_mm);
-extern void free_ldt(struct mmu_context_skas * mm);
-
-#define LDT_PAGES_MAX \
- ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE)
-#define LDT_ENTRIES_PER_PAGE \
- (PAGE_SIZE/LDT_ENTRY_SIZE)
-#define LDT_DIRECT_ENTRIES \
- ((LDT_PAGES_MAX*sizeof(void *))/LDT_ENTRY_SIZE)
-
-struct ldt_entry {
- __u32 a;
- __u32 b;
-};
-
-typedef struct uml_ldt {
- int entry_count;
- struct semaphore semaphore;
- union {
- struct ldt_entry * pages[LDT_PAGES_MAX];
- struct ldt_entry entries[LDT_DIRECT_ENTRIES];
- } u;
-} uml_ldt_t;
-
-/*
- * macros stolen from include/asm-x86_64/desc.h
- */
-#define LDT_entry_a(info) \
- ((((info)->base_addr & 0x0000ffff) << 16) | ((info)->limit & 0x0ffff))
-
-/* Don't allow setting of the lm bit. It is useless anyways because
- * 64bit system calls require __USER_CS. */
-#define LDT_entry_b(info) \
- (((info)->base_addr & 0xff000000) | \
- (((info)->base_addr & 0x00ff0000) >> 16) | \
- ((info)->limit & 0xf0000) | \
- (((info)->read_exec_only ^ 1) << 9) | \
- ((info)->contents << 10) | \
- (((info)->seg_not_present ^ 1) << 15) | \
- ((info)->seg_32bit << 22) | \
- ((info)->limit_in_pages << 23) | \
- ((info)->useable << 20) | \
- /* ((info)->lm << 21) | */ \
- 0x7000)
-
-#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 && \
- (info)->lm == 0)
-
-#endif
diff --git a/include/asm-um/ldt.h b/include/asm-um/ldt.h
new file mode 100644
index 0000000..96f82a4
--- /dev/null
+++ b/include/asm-um/ldt.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2004 Fujitsu Siemens Computers GmbH
+ * Licensed under the GPL
+ *
+ * Author: Bodo Stroesser <[email protected]>
+ */
+
+#ifndef __ASM_LDT_H
+#define __ASM_LDT_H
+
+#include "asm/semaphore.h"
+#include "asm/host_ldt.h"
+
+struct mmu_context_skas;
+extern void ldt_host_info(void);
+extern long init_new_ldt(struct mmu_context_skas * to_mm,
+ struct mmu_context_skas * from_mm);
+extern void free_ldt(struct mmu_context_skas * mm);
+
+#define LDT_PAGES_MAX \
+ ((LDT_ENTRIES * LDT_ENTRY_SIZE)/PAGE_SIZE)
+#define LDT_ENTRIES_PER_PAGE \
+ (PAGE_SIZE/LDT_ENTRY_SIZE)
+#define LDT_DIRECT_ENTRIES \
+ ((LDT_PAGES_MAX*sizeof(void *))/LDT_ENTRY_SIZE)
+
+struct ldt_entry {
+ __u32 a;
+ __u32 b;
+};
+
+typedef struct uml_ldt {
+ int entry_count;
+ struct semaphore semaphore;
+ union {
+ struct ldt_entry * pages[LDT_PAGES_MAX];
+ struct ldt_entry entries[LDT_DIRECT_ENTRIES];
+ } u;
+} uml_ldt_t;
+
+#endif
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
Call arch_switch also in switch_to_skas, even if it's, for now, a no-op for that
case (and mark this in the comment); this will change soon.
Also, arch_switch for TT mode is actually useless when the PT proxy (a
complicate debugging instrumentation for TT mode) is not enabled. In fact, it
only calls update_debugregs, which checks debugregs_seq against seq (to check
if the registers are up-to-date - seq here means a "version number" of the
registers).
If the ptrace proxy is not enabled, debugregs_seq always stays 0 and
update_debugregs will be a no-op. So, optimize this out (the compiler can't
do it).
Also, I've been disappointed by the fact that it would make a lot of sense
if, after calling a successful
update_debugregs(current->thread.arch.debugregs_seq),
current->thread.arch.debugregs_seq were updated with the new debugregs_seq.
But this is not done. Is this a bug or a feature? For all purposes, it seems
a bug (otherwise the whole mechanism does not make sense, which is also a
possibility to check), which causes some performance only problems (not
correctness), since we write_debugregs when not needed.
Also, as suggested by Jeff, remove a redundant enabling of SIGVTALRM,
comprised in the subsequent local_irq_enable(). I'm just a bit dubious if
ordering matters there...
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/include/kern_util.h | 6 +++++-
arch/um/include/sysdep-i386/ptrace.h | 5 +++++
arch/um/kernel/skas/process_kern.c | 2 ++
arch/um/kernel/tt/process_kern.c | 10 ++++++++--
arch/um/sys-i386/ptrace.c | 8 ++++++--
arch/um/sys-i386/ptrace_user.c | 10 +++++++++-
6 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/arch/um/include/kern_util.h b/arch/um/include/kern_util.h
index 07176d9..4255713 100644
--- a/arch/um/include/kern_util.h
+++ b/arch/um/include/kern_util.h
@@ -116,7 +116,11 @@ extern void *get_current(void);
extern struct task_struct *get_task(int pid, int require);
extern void machine_halt(void);
extern int is_syscall(unsigned long addr);
-extern void arch_switch(void);
+
+extern void arch_switch_to_tt(struct task_struct *from, struct task_struct *to);
+
+extern void arch_switch_to_skas(struct task_struct *from, struct task_struct *to);
+
extern void free_irq(unsigned int, void *);
extern int cpu(void);
diff --git a/arch/um/include/sysdep-i386/ptrace.h b/arch/um/include/sysdep-i386/ptrace.h
index c8ee955..6670cc9 100644
--- a/arch/um/include/sysdep-i386/ptrace.h
+++ b/arch/um/include/sysdep-i386/ptrace.h
@@ -14,7 +14,12 @@
#define MAX_REG_NR (UM_FRAME_SIZE / sizeof(unsigned long))
#define MAX_REG_OFFSET (UM_FRAME_SIZE)
+#ifdef UML_CONFIG_PT_PROXY
extern void update_debugregs(int seq);
+#else
+static inline void update_debugregs(int seq) {}
+#endif
+
/* syscall emulation path in ptrace */
diff --git a/arch/um/kernel/skas/process_kern.c b/arch/um/kernel/skas/process_kern.c
index 3f70a2e..14360ac 100644
--- a/arch/um/kernel/skas/process_kern.c
+++ b/arch/um/kernel/skas/process_kern.c
@@ -35,6 +35,8 @@ void switch_to_skas(void *prev, void *ne
switch_threads(&from->thread.mode.skas.switch_buf,
to->thread.mode.skas.switch_buf);
+ arch_switch_to_skas(current->thread.prev_sched, current);
+
if(current->pid == 0)
switch_timers(1);
}
diff --git a/arch/um/kernel/tt/process_kern.c b/arch/um/kernel/tt/process_kern.c
index 295c1ac..a9c1443 100644
--- a/arch/um/kernel/tt/process_kern.c
+++ b/arch/um/kernel/tt/process_kern.c
@@ -51,6 +51,13 @@ void switch_to_tt(void *prev, void *next
c = 0;
+ /* Notice that here we "up" the semaphore on which "to" is waiting, and
+ * below (the read) we wait on this semaphore (which is implemented by
+ * switch_pipe) and go sleeping. Thus, after that, we have resumed in
+ * "to", and can't use any more the value of "from" (which is outdated),
+ * nor the value in "to" (since it was the task which stole us the CPU,
+ * which we don't care about). */
+
err = os_write_file(to->thread.mode.tt.switch_pipe[1], &c, sizeof(c));
if(err != sizeof(c))
panic("write of switch_pipe failed, err = %d", -err);
@@ -77,7 +84,7 @@ void switch_to_tt(void *prev, void *next
change_sig(SIGALRM, alrm);
change_sig(SIGPROF, prof);
- arch_switch();
+ arch_switch_to_tt(prev_sched, current);
flush_tlb_all();
local_irq_restore(flags);
@@ -141,7 +148,6 @@ static void new_thread_handler(int sig)
set_cmdline("(kernel thread)");
change_sig(SIGUSR1, 1);
- change_sig(SIGVTALRM, 1);
change_sig(SIGPROF, 1);
local_irq_enable();
if(!run_kernel_thread(fn, arg, ¤t->thread.exec_buf))
diff --git a/arch/um/sys-i386/ptrace.c b/arch/um/sys-i386/ptrace.c
index 8032a10..bf896b8 100644
--- a/arch/um/sys-i386/ptrace.c
+++ b/arch/um/sys-i386/ptrace.c
@@ -15,9 +15,13 @@
#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(to->thread.arch.debugregs_seq);
+}
+
+void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
{
- update_debugregs(current->thread.arch.debugregs_seq);
}
int is_syscall(unsigned long addr)
diff --git a/arch/um/sys-i386/ptrace_user.c b/arch/um/sys-i386/ptrace_user.c
index 397e3c3..e6826ef 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
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, unsi
/* 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 p
write_debugregs(pid, TASK_DEBUGREGS(task));
}
+#ifdef UML_CONFIG_PT_PROXY
+/* Accessed only by the tracing thread */
+static int debugregs_seq = 0;
+
+/* 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 *ar
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.
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
If running on an host not supporting TLS (for instance 2.4) we should report
that cleanly to the user, instead of printing not comprehensible "error 5" for
that.
Additionally, i386 and x86_64 support different ranges for
user_desc->entry_number, and we must account for that; we couldn't pass
ourselves -1 because we need to override previously existing TLS descriptors
which glibc has possibly set, so test at startup the range to use.
x86 and x86_64 existing ranges are hardcoded.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/include/os.h | 1 +
arch/um/include/sysdep-i386/tls.h | 4 +++
arch/um/include/user_util.h | 3 ++
arch/um/os-Linux/sys-i386/Makefile | 2 +
arch/um/os-Linux/sys-i386/tls.c | 33 ++++++++++++++++++++++
arch/um/os-Linux/tls.c | 4 +--
arch/um/sys-i386/tls.c | 55 +++++++++++++++++++++++++++++++++++-
include/asm-um/segment.h | 6 +++-
8 files changed, 102 insertions(+), 6 deletions(-)
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index b14d403..88abc4b 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -173,6 +173,7 @@ extern int os_fchange_dir(int fd);
extern void os_early_checks(void);
extern int can_do_skas(void);
extern void os_check_bugs(void);
+extern void check_host_supports_tls(int *supports_tls, int *tls_min);
/* Make sure they are clear when running in TT mode. Required by
* SEGV_MAYBE_FIXABLE */
diff --git a/arch/um/include/sysdep-i386/tls.h b/arch/um/include/sysdep-i386/tls.h
index 938f953..918fd3c 100644
--- a/arch/um/include/sysdep-i386/tls.h
+++ b/arch/um/include/sysdep-i386/tls.h
@@ -25,4 +25,8 @@ typedef struct um_dup_user_desc {
typedef struct user_desc user_desc_t;
# endif /* __KERNEL__ */
+
+#define GDT_ENTRY_TLS_MIN_I386 6
+#define GDT_ENTRY_TLS_MIN_X86_64 12
+
#endif /* _SYSDEP_TLS_H */
diff --git a/arch/um/include/user_util.h b/arch/um/include/user_util.h
index e654a20..2926fb7 100644
--- a/arch/um/include/user_util.h
+++ b/arch/um/include/user_util.h
@@ -8,6 +8,9 @@
#include "sysdep/ptrace.h"
+/* Copied from kernel.h */
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
#define CATCH_EINTR(expr) while ((errno = 0, ((expr) < 0)) && (errno == EINTR))
extern int mode_tt;
diff --git a/arch/um/os-Linux/sys-i386/Makefile b/arch/um/os-Linux/sys-i386/Makefile
index 340ef26..b321361 100644
--- a/arch/um/os-Linux/sys-i386/Makefile
+++ b/arch/um/os-Linux/sys-i386/Makefile
@@ -3,7 +3,7 @@
# Licensed under the GPL
#
-obj-$(CONFIG_MODE_SKAS) = registers.o
+obj-$(CONFIG_MODE_SKAS) = registers.o tls.o
USER_OBJS := $(obj-y)
diff --git a/arch/um/os-Linux/sys-i386/tls.c b/arch/um/os-Linux/sys-i386/tls.c
new file mode 100644
index 0000000..ba21f0e
--- /dev/null
+++ b/arch/um/os-Linux/sys-i386/tls.c
@@ -0,0 +1,33 @@
+#include <linux/unistd.h>
+#include "sysdep/tls.h"
+#include "user_util.h"
+
+static _syscall1(int, get_thread_area, user_desc_t *, u_info);
+
+/* Checks whether host supports TLS, and sets *tls_min according to the value
+ * valid on the host.
+ * i386 host have it == 6; x86_64 host have it == 12, for i386 emulation. */
+void check_host_supports_tls(int *supports_tls, int *tls_min) {
+ /* Values for x86 and x86_64.*/
+ int val[] = {GDT_ENTRY_TLS_MIN_I386, GDT_ENTRY_TLS_MIN_X86_64};
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ user_desc_t info;
+ info.entry_number = val[i];
+
+ if (get_thread_area(&info) == 0) {
+ *tls_min = val[i];
+ *supports_tls = 1;
+ return;
+ } else {
+ if (errno == EINVAL)
+ continue;
+ else if (errno == ENOSYS)
+ *supports_tls = 0;
+ return;
+ }
+ }
+
+ *supports_tls = 0;
+}
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c
index 642db55..9cb09a4 100644
--- a/arch/um/os-Linux/tls.c
+++ b/arch/um/os-Linux/tls.c
@@ -48,8 +48,8 @@ int os_get_thread_area(user_desc_t *info
#ifdef UML_CONFIG_MODE_TT
#include "linux/unistd.h"
-_syscall1(int, get_thread_area, user_desc_t *, u_info);
-_syscall1(int, set_thread_area, user_desc_t *, u_info);
+static _syscall1(int, get_thread_area, user_desc_t *, u_info);
+static _syscall1(int, set_thread_area, user_desc_t *, u_info);
int do_set_thread_area_tt(user_desc_t *info)
{
diff --git a/arch/um/sys-i386/tls.c b/arch/um/sys-i386/tls.c
index 2251654..a3188e8 100644
--- a/arch/um/sys-i386/tls.c
+++ b/arch/um/sys-i386/tls.c
@@ -24,6 +24,10 @@
#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)
{
@@ -157,11 +161,20 @@ void clear_flushed_tls(struct task_struc
}
}
-/* 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
+/* 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. */
@@ -173,6 +186,9 @@ int arch_switch_tls_skas(struct task_str
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);
@@ -256,6 +272,9 @@ asmlinkage int sys_set_thread_area(struc
struct user_desc info;
int idx, ret;
+ if (!host_supports_tls)
+ return -ENOSYS;
+
if (copy_from_user(&info, user_desc, sizeof(info)))
return -EFAULT;
@@ -287,6 +306,9 @@ int ptrace_set_thread_area(struct task_s
{
struct user_desc info;
+ if (!host_supports_tls)
+ return -EIO;
+
if (copy_from_user(&info, user_desc, sizeof(info)))
return -EFAULT;
@@ -298,6 +320,9 @@ asmlinkage int sys_get_thread_area(struc
struct user_desc info;
int idx, ret;
+ if (!host_supports_tls)
+ return -ENOSYS;
+
if (get_user(idx, &user_desc->entry_number))
return -EFAULT;
@@ -321,6 +346,9 @@ int ptrace_get_thread_area(struct task_s
struct user_desc info;
int ret;
+ if (!host_supports_tls)
+ return -EIO;
+
ret = get_tls_entry(child, &info, idx);
if (ret < 0)
goto out;
@@ -331,3 +359,26 @@ 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/include/asm-um/segment.h b/include/asm-um/segment.h
index 4877545..45183fc 100644
--- a/include/asm-um/segment.h
+++ b/include/asm-um/segment.h
@@ -1,6 +1,10 @@
#ifndef __UM_SEGMENT_H
#define __UM_SEGMENT_H
-#include "asm/arch/segment.h"
+extern int host_gdt_entry_tls_min;
+
+#define GDT_ENTRY_TLS_ENTRIES 3
+#define GDT_ENTRY_TLS_MIN host_gdt_entry_tls_min
+#define GDT_ENTRY_TLS_MAX (GDT_ENTRY_TLS_MIN + GDT_ENTRY_TLS_ENTRIES - 1)
#endif
From: Paolo 'Blaisorblade' Giarrusso <[email protected]>
Copy the definition of struct user_desc (with another name) for use by userspace
sources (where we use the host headers, and we can't be sure about their
content) to make sure UML compiles.
Signed-off-by: Paolo 'Blaisorblade' Giarrusso <[email protected]>
---
arch/um/include/os.h | 7 ++++---
arch/um/include/sysdep-i386/tls.h | 28 ++++++++++++++++++++++++++++
arch/um/include/sysdep-x86_64/tls.h | 29 +++++++++++++++++++++++++++++
arch/um/os-Linux/tls.c | 15 +++++++--------
4 files changed, 68 insertions(+), 11 deletions(-)
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 90869a7..b14d403 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -13,6 +13,7 @@
#include "kern_util.h"
#include "skas/mm_id.h"
#include "irq_user.h"
+#include "sysdep/tls.h"
#define OS_TYPE_FILE 1
#define OS_TYPE_DIR 2
@@ -236,10 +237,10 @@ extern int helper_wait(int pid);
/* tls.c */
-extern int os_set_thread_area(void *data, int pid);
-extern int os_get_thread_area(void *data, int pid);
-/* umid.c */
+extern int os_set_thread_area(user_desc_t *info, int pid);
+extern int os_get_thread_area(user_desc_t *info, int pid);
+/* umid.c */
extern int umid_file_name(char *name, char *buf, int len);
extern int set_umid(char *name);
extern char *get_umid(void);
diff --git a/arch/um/include/sysdep-i386/tls.h b/arch/um/include/sysdep-i386/tls.h
new file mode 100644
index 0000000..938f953
--- /dev/null
+++ b/arch/um/include/sysdep-i386/tls.h
@@ -0,0 +1,28 @@
+#ifndef _SYSDEP_TLS_H
+#define _SYSDEP_TLS_H
+
+# ifndef __KERNEL__
+
+/* Change name to avoid conflicts with the original one from <asm/ldt.h>, which
+ * may be named user_desc (but in 2.4 and in header matching its API was named
+ * modify_ldt_ldt_s). */
+
+typedef struct um_dup_user_desc {
+ unsigned int entry_number;
+ unsigned int base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+ unsigned int seg_not_present:1;
+ unsigned int useable:1;
+} user_desc_t;
+
+# else /* __KERNEL__ */
+
+# include <asm/ldt.h>
+typedef struct user_desc user_desc_t;
+
+# endif /* __KERNEL__ */
+#endif /* _SYSDEP_TLS_H */
diff --git a/arch/um/include/sysdep-x86_64/tls.h b/arch/um/include/sysdep-x86_64/tls.h
new file mode 100644
index 0000000..35f19f2
--- /dev/null
+++ b/arch/um/include/sysdep-x86_64/tls.h
@@ -0,0 +1,29 @@
+#ifndef _SYSDEP_TLS_H
+#define _SYSDEP_TLS_H
+
+# ifndef __KERNEL__
+
+/* Change name to avoid conflicts with the original one from <asm/ldt.h>, which
+ * may be named user_desc (but in 2.4 and in header matching its API was named
+ * modify_ldt_ldt_s). */
+
+typedef struct um_dup_user_desc {
+ unsigned int entry_number;
+ unsigned int base_addr;
+ unsigned int limit;
+ unsigned int seg_32bit:1;
+ unsigned int contents:2;
+ unsigned int read_exec_only:1;
+ unsigned int limit_in_pages:1;
+ unsigned int seg_not_present:1;
+ unsigned int useable:1;
+ unsigned int lm:1;
+} user_desc_t;
+
+# else /* __KERNEL__ */
+
+# include <asm/ldt.h>
+typedef struct user_desc user_desc_t;
+
+# endif /* __KERNEL__ */
+#endif /* _SYSDEP_TLS_H */
diff --git a/arch/um/os-Linux/tls.c b/arch/um/os-Linux/tls.c
index 63dfcf7..642db55 100644
--- a/arch/um/os-Linux/tls.c
+++ b/arch/um/os-Linux/tls.c
@@ -1,6 +1,7 @@
#include <errno.h>
#include <sys/ptrace.h>
#include <asm/ldt.h>
+#include "sysdep/tls.h"
#include "uml-config.h"
/* TLS support - we basically rely on the host's one.*/
@@ -18,9 +19,8 @@
#define PTRACE_SET_THREAD_AREA 26
#endif
-int os_set_thread_area(void *data, int pid)
+int os_set_thread_area(user_desc_t *info, int pid)
{
- struct user_desc *info = data;
int ret;
ret = ptrace(PTRACE_SET_THREAD_AREA, pid, info->entry_number,
@@ -32,9 +32,8 @@ int os_set_thread_area(void *data, int p
#ifdef UML_CONFIG_MODE_SKAS
-int os_get_thread_area(void *data, int pid)
+int os_get_thread_area(user_desc_t *info, int pid)
{
- struct user_desc *info = data;
int ret;
ret = ptrace(PTRACE_GET_THREAD_AREA, pid, info->entry_number,
@@ -49,10 +48,10 @@ int os_get_thread_area(void *data, int p
#ifdef UML_CONFIG_MODE_TT
#include "linux/unistd.h"
-_syscall1(int, get_thread_area, struct user_desc *, u_info);
-_syscall1(int, set_thread_area, struct user_desc *, u_info);
+_syscall1(int, get_thread_area, user_desc_t *, u_info);
+_syscall1(int, set_thread_area, user_desc_t *, u_info);
-int do_set_thread_area_tt(struct user_desc *info)
+int do_set_thread_area_tt(user_desc_t *info)
{
int ret;
@@ -63,7 +62,7 @@ int do_set_thread_area_tt(struct user_de
return ret;
}
-int do_get_thread_area_tt(struct user_desc *info)
+int do_get_thread_area_tt(user_desc_t *info)
{
int ret;
On Wed, Mar 29, 2006 at 01:54:42AM +0200, Paolo 'Blaisorblade' Giarrusso wrote:
> This is UML support for TLS, which allows one to fully use NPTL glibc,
> finally, on a 2.6 host (either x86 or x86_64). This has been happily tested
> by many users and by us for some times and we've now fixed all known bugs,
> and tested with different glibc's. So this code can IMHO be merged finally.
Acked-by: Jeff Dike <[email protected]>