blob: e915eaec4f961bc83b69aa525e5c465324b6a133 [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++)
33 image->pages[i] = virt_to_page(image->data + i*PAGE_SIZE);
H. J. Lu1a21d4e2012-02-19 11:38:06 -080034
Andy Lutomirski6f121e52014-05-05 12:19:34 -070035 apply_alternatives((struct alt_instr *)(image->data + image->alt),
36 (struct alt_instr *)(image->data + image->alt +
37 image->alt_len));
H. J. Lu1a21d4e2012-02-19 11:38:06 -080038}
Andy Lutomirski6f121e52014-05-05 12:19:34 -070039
Andy Lutomirskib4b541a2014-03-17 23:22:08 +010040#if defined(CONFIG_X86_64)
Andy Lutomirskiaafade22011-07-21 15:47:10 -040041static int __init init_vdso(void)
Andi Kleen2aae9502007-07-21 17:10:01 +020042{
Andy Lutomirski6f121e52014-05-05 12:19:34 -070043 init_vdso_image(&vdso_image_64);
Andi Kleen2aae9502007-07-21 17:10:01 +020044
H. J. Lu1a21d4e2012-02-19 11:38:06 -080045#ifdef CONFIG_X86_X32_ABI
Andy Lutomirski6f121e52014-05-05 12:19:34 -070046 init_vdso_image(&vdso_image_x32);
H. J. Lu1a21d4e2012-02-19 11:38:06 -080047#endif
48
Andi Kleen2aae9502007-07-21 17:10:01 +020049 return 0;
Andi Kleen2aae9502007-07-21 17:10:01 +020050}
Andy Lutomirskiaafade22011-07-21 15:47:10 -040051subsys_initcall(init_vdso);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070052#endif
Andi Kleen2aae9502007-07-21 17:10:01 +020053
54struct 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.
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070059 This doesn't give that many random bits.
60
61 Only used for the 64-bit and x32 vdsos. */
Andi Kleen2aae9502007-07-21 17:10:01 +020062static 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;
Ingo Molnard9517342009-02-20 23:32:28 +010067 if (end >= TASK_SIZE_MAX)
68 end = TASK_SIZE_MAX;
Andi Kleen2aae9502007-07-21 17:10:01 +020069 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;
Borislav Petkovdfb09f92011-08-05 15:15:08 +020075
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);
Michel Lespinassef99024722012-12-11 16:01:52 -080082 addr = align_vdso_addr(addr);
Borislav Petkovdfb09f92011-08-05 15:15:08 +020083
Andi Kleen2aae9502007-07-21 17:10:01 +020084 return addr;
85}
86
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070087static int map_vdso(const struct vdso_image *image, bool calculate_addr)
Andi Kleen2aae9502007-07-21 17:10:01 +020088{
89 struct mm_struct *mm = current->mm;
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070090 struct vm_area_struct *vma;
Andi Kleen2aae9502007-07-21 17:10:01 +020091 unsigned long addr;
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070092 int ret = 0;
Andi Kleen2aae9502007-07-21 17:10:01 +020093
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -070094 if (calculate_addr) {
95 addr = vdso_addr(current->mm->start_stack,
96 image->sym_end_mapping);
97 } else {
98 addr = 0;
99 }
Andi Kleen2aae9502007-07-21 17:10:01 +0200100
101 down_write(&mm->mmap_sem);
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700102
103 addr = get_unmapped_area(NULL, addr, image->sym_end_mapping, 0, 0);
Andi Kleen2aae9502007-07-21 17:10:01 +0200104 if (IS_ERR_VALUE(addr)) {
105 ret = addr;
106 goto up_fail;
107 }
108
Andy Lutomirski6f121e52014-05-05 12:19:34 -0700109 current->mm->context.vdso = (void __user *)addr;
Peter Zijlstraf7b6eb32009-06-05 14:04:51 +0200110
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700111 /*
112 * MAYWRITE to allow gdb to COW and set breakpoints
113 */
114 ret = install_special_mapping(mm,
115 addr,
116 image->size,
Andi Kleen2aae9502007-07-21 17:10:01 +0200117 VM_READ|VM_EXEC|
Jason Baron909af762012-03-23 15:02:51 -0700118 VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700119 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);
Andi Kleen2aae9502007-07-21 17:10:01 +0200132 goto up_fail;
Peter Zijlstraf7b6eb32009-06-05 14:04:51 +0200133 }
Andi Kleen2aae9502007-07-21 17:10:01 +0200134
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700135 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
Andi Kleen2aae9502007-07-21 17:10:01 +0200158up_fail:
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700159 if (ret)
160 current->mm->context.vdso = NULL;
161
Andi Kleen2aae9502007-07-21 17:10:01 +0200162 up_write(&mm->mmap_sem);
163 return ret;
164}
165
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700166#if defined(CONFIG_X86_32) || defined(CONFIG_COMPAT)
167static int load_vdso32(void)
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800168{
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700169 int ret;
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800170
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700171 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;
H. J. Lu1a21d4e2012-02-19 11:38:06 -0800184}
185#endif
186
Andy Lutomirski18d0a6f2014-05-05 12:19:35 -0700187#ifdef CONFIG_X86_64
188int 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
197int 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
213int 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
Andi Kleen2aae9502007-07-21 17:10:01 +0200220static __init int vdso_setup(char *s)
221{
Andy Lutomirski3d7ee962014-05-05 12:19:32 -0700222 vdso64_enabled = simple_strtoul(s, NULL, 0);
Andi Kleen2aae9502007-07-21 17:10:01 +0200223 return 0;
224}
225__setup("vdso=", vdso_setup);
Andy Lutomirskib4b541a2014-03-17 23:22:08 +0100226#endif