xref: /openbmc/linux/arch/arm/kernel/vdso.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
1caab277bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ecf99a43SNathan Lynch /*
3ecf99a43SNathan Lynch  * Adapted from arm64 version.
4ecf99a43SNathan Lynch  *
5ecf99a43SNathan Lynch  * Copyright (C) 2012 ARM Limited
6ecf99a43SNathan Lynch  * Copyright (C) 2015 Mentor Graphics Corporation.
7ecf99a43SNathan Lynch  */
8ecf99a43SNathan Lynch 
992bb8d5dSJisheng Zhang #include <linux/cache.h>
10ecf99a43SNathan Lynch #include <linux/elf.h>
11ecf99a43SNathan Lynch #include <linux/err.h>
12ecf99a43SNathan Lynch #include <linux/kernel.h>
13ecf99a43SNathan Lynch #include <linux/mm.h>
14ecf99a43SNathan Lynch #include <linux/of.h>
15ecf99a43SNathan Lynch #include <linux/printk.h>
16ecf99a43SNathan Lynch #include <linux/slab.h>
17ecf99a43SNathan Lynch #include <linux/timekeeper_internal.h>
18ecf99a43SNathan Lynch #include <linux/vmalloc.h>
19ecf99a43SNathan Lynch #include <asm/arch_timer.h>
20ecf99a43SNathan Lynch #include <asm/barrier.h>
21ecf99a43SNathan Lynch #include <asm/cacheflush.h>
22ecf99a43SNathan Lynch #include <asm/page.h>
23ecf99a43SNathan Lynch #include <asm/vdso.h>
24ecf99a43SNathan Lynch #include <asm/vdso_datapage.h>
25ecf99a43SNathan Lynch #include <clocksource/arm_arch_timer.h>
2620e2fc42SVincenzo Frascino #include <vdso/helpers.h>
2720e2fc42SVincenzo Frascino #include <vdso/vsyscall.h>
28ecf99a43SNathan Lynch 
29ecf99a43SNathan Lynch #define MAX_SYMNAME	64
30ecf99a43SNathan Lynch 
31ecf99a43SNathan Lynch static struct page **vdso_text_pagelist;
32ecf99a43SNathan Lynch 
3373b9160dSJinbum Park extern char vdso_start[], vdso_end[];
3473b9160dSJinbum Park 
35ecf99a43SNathan Lynch /* Total number of pages needed for the data and text portions of the VDSO. */
3692bb8d5dSJisheng Zhang unsigned int vdso_total_pages __ro_after_init;
37ecf99a43SNathan Lynch 
38ecf99a43SNathan Lynch /*
39ecf99a43SNathan Lynch  * The VDSO data page.
40ecf99a43SNathan Lynch  */
41ecf99a43SNathan Lynch static union vdso_data_store vdso_data_store __page_aligned_data;
4220e2fc42SVincenzo Frascino struct vdso_data *vdso_data = vdso_data_store.data;
43ecf99a43SNathan Lynch 
4492bb8d5dSJisheng Zhang static struct page *vdso_data_page __ro_after_init;
4592bb8d5dSJisheng Zhang static const struct vm_special_mapping vdso_data_mapping = {
46ecf99a43SNathan Lynch 	.name = "[vvar]",
47ecf99a43SNathan Lynch 	.pages = &vdso_data_page,
48ecf99a43SNathan Lynch };
49ecf99a43SNathan Lynch 
vdso_mremap(const struct vm_special_mapping * sm,struct vm_area_struct * new_vma)50280e87e9SDmitry Safonov static int vdso_mremap(const struct vm_special_mapping *sm,
51280e87e9SDmitry Safonov 		struct vm_area_struct *new_vma)
52280e87e9SDmitry Safonov {
53280e87e9SDmitry Safonov 	current->mm->context.vdso = new_vma->vm_start;
54280e87e9SDmitry Safonov 
55280e87e9SDmitry Safonov 	return 0;
56280e87e9SDmitry Safonov }
57280e87e9SDmitry Safonov 
5892bb8d5dSJisheng Zhang static struct vm_special_mapping vdso_text_mapping __ro_after_init = {
59ecf99a43SNathan Lynch 	.name = "[vdso]",
60280e87e9SDmitry Safonov 	.mremap = vdso_mremap,
61ecf99a43SNathan Lynch };
62ecf99a43SNathan Lynch 
63ecf99a43SNathan Lynch struct elfinfo {
64ecf99a43SNathan Lynch 	Elf32_Ehdr	*hdr;		/* ptr to ELF */
65ecf99a43SNathan Lynch 	Elf32_Sym	*dynsym;	/* ptr to .dynsym section */
66ecf99a43SNathan Lynch 	unsigned long	dynsymsize;	/* size of .dynsym section */
67ecf99a43SNathan Lynch 	char		*dynstr;	/* ptr to .dynstr section */
68ecf99a43SNathan Lynch };
69ecf99a43SNathan Lynch 
70ecf99a43SNathan Lynch /* Cached result of boot-time check for whether the arch timer exists,
71ecf99a43SNathan Lynch  * and if so, whether the virtual counter is useable.
72ecf99a43SNathan Lynch  */
7320e2fc42SVincenzo Frascino bool cntvct_ok __ro_after_init;
74ecf99a43SNathan Lynch 
cntvct_functional(void)75ecf99a43SNathan Lynch static bool __init cntvct_functional(void)
76ecf99a43SNathan Lynch {
77ecf99a43SNathan Lynch 	struct device_node *np;
78ecf99a43SNathan Lynch 	bool ret = false;
79ecf99a43SNathan Lynch 
80ecf99a43SNathan Lynch 	if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
81ecf99a43SNathan Lynch 		goto out;
82ecf99a43SNathan Lynch 
83ecf99a43SNathan Lynch 	/* The arm_arch_timer core should export
84ecf99a43SNathan Lynch 	 * arch_timer_use_virtual or similar so we don't have to do
85ecf99a43SNathan Lynch 	 * this.
86ecf99a43SNathan Lynch 	 */
87ecf99a43SNathan Lynch 	np = of_find_compatible_node(NULL, NULL, "arm,armv7-timer");
88ecf99a43SNathan Lynch 	if (!np)
8945939ce2SFlorian Fainelli 		np = of_find_compatible_node(NULL, NULL, "arm,armv8-timer");
9045939ce2SFlorian Fainelli 	if (!np)
91ecf99a43SNathan Lynch 		goto out_put;
92ecf99a43SNathan Lynch 
93ecf99a43SNathan Lynch 	if (of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
94ecf99a43SNathan Lynch 		goto out_put;
95ecf99a43SNathan Lynch 
96ecf99a43SNathan Lynch 	ret = true;
97ecf99a43SNathan Lynch 
98ecf99a43SNathan Lynch out_put:
99ecf99a43SNathan Lynch 	of_node_put(np);
100ecf99a43SNathan Lynch out:
101ecf99a43SNathan Lynch 	return ret;
102ecf99a43SNathan Lynch }
103ecf99a43SNathan Lynch 
find_section(Elf32_Ehdr * ehdr,const char * name,unsigned long * size)104ecf99a43SNathan Lynch static void * __init find_section(Elf32_Ehdr *ehdr, const char *name,
105ecf99a43SNathan Lynch 				  unsigned long *size)
106ecf99a43SNathan Lynch {
107ecf99a43SNathan Lynch 	Elf32_Shdr *sechdrs;
108ecf99a43SNathan Lynch 	unsigned int i;
109ecf99a43SNathan Lynch 	char *secnames;
110ecf99a43SNathan Lynch 
111ecf99a43SNathan Lynch 	/* Grab section headers and strings so we can tell who is who */
112ecf99a43SNathan Lynch 	sechdrs = (void *)ehdr + ehdr->e_shoff;
113ecf99a43SNathan Lynch 	secnames = (void *)ehdr + sechdrs[ehdr->e_shstrndx].sh_offset;
114ecf99a43SNathan Lynch 
115ecf99a43SNathan Lynch 	/* Find the section they want */
116ecf99a43SNathan Lynch 	for (i = 1; i < ehdr->e_shnum; i++) {
117ecf99a43SNathan Lynch 		if (strcmp(secnames + sechdrs[i].sh_name, name) == 0) {
118ecf99a43SNathan Lynch 			if (size)
119ecf99a43SNathan Lynch 				*size = sechdrs[i].sh_size;
120ecf99a43SNathan Lynch 			return (void *)ehdr + sechdrs[i].sh_offset;
121ecf99a43SNathan Lynch 		}
122ecf99a43SNathan Lynch 	}
123ecf99a43SNathan Lynch 
124ecf99a43SNathan Lynch 	if (size)
125ecf99a43SNathan Lynch 		*size = 0;
126ecf99a43SNathan Lynch 	return NULL;
127ecf99a43SNathan Lynch }
128ecf99a43SNathan Lynch 
find_symbol(struct elfinfo * lib,const char * symname)129ecf99a43SNathan Lynch static Elf32_Sym * __init find_symbol(struct elfinfo *lib, const char *symname)
130ecf99a43SNathan Lynch {
131ecf99a43SNathan Lynch 	unsigned int i;
132ecf99a43SNathan Lynch 
133ecf99a43SNathan Lynch 	for (i = 0; i < (lib->dynsymsize / sizeof(Elf32_Sym)); i++) {
134ecf99a43SNathan Lynch 		char name[MAX_SYMNAME], *c;
135ecf99a43SNathan Lynch 
136ecf99a43SNathan Lynch 		if (lib->dynsym[i].st_name == 0)
137ecf99a43SNathan Lynch 			continue;
138*7611b335SAzeem Shaikh 		strscpy(name, lib->dynstr + lib->dynsym[i].st_name,
139ecf99a43SNathan Lynch 			MAX_SYMNAME);
140ecf99a43SNathan Lynch 		c = strchr(name, '@');
141ecf99a43SNathan Lynch 		if (c)
142ecf99a43SNathan Lynch 			*c = 0;
143ecf99a43SNathan Lynch 		if (strcmp(symname, name) == 0)
144ecf99a43SNathan Lynch 			return &lib->dynsym[i];
145ecf99a43SNathan Lynch 	}
146ecf99a43SNathan Lynch 	return NULL;
147ecf99a43SNathan Lynch }
148ecf99a43SNathan Lynch 
vdso_nullpatch_one(struct elfinfo * lib,const char * symname)149ecf99a43SNathan Lynch static void __init vdso_nullpatch_one(struct elfinfo *lib, const char *symname)
150ecf99a43SNathan Lynch {
151ecf99a43SNathan Lynch 	Elf32_Sym *sym;
152ecf99a43SNathan Lynch 
153ecf99a43SNathan Lynch 	sym = find_symbol(lib, symname);
154ecf99a43SNathan Lynch 	if (!sym)
155ecf99a43SNathan Lynch 		return;
156ecf99a43SNathan Lynch 
157ecf99a43SNathan Lynch 	sym->st_name = 0;
158ecf99a43SNathan Lynch }
159ecf99a43SNathan Lynch 
patch_vdso(void * ehdr)160ecf99a43SNathan Lynch static void __init patch_vdso(void *ehdr)
161ecf99a43SNathan Lynch {
162ecf99a43SNathan Lynch 	struct elfinfo einfo;
163ecf99a43SNathan Lynch 
164ecf99a43SNathan Lynch 	einfo = (struct elfinfo) {
165ecf99a43SNathan Lynch 		.hdr = ehdr,
166ecf99a43SNathan Lynch 	};
167ecf99a43SNathan Lynch 
168ecf99a43SNathan Lynch 	einfo.dynsym = find_section(einfo.hdr, ".dynsym", &einfo.dynsymsize);
169ecf99a43SNathan Lynch 	einfo.dynstr = find_section(einfo.hdr, ".dynstr", NULL);
170ecf99a43SNathan Lynch 
171ecf99a43SNathan Lynch 	/* If the virtual counter is absent or non-functional we don't
172ecf99a43SNathan Lynch 	 * want programs to incur the slight additional overhead of
173ecf99a43SNathan Lynch 	 * dispatching through the VDSO only to fall back to syscalls.
174ecf99a43SNathan Lynch 	 */
175ecf99a43SNathan Lynch 	if (!cntvct_ok) {
176ecf99a43SNathan Lynch 		vdso_nullpatch_one(&einfo, "__vdso_gettimeofday");
177ecf99a43SNathan Lynch 		vdso_nullpatch_one(&einfo, "__vdso_clock_gettime");
1784405bdf3SJaedon Shin 		vdso_nullpatch_one(&einfo, "__vdso_clock_gettime64");
179ecf99a43SNathan Lynch 	}
180ecf99a43SNathan Lynch }
181ecf99a43SNathan Lynch 
vdso_init(void)182ecf99a43SNathan Lynch static int __init vdso_init(void)
183ecf99a43SNathan Lynch {
184ecf99a43SNathan Lynch 	unsigned int text_pages;
185ecf99a43SNathan Lynch 	int i;
186ecf99a43SNathan Lynch 
18773b9160dSJinbum Park 	if (memcmp(vdso_start, "\177ELF", 4)) {
188ecf99a43SNathan Lynch 		pr_err("VDSO is not a valid ELF object!\n");
189ecf99a43SNathan Lynch 		return -ENOEXEC;
190ecf99a43SNathan Lynch 	}
191ecf99a43SNathan Lynch 
19273b9160dSJinbum Park 	text_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
193ecf99a43SNathan Lynch 
194ecf99a43SNathan Lynch 	/* Allocate the VDSO text pagelist */
195ecf99a43SNathan Lynch 	vdso_text_pagelist = kcalloc(text_pages, sizeof(struct page *),
196ecf99a43SNathan Lynch 				     GFP_KERNEL);
197ecf99a43SNathan Lynch 	if (vdso_text_pagelist == NULL)
198ecf99a43SNathan Lynch 		return -ENOMEM;
199ecf99a43SNathan Lynch 
200ecf99a43SNathan Lynch 	/* Grab the VDSO data page. */
201ecf99a43SNathan Lynch 	vdso_data_page = virt_to_page(vdso_data);
202ecf99a43SNathan Lynch 
203ecf99a43SNathan Lynch 	/* Grab the VDSO text pages. */
204ecf99a43SNathan Lynch 	for (i = 0; i < text_pages; i++) {
205ecf99a43SNathan Lynch 		struct page *page;
206ecf99a43SNathan Lynch 
20773b9160dSJinbum Park 		page = virt_to_page(vdso_start + i * PAGE_SIZE);
208ecf99a43SNathan Lynch 		vdso_text_pagelist[i] = page;
209ecf99a43SNathan Lynch 	}
210ecf99a43SNathan Lynch 
211ecf99a43SNathan Lynch 	vdso_text_mapping.pages = vdso_text_pagelist;
212ecf99a43SNathan Lynch 
213ecf99a43SNathan Lynch 	vdso_total_pages = 1; /* for the data/vvar page */
214ecf99a43SNathan Lynch 	vdso_total_pages += text_pages;
215ecf99a43SNathan Lynch 
216ecf99a43SNathan Lynch 	cntvct_ok = cntvct_functional();
217ecf99a43SNathan Lynch 
21873b9160dSJinbum Park 	patch_vdso(vdso_start);
219ecf99a43SNathan Lynch 
220ecf99a43SNathan Lynch 	return 0;
221ecf99a43SNathan Lynch }
222ecf99a43SNathan Lynch arch_initcall(vdso_init);
223ecf99a43SNathan Lynch 
install_vvar(struct mm_struct * mm,unsigned long addr)224ecf99a43SNathan Lynch static int install_vvar(struct mm_struct *mm, unsigned long addr)
225ecf99a43SNathan Lynch {
226ecf99a43SNathan Lynch 	struct vm_area_struct *vma;
227ecf99a43SNathan Lynch 
228ecf99a43SNathan Lynch 	vma = _install_special_mapping(mm, addr, PAGE_SIZE,
229ecf99a43SNathan Lynch 				       VM_READ | VM_MAYREAD,
230ecf99a43SNathan Lynch 				       &vdso_data_mapping);
231ecf99a43SNathan Lynch 
23238fc2f6cSPrasanna Karthik 	return PTR_ERR_OR_ZERO(vma);
233ecf99a43SNathan Lynch }
234ecf99a43SNathan Lynch 
235c1e8d7c6SMichel Lespinasse /* assumes mmap_lock is write-locked */
arm_install_vdso(struct mm_struct * mm,unsigned long addr)236ecf99a43SNathan Lynch void arm_install_vdso(struct mm_struct *mm, unsigned long addr)
237ecf99a43SNathan Lynch {
238ecf99a43SNathan Lynch 	struct vm_area_struct *vma;
239ecf99a43SNathan Lynch 	unsigned long len;
240ecf99a43SNathan Lynch 
241ecf99a43SNathan Lynch 	mm->context.vdso = 0;
242ecf99a43SNathan Lynch 
243ecf99a43SNathan Lynch 	if (vdso_text_pagelist == NULL)
244ecf99a43SNathan Lynch 		return;
245ecf99a43SNathan Lynch 
246ecf99a43SNathan Lynch 	if (install_vvar(mm, addr))
247ecf99a43SNathan Lynch 		return;
248ecf99a43SNathan Lynch 
249ecf99a43SNathan Lynch 	/* Account for vvar page. */
250ecf99a43SNathan Lynch 	addr += PAGE_SIZE;
251ecf99a43SNathan Lynch 	len = (vdso_total_pages - 1) << PAGE_SHIFT;
252ecf99a43SNathan Lynch 
253ecf99a43SNathan Lynch 	vma = _install_special_mapping(mm, addr, len,
254ecf99a43SNathan Lynch 		VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
255ecf99a43SNathan Lynch 		&vdso_text_mapping);
256ecf99a43SNathan Lynch 
257ecf99a43SNathan Lynch 	if (!IS_ERR(vma))
258ecf99a43SNathan Lynch 		mm->context.vdso = addr;
259ecf99a43SNathan Lynch }
260ecf99a43SNathan Lynch 
261