blob: 5a5176de8d0a4f5fe452647d54b1a61a52d909ef [file] [log] [blame]
Andi Kleen2aae9502007-07-21 17:10:01 +02001/*
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>
Alexey Dobriyan4e950f62007-07-30 02:36:13 +04007#include <linux/err.h>
Andi Kleen2aae9502007-07-21 17:10:01 +02008#include <linux/sched.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +09009#include <linux/slab.h>
Andi Kleen2aae9502007-07-21 17:10:01 +020010#include <linux/init.h>
11#include <linux/random.h>
Jaswinder Singh Rajput3fa89ca2009-04-12 20:37:25 +053012#include <linux/elf.h>
Andi Kleen2aae9502007-07-21 17:10:01 +020013#include <asm/vsyscall.h>
14#include <asm/vgtod.h>
15#include <asm/proto.h>
Roland McGrath7f3646a2008-01-30 13:30:41 +010016#include <asm/vdso.h>
Andy Lutomirskiaafade22011-07-21 15:47:10 -040017#include <asm/page.h>
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070018#include <asm/hpet.h>
Roland McGrath7f3646a2008-01-30 13:30:41 +010019
Andy Lutomirskib4b541a2014-03-17 23:22:08 +010020#if defined(CONFIG_X86_64)
Andy Lutomirski3d7ee962014-05-05 12:19:32 -070021unsigned int __read_mostly vdso64_enabled = 1;
Andi Kleen2aae9502007-07-21 17:10:01 +020022
Andi Kleen2aae9502007-07-21 17:10:01 +020023extern unsigned short vdso_sync_cpuid;
Andy Lutomirskib4b541a2014-03-17 23:22:08 +010024#endif
H. J. Lu1a21d4e2012-02-19 11:38:06 -080025
Andy Lutomirski6f121e52014-05-05 12:19:34 -070026void __init init_vdso_image(const struct vdso_image *image)
H. J. Lu1a21d4e2012-02-19 11:38:06 -080027{
H. J. Lu1a21d4e2012-02-19 11:38:06 -080028 int i;
Andy Lutomirski6f121e52014-05-05 12:19:34 -070029 int npages = (image->size) / PAGE_SIZE;
H. J. Lu1a21d4e2012-02-19 11:38:06 -080030
Andy Lutomirski6f121e52014-05-05 12:19:34 -070031 BUG_ON(image->size % PAGE_SIZE != 0);
32 for (i = 0; i < npages; i++)
Andy Lutomirskia62c34b2014-05-19 15:58:33 -070033 image->text_mapping.pages[i] =
34 virt_to_page(image->data + i*PAGE_SIZE);
H. J. Lu1a21d4e2012-02-19 11:38:06 -080035
Andy Lutomirski6f121e52014-05-05 12:19:34 -070036 apply_alternatives((struct alt_instr *)(image->data + image->alt),
37 (struct alt_instr *)(image->data + image->alt +
38 image->alt_len));
H. J. Lu1a21d4e2012-02-19 11:38:06 -080039}
Andy Lutomirski6f121e52014-05-05 12:19:34 -070040
Andy Lutomirskib4b541a2014-03-17 23:22:08 +010041#if defined(CONFIG_X86_64)
Andy Lutomirskiaafade22011-07-21 15:47:10 -040042static int __init init_vdso(void)
Andi Kleen2aae9502007-07-21 17:10:01 +020043{
Andy Lutomirski6f121e52014-05-05 12:19:34 -070044 init_vdso_image(&vdso_image_64);
Andi Kleen2aae9502007-07-21 17:10:01 +020045
H. J. Lu1a21d4e2012-02-19 11:38:06 -080046#ifdef CONFIG_X86_X32_ABI
Andy Lutomirski6f121e52014-05-05 12:19:34 -070047 init_vdso_image(&vdso_image_x32);
H. J. Lu1a21d4e2012-02-19 11:38:06 -080048#endif
49
Andi Kleen2aae9502007-07-21 17:10:01 +020050 return 0;
Andi Kleen2aae9502007-07-21 17:10:01 +020051}
Andy Lutomirskiaafade22011-07-21 15:47:10 -040052subsys_initcall(init_vdso);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070053#endif
Andi Kleen2aae9502007-07-21 17:10:01 +020054
55struct linux_binprm;
56
57/* Put the vdso above the (randomized) stack with another randomized offset.
58 This way there is no hole in the middle of address space.
59 To save memory make sure it is still in the same PTE as the stack top.
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070060 This doesn't give that many random bits.
61
62 Only used for the 64-bit and x32 vdsos. */
Andi Kleen2aae9502007-07-21 17:10:01 +020063static unsigned long vdso_addr(unsigned long start, unsigned len)
64{
Jan Beulichd0936012014-07-03 15:35:07 +010065#ifdef CONFIG_X86_32
66 return 0;
67#else
Andi Kleen2aae9502007-07-21 17:10:01 +020068 unsigned long addr, end;
69 unsigned offset;
70 end = (start + PMD_SIZE - 1) & PMD_MASK;
Ingo Molnard9517342009-02-20 23:32:28 +010071 if (end >= TASK_SIZE_MAX)
72 end = TASK_SIZE_MAX;
Andi Kleen2aae9502007-07-21 17:10:01 +020073 end -= len;
74 /* This loses some more bits than a modulo, but is cheaper */
75 offset = get_random_int() & (PTRS_PER_PTE - 1);
76 addr = start + (offset << PAGE_SHIFT);
77 if (addr >= end)
78 addr = end;
Borislav Petkovdfb09f92011-08-05 15:15:08 +020079
80 /*
81 * page-align it here so that get_unmapped_area doesn't
82 * align it wrongfully again to the next page. addr can come in 4K
83 * unaligned here as a result of stack start randomization.
84 */
85 addr = PAGE_ALIGN(addr);
Michel Lespinassef99024722012-12-11 16:01:52 -080086 addr = align_vdso_addr(addr);
Borislav Petkovdfb09f92011-08-05 15:15:08 +020087
Andi Kleen2aae9502007-07-21 17:10:01 +020088 return addr;
Jan Beulichd0936012014-07-03 15:35:07 +010089#endif
Andi Kleen2aae9502007-07-21 17:10:01 +020090}
91
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070092static int map_vdso(const struct vdso_image *image, bool calculate_addr)
Andi Kleen2aae9502007-07-21 17:10:01 +020093{
94 struct mm_struct *mm = current->mm;
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070095 struct vm_area_struct *vma;
Andi Kleen2aae9502007-07-21 17:10:01 +020096 unsigned long addr;
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070097 int ret = 0;
Andy Lutomirski1e844fb2014-05-19 15:58:31 -070098 static struct page *no_pages[] = {NULL};
Andy Lutomirskia62c34b2014-05-19 15:58:33 -070099 static struct vm_special_mapping vvar_mapping = {
100 .name = "[vvar]",
101 .pages = no_pages,
102 };
Andi Kleen2aae9502007-07-21 17:10:01 +0200103
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700104 if (calculate_addr) {
105 addr = vdso_addr(current->mm->start_stack,
106 image->sym_end_mapping);
107 } else {
108 addr = 0;
109 }
Andi Kleen2aae9502007-07-21 17:10:01 +0200110
111 down_write(&mm->mmap_sem);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700112
113 addr = get_unmapped_area(NULL, addr, image->sym_end_mapping, 0, 0);
Andi Kleen2aae9502007-07-21 17:10:01 +0200114 if (IS_ERR_VALUE(addr)) {
115 ret = addr;
116 goto up_fail;
117 }
118
Andy Lutomirski6f121e52014-05-05 12:19:34 -0700119 current->mm->context.vdso = (void __user *)addr;
Peter Zijlstraf7b6eb32009-06-05 14:04:51 +0200120
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700121 /*
122 * MAYWRITE to allow gdb to COW and set breakpoints
123 */
Andy Lutomirskia62c34b2014-05-19 15:58:33 -0700124 vma = _install_special_mapping(mm,
125 addr,
126 image->size,
127 VM_READ|VM_EXEC|
128 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
129 &image->text_mapping);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700130
Andy Lutomirskia62c34b2014-05-19 15:58:33 -0700131 if (IS_ERR(vma)) {
132 ret = PTR_ERR(vma);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700133 goto up_fail;
Andy Lutomirskia62c34b2014-05-19 15:58:33 -0700134 }
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700135
136 vma = _install_special_mapping(mm,
137 addr + image->size,
138 image->sym_end_mapping - image->size,
139 VM_READ,
Andy Lutomirskia62c34b2014-05-19 15:58:33 -0700140 &vvar_mapping);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700141
142 if (IS_ERR(vma)) {
143 ret = PTR_ERR(vma);
Andi Kleen2aae9502007-07-21 17:10:01 +0200144 goto up_fail;
Peter Zijlstraf7b6eb32009-06-05 14:04:51 +0200145 }
Andi Kleen2aae9502007-07-21 17:10:01 +0200146
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700147 if (image->sym_vvar_page)
148 ret = remap_pfn_range(vma,
149 addr + image->sym_vvar_page,
150 __pa_symbol(&__vvar_page) >> PAGE_SHIFT,
151 PAGE_SIZE,
152 PAGE_READONLY);
153
154 if (ret)
155 goto up_fail;
156
157#ifdef CONFIG_HPET_TIMER
158 if (hpet_address && image->sym_hpet_page) {
159 ret = io_remap_pfn_range(vma,
160 addr + image->sym_hpet_page,
161 hpet_address >> PAGE_SHIFT,
162 PAGE_SIZE,
163 pgprot_noncached(PAGE_READONLY));
164
165 if (ret)
166 goto up_fail;
167 }
168#endif
169
Andi Kleen2aae9502007-07-21 17:10:01 +0200170up_fail:
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700171 if (ret)
172 current->mm->context.vdso = NULL;
173
Andi Kleen2aae9502007-07-21 17:10:01 +0200174 up_write(&mm->mmap_sem);
175 return ret;
176}
177
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700178#if defined(CONFIG_X86_32) || defined(CONFIG_COMPAT)
179static int load_vdso32(void)
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800180{
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700181 int ret;
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800182
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700183 if (vdso32_enabled != 1) /* Other values all mean "disabled" */
184 return 0;
185
186 ret = map_vdso(selected_vdso32, false);
187 if (ret)
188 return ret;
189
190 if (selected_vdso32->sym_VDSO32_SYSENTER_RETURN)
191 current_thread_info()->sysenter_return =
192 current->mm->context.vdso +
193 selected_vdso32->sym_VDSO32_SYSENTER_RETURN;
194
195 return 0;
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800196}
197#endif
198
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700199#ifdef CONFIG_X86_64
200int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
201{
202 if (!vdso64_enabled)
203 return 0;
204
205 return map_vdso(&vdso_image_64, true);
206}
207
208#ifdef CONFIG_COMPAT
209int compat_arch_setup_additional_pages(struct linux_binprm *bprm,
210 int uses_interp)
211{
212#ifdef CONFIG_X86_X32_ABI
213 if (test_thread_flag(TIF_X32)) {
214 if (!vdso64_enabled)
215 return 0;
216
217 return map_vdso(&vdso_image_x32, true);
218 }
219#endif
220
221 return load_vdso32();
222}
223#endif
224#else
225int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
226{
227 return load_vdso32();
228}
229#endif
230
231#ifdef CONFIG_X86_64
Andi Kleen2aae9502007-07-21 17:10:01 +0200232static __init int vdso_setup(char *s)
233{
Andy Lutomirski3d7ee962014-05-05 12:19:32 -0700234 vdso64_enabled = simple_strtoul(s, NULL, 0);
Andi Kleen2aae9502007-07-21 17:10:01 +0200235 return 0;
236}
237__setup("vdso=", vdso_setup);
Andy Lutomirskib4b541a2014-03-17 23:22:08 +0100238#endif