diff options
Diffstat (limited to 'arch/x86/um/syscalls_64.c')
-rw-r--r-- | arch/x86/um/syscalls_64.c | 102 |
1 files changed, 102 insertions, 0 deletions
diff --git a/arch/x86/um/syscalls_64.c b/arch/x86/um/syscalls_64.c new file mode 100644 index 000000000000..f3d82bb6e15a --- /dev/null +++ b/arch/x86/um/syscalls_64.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2003 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | ||
3 | * Copyright 2003 PathScale, Inc. | ||
4 | * | ||
5 | * Licensed under the GPL | ||
6 | */ | ||
7 | |||
8 | #include "linux/linkage.h" | ||
9 | #include "linux/personality.h" | ||
10 | #include "linux/utsname.h" | ||
11 | #include "asm/prctl.h" /* XXX This should get the constants from libc */ | ||
12 | #include "asm/uaccess.h" | ||
13 | #include "os.h" | ||
14 | |||
15 | long arch_prctl(struct task_struct *task, int code, unsigned long __user *addr) | ||
16 | { | ||
17 | unsigned long *ptr = addr, tmp; | ||
18 | long ret; | ||
19 | int pid = task->mm->context.id.u.pid; | ||
20 | |||
21 | /* | ||
22 | * With ARCH_SET_FS (and ARCH_SET_GS is treated similarly to | ||
23 | * be safe), we need to call arch_prctl on the host because | ||
24 | * setting %fs may result in something else happening (like a | ||
25 | * GDT or thread.fs being set instead). So, we let the host | ||
26 | * fiddle the registers and thread struct and restore the | ||
27 | * registers afterwards. | ||
28 | * | ||
29 | * So, the saved registers are stored to the process (this | ||
30 | * needed because a stub may have been the last thing to run), | ||
31 | * arch_prctl is run on the host, then the registers are read | ||
32 | * back. | ||
33 | */ | ||
34 | switch (code) { | ||
35 | case ARCH_SET_FS: | ||
36 | case ARCH_SET_GS: | ||
37 | ret = restore_registers(pid, ¤t->thread.regs.regs); | ||
38 | if (ret) | ||
39 | return ret; | ||
40 | break; | ||
41 | case ARCH_GET_FS: | ||
42 | case ARCH_GET_GS: | ||
43 | /* | ||
44 | * With these two, we read to a local pointer and | ||
45 | * put_user it to the userspace pointer that we were | ||
46 | * given. If addr isn't valid (because it hasn't been | ||
47 | * faulted in or is just bogus), we want put_user to | ||
48 | * fault it in (or return -EFAULT) instead of having | ||
49 | * the host return -EFAULT. | ||
50 | */ | ||
51 | ptr = &tmp; | ||
52 | } | ||
53 | |||
54 | ret = os_arch_prctl(pid, code, ptr); | ||
55 | if (ret) | ||
56 | return ret; | ||
57 | |||
58 | switch (code) { | ||
59 | case ARCH_SET_FS: | ||
60 | current->thread.arch.fs = (unsigned long) ptr; | ||
61 | ret = save_registers(pid, ¤t->thread.regs.regs); | ||
62 | break; | ||
63 | case ARCH_SET_GS: | ||
64 | ret = save_registers(pid, ¤t->thread.regs.regs); | ||
65 | break; | ||
66 | case ARCH_GET_FS: | ||
67 | ret = put_user(tmp, addr); | ||
68 | break; | ||
69 | case ARCH_GET_GS: | ||
70 | ret = put_user(tmp, addr); | ||
71 | break; | ||
72 | } | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | long sys_arch_prctl(int code, unsigned long addr) | ||
78 | { | ||
79 | return arch_prctl(current, code, (unsigned long __user *) addr); | ||
80 | } | ||
81 | |||
82 | long sys_clone(unsigned long clone_flags, unsigned long newsp, | ||
83 | void __user *parent_tid, void __user *child_tid) | ||
84 | { | ||
85 | long ret; | ||
86 | |||
87 | if (!newsp) | ||
88 | newsp = UPT_SP(¤t->thread.regs.regs); | ||
89 | current->thread.forking = 1; | ||
90 | ret = do_fork(clone_flags, newsp, ¤t->thread.regs, 0, parent_tid, | ||
91 | child_tid); | ||
92 | current->thread.forking = 0; | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | void arch_switch_to(struct task_struct *to) | ||
97 | { | ||
98 | if ((to->thread.arch.fs == 0) || (to->mm == NULL)) | ||
99 | return; | ||
100 | |||
101 | arch_prctl(to, ARCH_SET_FS, (void __user *) to->thread.arch.fs); | ||
102 | } | ||