1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Author: Huacai Chen <chenhuacai@loongson.cn>
4 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5 */
6
7 #include <linux/binfmts.h>
8 #include <linux/elf.h>
9 #include <linux/err.h>
10 #include <linux/init.h>
11 #include <linux/ioport.h>
12 #include <linux/kernel.h>
13 #include <linux/mm.h>
14 #include <linux/random.h>
15 #include <linux/sched.h>
16 #include <linux/slab.h>
17 #include <linux/time_namespace.h>
18 #include <linux/timekeeper_internal.h>
19
20 #include <asm/page.h>
21 #include <asm/vdso.h>
22 #include <vdso/helpers.h>
23 #include <vdso/vsyscall.h>
24 #include <vdso/datapage.h>
25 #include <generated/vdso-offsets.h>
26
27 extern char vdso_start[], vdso_end[];
28
29 /* Kernel-provided data used by the VDSO. */
30 static union vdso_data_store generic_vdso_data __page_aligned_data;
31
32 static union {
33 u8 page[LOONGARCH_VDSO_DATA_SIZE];
34 struct loongarch_vdso_data vdata;
35 } loongarch_vdso_data __page_aligned_data;
36
37 struct vdso_data *vdso_data = generic_vdso_data.data;
38 struct vdso_pcpu_data *vdso_pdata = loongarch_vdso_data.vdata.pdata;
39 struct vdso_rng_data *vdso_rng_data = &loongarch_vdso_data.vdata.rng_data;
40
vdso_mremap(const struct vm_special_mapping * sm,struct vm_area_struct * new_vma)41 static int vdso_mremap(const struct vm_special_mapping *sm, struct vm_area_struct *new_vma)
42 {
43 current->mm->context.vdso = (void *)(new_vma->vm_start);
44
45 return 0;
46 }
47
vvar_fault(const struct vm_special_mapping * sm,struct vm_area_struct * vma,struct vm_fault * vmf)48 static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
49 struct vm_area_struct *vma, struct vm_fault *vmf)
50 {
51 unsigned long pfn;
52 struct page *timens_page = find_timens_vvar_page(vma);
53
54 switch (vmf->pgoff) {
55 case VVAR_GENERIC_PAGE_OFFSET:
56 if (!timens_page)
57 pfn = sym_to_pfn(vdso_data);
58 else
59 pfn = page_to_pfn(timens_page);
60 break;
61 #ifdef CONFIG_TIME_NS
62 case VVAR_TIMENS_PAGE_OFFSET:
63 /*
64 * If a task belongs to a time namespace then a namespace specific
65 * VVAR is mapped with the VVAR_GENERIC_PAGE_OFFSET and the real
66 * VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET offset.
67 * See also the comment near timens_setup_vdso_data().
68 */
69 if (!timens_page)
70 return VM_FAULT_SIGBUS;
71 else
72 pfn = sym_to_pfn(vdso_data);
73 break;
74 #endif /* CONFIG_TIME_NS */
75 case VVAR_LOONGARCH_PAGES_START ... VVAR_LOONGARCH_PAGES_END:
76 pfn = sym_to_pfn(&loongarch_vdso_data) + vmf->pgoff - VVAR_LOONGARCH_PAGES_START;
77 break;
78 default:
79 return VM_FAULT_SIGBUS;
80 }
81
82 return vmf_insert_pfn(vma, vmf->address, pfn);
83 }
84
85 struct loongarch_vdso_info vdso_info = {
86 .vdso = vdso_start,
87 .code_mapping = {
88 .name = "[vdso]",
89 .mremap = vdso_mremap,
90 },
91 .data_mapping = {
92 .name = "[vvar]",
93 .fault = vvar_fault,
94 },
95 .offset_sigreturn = vdso_offset_sigreturn,
96 };
97
init_vdso(void)98 static int __init init_vdso(void)
99 {
100 unsigned long i, cpu, pfn;
101
102 BUG_ON(!PAGE_ALIGNED(vdso_info.vdso));
103
104 for_each_possible_cpu(cpu)
105 vdso_pdata[cpu].node = cpu_to_node(cpu);
106
107 vdso_info.size = PAGE_ALIGN(vdso_end - vdso_start);
108 vdso_info.code_mapping.pages =
109 kcalloc(vdso_info.size / PAGE_SIZE, sizeof(struct page *), GFP_KERNEL);
110
111 pfn = __phys_to_pfn(__pa_symbol(vdso_info.vdso));
112 for (i = 0; i < vdso_info.size / PAGE_SIZE; i++)
113 vdso_info.code_mapping.pages[i] = pfn_to_page(pfn + i);
114
115 return 0;
116 }
117 subsys_initcall(init_vdso);
118
119 #ifdef CONFIG_TIME_NS
arch_get_vdso_data(void * vvar_page)120 struct vdso_data *arch_get_vdso_data(void *vvar_page)
121 {
122 return (struct vdso_data *)(vvar_page);
123 }
124
125 /*
126 * The vvar mapping contains data for a specific time namespace, so when a
127 * task changes namespace we must unmap its vvar data for the old namespace.
128 * Subsequent faults will map in data for the new namespace.
129 *
130 * For more details see timens_setup_vdso_data().
131 */
vdso_join_timens(struct task_struct * task,struct time_namespace * ns)132 int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
133 {
134 struct mm_struct *mm = task->mm;
135 struct vm_area_struct *vma;
136
137 VMA_ITERATOR(vmi, mm, 0);
138
139 mmap_read_lock(mm);
140 for_each_vma(vmi, vma) {
141 if (vma_is_special_mapping(vma, &vdso_info.data_mapping))
142 zap_vma_pages(vma);
143 }
144 mmap_read_unlock(mm);
145
146 return 0;
147 }
148 #endif
149
vdso_base(void)150 static unsigned long vdso_base(void)
151 {
152 unsigned long base = STACK_TOP;
153
154 if (current->flags & PF_RANDOMIZE) {
155 base += get_random_u32_below(VDSO_RANDOMIZE_SIZE);
156 base = PAGE_ALIGN(base);
157 }
158
159 return base;
160 }
161
arch_setup_additional_pages(struct linux_binprm * bprm,int uses_interp)162 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
163 {
164 int ret;
165 unsigned long size, data_addr, vdso_addr;
166 struct mm_struct *mm = current->mm;
167 struct vm_area_struct *vma;
168 struct loongarch_vdso_info *info = current->thread.vdso;
169
170 if (mmap_write_lock_killable(mm))
171 return -EINTR;
172
173 /*
174 * Determine total area size. This includes the VDSO data itself
175 * and the data pages.
176 */
177 size = VVAR_SIZE + info->size;
178
179 data_addr = get_unmapped_area(NULL, vdso_base(), size, 0, 0);
180 if (IS_ERR_VALUE(data_addr)) {
181 ret = data_addr;
182 goto out;
183 }
184
185 vma = _install_special_mapping(mm, data_addr, VVAR_SIZE,
186 VM_READ | VM_MAYREAD | VM_PFNMAP,
187 &info->data_mapping);
188 if (IS_ERR(vma)) {
189 ret = PTR_ERR(vma);
190 goto out;
191 }
192
193 vdso_addr = data_addr + VVAR_SIZE;
194 vma = _install_special_mapping(mm, vdso_addr, info->size,
195 VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
196 &info->code_mapping);
197 if (IS_ERR(vma)) {
198 ret = PTR_ERR(vma);
199 goto out;
200 }
201
202 mm->context.vdso = (void *)vdso_addr;
203 ret = 0;
204
205 out:
206 mmap_write_unlock(mm);
207 return ret;
208 }
209