x86: Remove unnecessary compile flag tweaks for vsyscall code
[cascardo/linux.git] / arch / x86 / kernel / vsyscall_64.c
1 /*
2  *  Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
3  *  Copyright 2003 Andi Kleen, SuSE Labs.
4  *
5  *  [ NOTE: this mechanism is now deprecated in favor of the vDSO. ]
6  *
7  *  Thanks to hpa@transmeta.com for some useful hint.
8  *  Special thanks to Ingo Molnar for his early experience with
9  *  a different vsyscall implementation for Linux/IA32 and for the name.
10  *
11  *  vsyscall 1 is located at -10Mbyte, vsyscall 2 is located
12  *  at virtual address -10Mbyte+1024bytes etc... There are at max 4
13  *  vsyscalls. One vsyscall can reserve more than 1 slot to avoid
14  *  jumping out of line if necessary. We cannot add more with this
15  *  mechanism because older kernels won't return -ENOSYS.
16  *
17  *  Note: the concept clashes with user mode linux.  UML users should
18  *  use the vDSO.
19  */
20
21 #include <linux/time.h>
22 #include <linux/init.h>
23 #include <linux/kernel.h>
24 #include <linux/timer.h>
25 #include <linux/seqlock.h>
26 #include <linux/jiffies.h>
27 #include <linux/sysctl.h>
28 #include <linux/clocksource.h>
29 #include <linux/getcpu.h>
30 #include <linux/cpu.h>
31 #include <linux/smp.h>
32 #include <linux/notifier.h>
33 #include <linux/syscalls.h>
34 #include <linux/ratelimit.h>
35
36 #include <asm/vsyscall.h>
37 #include <asm/pgtable.h>
38 #include <asm/compat.h>
39 #include <asm/page.h>
40 #include <asm/unistd.h>
41 #include <asm/fixmap.h>
42 #include <asm/errno.h>
43 #include <asm/io.h>
44 #include <asm/segment.h>
45 #include <asm/desc.h>
46 #include <asm/topology.h>
47 #include <asm/vgtod.h>
48 #include <asm/traps.h>
49
50 #define CREATE_TRACE_POINTS
51 #include "vsyscall_trace.h"
52
53 DEFINE_VVAR(int, vgetcpu_mode);
54 DEFINE_VVAR(struct vsyscall_gtod_data, vsyscall_gtod_data) =
55 {
56         .lock = __SEQLOCK_UNLOCKED(__vsyscall_gtod_data.lock),
57 };
58
59 void update_vsyscall_tz(void)
60 {
61         unsigned long flags;
62
63         write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags);
64         /* sys_tz has changed */
65         vsyscall_gtod_data.sys_tz = sys_tz;
66         write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
67 }
68
69 void update_vsyscall(struct timespec *wall_time, struct timespec *wtm,
70                         struct clocksource *clock, u32 mult)
71 {
72         unsigned long flags;
73
74         write_seqlock_irqsave(&vsyscall_gtod_data.lock, flags);
75
76         /* copy vsyscall data */
77         vsyscall_gtod_data.clock.vclock_mode    = clock->archdata.vclock_mode;
78         vsyscall_gtod_data.clock.cycle_last     = clock->cycle_last;
79         vsyscall_gtod_data.clock.mask           = clock->mask;
80         vsyscall_gtod_data.clock.mult           = mult;
81         vsyscall_gtod_data.clock.shift          = clock->shift;
82         vsyscall_gtod_data.wall_time_sec        = wall_time->tv_sec;
83         vsyscall_gtod_data.wall_time_nsec       = wall_time->tv_nsec;
84         vsyscall_gtod_data.wall_to_monotonic    = *wtm;
85         vsyscall_gtod_data.wall_time_coarse     = __current_kernel_time();
86
87         write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
88 }
89
90 static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
91                               const char *message)
92 {
93         static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST);
94         struct task_struct *tsk;
95
96         if (!show_unhandled_signals || !__ratelimit(&rs))
97                 return;
98
99         tsk = current;
100
101         printk("%s%s[%d] %s ip:%lx cs:%lx sp:%lx ax:%lx si:%lx di:%lx\n",
102                level, tsk->comm, task_pid_nr(tsk),
103                message, regs->ip - 2, regs->cs,
104                regs->sp, regs->ax, regs->si, regs->di);
105 }
106
107 static int addr_to_vsyscall_nr(unsigned long addr)
108 {
109         int nr;
110
111         if ((addr & ~0xC00UL) != VSYSCALL_START)
112                 return -EINVAL;
113
114         nr = (addr & 0xC00UL) >> 10;
115         if (nr >= 3)
116                 return -EINVAL;
117
118         return nr;
119 }
120
121 void dotraplinkage do_emulate_vsyscall(struct pt_regs *regs, long error_code)
122 {
123         struct task_struct *tsk;
124         unsigned long caller;
125         int vsyscall_nr;
126         long ret;
127
128         local_irq_enable();
129
130         if (!user_64bit_mode(regs)) {
131                 /*
132                  * If we trapped from kernel mode, we might as well OOPS now
133                  * instead of returning to some random address and OOPSing
134                  * then.
135                  */
136                 BUG_ON(!user_mode(regs));
137
138                 /* Compat mode and non-compat 32-bit CS should both segfault. */
139                 warn_bad_vsyscall(KERN_WARNING, regs,
140                                   "illegal int 0xcc from 32-bit mode");
141                 goto sigsegv;
142         }
143
144         /*
145          * x86-ism here: regs->ip points to the instruction after the int 0xcc,
146          * and int 0xcc is two bytes long.
147          */
148         vsyscall_nr = addr_to_vsyscall_nr(regs->ip - 2);
149
150         trace_emulate_vsyscall(vsyscall_nr);
151
152         if (vsyscall_nr < 0) {
153                 warn_bad_vsyscall(KERN_WARNING, regs,
154                                   "illegal int 0xcc (exploit attempt?)");
155                 goto sigsegv;
156         }
157
158         if (get_user(caller, (unsigned long __user *)regs->sp) != 0) {
159                 warn_bad_vsyscall(KERN_WARNING, regs, "int 0xcc with bad stack (exploit attempt?)");
160                 goto sigsegv;
161         }
162
163         tsk = current;
164         if (seccomp_mode(&tsk->seccomp))
165                 do_exit(SIGKILL);
166
167         switch (vsyscall_nr) {
168         case 0:
169                 ret = sys_gettimeofday(
170                         (struct timeval __user *)regs->di,
171                         (struct timezone __user *)regs->si);
172                 break;
173
174         case 1:
175                 ret = sys_time((time_t __user *)regs->di);
176                 break;
177
178         case 2:
179                 ret = sys_getcpu((unsigned __user *)regs->di,
180                                  (unsigned __user *)regs->si,
181                                  0);
182                 break;
183         }
184
185         if (ret == -EFAULT) {
186                 /*
187                  * Bad news -- userspace fed a bad pointer to a vsyscall.
188                  *
189                  * With a real vsyscall, that would have caused SIGSEGV.
190                  * To make writing reliable exploits using the emulated
191                  * vsyscalls harder, generate SIGSEGV here as well.
192                  */
193                 warn_bad_vsyscall(KERN_INFO, regs,
194                                   "vsyscall fault (exploit attempt?)");
195                 goto sigsegv;
196         }
197
198         regs->ax = ret;
199
200         /* Emulate a ret instruction. */
201         regs->ip = caller;
202         regs->sp += 8;
203
204         local_irq_disable();
205         return;
206
207 sigsegv:
208         regs->ip -= 2;  /* The faulting instruction should be the int 0xcc. */
209         force_sig(SIGSEGV, current);
210         local_irq_disable();
211 }
212
213 /*
214  * Assume __initcall executes before all user space. Hopefully kmod
215  * doesn't violate that. We'll find out if it does.
216  */
217 static void __cpuinit vsyscall_set_cpu(int cpu)
218 {
219         unsigned long d;
220         unsigned long node = 0;
221 #ifdef CONFIG_NUMA
222         node = cpu_to_node(cpu);
223 #endif
224         if (cpu_has(&cpu_data(cpu), X86_FEATURE_RDTSCP))
225                 write_rdtscp_aux((node << 12) | cpu);
226
227         /*
228          * Store cpu number in limit so that it can be loaded quickly
229          * in user space in vgetcpu. (12 bits for the CPU and 8 bits for the node)
230          */
231         d = 0x0f40000000000ULL;
232         d |= cpu;
233         d |= (node & 0xf) << 12;
234         d |= (node >> 4) << 48;
235
236         write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_PER_CPU, &d, DESCTYPE_S);
237 }
238
239 static void __cpuinit cpu_vsyscall_init(void *arg)
240 {
241         /* preemption should be already off */
242         vsyscall_set_cpu(raw_smp_processor_id());
243 }
244
245 static int __cpuinit
246 cpu_vsyscall_notifier(struct notifier_block *n, unsigned long action, void *arg)
247 {
248         long cpu = (long)arg;
249
250         if (action == CPU_ONLINE || action == CPU_ONLINE_FROZEN)
251                 smp_call_function_single(cpu, cpu_vsyscall_init, NULL, 1);
252
253         return NOTIFY_DONE;
254 }
255
256 void __init map_vsyscall(void)
257 {
258         extern char __vsyscall_0;
259         unsigned long physaddr_page0 = __pa_symbol(&__vsyscall_0);
260         extern char __vvar_page;
261         unsigned long physaddr_vvar_page = __pa_symbol(&__vvar_page);
262
263         /* Note that VSYSCALL_MAPPED_PAGES must agree with the code below. */
264         __set_fixmap(VSYSCALL_FIRST_PAGE, physaddr_page0, PAGE_KERNEL_VSYSCALL);
265         __set_fixmap(VVAR_PAGE, physaddr_vvar_page, PAGE_KERNEL_VVAR);
266         BUILD_BUG_ON((unsigned long)__fix_to_virt(VVAR_PAGE) != (unsigned long)VVAR_ADDRESS);
267 }
268
269 static int __init vsyscall_init(void)
270 {
271         BUG_ON(VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE));
272
273         on_each_cpu(cpu_vsyscall_init, NULL, 1);
274         /* notifier priority > KVM */
275         hotcpu_notifier(cpu_vsyscall_notifier, 30);
276
277         return 0;
278 }
279 __initcall(vsyscall_init);