1a17ae4c3SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b020632eSMartin Schwidefsky /*
3b020632eSMartin Schwidefsky * vdso setup for s390
4b020632eSMartin Schwidefsky *
5b020632eSMartin Schwidefsky * Copyright IBM Corp. 2008
6b020632eSMartin Schwidefsky * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
7b020632eSMartin Schwidefsky */
8b020632eSMartin Schwidefsky
98d4be7f3SHeiko Carstens #include <linux/binfmts.h>
108d4be7f3SHeiko Carstens #include <linux/compat.h>
118d4be7f3SHeiko Carstens #include <linux/elf.h>
12b020632eSMartin Schwidefsky #include <linux/errno.h>
138d4be7f3SHeiko Carstens #include <linux/init.h>
14b020632eSMartin Schwidefsky #include <linux/kernel.h>
15b020632eSMartin Schwidefsky #include <linux/mm.h>
16b020632eSMartin Schwidefsky #include <linux/slab.h>
178d4be7f3SHeiko Carstens #include <linux/smp.h>
18eeab78b0SHeiko Carstens #include <linux/time_namespace.h>
1941cd81abSSven Schnelle #include <linux/random.h>
204bff8cb5SSven Schnelle #include <vdso/datapage.h>
21b020632eSMartin Schwidefsky #include <asm/vdso.h>
22b020632eSMartin Schwidefsky
236755270bSHeiko Carstens extern char vdso64_start[], vdso64_end[];
24779df224SSven Schnelle extern char vdso32_start[], vdso32_end[];
25b020632eSMartin Schwidefsky
26eeab78b0SHeiko Carstens static struct vm_special_mapping vvar_mapping;
27eeab78b0SHeiko Carstens
288d4be7f3SHeiko Carstens static union {
298d4be7f3SHeiko Carstens struct vdso_data data[CS_BASES];
308d4be7f3SHeiko Carstens u8 page[PAGE_SIZE];
318d4be7f3SHeiko Carstens } vdso_data_store __page_aligned_data;
328d4be7f3SHeiko Carstens
338d4be7f3SHeiko Carstens struct vdso_data *vdso_data = vdso_data_store.data;
348d4be7f3SHeiko Carstens
35eeab78b0SHeiko Carstens enum vvar_pages {
36eeab78b0SHeiko Carstens VVAR_DATA_PAGE_OFFSET,
37eeab78b0SHeiko Carstens VVAR_TIMENS_PAGE_OFFSET,
38eeab78b0SHeiko Carstens VVAR_NR_PAGES,
39eeab78b0SHeiko Carstens };
40eeab78b0SHeiko Carstens
41eeab78b0SHeiko Carstens #ifdef CONFIG_TIME_NS
arch_get_vdso_data(void * vvar_page)42eeab78b0SHeiko Carstens struct vdso_data *arch_get_vdso_data(void *vvar_page)
43eeab78b0SHeiko Carstens {
44eeab78b0SHeiko Carstens return (struct vdso_data *)(vvar_page);
45eeab78b0SHeiko Carstens }
46eeab78b0SHeiko Carstens
47eeab78b0SHeiko Carstens /*
48eeab78b0SHeiko Carstens * The VVAR page layout depends on whether a task belongs to the root or
49eeab78b0SHeiko Carstens * non-root time namespace. Whenever a task changes its namespace, the VVAR
50eeab78b0SHeiko Carstens * page tables are cleared and then they will be re-faulted with a
51eeab78b0SHeiko Carstens * corresponding layout.
52eeab78b0SHeiko Carstens * See also the comment near timens_setup_vdso_data() for details.
53eeab78b0SHeiko Carstens */
vdso_join_timens(struct task_struct * task,struct time_namespace * ns)54eeab78b0SHeiko Carstens int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
55eeab78b0SHeiko Carstens {
56eeab78b0SHeiko Carstens struct mm_struct *mm = task->mm;
57e7b6b990SMatthew Wilcox (Oracle) VMA_ITERATOR(vmi, mm, 0);
58eeab78b0SHeiko Carstens struct vm_area_struct *vma;
59eeab78b0SHeiko Carstens
60eeab78b0SHeiko Carstens mmap_read_lock(mm);
61e7b6b990SMatthew Wilcox (Oracle) for_each_vma(vmi, vma) {
62eeab78b0SHeiko Carstens if (!vma_is_special_mapping(vma, &vvar_mapping))
63eeab78b0SHeiko Carstens continue;
64*e9adcfecSMike Kravetz zap_vma_pages(vma);
65eeab78b0SHeiko Carstens break;
66eeab78b0SHeiko Carstens }
67eeab78b0SHeiko Carstens mmap_read_unlock(mm);
68eeab78b0SHeiko Carstens return 0;
69eeab78b0SHeiko Carstens }
70eeab78b0SHeiko Carstens #endif
71eeab78b0SHeiko Carstens
vvar_fault(const struct vm_special_mapping * sm,struct vm_area_struct * vma,struct vm_fault * vmf)725056c2c5SHeiko Carstens static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
735056c2c5SHeiko Carstens struct vm_area_struct *vma, struct vm_fault *vmf)
745056c2c5SHeiko Carstens {
75eeab78b0SHeiko Carstens struct page *timens_page = find_timens_vvar_page(vma);
76fe8344a0SHeiko Carstens unsigned long addr, pfn;
77fe8344a0SHeiko Carstens vm_fault_t err;
78eeab78b0SHeiko Carstens
79eeab78b0SHeiko Carstens switch (vmf->pgoff) {
80eeab78b0SHeiko Carstens case VVAR_DATA_PAGE_OFFSET:
81eeab78b0SHeiko Carstens pfn = virt_to_pfn(vdso_data);
82fe8344a0SHeiko Carstens if (timens_page) {
83fe8344a0SHeiko Carstens /*
84fe8344a0SHeiko Carstens * Fault in VVAR page too, since it will be accessed
85fe8344a0SHeiko Carstens * to get clock data anyway.
86fe8344a0SHeiko Carstens */
87fe8344a0SHeiko Carstens addr = vmf->address + VVAR_TIMENS_PAGE_OFFSET * PAGE_SIZE;
88fe8344a0SHeiko Carstens err = vmf_insert_pfn(vma, addr, pfn);
89fe8344a0SHeiko Carstens if (unlikely(err & VM_FAULT_ERROR))
90fe8344a0SHeiko Carstens return err;
91fe8344a0SHeiko Carstens pfn = page_to_pfn(timens_page);
92fe8344a0SHeiko Carstens }
93eeab78b0SHeiko Carstens break;
94eeab78b0SHeiko Carstens #ifdef CONFIG_TIME_NS
95eeab78b0SHeiko Carstens case VVAR_TIMENS_PAGE_OFFSET:
96eeab78b0SHeiko Carstens /*
97eeab78b0SHeiko Carstens * If a task belongs to a time namespace then a namespace
98eeab78b0SHeiko Carstens * specific VVAR is mapped with the VVAR_DATA_PAGE_OFFSET and
99eeab78b0SHeiko Carstens * the real VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET
100eeab78b0SHeiko Carstens * offset.
101eeab78b0SHeiko Carstens * See also the comment near timens_setup_vdso_data().
102eeab78b0SHeiko Carstens */
103eeab78b0SHeiko Carstens if (!timens_page)
1045056c2c5SHeiko Carstens return VM_FAULT_SIGBUS;
105eeab78b0SHeiko Carstens pfn = virt_to_pfn(vdso_data);
106eeab78b0SHeiko Carstens break;
107eeab78b0SHeiko Carstens #endif /* CONFIG_TIME_NS */
108eeab78b0SHeiko Carstens default:
109eeab78b0SHeiko Carstens return VM_FAULT_SIGBUS;
110eeab78b0SHeiko Carstens }
111eeab78b0SHeiko Carstens return vmf_insert_pfn(vma, vmf->address, pfn);
1125056c2c5SHeiko Carstens }
1135056c2c5SHeiko Carstens
vdso_mremap(const struct vm_special_mapping * sm,struct vm_area_struct * vma)11435bb092aSMartin Schwidefsky static int vdso_mremap(const struct vm_special_mapping *sm,
11535bb092aSMartin Schwidefsky struct vm_area_struct *vma)
11635bb092aSMartin Schwidefsky {
11735bb092aSMartin Schwidefsky current->mm->context.vdso_base = vma->vm_start;
11835bb092aSMartin Schwidefsky return 0;
11935bb092aSMartin Schwidefsky }
12035bb092aSMartin Schwidefsky
1215056c2c5SHeiko Carstens static struct vm_special_mapping vvar_mapping = {
1225056c2c5SHeiko Carstens .name = "[vvar]",
1235056c2c5SHeiko Carstens .fault = vvar_fault,
1245056c2c5SHeiko Carstens };
1255056c2c5SHeiko Carstens
126779df224SSven Schnelle static struct vm_special_mapping vdso64_mapping = {
127779df224SSven Schnelle .name = "[vdso]",
128779df224SSven Schnelle .mremap = vdso_mremap,
129779df224SSven Schnelle };
130779df224SSven Schnelle
131779df224SSven Schnelle static struct vm_special_mapping vdso32_mapping = {
13235bb092aSMartin Schwidefsky .name = "[vdso]",
13335bb092aSMartin Schwidefsky .mremap = vdso_mremap,
13435bb092aSMartin Schwidefsky };
13535bb092aSMartin Schwidefsky
vdso_getcpu_init(void)13696c0c7aeSHeiko Carstens int vdso_getcpu_init(void)
13780f06306SHeiko Carstens {
13880f06306SHeiko Carstens set_tod_programmable_field(smp_processor_id());
13996c0c7aeSHeiko Carstens return 0;
14080f06306SHeiko Carstens }
14196c0c7aeSHeiko Carstens early_initcall(vdso_getcpu_init); /* Must be called before SMP init */
14280f06306SHeiko Carstens
map_vdso(unsigned long addr,unsigned long vdso_mapping_len)14357761da4SSven Schnelle static int map_vdso(unsigned long addr, unsigned long vdso_mapping_len)
144b020632eSMartin Schwidefsky {
14557761da4SSven Schnelle unsigned long vvar_start, vdso_text_start, vdso_text_len;
146779df224SSven Schnelle struct vm_special_mapping *vdso_mapping;
147b020632eSMartin Schwidefsky struct mm_struct *mm = current->mm;
14835bb092aSMartin Schwidefsky struct vm_area_struct *vma;
149b020632eSMartin Schwidefsky int rc;
150b020632eSMartin Schwidefsky
151eeab78b0SHeiko Carstens BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES);
152d8ed45c5SMichel Lespinasse if (mmap_write_lock_killable(mm))
15369048176SMichal Hocko return -EINTR;
154779df224SSven Schnelle
155779df224SSven Schnelle if (is_compat_task()) {
156779df224SSven Schnelle vdso_text_len = vdso32_end - vdso32_start;
157779df224SSven Schnelle vdso_mapping = &vdso32_mapping;
158779df224SSven Schnelle } else {
159779df224SSven Schnelle vdso_text_len = vdso64_end - vdso64_start;
160779df224SSven Schnelle vdso_mapping = &vdso64_mapping;
161779df224SSven Schnelle }
16257761da4SSven Schnelle vvar_start = get_unmapped_area(NULL, addr, vdso_mapping_len, 0, 0);
163214b3564SHeiko Carstens rc = vvar_start;
164214b3564SHeiko Carstens if (IS_ERR_VALUE(vvar_start))
1658d4be7f3SHeiko Carstens goto out;
166eeab78b0SHeiko Carstens vma = _install_special_mapping(mm, vvar_start, VVAR_NR_PAGES*PAGE_SIZE,
167eeab78b0SHeiko Carstens VM_READ|VM_MAYREAD|VM_IO|VM_DONTDUMP|
168eeab78b0SHeiko Carstens VM_PFNMAP,
169214b3564SHeiko Carstens &vvar_mapping);
17035bb092aSMartin Schwidefsky rc = PTR_ERR(vma);
1718d4be7f3SHeiko Carstens if (IS_ERR(vma))
1728d4be7f3SHeiko Carstens goto out;
173eeab78b0SHeiko Carstens vdso_text_start = vvar_start + VVAR_NR_PAGES * PAGE_SIZE;
174214b3564SHeiko Carstens /* VM_MAYWRITE for COW so gdb can set breakpoints */
175214b3564SHeiko Carstens vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len,
176214b3564SHeiko Carstens VM_READ|VM_EXEC|
177214b3564SHeiko Carstens VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
178779df224SSven Schnelle vdso_mapping);
1795056c2c5SHeiko Carstens if (IS_ERR(vma)) {
180214b3564SHeiko Carstens do_munmap(mm, vvar_start, PAGE_SIZE, NULL);
1815056c2c5SHeiko Carstens rc = PTR_ERR(vma);
1825056c2c5SHeiko Carstens } else {
1835056c2c5SHeiko Carstens current->mm->context.vdso_base = vdso_text_start;
18435bb092aSMartin Schwidefsky rc = 0;
1855056c2c5SHeiko Carstens }
1868d4be7f3SHeiko Carstens out:
187d8ed45c5SMichel Lespinasse mmap_write_unlock(mm);
188b020632eSMartin Schwidefsky return rc;
189b020632eSMartin Schwidefsky }
190b020632eSMartin Schwidefsky
vdso_addr(unsigned long start,unsigned long len)19141cd81abSSven Schnelle static unsigned long vdso_addr(unsigned long start, unsigned long len)
19241cd81abSSven Schnelle {
19341cd81abSSven Schnelle unsigned long addr, end, offset;
19441cd81abSSven Schnelle
19541cd81abSSven Schnelle /*
19641cd81abSSven Schnelle * Round up the start address. It can start out unaligned as a result
19741cd81abSSven Schnelle * of stack start randomization.
19841cd81abSSven Schnelle */
19941cd81abSSven Schnelle start = PAGE_ALIGN(start);
20041cd81abSSven Schnelle
20141cd81abSSven Schnelle /* Round the lowest possible end address up to a PMD boundary. */
20241cd81abSSven Schnelle end = (start + len + PMD_SIZE - 1) & PMD_MASK;
20341cd81abSSven Schnelle if (end >= VDSO_BASE)
20441cd81abSSven Schnelle end = VDSO_BASE;
20541cd81abSSven Schnelle end -= len;
20641cd81abSSven Schnelle
20741cd81abSSven Schnelle if (end > start) {
2088032bf12SJason A. Donenfeld offset = get_random_u32_below(((end - start) >> PAGE_SHIFT) + 1);
20941cd81abSSven Schnelle addr = start + (offset << PAGE_SHIFT);
21041cd81abSSven Schnelle } else {
21141cd81abSSven Schnelle addr = start;
21241cd81abSSven Schnelle }
21341cd81abSSven Schnelle return addr;
21441cd81abSSven Schnelle }
21541cd81abSSven Schnelle
vdso_size(void)21657761da4SSven Schnelle unsigned long vdso_size(void)
21757761da4SSven Schnelle {
21857761da4SSven Schnelle unsigned long size = VVAR_NR_PAGES * PAGE_SIZE;
21957761da4SSven Schnelle
22057761da4SSven Schnelle if (is_compat_task())
22157761da4SSven Schnelle size += vdso32_end - vdso32_start;
22257761da4SSven Schnelle else
22357761da4SSven Schnelle size += vdso64_end - vdso64_start;
22457761da4SSven Schnelle return PAGE_ALIGN(size);
22557761da4SSven Schnelle }
22657761da4SSven Schnelle
arch_setup_additional_pages(struct linux_binprm * bprm,int uses_interp)22757761da4SSven Schnelle int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
22857761da4SSven Schnelle {
22941cd81abSSven Schnelle unsigned long addr = VDSO_BASE;
23041cd81abSSven Schnelle unsigned long size = vdso_size();
23141cd81abSSven Schnelle
23241cd81abSSven Schnelle if (current->flags & PF_RANDOMIZE)
23341cd81abSSven Schnelle addr = vdso_addr(current->mm->start_stack + PAGE_SIZE, size);
23441cd81abSSven Schnelle return map_vdso(addr, size);
23557761da4SSven Schnelle }
23657761da4SSven Schnelle
vdso_setup_pages(void * start,void * end)237779df224SSven Schnelle static struct page ** __init vdso_setup_pages(void *start, void *end)
238b020632eSMartin Schwidefsky {
239779df224SSven Schnelle int pages = (end - start) >> PAGE_SHIFT;
240779df224SSven Schnelle struct page **pagelist;
241b020632eSMartin Schwidefsky int i;
242b020632eSMartin Schwidefsky
243779df224SSven Schnelle pagelist = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL);
244779df224SSven Schnelle if (!pagelist)
245779df224SSven Schnelle panic("%s: Cannot allocate page list for VDSO", __func__);
246779df224SSven Schnelle for (i = 0; i < pages; i++)
247779df224SSven Schnelle pagelist[i] = virt_to_page(start + i * PAGE_SIZE);
248779df224SSven Schnelle return pagelist;
249779df224SSven Schnelle }
250d57778feSSven Schnelle
vdso_init(void)251779df224SSven Schnelle static int __init vdso_init(void)
252779df224SSven Schnelle {
253779df224SSven Schnelle vdso64_mapping.pages = vdso_setup_pages(vdso64_start, vdso64_end);
254779df224SSven Schnelle if (IS_ENABLED(CONFIG_COMPAT))
255779df224SSven Schnelle vdso32_mapping.pages = vdso_setup_pages(vdso32_start, vdso32_end);
256b020632eSMartin Schwidefsky return 0;
257b020632eSMartin Schwidefsky }
25896c0c7aeSHeiko Carstens arch_initcall(vdso_init);
259