18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2011 Richard Weinberger <richrd@nod.at>
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/sched.h>
88c2ecf20Sopenharmony_ci#include <linux/mm.h>
98c2ecf20Sopenharmony_ci#include <asm/page.h>
108c2ecf20Sopenharmony_ci#include <asm/elf.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic unsigned int __read_mostly vdso_enabled = 1;
148c2ecf20Sopenharmony_ciunsigned long um_vdso_addr;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciextern unsigned long task_size;
178c2ecf20Sopenharmony_ciextern char vdso_start[], vdso_end[];
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic struct page **vdsop;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int __init init_vdso(void)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	struct page *um_vdso;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	BUG_ON(vdso_end - vdso_start > PAGE_SIZE);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	um_vdso_addr = task_size - PAGE_SIZE;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	vdsop = kmalloc(sizeof(struct page *), GFP_KERNEL);
308c2ecf20Sopenharmony_ci	if (!vdsop)
318c2ecf20Sopenharmony_ci		goto oom;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	um_vdso = alloc_page(GFP_KERNEL);
348c2ecf20Sopenharmony_ci	if (!um_vdso) {
358c2ecf20Sopenharmony_ci		kfree(vdsop);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci		goto oom;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	copy_page(page_address(um_vdso), vdso_start);
418c2ecf20Sopenharmony_ci	*vdsop = um_vdso;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	return 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cioom:
468c2ecf20Sopenharmony_ci	printk(KERN_ERR "Cannot allocate vdso\n");
478c2ecf20Sopenharmony_ci	vdso_enabled = 0;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return -ENOMEM;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_cisubsys_initcall(init_vdso);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ciint arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	int err;
568c2ecf20Sopenharmony_ci	struct mm_struct *mm = current->mm;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (!vdso_enabled)
598c2ecf20Sopenharmony_ci		return 0;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	if (mmap_write_lock_killable(mm))
628c2ecf20Sopenharmony_ci		return -EINTR;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	err = install_special_mapping(mm, um_vdso_addr, PAGE_SIZE,
658c2ecf20Sopenharmony_ci		VM_READ|VM_EXEC|
668c2ecf20Sopenharmony_ci		VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
678c2ecf20Sopenharmony_ci		vdsop);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	mmap_write_unlock(mm);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return err;
728c2ecf20Sopenharmony_ci}
73