aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/um/include/os.h2
-rw-r--r--arch/um/os-Linux/sys-x86_64/Makefile2
-rw-r--r--arch/um/os-Linux/sys-x86_64/prctl.c12
-rw-r--r--arch/um/sys-x86_64/syscalls.c76
-rw-r--r--arch/um/sys-x86_64/tls.c11
-rw-r--r--include/asm-um/processor-x86_64.h6
-rw-r--r--include/asm-um/ptrace-x86_64.h6
7 files changed, 86 insertions, 29 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h
index 13a86bd383d3..4e9a13e7bde6 100644
--- a/arch/um/include/os.h
+++ b/arch/um/include/os.h
@@ -341,4 +341,6 @@ extern void maybe_sigio_broken(int fd, int read);
extern void sig_handler_common_skas(int sig, void *sc_ptr);
extern void user_signal(int sig, union uml_pt_regs *regs, int pid);
+extern int os_arch_prctl(int pid, int code, unsigned long *addr);
+
#endif
diff --git a/arch/um/os-Linux/sys-x86_64/Makefile b/arch/um/os-Linux/sys-x86_64/Makefile
index f67842a7735b..7955e061a678 100644
--- a/arch/um/os-Linux/sys-x86_64/Makefile
+++ b/arch/um/os-Linux/sys-x86_64/Makefile
@@ -3,7 +3,7 @@
# Licensed under the GPL
#
-obj-$(CONFIG_MODE_SKAS) = registers.o signal.o
+obj-$(CONFIG_MODE_SKAS) = registers.o prctl.o signal.o
USER_OBJS := $(obj-y)
diff --git a/arch/um/os-Linux/sys-x86_64/prctl.c b/arch/um/os-Linux/sys-x86_64/prctl.c
new file mode 100644
index 000000000000..79c278a47343
--- /dev/null
+++ b/arch/um/os-Linux/sys-x86_64/prctl.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com})
+ * Licensed under the GPL
+ */
+
+#include <sys/ptrace.h>
+#include <linux/ptrace.h>
+
+int os_arch_prctl(int pid, int code, unsigned long *addr)
+{
+ return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code);
+}
diff --git a/arch/um/sys-x86_64/syscalls.c b/arch/um/sys-x86_64/syscalls.c
index 73ce4463f70c..6d5605b7b2dd 100644
--- a/arch/um/sys-x86_64/syscalls.c
+++ b/arch/um/sys-x86_64/syscalls.c
@@ -16,6 +16,7 @@
#include "asm/prctl.h" /* XXX This should get the constants from libc */
#include "choose-mode.h"
#include "kern.h"
+#include "os.h"
asmlinkage long sys_uname64(struct new_utsname __user * name)
{
@@ -58,40 +59,70 @@ static long arch_prctl_tt(int code, unsigned long addr)
#ifdef CONFIG_MODE_SKAS
-/* XXX: Must also call arch_prctl in the host, beside saving the segment bases! */
-static long arch_prctl_skas(int code, unsigned long addr)
+static long arch_prctl_skas(int code, unsigned long __user *addr)
{
- long ret = 0;
+ unsigned long *ptr = addr, tmp;
+ long ret;
+ int pid = current->mm->context.skas.id.u.pid;
+
+ /*
+ * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to
+ * be safe), we need to call arch_prctl on the host because
+ * setting %fs may result in something else happening (like a
+ * GDT being set instead). So, we let the host fiddle the
+ * registers and restore them afterwards.
+ *
+ * So, the saved registers are stored to the process (this
+ * needed because a stub may have been the last thing to run),
+ * arch_prctl is run on the host, then the registers are read
+ * back.
+ */
+ switch(code){
+ case ARCH_SET_FS:
+ case ARCH_SET_GS:
+ restore_registers(pid, &current->thread.regs.regs);
+ break;
+ case ARCH_GET_FS:
+ case ARCH_GET_GS:
+ /*
+ * With these two, we read to a local pointer and
+ * put_user it to the userspace pointer that we were
+ * given. If addr isn't valid (because it hasn't been
+ * faulted in or is just bogus), we want put_user to
+ * fault it in (or return -EFAULT) instead of having
+ * the host return -EFAULT.
+ */
+ ptr = &tmp;
+ }
+
+ ret = os_arch_prctl(pid, code, ptr);
+ if(ret)
+ return ret;
switch(code){
case ARCH_SET_FS:
- current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr;
+ current->thread.arch.fs = (unsigned long) ptr;
+ save_registers(pid, &current->thread.regs.regs);
break;
case ARCH_SET_GS:
- current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr;
+ save_registers(pid, &current->thread.regs.regs);
break;
case ARCH_GET_FS:
- ret = put_user(current->thread.regs.regs.skas.
- regs[FS_BASE / sizeof(unsigned long)],
- (unsigned long __user *)addr);
- break;
+ ret = put_user(tmp, addr);
+ break;
case ARCH_GET_GS:
- ret = put_user(current->thread.regs.regs.skas.
- regs[GS_BASE / sizeof(unsigned long)],
- (unsigned long __user *)addr);
- break;
- default:
- ret = -EINVAL;
+ ret = put_user(tmp, addr);
break;
}
- return(ret);
+ return ret;
}
#endif
long sys_arch_prctl(int code, unsigned long addr)
{
- return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr));
+ return CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code,
+ (unsigned long __user *) addr);
}
long sys_clone(unsigned long clone_flags, unsigned long newsp,
@@ -105,5 +136,14 @@ long sys_clone(unsigned long clone_flags, unsigned long newsp,
ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
child_tid);
current->thread.forking = 0;
- return(ret);
+ return ret;
}
+
+void arch_switch_to_skas(struct task_struct *from, struct task_struct *to)
+{
+ if((to->thread.arch.fs == 0) || (to->mm == NULL))
+ return;
+
+ arch_prctl_skas(ARCH_SET_FS, (void __user *) to->thread.arch.fs);
+}
+
diff --git a/arch/um/sys-x86_64/tls.c b/arch/um/sys-x86_64/tls.c
index ce1bf1b81c43..febbc94be25f 100644
--- a/arch/um/sys-x86_64/tls.c
+++ b/arch/um/sys-x86_64/tls.c
@@ -1,14 +1,17 @@
#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)
{
+ /*
+ * If CLONE_SETTLS is set, we need to save the thread id
+ * (which is argument 5, child_tid, of clone) so it can be set
+ * during context switches.
+ */
+ t->thread.arch.fs = t->thread.regs.regs.skas.regs[R8 / sizeof(long)];
+
return 0;
}
diff --git a/include/asm-um/processor-x86_64.h b/include/asm-um/processor-x86_64.h
index 10609af376c0..578ca0492cbe 100644
--- a/include/asm-um/processor-x86_64.h
+++ b/include/asm-um/processor-x86_64.h
@@ -13,6 +13,7 @@
struct arch_thread {
unsigned long debugregs[8];
int debugregs_seq;
+ unsigned long fs;
struct faultinfo faultinfo;
};
@@ -25,8 +26,9 @@ extern inline void rep_nop(void)
#define cpu_relax() rep_nop()
#define INIT_ARCH_THREAD { .debugregs = { [ 0 ... 7 ] = 0 }, \
- .debugregs_seq = 0, \
- .faultinfo = { 0, 0, 0 } }
+ .debugregs_seq = 0, \
+ .fs = 0, \
+ .faultinfo = { 0, 0, 0 } }
static inline void arch_flush_thread(struct arch_thread *thread)
{
diff --git a/include/asm-um/ptrace-x86_64.h b/include/asm-um/ptrace-x86_64.h
index 03b4af4ac09a..a927450ce401 100644
--- a/include/asm-um/ptrace-x86_64.h
+++ b/include/asm-um/ptrace-x86_64.h
@@ -81,9 +81,7 @@ static inline void arch_switch_to_tt(struct task_struct *from,
{
}
-static inline void arch_switch_to_skas(struct task_struct *from,
- struct task_struct *to)
-{
-}
+extern void arch_switch_to_skas(struct task_struct *from,
+ struct task_struct *to);
#endif