| /* | |
| * Copyright (C) 2011 Richard Weinberger <richrd@nod.at> | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License version 2 as | |
| * published by the Free Software Foundation. | |
| */ | |
| #include <linux/slab.h> | |
| #include <linux/sched.h> | |
| #include <linux/mm.h> | |
| #include <asm/page.h> | |
| #include <asm/elf.h> | |
| #include <linux/init.h> | |
| static unsigned int __read_mostly vdso_enabled = 1; | |
| unsigned long um_vdso_addr; | |
| extern unsigned long task_size; | |
| extern char vdso_start[], vdso_end[]; | |
| static struct page **vdsop; | |
| static int __init init_vdso(void) | |
| { | |
| struct page *um_vdso; | |
| BUG_ON(vdso_end - vdso_start > PAGE_SIZE); | |
| um_vdso_addr = task_size - PAGE_SIZE; | |
| vdsop = kmalloc(sizeof(struct page *), GFP_KERNEL); | |
| if (!vdsop) | |
| goto oom; | |
| um_vdso = alloc_page(GFP_KERNEL); | |
| if (!um_vdso) { | |
| kfree(vdsop); | |
| goto oom; | |
| } | |
| copy_page(page_address(um_vdso), vdso_start); | |
| *vdsop = um_vdso; | |
| return 0; | |
| oom: | |
| printk(KERN_ERR "Cannot allocate vdso\n"); | |
| vdso_enabled = 0; | |
| return -ENOMEM; | |
| } | |
| subsys_initcall(init_vdso); | |
| int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | |
| { | |
| int err; | |
| struct mm_struct *mm = current->mm; | |
| if (!vdso_enabled) | |
| return 0; | |
| if (down_write_killable(&mm->mmap_sem)) | |
| return -EINTR; | |
| err = install_special_mapping(mm, um_vdso_addr, PAGE_SIZE, | |
| VM_READ|VM_EXEC| | |
| VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, | |
| vdsop); | |
| up_write(&mm->mmap_sem); | |
| return err; | |
| } |