Merge branch 'x86-cpu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[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 unsigned int __read_mostly vdso_enabled = 1;
20
21 extern char vdso_start[], vdso_end[];
22 extern unsigned short vdso_sync_cpuid;
23
24 extern struct page *vdso_pages[];
25 static unsigned vdso_size;
26
27 static void __init patch_vdso(void *vdso, size_t len)
28 {
29         Elf64_Ehdr *hdr = vdso;
30         Elf64_Shdr *sechdrs, *alt_sec = 0;
31         char *secstrings;
32         void *alt_data;
33         int i;
34
35         BUG_ON(len < sizeof(Elf64_Ehdr));
36         BUG_ON(memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0);
37
38         sechdrs = (void *)hdr + hdr->e_shoff;
39         secstrings = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
40
41         for (i = 1; i < hdr->e_shnum; i++) {
42                 Elf64_Shdr *shdr = &sechdrs[i];
43                 if (!strcmp(secstrings + shdr->sh_name, ".altinstructions")) {
44                         alt_sec = shdr;
45                         goto found;
46                 }
47         }
48
49         /* If we get here, it's probably a bug. */
50         pr_warning("patch_vdso: .altinstructions not found\n");
51         return;  /* nothing to patch */
52
53 found:
54         alt_data = (void *)hdr + alt_sec->sh_offset;
55         apply_alternatives(alt_data, alt_data + alt_sec->sh_size);
56 }
57
58 static int __init init_vdso(void)
59 {
60         int npages = (vdso_end - vdso_start + PAGE_SIZE - 1) / PAGE_SIZE;
61         int i;
62
63         patch_vdso(vdso_start, vdso_end - vdso_start);
64
65         vdso_size = npages << PAGE_SHIFT;
66         for (i = 0; i < npages; i++)
67                 vdso_pages[i] = virt_to_page(vdso_start + i*PAGE_SIZE);
68
69         return 0;
70 }
71 subsys_initcall(init_vdso);
72
73 struct linux_binprm;
74
75 /* Put the vdso above the (randomized) stack with another randomized offset.
76    This way there is no hole in the middle of address space.
77    To save memory make sure it is still in the same PTE as the stack top.
78    This doesn't give that many random bits */
79 static unsigned long vdso_addr(unsigned long start, unsigned len)
80 {
81         unsigned long addr, end;
82         unsigned offset;
83         end = (start + PMD_SIZE - 1) & PMD_MASK;
84         if (end >= TASK_SIZE_MAX)
85                 end = TASK_SIZE_MAX;
86         end -= len;
87         /* This loses some more bits than a modulo, but is cheaper */
88         offset = get_random_int() & (PTRS_PER_PTE - 1);
89         addr = start + (offset << PAGE_SHIFT);
90         if (addr >= end)
91                 addr = end;
92
93         /*
94          * page-align it here so that get_unmapped_area doesn't
95          * align it wrongfully again to the next page. addr can come in 4K
96          * unaligned here as a result of stack start randomization.
97          */
98         addr = PAGE_ALIGN(addr);
99         addr = align_addr(addr, NULL, ALIGN_VDSO);
100
101         return addr;
102 }
103
104 /* Setup a VMA at program startup for the vsyscall page.
105    Not called for compat tasks */
106 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
107 {
108         struct mm_struct *mm = current->mm;
109         unsigned long addr;
110         int ret;
111
112         if (!vdso_enabled)
113                 return 0;
114
115         down_write(&mm->mmap_sem);
116         addr = vdso_addr(mm->start_stack, vdso_size);
117         addr = get_unmapped_area(NULL, addr, vdso_size, 0, 0);
118         if (IS_ERR_VALUE(addr)) {
119                 ret = addr;
120                 goto up_fail;
121         }
122
123         current->mm->context.vdso = (void *)addr;
124
125         ret = install_special_mapping(mm, addr, vdso_size,
126                                       VM_READ|VM_EXEC|
127                                       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC|
128                                       VM_ALWAYSDUMP,
129                                       vdso_pages);
130         if (ret) {
131                 current->mm->context.vdso = NULL;
132                 goto up_fail;
133         }
134
135 up_fail:
136         up_write(&mm->mmap_sem);
137         return ret;
138 }
139
140 static __init int vdso_setup(char *s)
141 {
142         vdso_enabled = simple_strtoul(s, NULL, 0);
143         return 0;
144 }
145 __setup("vdso=", vdso_setup);