e915eaec4f961bc83b69aa525e5c465324b6a133
[cascardo/linux.git] / arch / x86 / vdso / vma.c
1 /*
2  * Set up the VMAs to tell the VM about the vDSO.
3  * Copyright 2007 Andi Kleen, SUSE Labs.
4  * Subject to the GPL, v.2
5  */
6 #include <linux/mm.h>
7 #include <linux/err.h>
8 #include <linux/sched.h>
9 #include <linux/slab.h>
10 #include <linux/init.h>
11 #include <linux/random.h>
12 #include <linux/elf.h>
13 #include <asm/vsyscall.h>
14 #include <asm/vgtod.h>
15 #include <asm/proto.h>
16 #include <asm/vdso.h>
17 #include <asm/page.h>
18 #include <asm/hpet.h>
19
20 #if defined(CONFIG_X86_64)
21 unsigned int __read_mostly vdso64_enabled = 1;
22
23 extern unsigned short vdso_sync_cpuid;
24 #endif
25
26 void __init init_vdso_image(const struct vdso_image *image)
27 {
28         int i;
29         int npages = (image->size) / PAGE_SIZE;
30
31         BUG_ON(image->size % PAGE_SIZE != 0);
32         for (i = 0; i < npages; i++)
33                 image->pages[i] = virt_to_page(image->data + i*PAGE_SIZE);
34
35         apply_alternatives((struct alt_instr *)(image->data + image->alt),
36                            (struct alt_instr *)(image->data + image->alt +
37                                                 image->alt_len));
38 }
39
40 #if defined(CONFIG_X86_64)
41 static int __init init_vdso(void)
42 {
43         init_vdso_image(&vdso_image_64);
44
45 #ifdef CONFIG_X86_X32_ABI
46         init_vdso_image(&vdso_image_x32);
47 #endif
48
49         return 0;
50 }
51 subsys_initcall(init_vdso);
52 #endif
53
54 struct linux_binprm;
55
56 /* Put the vdso above the (randomized) stack with another randomized offset.
57    This way there is no hole in the middle of address space.
58    To save memory make sure it is still in the same PTE as the stack top.
59    This doesn't give that many random bits.
60
61    Only used for the 64-bit and x32 vdsos. */
62 static unsigned long vdso_addr(unsigned long start, unsigned len)
63 {
64         unsigned long addr, end;
65         unsigned offset;
66         end = (start + PMD_SIZE - 1) & PMD_MASK;
67         if (end >= TASK_SIZE_MAX)
68                 end = TASK_SIZE_MAX;
69         end -= len;
70         /* This loses some more bits than a modulo, but is cheaper */
71         offset = get_random_int() & (PTRS_PER_PTE - 1);
72         addr = start + (offset << PAGE_SHIFT);
73         if (addr >= end)
74                 addr = end;
75
76         /*
77          * page-align it here so that get_unmapped_area doesn't
78          * align it wrongfully again to the next page. addr can come in 4K
79          * unaligned here as a result of stack start randomization.
80          */
81         addr = PAGE_ALIGN(addr);
82         addr = align_vdso_addr(addr);
83
84         return addr;
85 }
86
87 static int map_vdso(const struct vdso_image *image, bool calculate_addr)
88 {
89         struct mm_struct *mm = current->mm;
90         struct vm_area_struct *vma;
91         unsigned long addr;
92         int ret = 0;
93
94         if (calculate_addr) {
95                 addr = vdso_addr(current->mm->start_stack,
96                                  image->sym_end_mapping);
97         } else {
98                 addr = 0;
99         }
100
101         down_write(&mm->mmap_sem);
102
103         addr = get_unmapped_area(NULL, addr, image->sym_end_mapping, 0, 0);
104         if (IS_ERR_VALUE(addr)) {
105                 ret = addr;
106                 goto up_fail;
107         }
108
109         current->mm->context.vdso = (void __user *)addr;
110
111         /*
112          * MAYWRITE to allow gdb to COW and set breakpoints
113          */
114         ret = install_special_mapping(mm,
115                                       addr,
116                                       image->size,
117                                       VM_READ|VM_EXEC|
118                                       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
119                                       image->pages);
120
121         if (ret)
122                 goto up_fail;
123
124         vma = _install_special_mapping(mm,
125                                        addr + image->size,
126                                        image->sym_end_mapping - image->size,
127                                        VM_READ,
128                                        NULL);
129
130         if (IS_ERR(vma)) {
131                 ret = PTR_ERR(vma);
132                 goto up_fail;
133         }
134
135         if (image->sym_vvar_page)
136                 ret = remap_pfn_range(vma,
137                                       addr + image->sym_vvar_page,
138                                       __pa_symbol(&__vvar_page) >> PAGE_SHIFT,
139                                       PAGE_SIZE,
140                                       PAGE_READONLY);
141
142         if (ret)
143                 goto up_fail;
144
145 #ifdef CONFIG_HPET_TIMER
146         if (hpet_address && image->sym_hpet_page) {
147                 ret = io_remap_pfn_range(vma,
148                         addr + image->sym_hpet_page,
149                         hpet_address >> PAGE_SHIFT,
150                         PAGE_SIZE,
151                         pgprot_noncached(PAGE_READONLY));
152
153                 if (ret)
154                         goto up_fail;
155         }
156 #endif
157
158 up_fail:
159         if (ret)
160                 current->mm->context.vdso = NULL;
161
162         up_write(&mm->mmap_sem);
163         return ret;
164 }
165
166 #if defined(CONFIG_X86_32) || defined(CONFIG_COMPAT)
167 static int load_vdso32(void)
168 {
169         int ret;
170
171         if (vdso32_enabled != 1)  /* Other values all mean "disabled" */
172                 return 0;
173
174         ret = map_vdso(selected_vdso32, false);
175         if (ret)
176                 return ret;
177
178         if (selected_vdso32->sym_VDSO32_SYSENTER_RETURN)
179                 current_thread_info()->sysenter_return =
180                         current->mm->context.vdso +
181                         selected_vdso32->sym_VDSO32_SYSENTER_RETURN;
182
183         return 0;
184 }
185 #endif
186
187 #ifdef CONFIG_X86_64
188 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
189 {
190         if (!vdso64_enabled)
191                 return 0;
192
193         return map_vdso(&vdso_image_64, true);
194 }
195
196 #ifdef CONFIG_COMPAT
197 int compat_arch_setup_additional_pages(struct linux_binprm *bprm,
198                                        int uses_interp)
199 {
200 #ifdef CONFIG_X86_X32_ABI
201         if (test_thread_flag(TIF_X32)) {
202                 if (!vdso64_enabled)
203                         return 0;
204
205                 return map_vdso(&vdso_image_x32, true);
206         }
207 #endif
208
209         return load_vdso32();
210 }
211 #endif
212 #else
213 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
214 {
215         return load_vdso32();
216 }
217 #endif
218
219 #ifdef CONFIG_X86_64
220 static __init int vdso_setup(char *s)
221 {
222         vdso64_enabled = simple_strtoul(s, NULL, 0);
223         return 0;
224 }
225 __setup("vdso=", vdso_setup);
226 #endif