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