7345bc9a1af69a5e04b90ab8a971a9061e9940b4
[pandora-kernel.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
19 #if defined(CONFIG_X86_64)
20 unsigned int __read_mostly vdso_enabled = 1;
21
22 extern char vdso_start[], vdso_end[];
23 extern unsigned short vdso_sync_cpuid;
24
25 extern struct page *vdso_pages[];
26 static unsigned vdso_size;
27
28 #ifdef CONFIG_X86_X32_ABI
29 extern char vdsox32_start[], vdsox32_end[];
30 extern struct page *vdsox32_pages[];
31 static unsigned vdsox32_size;
32 #endif
33 #endif
34
35 #if defined(CONFIG_X86_32) || defined(CONFIG_X86_X32_ABI) || \
36         defined(CONFIG_COMPAT)
37 void __init patch_vdso32(void *vdso, size_t len)
38 {
39         Elf32_Ehdr *hdr = vdso;
40         Elf32_Shdr *sechdrs, *alt_sec = 0;
41         char *secstrings;
42         void *alt_data;
43         int i;
44
45         BUG_ON(len < sizeof(Elf32_Ehdr));
46         BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0);
47
48         sechdrs = (void *)hdr + hdr->e_shoff;
49         secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
50
51         for (i = 1; i < hdr->e_shnum; i++) {
52                 Elf32_Shdr *shdr = &sechdrs[i];
53                 if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) {
54                         alt_sec = shdr;
55                         goto found;
56                 }
57         }
58
59         /* If we get here, it's probably a bug. */
60         pr_warning("patch_vdso32: .altinstructions not found\n");
61         return;  /* nothing to patch */
62
63 found:
64         alt_data = (void *)hdr + alt_sec->sh_offset;
65         apply_alternatives(alt_data, alt_data + alt_sec->sh_size);
66 }
67 #endif
68
69 #if defined(CONFIG_X86_64)
70 static void __init patch_vdso64(void *vdso, size_t len)
71 {
72         Elf64_Ehdr *hdr = vdso;
73         Elf64_Shdr *sechdrs, *alt_sec = 0;
74         char *secstrings;
75         void *alt_data;
76         int i;
77
78         BUG_ON(len < sizeof(Elf64_Ehdr));
79         BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0);
80
81         sechdrs = (void *)hdr + hdr->e_shoff;
82         secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
83
84         for (i = 1; i < hdr->e_shnum; i++) {
85                 Elf64_Shdr *shdr = &sechdrs[i];
86                 if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) {
87                         alt_sec = shdr;
88                         goto found;
89                 }
90         }
91
92         /* If we get here, it's probably a bug. */
93         pr_warning("patch_vdso64: .altinstructions not found\n");
94         return;  /* nothing to patch */
95
96 found:
97         alt_data = (void *)hdr + alt_sec->sh_offset;
98         apply_alternatives(alt_data, alt_data + alt_sec->sh_size);
99 }
100
101 static int __init init_vdso(void)
102 {
103         int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE;
104         int i;
105
106         patch_vdso64(vdso_start, vdso_end - vdso_start);
107
108         vdso_size = npages << PAGE_SHIFT;
109         for (i = 0; i < npages; i++)
110                 vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE);
111
112 #ifdef CONFIG_X86_X32_ABI
113         patch_vdso32(vdsox32_start, vdsox32_end - vdsox32_start);
114         npages = (vdsox32_end - vdsox32_start + PAGE_SIZE - 1) / PAGE_SIZE;
115         vdsox32_size = npages << PAGE_SHIFT;
116         for (i = 0; i < npages; i++)
117                 vdsox32_pages[i] = virt_to_page(vdsox32_start + i*PAGE_SIZE);
118 #endif
119
120         return 0;
121 }
122 subsys_initcall(init_vdso);
123
124 struct linux_binprm;
125
126 /* Put the vdso above the (randomized) stack with another randomized offset.
127    This way there is no hole in the middle of address space.
128    To save memory make sure it is still in the same PTE as the stack top.
129    This doesn't give that many random bits */
130 static unsigned long vdso_addr(unsigned long start, unsigned len)
131 {
132         unsigned long addr, end;
133         unsigned offset;
134         end = (start + PMD_SIZE - 1) & PMD_MASK;
135         if (end >= TASK_SIZE_MAX)
136                 end = TASK_SIZE_MAX;
137         end -= len;
138         /* This loses some more bits than a modulo, but is cheaper */
139         offset = get_random_int() & (PTRS_PER_PTE - 1);
140         addr = start + (offset << PAGE_SHIFT);
141         if (addr >= end)
142                 addr = end;
143
144         /*
145          * page-align it here so that get_unmapped_area doesn't
146          * align it wrongfully again to the next page. addr can come in 4K
147          * unaligned here as a result of stack start randomization.
148          */
149         addr = PAGE_ALIGN(addr);
150         addr = align_vdso_addr(addr);
151
152         return addr;
153 }
154
155 /* Setup a VMA at program startup for the vsyscall page.
156    Not called for compat tasks */
157 static int setup_additional_pages(struct linux_binprm *bprm,
158                                   int uses_interp,
159                                   struct page **pages,
160                                   unsigned size)
161 {
162         struct mm_struct *mm = current->mm;
163         unsigned long addr;
164         int ret;
165
166         if (!vdso_enabled)
167                 return 0;
168
169         down_write(&mm->mmap_sem);
170         addr = vdso_addr(mm->start_stack, size);
171         addr = get_unmapped_area(NULL, addr, size, 0, 0);
172         if (IS_ERR_VALUE(addr)) {
173                 ret = addr;
174                 goto up_fail;
175         }
176
177         current->mm->context.vdso = (void *)addr;
178
179         ret = install_special_mapping(mm, addr, size,
180                                       VM_READ|VM_EXEC|
181                                       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
182                                       pages);
183         if (ret) {
184                 current->mm->context.vdso = NULL;
185                 goto up_fail;
186         }
187
188 up_fail:
189         up_write(&mm->mmap_sem);
190         return ret;
191 }
192
193 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
194 {
195         return setup_additional_pages(bprm, uses_interp, vdso_pages,
196                                       vdso_size);
197 }
198
199 #ifdef CONFIG_X86_X32_ABI
200 int x32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
201 {
202         return setup_additional_pages(bprm, uses_interp, vdsox32_pages,
203                                       vdsox32_size);
204 }
205 #endif
206
207 static __init int vdso_setup(char *s)
208 {
209         vdso_enabled = simple_strtoul(s, NULL, 0);
210         return 0;
211 }
212 __setup("vdso=", vdso_setup);
213 #endif