2 * VDSO implementation for AArch64 and vector page setup for AArch32.
4 * Copyright (C) 2012 ARM Limited
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 * Author: Will Deacon <will.deacon@arm.com>
21 #include <linux/cache.h>
22 #include <linux/clocksource.h>
23 #include <linux/elf.h>
24 #include <linux/err.h>
25 #include <linux/errno.h>
26 #include <linux/gfp.h>
27 #include <linux/kernel.h>
29 #include <linux/sched.h>
30 #include <linux/signal.h>
31 #include <linux/slab.h>
32 #include <linux/timekeeper_internal.h>
33 #include <linux/vmalloc.h>
35 #include <asm/cacheflush.h>
36 #include <asm/signal32.h>
38 #include <asm/vdso_datapage.h>
40 extern char vdso_start
[], vdso_end
[];
41 static unsigned long vdso_pages __ro_after_init
;
47 struct vdso_data data
;
49 } vdso_data_store __page_aligned_data
;
50 struct vdso_data
*vdso_data
= &vdso_data_store
.data
;
54 * Create and map the vectors page for AArch32 tasks.
56 static struct page
*vectors_page
[1] __ro_after_init
;
58 static int __init
alloc_vectors_page(void)
60 extern char __kuser_helper_start
[], __kuser_helper_end
[];
61 extern char __aarch32_sigret_code_start
[], __aarch32_sigret_code_end
[];
63 int kuser_sz
= __kuser_helper_end
- __kuser_helper_start
;
64 int sigret_sz
= __aarch32_sigret_code_end
- __aarch32_sigret_code_start
;
67 vpage
= get_zeroed_page(GFP_ATOMIC
);
73 memcpy((void *)vpage
+ 0x1000 - kuser_sz
, __kuser_helper_start
,
77 memcpy((void *)vpage
+ AARCH32_KERN_SIGRET_CODE_OFFSET
,
78 __aarch32_sigret_code_start
, sigret_sz
);
80 flush_icache_range(vpage
, vpage
+ PAGE_SIZE
);
81 vectors_page
[0] = virt_to_page(vpage
);
85 arch_initcall(alloc_vectors_page
);
87 int aarch32_setup_vectors_page(struct linux_binprm
*bprm
, int uses_interp
)
89 struct mm_struct
*mm
= current
->mm
;
90 unsigned long addr
= AARCH32_VECTORS_BASE
;
91 static const struct vm_special_mapping spec
= {
93 .pages
= vectors_page
,
98 if (down_write_killable(&mm
->mmap_sem
))
100 current
->mm
->context
.vdso
= (void *)addr
;
102 /* Map vectors page at the high address. */
103 ret
= _install_special_mapping(mm
, addr
, PAGE_SIZE
,
104 VM_READ
|VM_EXEC
|VM_MAYREAD
|VM_MAYEXEC
,
107 up_write(&mm
->mmap_sem
);
109 return PTR_ERR_OR_ZERO(ret
);
111 #endif /* CONFIG_COMPAT */
113 static int vdso_mremap(const struct vm_special_mapping
*sm
,
114 struct vm_area_struct
*new_vma
)
116 unsigned long new_size
= new_vma
->vm_end
- new_vma
->vm_start
;
117 unsigned long vdso_size
= vdso_end
- vdso_start
;
119 if (vdso_size
!= new_size
)
122 current
->mm
->context
.vdso
= (void *)new_vma
->vm_start
;
127 static struct vm_special_mapping vdso_spec
[2] __ro_after_init
= {
133 .mremap
= vdso_mremap
,
137 static int __init
vdso_init(void)
140 struct page
**vdso_pagelist
;
143 if (memcmp(vdso_start
, "\177ELF", 4)) {
144 pr_err("vDSO is not a valid ELF object!\n");
148 vdso_pages
= (vdso_end
- vdso_start
) >> PAGE_SHIFT
;
149 pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n",
150 vdso_pages
+ 1, vdso_pages
, vdso_start
, 1L, vdso_data
);
152 /* Allocate the vDSO pagelist, plus a page for the data. */
153 vdso_pagelist
= kcalloc(vdso_pages
+ 1, sizeof(struct page
*),
155 if (vdso_pagelist
== NULL
)
158 /* Grab the vDSO data page. */
159 vdso_pagelist
[0] = phys_to_page(__pa_symbol(vdso_data
));
162 /* Grab the vDSO code pages. */
163 pfn
= sym_to_pfn(vdso_start
);
165 for (i
= 0; i
< vdso_pages
; i
++)
166 vdso_pagelist
[i
+ 1] = pfn_to_page(pfn
+ i
);
168 vdso_spec
[0].pages
= &vdso_pagelist
[0];
169 vdso_spec
[1].pages
= &vdso_pagelist
[1];
173 arch_initcall(vdso_init
);
175 int arch_setup_additional_pages(struct linux_binprm
*bprm
,
178 struct mm_struct
*mm
= current
->mm
;
179 unsigned long vdso_base
, vdso_text_len
, vdso_mapping_len
;
182 vdso_text_len
= vdso_pages
<< PAGE_SHIFT
;
183 /* Be sure to map the data page */
184 vdso_mapping_len
= vdso_text_len
+ PAGE_SIZE
;
186 if (down_write_killable(&mm
->mmap_sem
))
188 vdso_base
= get_unmapped_area(NULL
, 0, vdso_mapping_len
, 0, 0);
189 if (IS_ERR_VALUE(vdso_base
)) {
190 ret
= ERR_PTR(vdso_base
);
193 ret
= _install_special_mapping(mm
, vdso_base
, PAGE_SIZE
,
199 vdso_base
+= PAGE_SIZE
;
200 mm
->context
.vdso
= (void *)vdso_base
;
201 ret
= _install_special_mapping(mm
, vdso_base
, vdso_text_len
,
203 VM_MAYREAD
|VM_MAYWRITE
|VM_MAYEXEC
,
209 up_write(&mm
->mmap_sem
);
213 mm
->context
.vdso
= NULL
;
214 up_write(&mm
->mmap_sem
);
219 * Update the vDSO data page to keep in sync with kernel timekeeping.
221 void update_vsyscall(struct timekeeper
*tk
)
223 u32 use_syscall
= !tk
->tkr_mono
.clock
->archdata
.vdso_direct
;
225 ++vdso_data
->tb_seq_count
;
228 vdso_data
->use_syscall
= use_syscall
;
229 vdso_data
->xtime_coarse_sec
= tk
->xtime_sec
;
230 vdso_data
->xtime_coarse_nsec
= tk
->tkr_mono
.xtime_nsec
>>
232 vdso_data
->wtm_clock_sec
= tk
->wall_to_monotonic
.tv_sec
;
233 vdso_data
->wtm_clock_nsec
= tk
->wall_to_monotonic
.tv_nsec
;
236 /* tkr_mono.cycle_last == tkr_raw.cycle_last */
237 vdso_data
->cs_cycle_last
= tk
->tkr_mono
.cycle_last
;
238 vdso_data
->raw_time_sec
= tk
->raw_sec
;
239 vdso_data
->raw_time_nsec
= tk
->tkr_raw
.xtime_nsec
;
240 vdso_data
->xtime_clock_sec
= tk
->xtime_sec
;
241 vdso_data
->xtime_clock_nsec
= tk
->tkr_mono
.xtime_nsec
;
242 vdso_data
->cs_mono_mult
= tk
->tkr_mono
.mult
;
243 vdso_data
->cs_raw_mult
= tk
->tkr_raw
.mult
;
244 /* tkr_mono.shift == tkr_raw.shift */
245 vdso_data
->cs_shift
= tk
->tkr_mono
.shift
;
249 ++vdso_data
->tb_seq_count
;
252 void update_vsyscall_tz(void)
254 vdso_data
->tz_minuteswest
= sys_tz
.tz_minuteswest
;
255 vdso_data
->tz_dsttime
= sys_tz
.tz_dsttime
;