diff options
Diffstat (limited to 'arch/um')
-rw-r--r-- | arch/um/include/os.h | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/sys-x86_64/Makefile | 2 | ||||
-rw-r--r-- | arch/um/os-Linux/sys-x86_64/prctl.c | 12 | ||||
-rw-r--r-- | arch/um/sys-x86_64/syscalls.c | 73 | ||||
-rw-r--r-- | arch/um/sys-x86_64/tls.c | 11 |
5 files changed, 77 insertions, 23 deletions
diff --git a/arch/um/include/os.h b/arch/um/include/os.h index 8f602667296d..8629bd191492 100644 --- a/arch/um/include/os.h +++ b/arch/um/include/os.h | |||
@@ -340,4 +340,6 @@ extern void maybe_sigio_broken(int fd, int read); | |||
340 | extern void sig_handler_common_skas(int sig, void *sc_ptr); | 340 | extern void sig_handler_common_skas(int sig, void *sc_ptr); |
341 | extern void user_signal(int sig, union uml_pt_regs *regs, int pid); | 341 | extern void user_signal(int sig, union uml_pt_regs *regs, int pid); |
342 | 342 | ||
343 | extern int os_arch_prctl(int pid, int code, unsigned long *addr); | ||
344 | |||
343 | #endif | 345 | #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 @@ | |||
3 | # Licensed under the GPL | 3 | # Licensed under the GPL |
4 | # | 4 | # |
5 | 5 | ||
6 | obj-$(CONFIG_MODE_SKAS) = registers.o signal.o | 6 | obj-$(CONFIG_MODE_SKAS) = registers.o prctl.o signal.o |
7 | 7 | ||
8 | USER_OBJS := $(obj-y) | 8 | USER_OBJS := $(obj-y) |
9 | 9 | ||
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..9d34eddb517f --- /dev/null +++ b/arch/um/os-Linux/sys-x86_64/prctl.c | |||
@@ -0,0 +1,12 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2007 Jeff Dike (jdike@{addtoit.com,linux.intel.com}) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <sys/ptrace.h> | ||
7 | #include <linux/ptrace.h> | ||
8 | |||
9 | int os_arch_prctl(int pid, int code, unsigned long *addr) | ||
10 | { | ||
11 | return ptrace(PTRACE_ARCH_PRCTL, pid, (unsigned long) addr, code); | ||
12 | } | ||
diff --git a/arch/um/sys-x86_64/syscalls.c b/arch/um/sys-x86_64/syscalls.c index 73ce4463f70c..f309fa9bc232 100644 --- a/arch/um/sys-x86_64/syscalls.c +++ b/arch/um/sys-x86_64/syscalls.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include "asm/prctl.h" /* XXX This should get the constants from libc */ | 16 | #include "asm/prctl.h" /* XXX This should get the constants from libc */ |
17 | #include "choose-mode.h" | 17 | #include "choose-mode.h" |
18 | #include "kern.h" | 18 | #include "kern.h" |
19 | #include "os.h" | ||
19 | 20 | ||
20 | asmlinkage long sys_uname64(struct new_utsname __user * name) | 21 | asmlinkage long sys_uname64(struct new_utsname __user * name) |
21 | { | 22 | { |
@@ -58,40 +59,67 @@ static long arch_prctl_tt(int code, unsigned long addr) | |||
58 | 59 | ||
59 | #ifdef CONFIG_MODE_SKAS | 60 | #ifdef CONFIG_MODE_SKAS |
60 | 61 | ||
61 | /* XXX: Must also call arch_prctl in the host, beside saving the segment bases! */ | 62 | static long arch_prctl_skas(int code, unsigned long __user *addr) |
62 | static long arch_prctl_skas(int code, unsigned long addr) | ||
63 | { | 63 | { |
64 | long ret = 0; | 64 | unsigned long *ptr = addr, tmp; |
65 | long ret; | ||
66 | int pid = current->mm->context.skas.id.u.pid; | ||
65 | 67 | ||
68 | /* | ||
69 | * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to | ||
70 | * be safe), we need to call arch_prctl on the host because | ||
71 | * setting %fs may result in something else happening (like a | ||
72 | * GDT being set instead). So, we let the host fiddle the | ||
73 | * registers and restore them afterwards. | ||
74 | * | ||
75 | * So, the saved registers are stored to the process (this | ||
76 | * needed because a stub may have been the last thing to run), | ||
77 | * arch_prctl is run on the host, then the registers are read | ||
78 | * back. | ||
79 | */ | ||
66 | switch(code){ | 80 | switch(code){ |
67 | case ARCH_SET_FS: | 81 | case ARCH_SET_FS: |
68 | current->thread.regs.regs.skas.regs[FS_BASE / sizeof(unsigned long)] = addr; | ||
69 | break; | ||
70 | case ARCH_SET_GS: | 82 | case ARCH_SET_GS: |
71 | current->thread.regs.regs.skas.regs[GS_BASE / sizeof(unsigned long)] = addr; | 83 | restore_registers(pid, ¤t->thread.regs.regs); |
84 | break; | ||
85 | case ARCH_GET_FS: | ||
86 | case ARCH_GET_GS: | ||
87 | /* | ||
88 | * With these two, we read to a local pointer and | ||
89 | * put_user it to the userspace pointer that we were | ||
90 | * given. If addr isn't valid (because it hasn't been | ||
91 | * faulted in or is just bogus), we want put_user to | ||
92 | * fault it in (or return -EFAULT) instead of having | ||
93 | * the host return -EFAULT. | ||
94 | */ | ||
95 | ptr = &tmp; | ||
96 | } | ||
97 | |||
98 | ret = os_arch_prctl(pid, code, ptr); | ||
99 | if(ret) | ||
100 | return ret; | ||
101 | |||
102 | switch(code){ | ||
103 | case ARCH_SET_FS: | ||
104 | case ARCH_SET_GS: | ||
105 | save_registers(pid, ¤t->thread.regs.regs); | ||
72 | break; | 106 | break; |
73 | case ARCH_GET_FS: | 107 | case ARCH_GET_FS: |
74 | ret = put_user(current->thread.regs.regs.skas. | 108 | ret = put_user(tmp, addr); |
75 | regs[FS_BASE / sizeof(unsigned long)], | ||
76 | (unsigned long __user *)addr); | ||
77 | break; | 109 | break; |
78 | case ARCH_GET_GS: | 110 | case ARCH_GET_GS: |
79 | ret = put_user(current->thread.regs.regs.skas. | 111 | ret = put_user(tmp, addr); |
80 | regs[GS_BASE / sizeof(unsigned long)], | ||
81 | (unsigned long __user *)addr); | ||
82 | break; | 112 | break; |
83 | default: | ||
84 | ret = -EINVAL; | ||
85 | break; | ||
86 | } | 113 | } |
87 | 114 | ||
88 | return(ret); | 115 | return ret; |
89 | } | 116 | } |
90 | #endif | 117 | #endif |
91 | 118 | ||
92 | long sys_arch_prctl(int code, unsigned long addr) | 119 | long sys_arch_prctl(int code, unsigned long addr) |
93 | { | 120 | { |
94 | return(CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, addr)); | 121 | return CHOOSE_MODE_PROC(arch_prctl_tt, arch_prctl_skas, code, |
122 | (unsigned long __user *) addr); | ||
95 | } | 123 | } |
96 | 124 | ||
97 | long sys_clone(unsigned long clone_flags, unsigned long newsp, | 125 | long sys_clone(unsigned long clone_flags, unsigned long newsp, |
@@ -105,5 +133,14 @@ long sys_clone(unsigned long clone_flags, unsigned long newsp, | |||
105 | ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, | 133 | ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, |
106 | child_tid); | 134 | child_tid); |
107 | current->thread.forking = 0; | 135 | current->thread.forking = 0; |
108 | return(ret); | 136 | return ret; |
137 | } | ||
138 | |||
139 | void arch_switch_to_skas(struct task_struct *from, struct task_struct *to) | ||
140 | { | ||
141 | if(to->thread.arch.fs == 0) | ||
142 | return; | ||
143 | |||
144 | arch_prctl_skas(ARCH_SET_FS, (void __user *) to->thread.arch.fs); | ||
109 | } | 145 | } |
146 | |||
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 @@ | |||
1 | #include "linux/sched.h" | 1 | #include "linux/sched.h" |
2 | 2 | ||
3 | void debug_arch_force_load_TLS(void) | ||
4 | { | ||
5 | } | ||
6 | |||
7 | void clear_flushed_tls(struct task_struct *task) | 3 | void clear_flushed_tls(struct task_struct *task) |
8 | { | 4 | { |
9 | } | 5 | } |
10 | 6 | ||
11 | int arch_copy_tls(struct task_struct *t) | 7 | int arch_copy_tls(struct task_struct *t) |
12 | { | 8 | { |
9 | /* | ||
10 | * If CLONE_SETTLS is set, we need to save the thread id | ||
11 | * (which is argument 5, child_tid, of clone) so it can be set | ||
12 | * during context switches. | ||
13 | */ | ||
14 | t->thread.arch.fs = t->thread.regs.regs.skas.regs[R8 / sizeof(long)]; | ||
15 | |||
13 | return 0; | 16 | return 0; |
14 | } | 17 | } |