Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  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
  7/*
  8 * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
  9 */
 10
 11#include <linux/mm.h>
 12#include <linux/err.h>
 13#include <linux/sched.h>
 14#include <linux/slab.h>
 15#include <linux/init.h>
 16#include <linux/linkage.h>
 17#include <linux/random.h>
 18#include <linux/elf.h>
 19#include <asm/vdso.h>
 20#include <asm/vvar.h>
 21#include <asm/page.h>
 22
 23unsigned int __read_mostly vdso_enabled = 1;
 24
 25static struct vm_special_mapping vvar_mapping = {
 26	.name = "[vvar]"
 27};
 28
 29#ifdef	CONFIG_SPARC64
 30static struct vm_special_mapping vdso_mapping64 = {
 31	.name = "[vdso]"
 32};
 33#endif
 34
 35#ifdef CONFIG_COMPAT
 36static struct vm_special_mapping vdso_mapping32 = {
 37	.name = "[vdso]"
 38};
 39#endif
 40
 41struct vvar_data *vvar_data;
 42
 43#define	SAVE_INSTR_SIZE	4
 44
 45/*
 46 * Allocate pages for the vdso and vvar, and copy in the vdso text from the
 47 * kernel image.
 48 */
 49int __init init_vdso_image(const struct vdso_image *image,
 50		struct vm_special_mapping *vdso_mapping)
 51{
 52	int i;
 53	struct page *dp, **dpp = NULL;
 54	int dnpages = 0;
 55	struct page *cp, **cpp = NULL;
 56	int cnpages = (image->size) / PAGE_SIZE;
 57
 58	/*
 59	 * First, the vdso text.  This is initialied data, an integral number of
 60	 * pages long.
 61	 */
 62	if (WARN_ON(image->size % PAGE_SIZE != 0))
 63		goto oom;
 64
 65	cpp = kcalloc(cnpages, sizeof(struct page *), GFP_KERNEL);
 66	vdso_mapping->pages = cpp;
 67
 68	if (!cpp)
 69		goto oom;
 70
 71	if (vdso_fix_stick) {
 72		/*
 73		 * If the system uses %tick instead of %stick, patch the VDSO
 74		 * with instruction reading %tick instead of %stick.
 75		 */
 76		unsigned int j, k = SAVE_INSTR_SIZE;
 77		unsigned char *data = image->data;
 78
 79		for (j = image->sym_vread_tick_patch_start;
 80		     j < image->sym_vread_tick_patch_end; j++) {
 81
 82			data[image->sym_vread_tick + k] = data[j];
 83			k++;
 84		}
 85	}
 86
 87	for (i = 0; i < cnpages; i++) {
 88		cp = alloc_page(GFP_KERNEL);
 89		if (!cp)
 90			goto oom;
 91		cpp[i] = cp;
 92		copy_page(page_address(cp), image->data + i * PAGE_SIZE);
 93	}
 94
 95	/*
 96	 * Now the vvar page.  This is uninitialized data.
 97	 */
 98
 99	if (vvar_data == NULL) {
100		dnpages = (sizeof(struct vvar_data) / PAGE_SIZE) + 1;
101		if (WARN_ON(dnpages != 1))
102			goto oom;
103		dpp = kcalloc(dnpages, sizeof(struct page *), GFP_KERNEL);
104		vvar_mapping.pages = dpp;
105
106		if (!dpp)
107			goto oom;
108
109		dp = alloc_page(GFP_KERNEL);
110		if (!dp)
111			goto oom;
112
113		dpp[0] = dp;
114		vvar_data = page_address(dp);
115		memset(vvar_data, 0, PAGE_SIZE);
116
117		vvar_data->seq = 0;
118	}
119
120	return 0;
121 oom:
122	if (cpp != NULL) {
123		for (i = 0; i < cnpages; i++) {
124			if (cpp[i] != NULL)
125				__free_page(cpp[i]);
126		}
127		kfree(cpp);
128		vdso_mapping->pages = NULL;
129	}
130
131	if (dpp != NULL) {
132		for (i = 0; i < dnpages; i++) {
133			if (dpp[i] != NULL)
134				__free_page(dpp[i]);
135		}
136		kfree(dpp);
137		vvar_mapping.pages = NULL;
138	}
139
140	pr_warn("Cannot allocate vdso\n");
141	vdso_enabled = 0;
142	return -ENOMEM;
143}
144
145static int __init init_vdso(void)
146{
147	int err = 0;
148#ifdef CONFIG_SPARC64
149	err = init_vdso_image(&vdso_image_64_builtin, &vdso_mapping64);
150	if (err)
151		return err;
152#endif
153
154#ifdef CONFIG_COMPAT
155	err = init_vdso_image(&vdso_image_32_builtin, &vdso_mapping32);
156#endif
157	return err;
158
159}
160subsys_initcall(init_vdso);
161
162struct linux_binprm;
163
164/* Shuffle the vdso up a bit, randomly. */
165static unsigned long vdso_addr(unsigned long start, unsigned int len)
166{
167	unsigned int offset;
168
169	/* This loses some more bits than a modulo, but is cheaper */
170	offset = get_random_int() & (PTRS_PER_PTE - 1);
171	return start + (offset << PAGE_SHIFT);
172}
173
174static int map_vdso(const struct vdso_image *image,
175		struct vm_special_mapping *vdso_mapping)
176{
177	struct mm_struct *mm = current->mm;
178	struct vm_area_struct *vma;
179	unsigned long text_start, addr = 0;
180	int ret = 0;
181
182	down_write(&mm->mmap_sem);
183
184	/*
185	 * First, get an unmapped region: then randomize it, and make sure that
186	 * region is free.
187	 */
188	if (current->flags & PF_RANDOMIZE) {
189		addr = get_unmapped_area(NULL, 0,
190					 image->size - image->sym_vvar_start,
191					 0, 0);
192		if (IS_ERR_VALUE(addr)) {
193			ret = addr;
194			goto up_fail;
195		}
196		addr = vdso_addr(addr, image->size - image->sym_vvar_start);
197	}
198	addr = get_unmapped_area(NULL, addr,
199				 image->size - image->sym_vvar_start, 0, 0);
200	if (IS_ERR_VALUE(addr)) {
201		ret = addr;
202		goto up_fail;
203	}
204
205	text_start = addr - image->sym_vvar_start;
206	current->mm->context.vdso = (void __user *)text_start;
207
208	/*
209	 * MAYWRITE to allow gdb to COW and set breakpoints
210	 */
211	vma = _install_special_mapping(mm,
212				       text_start,
213				       image->size,
214				       VM_READ|VM_EXEC|
215				       VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
216				       vdso_mapping);
217
218	if (IS_ERR(vma)) {
219		ret = PTR_ERR(vma);
220		goto up_fail;
221	}
222
223	vma = _install_special_mapping(mm,
224				       addr,
225				       -image->sym_vvar_start,
226				       VM_READ|VM_MAYREAD,
227				       &vvar_mapping);
228
229	if (IS_ERR(vma)) {
230		ret = PTR_ERR(vma);
231		do_munmap(mm, text_start, image->size, NULL);
232	}
233
234up_fail:
235	if (ret)
236		current->mm->context.vdso = NULL;
237
238	up_write(&mm->mmap_sem);
239	return ret;
240}
241
242int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
243{
244
245	if (!vdso_enabled)
246		return 0;
247
248#if defined CONFIG_COMPAT
249	if (!(is_32bit_task()))
250		return map_vdso(&vdso_image_64_builtin, &vdso_mapping64);
251	else
252		return map_vdso(&vdso_image_32_builtin, &vdso_mapping32);
253#else
254	return map_vdso(&vdso_image_64_builtin, &vdso_mapping64);
255#endif
256
257}
258
259static __init int vdso_setup(char *s)
260{
261	int err;
262	unsigned long val;
263
264	err = kstrtoul(s, 10, &val);
265	vdso_enabled = val;
266	return err;
267}
268__setup("vdso=", vdso_setup);