diff options
Diffstat (limited to 'arch/powerpc/kernel/vdso.c')
-rw-r--r-- | arch/powerpc/kernel/vdso.c | 124 |
1 files changed, 43 insertions, 81 deletions
diff --git a/arch/powerpc/kernel/vdso.c b/arch/powerpc/kernel/vdso.c index ae0ede19879d..e46c31b36641 100644 --- a/arch/powerpc/kernel/vdso.c +++ b/arch/powerpc/kernel/vdso.c | |||
@@ -51,17 +51,21 @@ | |||
51 | 51 | ||
52 | extern char vdso32_start, vdso32_end; | 52 | extern char vdso32_start, vdso32_end; |
53 | static void *vdso32_kbase = &vdso32_start; | 53 | static void *vdso32_kbase = &vdso32_start; |
54 | unsigned int vdso32_pages; | 54 | static unsigned int vdso32_pages; |
55 | static struct page **vdso32_pagelist; | ||
55 | unsigned long vdso32_sigtramp; | 56 | unsigned long vdso32_sigtramp; |
56 | unsigned long vdso32_rt_sigtramp; | 57 | unsigned long vdso32_rt_sigtramp; |
57 | 58 | ||
58 | #ifdef CONFIG_PPC64 | 59 | #ifdef CONFIG_PPC64 |
59 | extern char vdso64_start, vdso64_end; | 60 | extern char vdso64_start, vdso64_end; |
60 | static void *vdso64_kbase = &vdso64_start; | 61 | static void *vdso64_kbase = &vdso64_start; |
61 | unsigned int vdso64_pages; | 62 | static unsigned int vdso64_pages; |
63 | static struct page **vdso64_pagelist; | ||
62 | unsigned long vdso64_rt_sigtramp; | 64 | unsigned long vdso64_rt_sigtramp; |
63 | #endif /* CONFIG_PPC64 */ | 65 | #endif /* CONFIG_PPC64 */ |
64 | 66 | ||
67 | static int vdso_ready; | ||
68 | |||
65 | /* | 69 | /* |
66 | * The vdso data page (aka. systemcfg for old ppc64 fans) is here. | 70 | * The vdso data page (aka. systemcfg for old ppc64 fans) is here. |
67 | * Once the early boot kernel code no longer needs to muck around | 71 | * Once the early boot kernel code no longer needs to muck around |
@@ -165,55 +169,6 @@ static void dump_vdso_pages(struct vm_area_struct * vma) | |||
165 | #endif /* DEBUG */ | 169 | #endif /* DEBUG */ |
166 | 170 | ||
167 | /* | 171 | /* |
168 | * Keep a dummy vma_close for now, it will prevent VMA merging. | ||
169 | */ | ||
170 | static void vdso_vma_close(struct vm_area_struct * vma) | ||
171 | { | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Our nopage() function, maps in the actual vDSO kernel pages, they will | ||
176 | * be mapped read-only by do_no_page(), and eventually COW'ed, either | ||
177 | * right away for an initial write access, or by do_wp_page(). | ||
178 | */ | ||
179 | static struct page * vdso_vma_nopage(struct vm_area_struct * vma, | ||
180 | unsigned long address, int *type) | ||
181 | { | ||
182 | unsigned long offset = address - vma->vm_start; | ||
183 | struct page *pg; | ||
184 | #ifdef CONFIG_PPC64 | ||
185 | void *vbase = (vma->vm_mm->task_size > TASK_SIZE_USER32) ? | ||
186 | vdso64_kbase : vdso32_kbase; | ||
187 | #else | ||
188 | void *vbase = vdso32_kbase; | ||
189 | #endif | ||
190 | |||
191 | DBG("vdso_vma_nopage(current: %s, address: %016lx, off: %lx)\n", | ||
192 | current->comm, address, offset); | ||
193 | |||
194 | if (address < vma->vm_start || address > vma->vm_end) | ||
195 | return NOPAGE_SIGBUS; | ||
196 | |||
197 | /* | ||
198 | * Last page is systemcfg. | ||
199 | */ | ||
200 | if ((vma->vm_end - address) <= PAGE_SIZE) | ||
201 | pg = virt_to_page(vdso_data); | ||
202 | else | ||
203 | pg = virt_to_page(vbase + offset); | ||
204 | |||
205 | get_page(pg); | ||
206 | DBG(" ->page count: %d\n", page_count(pg)); | ||
207 | |||
208 | return pg; | ||
209 | } | ||
210 | |||
211 | static struct vm_operations_struct vdso_vmops = { | ||
212 | .close = vdso_vma_close, | ||
213 | .nopage = vdso_vma_nopage, | ||
214 | }; | ||
215 | |||
216 | /* | ||
217 | * This is called from binfmt_elf, we create the special vma for the | 172 | * This is called from binfmt_elf, we create the special vma for the |
218 | * vDSO and insert it into the mm struct tree | 173 | * vDSO and insert it into the mm struct tree |
219 | */ | 174 | */ |
@@ -221,20 +176,26 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, | |||
221 | int executable_stack) | 176 | int executable_stack) |
222 | { | 177 | { |
223 | struct mm_struct *mm = current->mm; | 178 | struct mm_struct *mm = current->mm; |
224 | struct vm_area_struct *vma; | 179 | struct page **vdso_pagelist; |
225 | unsigned long vdso_pages; | 180 | unsigned long vdso_pages; |
226 | unsigned long vdso_base; | 181 | unsigned long vdso_base; |
227 | int rc; | 182 | int rc; |
228 | 183 | ||
184 | if (!vdso_ready) | ||
185 | return 0; | ||
186 | |||
229 | #ifdef CONFIG_PPC64 | 187 | #ifdef CONFIG_PPC64 |
230 | if (test_thread_flag(TIF_32BIT)) { | 188 | if (test_thread_flag(TIF_32BIT)) { |
189 | vdso_pagelist = vdso32_pagelist; | ||
231 | vdso_pages = vdso32_pages; | 190 | vdso_pages = vdso32_pages; |
232 | vdso_base = VDSO32_MBASE; | 191 | vdso_base = VDSO32_MBASE; |
233 | } else { | 192 | } else { |
193 | vdso_pagelist = vdso64_pagelist; | ||
234 | vdso_pages = vdso64_pages; | 194 | vdso_pages = vdso64_pages; |
235 | vdso_base = VDSO64_MBASE; | 195 | vdso_base = VDSO64_MBASE; |
236 | } | 196 | } |
237 | #else | 197 | #else |
198 | vdso_pagelist = vdso32_pagelist; | ||
238 | vdso_pages = vdso32_pages; | 199 | vdso_pages = vdso32_pages; |
239 | vdso_base = VDSO32_MBASE; | 200 | vdso_base = VDSO32_MBASE; |
240 | #endif | 201 | #endif |
@@ -262,17 +223,6 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, | |||
262 | goto fail_mmapsem; | 223 | goto fail_mmapsem; |
263 | } | 224 | } |
264 | 225 | ||
265 | |||
266 | /* Allocate a VMA structure and fill it up */ | ||
267 | vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL); | ||
268 | if (vma == NULL) { | ||
269 | rc = -ENOMEM; | ||
270 | goto fail_mmapsem; | ||
271 | } | ||
272 | vma->vm_mm = mm; | ||
273 | vma->vm_start = vdso_base; | ||
274 | vma->vm_end = vma->vm_start + (vdso_pages << PAGE_SHIFT); | ||
275 | |||
276 | /* | 226 | /* |
277 | * our vma flags don't have VM_WRITE so by default, the process isn't | 227 | * our vma flags don't have VM_WRITE so by default, the process isn't |
278 | * allowed to write those pages. | 228 | * allowed to write those pages. |
@@ -282,32 +232,26 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, | |||
282 | * and your nice userland gettimeofday will be totally dead. | 232 | * and your nice userland gettimeofday will be totally dead. |
283 | * It's fine to use that for setting breakpoints in the vDSO code | 233 | * It's fine to use that for setting breakpoints in the vDSO code |
284 | * pages though | 234 | * pages though |
285 | */ | 235 | * |
286 | vma->vm_flags = VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC; | ||
287 | /* | ||
288 | * Make sure the vDSO gets into every core dump. | 236 | * Make sure the vDSO gets into every core dump. |
289 | * Dumping its contents makes post-mortem fully interpretable later | 237 | * Dumping its contents makes post-mortem fully interpretable later |
290 | * without matching up the same kernel and hardware config to see | 238 | * without matching up the same kernel and hardware config to see |
291 | * what PC values meant. | 239 | * what PC values meant. |
292 | */ | 240 | */ |
293 | vma->vm_flags |= VM_ALWAYSDUMP; | 241 | rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT, |
294 | vma->vm_flags |= mm->def_flags; | 242 | VM_READ|VM_EXEC| |
295 | vma->vm_page_prot = protection_map[vma->vm_flags & 0x7]; | 243 | VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC| |
296 | vma->vm_ops = &vdso_vmops; | 244 | VM_ALWAYSDUMP, |
297 | 245 | vdso_pagelist); | |
298 | /* Insert new VMA */ | ||
299 | rc = insert_vm_struct(mm, vma); | ||
300 | if (rc) | 246 | if (rc) |
301 | goto fail_vma; | 247 | goto fail_mmapsem; |
302 | 248 | ||
303 | /* Put vDSO base into mm struct and account for memory usage */ | 249 | /* Put vDSO base into mm struct */ |
304 | current->mm->context.vdso_base = vdso_base; | 250 | current->mm->context.vdso_base = vdso_base; |
305 | mm->total_vm += (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | 251 | |
306 | up_write(&mm->mmap_sem); | 252 | up_write(&mm->mmap_sem); |
307 | return 0; | 253 | return 0; |
308 | 254 | ||
309 | fail_vma: | ||
310 | kmem_cache_free(vm_area_cachep, vma); | ||
311 | fail_mmapsem: | 255 | fail_mmapsem: |
312 | up_write(&mm->mmap_sem); | 256 | up_write(&mm->mmap_sem); |
313 | return rc; | 257 | return rc; |
@@ -719,7 +663,7 @@ static void __init vdso_setup_syscall_map(void) | |||
719 | } | 663 | } |
720 | 664 | ||
721 | 665 | ||
722 | void __init vdso_init(void) | 666 | static int __init vdso_init(void) |
723 | { | 667 | { |
724 | int i; | 668 | int i; |
725 | 669 | ||
@@ -774,26 +718,44 @@ void __init vdso_init(void) | |||
774 | #ifdef CONFIG_PPC64 | 718 | #ifdef CONFIG_PPC64 |
775 | vdso64_pages = 0; | 719 | vdso64_pages = 0; |
776 | #endif | 720 | #endif |
777 | return; | 721 | return 0; |
778 | } | 722 | } |
779 | 723 | ||
780 | /* Make sure pages are in the correct state */ | 724 | /* Make sure pages are in the correct state */ |
725 | vdso32_pagelist = kzalloc(sizeof(struct page *) * (vdso32_pages + 2), | ||
726 | GFP_KERNEL); | ||
727 | BUG_ON(vdso32_pagelist == NULL); | ||
781 | for (i = 0; i < vdso32_pages; i++) { | 728 | for (i = 0; i < vdso32_pages; i++) { |
782 | struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE); | 729 | struct page *pg = virt_to_page(vdso32_kbase + i*PAGE_SIZE); |
783 | ClearPageReserved(pg); | 730 | ClearPageReserved(pg); |
784 | get_page(pg); | 731 | get_page(pg); |
785 | 732 | vdso32_pagelist[i] = pg; | |
786 | } | 733 | } |
734 | vdso32_pagelist[i++] = virt_to_page(vdso_data); | ||
735 | vdso32_pagelist[i] = NULL; | ||
736 | |||
787 | #ifdef CONFIG_PPC64 | 737 | #ifdef CONFIG_PPC64 |
738 | vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 2), | ||
739 | GFP_KERNEL); | ||
740 | BUG_ON(vdso64_pagelist == NULL); | ||
788 | for (i = 0; i < vdso64_pages; i++) { | 741 | for (i = 0; i < vdso64_pages; i++) { |
789 | struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE); | 742 | struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE); |
790 | ClearPageReserved(pg); | 743 | ClearPageReserved(pg); |
791 | get_page(pg); | 744 | get_page(pg); |
745 | vdso64_pagelist[i] = pg; | ||
792 | } | 746 | } |
747 | vdso64_pagelist[i++] = virt_to_page(vdso_data); | ||
748 | vdso64_pagelist[i] = NULL; | ||
793 | #endif /* CONFIG_PPC64 */ | 749 | #endif /* CONFIG_PPC64 */ |
794 | 750 | ||
795 | get_page(virt_to_page(vdso_data)); | 751 | get_page(virt_to_page(vdso_data)); |
752 | |||
753 | smp_wmb(); | ||
754 | vdso_ready = 1; | ||
755 | |||
756 | return 0; | ||
796 | } | 757 | } |
758 | arch_initcall(vdso_init); | ||
797 | 759 | ||
798 | int in_gate_area_no_task(unsigned long addr) | 760 | int in_gate_area_no_task(unsigned long addr) |
799 | { | 761 | { |