]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 | 2 | * (C) Copyright 2002 Linus Torvalds |
e6e5494c IM |
3 | * Portions based on the vdso-randomization code from exec-shield: |
4 | * Copyright(C) 2005-2006, Red Hat, Inc., Ingo Molnar | |
1da177e4 LT |
5 | * |
6 | * This file contains the needed initializations to support sysenter. | |
7 | */ | |
8 | ||
9 | #include <linux/init.h> | |
10 | #include <linux/smp.h> | |
11 | #include <linux/thread_info.h> | |
12 | #include <linux/sched.h> | |
13 | #include <linux/gfp.h> | |
14 | #include <linux/string.h> | |
15 | #include <linux/elf.h> | |
e6e5494c | 16 | #include <linux/mm.h> |
4e950f6f | 17 | #include <linux/err.h> |
e6e5494c | 18 | #include <linux/module.h> |
4e40112c | 19 | #include <linux/slab.h> |
1da177e4 LT |
20 | |
21 | #include <asm/cpufeature.h> | |
22 | #include <asm/msr.h> | |
23 | #include <asm/pgtable.h> | |
24 | #include <asm/unistd.h> | |
d4f7a2c1 | 25 | #include <asm/elf.h> |
1dbf527c | 26 | #include <asm/tlbflush.h> |
6c3652ef | 27 | #include <asm/vdso.h> |
af65d648 | 28 | #include <asm/proto.h> |
7a59ed41 SS |
29 | #include <asm/fixmap.h> |
30 | #include <asm/hpet.h> | |
31 | #include <asm/vvar.h> | |
1dbf527c | 32 | |
1dbf527c | 33 | #ifdef CONFIG_COMPAT_VDSO |
b0b49f26 | 34 | #define VDSO_DEFAULT 0 |
1dbf527c | 35 | #else |
b0b49f26 | 36 | #define VDSO_DEFAULT 1 |
1dbf527c | 37 | #endif |
1da177e4 | 38 | |
af65d648 | 39 | #ifdef CONFIG_X86_64 |
af65d648 RM |
40 | #define arch_setup_additional_pages syscall32_setup_pages |
41 | #endif | |
42 | ||
e6e5494c IM |
43 | /* |
44 | * Should the kernel map a VDSO page into processes and pass its | |
45 | * address down to glibc upon exec()? | |
46 | */ | |
3d7ee969 | 47 | unsigned int __read_mostly vdso32_enabled = VDSO_DEFAULT; |
e6e5494c | 48 | |
3d7ee969 | 49 | static int __init vdso32_setup(char *s) |
e6e5494c | 50 | { |
3d7ee969 | 51 | vdso32_enabled = simple_strtoul(s, NULL, 0); |
e6e5494c | 52 | |
3d7ee969 | 53 | if (vdso32_enabled > 1) |
b0b49f26 AL |
54 | pr_warn("vdso32 values other than 0 and 1 are no longer allowed; vdso disabled\n"); |
55 | ||
e6e5494c IM |
56 | return 1; |
57 | } | |
58 | ||
af65d648 RM |
59 | /* |
60 | * For consistency, the argument vdso32=[012] affects the 32-bit vDSO | |
61 | * behavior on both 64-bit and 32-bit kernels. | |
62 | * On 32-bit kernels, vdso=[012] means the same thing. | |
63 | */ | |
3d7ee969 | 64 | __setup("vdso32=", vdso32_setup); |
e6e5494c | 65 | |
af65d648 | 66 | #ifdef CONFIG_X86_32 |
3d7ee969 | 67 | __setup_param("vdso=", vdso_setup, vdso32_setup, 0); |
af65d648 | 68 | #endif |
1da177e4 | 69 | |
4e40112c | 70 | static struct page **vdso32_pages; |
b67e612c | 71 | static unsigned vdso32_size; |
af65d648 RM |
72 | |
73 | #ifdef CONFIG_X86_64 | |
74 | ||
b6ad92d4 | 75 | #define vdso32_sysenter() (boot_cpu_has(X86_FEATURE_SYSENTER32)) |
6a52e4b1 | 76 | #define vdso32_syscall() (boot_cpu_has(X86_FEATURE_SYSCALL32)) |
af65d648 RM |
77 | |
78 | /* May not be __init: called during resume */ | |
79 | void syscall32_cpu_init(void) | |
80 | { | |
af65d648 RM |
81 | /* Load these always in case some future AMD CPU supports |
82 | SYSENTER from compat mode too. */ | |
715c85b1 PA |
83 | wrmsrl_safe(MSR_IA32_SYSENTER_CS, (u64)__KERNEL_CS); |
84 | wrmsrl_safe(MSR_IA32_SYSENTER_ESP, 0ULL); | |
85 | wrmsrl_safe(MSR_IA32_SYSENTER_EIP, (u64)ia32_sysenter_target); | |
af65d648 RM |
86 | |
87 | wrmsrl(MSR_CSTAR, ia32_cstar_target); | |
88 | } | |
89 | ||
af65d648 RM |
90 | #else /* CONFIG_X86_32 */ |
91 | ||
92 | #define vdso32_sysenter() (boot_cpu_has(X86_FEATURE_SEP)) | |
6a52e4b1 | 93 | #define vdso32_syscall() (0) |
af65d648 | 94 | |
6fe940d6 | 95 | void enable_sep_cpu(void) |
1da177e4 LT |
96 | { |
97 | int cpu = get_cpu(); | |
98 | struct tss_struct *tss = &per_cpu(init_tss, cpu); | |
99 | ||
6fe940d6 LS |
100 | if (!boot_cpu_has(X86_FEATURE_SEP)) { |
101 | put_cpu(); | |
102 | return; | |
103 | } | |
104 | ||
a75c54f9 | 105 | tss->x86_tss.ss1 = __KERNEL_CS; |
faca6227 | 106 | tss->x86_tss.sp1 = sizeof(struct tss_struct) + (unsigned long) tss; |
1da177e4 | 107 | wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0); |
faca6227 | 108 | wrmsr(MSR_IA32_SYSENTER_ESP, tss->x86_tss.sp1, 0); |
0aa97fb2 | 109 | wrmsr(MSR_IA32_SYSENTER_EIP, (unsigned long) ia32_sysenter_target, 0); |
1da177e4 LT |
110 | put_cpu(); |
111 | } | |
112 | ||
af65d648 RM |
113 | #endif /* CONFIG_X86_64 */ |
114 | ||
a6c4e076 | 115 | int __init sysenter_setup(void) |
1da177e4 | 116 | { |
b67e612c AL |
117 | char *vdso32_start, *vdso32_end; |
118 | int npages, i; | |
1da177e4 | 119 | |
b67e612c | 120 | #ifdef CONFIG_COMPAT |
6a52e4b1 | 121 | if (vdso32_syscall()) { |
b67e612c AL |
122 | vdso32_start = vdso32_syscall_start; |
123 | vdso32_end = vdso32_syscall_end; | |
124 | vdso32_pages = vdso32_syscall_pages; | |
125 | } else | |
126 | #endif | |
127 | if (vdso32_sysenter()) { | |
128 | vdso32_start = vdso32_sysenter_start; | |
129 | vdso32_end = vdso32_sysenter_end; | |
130 | vdso32_pages = vdso32_sysenter_pages; | |
6a52e4b1 | 131 | } else { |
b67e612c AL |
132 | vdso32_start = vdso32_int80_start; |
133 | vdso32_end = vdso32_int80_end; | |
134 | vdso32_pages = vdso32_int80_pages; | |
1da177e4 LT |
135 | } |
136 | ||
b67e612c AL |
137 | npages = ((vdso32_end - vdso32_start) + PAGE_SIZE - 1) / PAGE_SIZE; |
138 | vdso32_size = npages << PAGE_SHIFT; | |
139 | for (i = 0; i < npages; i++) | |
140 | vdso32_pages[i] = virt_to_page(vdso32_start + i*PAGE_SIZE); | |
4e40112c | 141 | |
b67e612c | 142 | patch_vdso32(vdso32_start, vdso32_size); |
1da177e4 | 143 | |
1da177e4 LT |
144 | return 0; |
145 | } | |
e6e5494c | 146 | |
e6e5494c | 147 | /* Setup a VMA at program startup for the vsyscall page */ |
fc5243d9 | 148 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) |
e6e5494c | 149 | { |
e6e5494c IM |
150 | struct mm_struct *mm = current->mm; |
151 | unsigned long addr; | |
752783c0 | 152 | int ret = 0; |
7a59ed41 | 153 | struct vm_area_struct *vma; |
e6e5494c | 154 | |
1a21d4e0 | 155 | #ifdef CONFIG_X86_X32_ABI |
1a21d4e0 | 156 | if (test_thread_flag(TIF_X32)) |
22e842d4 | 157 | return x32_setup_additional_pages(bprm, uses_interp); |
1a21d4e0 L |
158 | #endif |
159 | ||
3d7ee969 | 160 | if (vdso32_enabled != 1) /* Other values all mean "disabled" */ |
5de253cc RM |
161 | return 0; |
162 | ||
e6e5494c | 163 | down_write(&mm->mmap_sem); |
e6e5494c | 164 | |
645a387e | 165 | addr = get_unmapped_area(NULL, 0, vdso32_size + VDSO_OFFSET(VDSO_PREV_PAGES), 0, 0); |
b0b49f26 AL |
166 | if (IS_ERR_VALUE(addr)) { |
167 | ret = addr; | |
168 | goto up_fail; | |
af65d648 | 169 | } |
1dbf527c | 170 | |
645a387e SS |
171 | addr += VDSO_OFFSET(VDSO_PREV_PAGES); |
172 | ||
f7b6eb3f PZ |
173 | current->mm->context.vdso = (void *)addr; |
174 | ||
b0b49f26 AL |
175 | /* |
176 | * MAYWRITE to allow gdb to COW and set breakpoints | |
177 | */ | |
7a59ed41 SS |
178 | ret = install_special_mapping(mm, |
179 | addr, | |
b67e612c | 180 | vdso32_size, |
7a59ed41 SS |
181 | VM_READ|VM_EXEC| |
182 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | |
183 | vdso32_pages); | |
b0b49f26 AL |
184 | |
185 | if (ret) | |
186 | goto up_fail; | |
e6e5494c | 187 | |
7a59ed41 SS |
188 | vma = _install_special_mapping(mm, |
189 | addr - VDSO_OFFSET(VDSO_PREV_PAGES), | |
190 | VDSO_OFFSET(VDSO_PREV_PAGES), | |
191 | VM_READ, | |
192 | NULL); | |
193 | ||
194 | if (IS_ERR(vma)) { | |
195 | ret = PTR_ERR(vma); | |
196 | goto up_fail; | |
197 | } | |
198 | ||
199 | ret = remap_pfn_range(vma, | |
200 | addr - VDSO_OFFSET(VDSO_VVAR_PAGE), | |
201 | __pa_symbol(&__vvar_page) >> PAGE_SHIFT, | |
202 | PAGE_SIZE, | |
203 | PAGE_READONLY); | |
204 | ||
205 | if (ret) | |
206 | goto up_fail; | |
207 | ||
208 | #ifdef CONFIG_HPET_TIMER | |
209 | if (hpet_address) { | |
210 | ret = io_remap_pfn_range(vma, | |
211 | addr - VDSO_OFFSET(VDSO_HPET_PAGE), | |
212 | hpet_address >> PAGE_SHIFT, | |
213 | PAGE_SIZE, | |
214 | pgprot_noncached(PAGE_READONLY)); | |
215 | ||
216 | if (ret) | |
217 | goto up_fail; | |
218 | } | |
219 | #endif | |
220 | ||
e6e5494c | 221 | current_thread_info()->sysenter_return = |
6c3652ef | 222 | VDSO32_SYMBOL(addr, SYSENTER_RETURN); |
1dbf527c JF |
223 | |
224 | up_fail: | |
f7b6eb3f PZ |
225 | if (ret) |
226 | current->mm->context.vdso = NULL; | |
227 | ||
e6e5494c | 228 | up_write(&mm->mmap_sem); |
1dbf527c | 229 | |
e6e5494c | 230 | return ret; |
e6e5494c IM |
231 | } |
232 | ||
af65d648 RM |
233 | #ifdef CONFIG_X86_64 |
234 | ||
d7a0380d | 235 | subsys_initcall(sysenter_setup); |
af65d648 | 236 | |
a97f52e6 RM |
237 | #ifdef CONFIG_SYSCTL |
238 | /* Register vsyscall32 into the ABI table */ | |
239 | #include <linux/sysctl.h> | |
240 | ||
f07d91ed | 241 | static struct ctl_table abi_table2[] = { |
a97f52e6 RM |
242 | { |
243 | .procname = "vsyscall32", | |
3d7ee969 | 244 | .data = &vdso32_enabled, |
a97f52e6 RM |
245 | .maxlen = sizeof(int), |
246 | .mode = 0644, | |
247 | .proc_handler = proc_dointvec | |
248 | }, | |
249 | {} | |
250 | }; | |
251 | ||
f07d91ed | 252 | static struct ctl_table abi_root_table2[] = { |
a97f52e6 | 253 | { |
a97f52e6 RM |
254 | .procname = "abi", |
255 | .mode = 0555, | |
256 | .child = abi_table2 | |
257 | }, | |
258 | {} | |
259 | }; | |
260 | ||
261 | static __init int ia32_binfmt_init(void) | |
262 | { | |
263 | register_sysctl_table(abi_root_table2); | |
264 | return 0; | |
265 | } | |
266 | __initcall(ia32_binfmt_init); | |
267 | #endif | |
268 | ||
af65d648 RM |
269 | #else /* CONFIG_X86_32 */ |
270 | ||
e6e5494c IM |
271 | const char *arch_vma_name(struct vm_area_struct *vma) |
272 | { | |
273 | if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) | |
274 | return "[vdso]"; | |
275 | return NULL; | |
276 | } | |
277 | ||
31db58b3 | 278 | struct vm_area_struct *get_gate_vma(struct mm_struct *mm) |
e6e5494c IM |
279 | { |
280 | return NULL; | |
281 | } | |
282 | ||
83b964bb | 283 | int in_gate_area(struct mm_struct *mm, unsigned long addr) |
e6e5494c | 284 | { |
b0b49f26 | 285 | return 0; |
e6e5494c IM |
286 | } |
287 | ||
cae5d390 | 288 | int in_gate_area_no_mm(unsigned long addr) |
e6e5494c IM |
289 | { |
290 | return 0; | |
291 | } | |
af65d648 RM |
292 | |
293 | #endif /* CONFIG_X86_64 */ |