diff options
author | Greentime Hu <greentime@andestech.com> | 2017-10-24 23:16:56 -0400 |
---|---|---|
committer | Greentime Hu <greentime@andestech.com> | 2018-02-21 21:44:33 -0500 |
commit | eefacd1dfe38a3334d2e1c44fee0d7b0fdd5d252 (patch) | |
tree | ecc49e54c9a797a7f402c39da9a2d7f25cd1c104 | |
parent | 1932fbe36e02f54223ac4c6779b92269ca8b9b60 (diff) |
nds32: VDSO support
This patch adds VDSO support. The VDSO code is currently used for
sys_rt_sigreturn() and optimised gettimeofday() (using the SoC timer counter).
Signed-off-by: Vincent Chen <vincentc@andestech.com>
Signed-off-by: Greentime Hu <greentime@andestech.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
-rw-r--r-- | arch/nds32/include/asm/vdso.h | 24 | ||||
-rw-r--r-- | arch/nds32/include/asm/vdso_datapage.h | 36 | ||||
-rw-r--r-- | arch/nds32/include/asm/vdso_timer_info.h | 14 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso.c | 230 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/Makefile | 82 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/datapage.S | 21 | ||||
-rwxr-xr-x | arch/nds32/kernel/vdso/gen_vdso_offsets.sh | 15 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/gettimeofday.c | 270 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/note.S | 11 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/sigreturn.S | 19 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/vdso.S | 18 | ||||
-rw-r--r-- | arch/nds32/kernel/vdso/vdso.lds.S | 76 |
12 files changed, 816 insertions, 0 deletions
diff --git a/arch/nds32/include/asm/vdso.h b/arch/nds32/include/asm/vdso.h new file mode 100644 index 000000000000..af2c6afc2469 --- /dev/null +++ b/arch/nds32/include/asm/vdso.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * SPDX-License-Identifier: GPL-2.0 | ||
3 | * Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | */ | ||
5 | |||
6 | #ifndef __ASM_VDSO_H | ||
7 | #define __ASM_VDSO_H | ||
8 | |||
9 | #ifdef __KERNEL__ | ||
10 | |||
11 | #ifndef __ASSEMBLY__ | ||
12 | |||
13 | #include <generated/vdso-offsets.h> | ||
14 | |||
15 | #define VDSO_SYMBOL(base, name) \ | ||
16 | ({ \ | ||
17 | (unsigned long)(vdso_offset_##name + (unsigned long)(base)); \ | ||
18 | }) | ||
19 | |||
20 | #endif /* !__ASSEMBLY__ */ | ||
21 | |||
22 | #endif /* __KERNEL__ */ | ||
23 | |||
24 | #endif /* __ASM_VDSO_H */ | ||
diff --git a/arch/nds32/include/asm/vdso_datapage.h b/arch/nds32/include/asm/vdso_datapage.h new file mode 100644 index 000000000000..79db5a12ca5e --- /dev/null +++ b/arch/nds32/include/asm/vdso_datapage.h | |||
@@ -0,0 +1,36 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2012 ARM Limited | ||
3 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | #ifndef __ASM_VDSO_DATAPAGE_H | ||
5 | #define __ASM_VDSO_DATAPAGE_H | ||
6 | |||
7 | #ifdef __KERNEL__ | ||
8 | |||
9 | #ifndef __ASSEMBLY__ | ||
10 | |||
11 | struct vdso_data { | ||
12 | bool cycle_count_down; /* timer cyclye counter is decrease with time */ | ||
13 | u32 cycle_count_offset; /* offset of timer cycle counter register */ | ||
14 | u32 seq_count; /* sequence count - odd during updates */ | ||
15 | u32 xtime_coarse_sec; /* coarse time */ | ||
16 | u32 xtime_coarse_nsec; | ||
17 | |||
18 | u32 wtm_clock_sec; /* wall to monotonic offset */ | ||
19 | u32 wtm_clock_nsec; | ||
20 | u32 xtime_clock_sec; /* CLOCK_REALTIME - seconds */ | ||
21 | u32 cs_mult; /* clocksource multiplier */ | ||
22 | u32 cs_shift; /* Cycle to nanosecond divisor (power of two) */ | ||
23 | |||
24 | u64 cs_cycle_last; /* last cycle value */ | ||
25 | u64 cs_mask; /* clocksource mask */ | ||
26 | |||
27 | u64 xtime_clock_nsec; /* CLOCK_REALTIME sub-ns base */ | ||
28 | u32 tz_minuteswest; /* timezone info for gettimeofday(2) */ | ||
29 | u32 tz_dsttime; | ||
30 | }; | ||
31 | |||
32 | #endif /* !__ASSEMBLY__ */ | ||
33 | |||
34 | #endif /* __KERNEL__ */ | ||
35 | |||
36 | #endif /* __ASM_VDSO_DATAPAGE_H */ | ||
diff --git a/arch/nds32/include/asm/vdso_timer_info.h b/arch/nds32/include/asm/vdso_timer_info.h new file mode 100644 index 000000000000..50ba117cff12 --- /dev/null +++ b/arch/nds32/include/asm/vdso_timer_info.h | |||
@@ -0,0 +1,14 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | extern struct timer_info_t timer_info; | ||
5 | #define EMPTY_VALUE ~(0UL) | ||
6 | #define EMPTY_TIMER_MAPPING EMPTY_VALUE | ||
7 | #define EMPTY_REG_OFFSET EMPTY_VALUE | ||
8 | |||
9 | struct timer_info_t | ||
10 | { | ||
11 | bool cycle_count_down; | ||
12 | unsigned long mapping_base; | ||
13 | unsigned long cycle_count_reg_offset; | ||
14 | }; | ||
diff --git a/arch/nds32/kernel/vdso.c b/arch/nds32/kernel/vdso.c new file mode 100644 index 000000000000..f1198d7a5654 --- /dev/null +++ b/arch/nds32/kernel/vdso.c | |||
@@ -0,0 +1,230 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2012 ARM Limited | ||
3 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | |||
5 | #include <linux/cache.h> | ||
6 | #include <linux/clocksource.h> | ||
7 | #include <linux/elf.h> | ||
8 | #include <linux/err.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/gfp.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/signal.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/timekeeper_internal.h> | ||
17 | #include <linux/vmalloc.h> | ||
18 | #include <linux/random.h> | ||
19 | |||
20 | #include <asm/cacheflush.h> | ||
21 | #include <asm/vdso.h> | ||
22 | #include <asm/vdso_datapage.h> | ||
23 | #include <asm/vdso_timer_info.h> | ||
24 | #include <asm/cache_info.h> | ||
25 | extern struct cache_info L1_cache_info[2]; | ||
26 | extern char vdso_start, vdso_end; | ||
27 | static unsigned long vdso_pages __ro_after_init; | ||
28 | static unsigned long timer_mapping_base; | ||
29 | |||
30 | struct timer_info_t timer_info = { | ||
31 | .cycle_count_down = true, | ||
32 | .mapping_base = EMPTY_TIMER_MAPPING, | ||
33 | .cycle_count_reg_offset = EMPTY_REG_OFFSET | ||
34 | }; | ||
35 | /* | ||
36 | * The vDSO data page. | ||
37 | */ | ||
38 | static struct page *no_pages[] = { NULL }; | ||
39 | |||
40 | static union { | ||
41 | struct vdso_data data; | ||
42 | u8 page[PAGE_SIZE]; | ||
43 | } vdso_data_store __page_aligned_data; | ||
44 | struct vdso_data *vdso_data = &vdso_data_store.data; | ||
45 | static struct vm_special_mapping vdso_spec[2] __ro_after_init = { | ||
46 | { | ||
47 | .name = "[vvar]", | ||
48 | .pages = no_pages, | ||
49 | }, | ||
50 | { | ||
51 | .name = "[vdso]", | ||
52 | }, | ||
53 | }; | ||
54 | |||
55 | static void get_timer_node_info(void) | ||
56 | { | ||
57 | timer_mapping_base = timer_info.mapping_base; | ||
58 | vdso_data->cycle_count_offset = | ||
59 | timer_info.cycle_count_reg_offset; | ||
60 | vdso_data->cycle_count_down = | ||
61 | timer_info.cycle_count_down; | ||
62 | } | ||
63 | |||
64 | static int __init vdso_init(void) | ||
65 | { | ||
66 | int i; | ||
67 | struct page **vdso_pagelist; | ||
68 | |||
69 | if (memcmp(&vdso_start, "\177ELF", 4)) { | ||
70 | pr_err("vDSO is not a valid ELF object!\n"); | ||
71 | return -EINVAL; | ||
72 | } | ||
73 | /* Creat a timer io mapping to get clock cycles counter */ | ||
74 | get_timer_node_info(); | ||
75 | |||
76 | vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT; | ||
77 | pr_info("vdso: %ld pages (%ld code @ %p, %ld data @ %p)\n", | ||
78 | vdso_pages + 1, vdso_pages, &vdso_start, 1L, vdso_data); | ||
79 | |||
80 | /* Allocate the vDSO pagelist */ | ||
81 | vdso_pagelist = kcalloc(vdso_pages, sizeof(struct page *), GFP_KERNEL); | ||
82 | if (vdso_pagelist == NULL) | ||
83 | return -ENOMEM; | ||
84 | |||
85 | for (i = 0; i < vdso_pages; i++) | ||
86 | vdso_pagelist[i] = virt_to_page(&vdso_start + i * PAGE_SIZE); | ||
87 | vdso_spec[1].pages = &vdso_pagelist[0]; | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | arch_initcall(vdso_init); | ||
93 | |||
94 | unsigned long inline vdso_random_addr(unsigned long vdso_mapping_len) | ||
95 | { | ||
96 | unsigned long start = current->mm->mmap_base, end, offset, addr; | ||
97 | start = PAGE_ALIGN(start); | ||
98 | |||
99 | /* Round the lowest possible end address up to a PMD boundary. */ | ||
100 | end = (start + vdso_mapping_len + PMD_SIZE - 1) & PMD_MASK; | ||
101 | if (end >= TASK_SIZE) | ||
102 | end = TASK_SIZE; | ||
103 | end -= vdso_mapping_len; | ||
104 | |||
105 | if (end > start) { | ||
106 | offset = get_random_int() % (((end - start) >> PAGE_SHIFT) + 1); | ||
107 | addr = start + (offset << PAGE_SHIFT); | ||
108 | } else { | ||
109 | addr = start; | ||
110 | } | ||
111 | return addr; | ||
112 | } | ||
113 | |||
114 | int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) | ||
115 | { | ||
116 | struct mm_struct *mm = current->mm; | ||
117 | unsigned long vdso_base, vdso_text_len, vdso_mapping_len; | ||
118 | struct vm_area_struct *vma; | ||
119 | unsigned long addr = 0; | ||
120 | pgprot_t prot; | ||
121 | int ret, vvar_page_num = 2; | ||
122 | |||
123 | vdso_text_len = vdso_pages << PAGE_SHIFT; | ||
124 | |||
125 | if(timer_mapping_base == EMPTY_VALUE) | ||
126 | vvar_page_num = 1; | ||
127 | /* Be sure to map the data page */ | ||
128 | vdso_mapping_len = vdso_text_len + vvar_page_num * PAGE_SIZE; | ||
129 | #ifdef CONFIG_CPU_CACHE_ALIASING | ||
130 | vdso_mapping_len += L1_cache_info[DCACHE].aliasing_num - 1; | ||
131 | #endif | ||
132 | |||
133 | if (down_write_killable(&mm->mmap_sem)) | ||
134 | return -EINTR; | ||
135 | |||
136 | addr = vdso_random_addr(vdso_mapping_len); | ||
137 | vdso_base = get_unmapped_area(NULL, addr, vdso_mapping_len, 0, 0); | ||
138 | if (IS_ERR_VALUE(vdso_base)) { | ||
139 | ret = vdso_base; | ||
140 | goto up_fail; | ||
141 | } | ||
142 | |||
143 | #ifdef CONFIG_CPU_CACHE_ALIASING | ||
144 | { | ||
145 | unsigned int aliasing_mask = | ||
146 | L1_cache_info[DCACHE].aliasing_mask; | ||
147 | unsigned int page_colour_ofs; | ||
148 | page_colour_ofs = ((unsigned int)vdso_data & aliasing_mask) - | ||
149 | (vdso_base & aliasing_mask); | ||
150 | vdso_base += page_colour_ofs & aliasing_mask; | ||
151 | } | ||
152 | #endif | ||
153 | |||
154 | vma = _install_special_mapping(mm, vdso_base, vvar_page_num * PAGE_SIZE, | ||
155 | VM_READ | VM_MAYREAD, &vdso_spec[0]); | ||
156 | if (IS_ERR(vma)) { | ||
157 | ret = PTR_ERR(vma); | ||
158 | goto up_fail; | ||
159 | } | ||
160 | |||
161 | /*Map vdata to user space */ | ||
162 | ret = io_remap_pfn_range(vma, vdso_base, | ||
163 | virt_to_phys(vdso_data) >> PAGE_SHIFT, | ||
164 | PAGE_SIZE, vma->vm_page_prot); | ||
165 | if (ret) | ||
166 | goto up_fail; | ||
167 | |||
168 | /*Map timer to user space */ | ||
169 | vdso_base += PAGE_SIZE; | ||
170 | prot = __pgprot(_PAGE_V | _PAGE_M_UR_KR | _PAGE_D | _PAGE_C_DEV); | ||
171 | ret = io_remap_pfn_range(vma, vdso_base, timer_mapping_base >> PAGE_SHIFT, | ||
172 | PAGE_SIZE, prot); | ||
173 | if (ret) | ||
174 | goto up_fail; | ||
175 | |||
176 | /*Map vdso to user space */ | ||
177 | vdso_base += PAGE_SIZE; | ||
178 | mm->context.vdso = (void *)vdso_base; | ||
179 | vma = _install_special_mapping(mm, vdso_base, vdso_text_len, | ||
180 | VM_READ | VM_EXEC | | ||
181 | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, | ||
182 | &vdso_spec[1]); | ||
183 | if (IS_ERR(vma)) { | ||
184 | ret = PTR_ERR(vma); | ||
185 | goto up_fail; | ||
186 | } | ||
187 | |||
188 | up_write(&mm->mmap_sem); | ||
189 | return 0; | ||
190 | |||
191 | up_fail: | ||
192 | mm->context.vdso = NULL; | ||
193 | up_write(&mm->mmap_sem); | ||
194 | return ret; | ||
195 | } | ||
196 | |||
197 | static void vdso_write_begin(struct vdso_data *vdata) | ||
198 | { | ||
199 | ++vdso_data->seq_count; | ||
200 | smp_wmb(); /* Pairs with smp_rmb in vdso_read_retry */ | ||
201 | } | ||
202 | |||
203 | static void vdso_write_end(struct vdso_data *vdata) | ||
204 | { | ||
205 | smp_wmb(); /* Pairs with smp_rmb in vdso_read_begin */ | ||
206 | ++vdso_data->seq_count; | ||
207 | } | ||
208 | |||
209 | void update_vsyscall(struct timekeeper *tk) | ||
210 | { | ||
211 | vdso_write_begin(vdso_data); | ||
212 | vdso_data->cs_mask = tk->tkr_mono.mask; | ||
213 | vdso_data->cs_mult = tk->tkr_mono.mult; | ||
214 | vdso_data->cs_shift = tk->tkr_mono.shift; | ||
215 | vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last; | ||
216 | vdso_data->wtm_clock_sec = tk->wall_to_monotonic.tv_sec; | ||
217 | vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; | ||
218 | vdso_data->xtime_clock_sec = tk->xtime_sec; | ||
219 | vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec; | ||
220 | vdso_data->xtime_coarse_sec = tk->xtime_sec; | ||
221 | vdso_data->xtime_coarse_nsec = tk->tkr_mono.xtime_nsec >> | ||
222 | tk->tkr_mono.shift; | ||
223 | vdso_write_end(vdso_data); | ||
224 | } | ||
225 | |||
226 | void update_vsyscall_tz(void) | ||
227 | { | ||
228 | vdso_data->tz_minuteswest = sys_tz.tz_minuteswest; | ||
229 | vdso_data->tz_dsttime = sys_tz.tz_dsttime; | ||
230 | } | ||
diff --git a/arch/nds32/kernel/vdso/Makefile b/arch/nds32/kernel/vdso/Makefile new file mode 100644 index 000000000000..e6c50a701313 --- /dev/null +++ b/arch/nds32/kernel/vdso/Makefile | |||
@@ -0,0 +1,82 @@ | |||
1 | # | ||
2 | # Building a vDSO image for AArch64. | ||
3 | # | ||
4 | # Author: Will Deacon <will.deacon@arm.com> | ||
5 | # Heavily based on the vDSO Makefiles for other archs. | ||
6 | # | ||
7 | |||
8 | obj-vdso := note.o datapage.o sigreturn.o gettimeofday.o | ||
9 | |||
10 | # Build rules | ||
11 | targets := $(obj-vdso) vdso.so vdso.so.dbg | ||
12 | obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) | ||
13 | |||
14 | ccflags-y := -shared -fno-common -fno-builtin | ||
15 | ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \ | ||
16 | $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) | ||
17 | ccflags-y += -fPIC -Wl,-shared -g | ||
18 | |||
19 | # Disable gcov profiling for VDSO code | ||
20 | GCOV_PROFILE := n | ||
21 | |||
22 | |||
23 | obj-y += vdso.o | ||
24 | extra-y += vdso.lds | ||
25 | CPPFLAGS_vdso.lds += -P -C -U$(ARCH) | ||
26 | |||
27 | # Force dependency | ||
28 | $(obj)/vdso.o : $(obj)/vdso.so | ||
29 | |||
30 | # Link rule for the .so file, .lds has to be first | ||
31 | $(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) | ||
32 | $(call if_changed,vdsold) | ||
33 | |||
34 | |||
35 | # Strip rule for the .so file | ||
36 | $(obj)/%.so: OBJCOPYFLAGS := -S | ||
37 | $(obj)/%.so: $(obj)/%.so.dbg FORCE | ||
38 | $(call if_changed,objcopy) | ||
39 | |||
40 | # Generate VDSO offsets using helper script | ||
41 | gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh | ||
42 | quiet_cmd_vdsosym = VDSOSYM $@ | ||
43 | define cmd_vdsosym | ||
44 | $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ | ||
45 | endef | ||
46 | |||
47 | include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE | ||
48 | $(call if_changed,vdsosym) | ||
49 | |||
50 | |||
51 | |||
52 | # Assembly rules for the .S files | ||
53 | |||
54 | sigreturn.o : sigreturn.S | ||
55 | $(call if_changed_dep,vdsoas) | ||
56 | |||
57 | note.o : note.S | ||
58 | $(call if_changed_dep,vdsoas) | ||
59 | |||
60 | datapage.o : datapage.S | ||
61 | $(call if_changed_dep,vdsoas) | ||
62 | |||
63 | gettimeofday.o : gettimeofday.c FORCE | ||
64 | $(call if_changed_dep,vdsocc) | ||
65 | |||
66 | # Actual build commands | ||
67 | quiet_cmd_vdsold = VDSOL $@ | ||
68 | cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@ | ||
69 | quiet_cmd_vdsoas = VDSOA $@ | ||
70 | cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $< | ||
71 | quiet_cmd_vdsocc = VDSOA $@ | ||
72 | cmd_vdsocc = $(CC) $(c_flags) -c -o $@ $< | ||
73 | |||
74 | # Install commands for the unstripped file | ||
75 | quiet_cmd_vdso_install = INSTALL $@ | ||
76 | cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@ | ||
77 | |||
78 | vdso.so: $(obj)/vdso.so.dbg | ||
79 | @mkdir -p $(MODLIB)/vdso | ||
80 | $(call cmd,vdso_install) | ||
81 | |||
82 | vdso_install: vdso.so | ||
diff --git a/arch/nds32/kernel/vdso/datapage.S b/arch/nds32/kernel/vdso/datapage.S new file mode 100644 index 000000000000..4a62c3cab1c8 --- /dev/null +++ b/arch/nds32/kernel/vdso/datapage.S | |||
@@ -0,0 +1,21 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/linkage.h> | ||
5 | #include <asm/page.h> | ||
6 | |||
7 | ENTRY(__get_timerpage) | ||
8 | sethi $r0, hi20(. + PAGE_SIZE + 8) | ||
9 | ori $r0, $r0, lo12(. + PAGE_SIZE + 4) | ||
10 | mfusr $r1, $pc | ||
11 | sub $r0, $r1, $r0 | ||
12 | ret | ||
13 | ENDPROC(__get_timerpage) | ||
14 | |||
15 | ENTRY(__get_datapage) | ||
16 | sethi $r0, hi20(. + 2*PAGE_SIZE + 8) | ||
17 | ori $r0, $r0, lo12(. + 2*PAGE_SIZE + 4) | ||
18 | mfusr $r1, $pc | ||
19 | sub $r0, $r1, $r0 | ||
20 | ret | ||
21 | ENDPROC(__get_datapage) | ||
diff --git a/arch/nds32/kernel/vdso/gen_vdso_offsets.sh b/arch/nds32/kernel/vdso/gen_vdso_offsets.sh new file mode 100755 index 000000000000..01924ff071ad --- /dev/null +++ b/arch/nds32/kernel/vdso/gen_vdso_offsets.sh | |||
@@ -0,0 +1,15 @@ | |||
1 | #!/bin/sh | ||
2 | |||
3 | # | ||
4 | # Match symbols in the DSO that look like VDSO_*; produce a header file | ||
5 | # of constant offsets into the shared object. | ||
6 | # | ||
7 | # Doing this inside the Makefile will break the $(filter-out) function, | ||
8 | # causing Kbuild to rebuild the vdso-offsets header file every time. | ||
9 | # | ||
10 | # Author: Will Deacon <will.deacon@arm.com | ||
11 | # | ||
12 | |||
13 | LC_ALL=C | ||
14 | sed -n -e 's/^00*/0/' -e \ | ||
15 | 's/^\([0-9a-fA-F]*\) . VDSO_\([a-zA-Z0-9_]*\)$/\#define vdso_offset_\2\t0x\1/p' | ||
diff --git a/arch/nds32/kernel/vdso/gettimeofday.c b/arch/nds32/kernel/vdso/gettimeofday.c new file mode 100644 index 000000000000..038721af40e3 --- /dev/null +++ b/arch/nds32/kernel/vdso/gettimeofday.c | |||
@@ -0,0 +1,270 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
3 | |||
4 | #include <linux/compiler.h> | ||
5 | #include <linux/hrtimer.h> | ||
6 | #include <linux/time.h> | ||
7 | #include <asm/io.h> | ||
8 | #include <asm/barrier.h> | ||
9 | #include <asm/bug.h> | ||
10 | #include <asm/page.h> | ||
11 | #include <asm/unistd.h> | ||
12 | #include <asm/vdso_datapage.h> | ||
13 | #include <asm/vdso_timer_info.h> | ||
14 | #include <asm/asm-offsets.h> | ||
15 | |||
16 | #define X(x) #x | ||
17 | #define Y(x) X(x) | ||
18 | |||
19 | extern struct vdso_data *__get_datapage(void); | ||
20 | extern struct vdso_data *__get_timerpage(void); | ||
21 | |||
22 | static notrace unsigned int __vdso_read_begin(const struct vdso_data *vdata) | ||
23 | { | ||
24 | u32 seq; | ||
25 | repeat: | ||
26 | seq = READ_ONCE(vdata->seq_count); | ||
27 | if (seq & 1) { | ||
28 | cpu_relax(); | ||
29 | goto repeat; | ||
30 | } | ||
31 | return seq; | ||
32 | } | ||
33 | |||
34 | static notrace unsigned int vdso_read_begin(const struct vdso_data *vdata) | ||
35 | { | ||
36 | unsigned int seq; | ||
37 | |||
38 | seq = __vdso_read_begin(vdata); | ||
39 | |||
40 | smp_rmb(); /* Pairs with smp_wmb in vdso_write_end */ | ||
41 | return seq; | ||
42 | } | ||
43 | |||
44 | static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start) | ||
45 | { | ||
46 | smp_rmb(); /* Pairs with smp_wmb in vdso_write_begin */ | ||
47 | return vdata->seq_count != start; | ||
48 | } | ||
49 | |||
50 | static notrace long clock_gettime_fallback(clockid_t _clkid, | ||
51 | struct timespec *_ts) | ||
52 | { | ||
53 | register struct timespec *ts asm("$r1") = _ts; | ||
54 | register clockid_t clkid asm("$r0") = _clkid; | ||
55 | register long ret asm("$r0"); | ||
56 | |||
57 | asm volatile ("movi $r15, %3\n" | ||
58 | "syscall 0x0\n" | ||
59 | :"=r" (ret) | ||
60 | :"r"(clkid), "r"(ts), "i"(__NR_clock_gettime) | ||
61 | :"$r15", "memory"); | ||
62 | |||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | static notrace int do_realtime_coarse(struct timespec *ts, | ||
67 | struct vdso_data *vdata) | ||
68 | { | ||
69 | u32 seq; | ||
70 | |||
71 | do { | ||
72 | seq = vdso_read_begin(vdata); | ||
73 | |||
74 | ts->tv_sec = vdata->xtime_coarse_sec; | ||
75 | ts->tv_nsec = vdata->xtime_coarse_nsec; | ||
76 | |||
77 | } while (vdso_read_retry(vdata, seq)); | ||
78 | return 0; | ||
79 | } | ||
80 | |||
81 | static notrace int do_monotonic_coarse(struct timespec *ts, | ||
82 | struct vdso_data *vdata) | ||
83 | { | ||
84 | struct timespec tomono; | ||
85 | u32 seq; | ||
86 | |||
87 | do { | ||
88 | seq = vdso_read_begin(vdata); | ||
89 | |||
90 | ts->tv_sec = vdata->xtime_coarse_sec; | ||
91 | ts->tv_nsec = vdata->xtime_coarse_nsec; | ||
92 | |||
93 | tomono.tv_sec = vdata->wtm_clock_sec; | ||
94 | tomono.tv_nsec = vdata->wtm_clock_nsec; | ||
95 | |||
96 | } while (vdso_read_retry(vdata, seq)); | ||
97 | |||
98 | ts->tv_sec += tomono.tv_sec; | ||
99 | timespec_add_ns(ts, tomono.tv_nsec); | ||
100 | return 0; | ||
101 | } | ||
102 | |||
103 | static notrace inline u64 vgetsns(struct vdso_data *vdso) | ||
104 | { | ||
105 | u32 cycle_now; | ||
106 | u32 cycle_delta; | ||
107 | u32 *timer_cycle_base; | ||
108 | |||
109 | timer_cycle_base = | ||
110 | (u32 *) ((char *)__get_timerpage() + vdso->cycle_count_offset); | ||
111 | cycle_now = readl_relaxed(timer_cycle_base); | ||
112 | if (true == vdso->cycle_count_down) | ||
113 | cycle_now = ~(*timer_cycle_base); | ||
114 | cycle_delta = cycle_now - (u32) vdso->cs_cycle_last; | ||
115 | return ((u64) cycle_delta & vdso->cs_mask) * vdso->cs_mult; | ||
116 | } | ||
117 | |||
118 | static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata) | ||
119 | { | ||
120 | unsigned count; | ||
121 | u64 ns; | ||
122 | do { | ||
123 | count = vdso_read_begin(vdata); | ||
124 | ts->tv_sec = vdata->xtime_clock_sec; | ||
125 | ns = vdata->xtime_clock_nsec; | ||
126 | ns += vgetsns(vdata); | ||
127 | ns >>= vdata->cs_shift; | ||
128 | } while (vdso_read_retry(vdata, count)); | ||
129 | |||
130 | ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns); | ||
131 | ts->tv_nsec = ns; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata) | ||
137 | { | ||
138 | struct timespec tomono; | ||
139 | u64 nsecs; | ||
140 | u32 seq; | ||
141 | |||
142 | do { | ||
143 | seq = vdso_read_begin(vdata); | ||
144 | |||
145 | ts->tv_sec = vdata->xtime_clock_sec; | ||
146 | nsecs = vdata->xtime_clock_nsec; | ||
147 | nsecs += vgetsns(vdata); | ||
148 | nsecs >>= vdata->cs_shift; | ||
149 | |||
150 | tomono.tv_sec = vdata->wtm_clock_sec; | ||
151 | tomono.tv_nsec = vdata->wtm_clock_nsec; | ||
152 | |||
153 | } while (vdso_read_retry(vdata, seq)); | ||
154 | |||
155 | ts->tv_sec += tomono.tv_sec; | ||
156 | ts->tv_nsec = 0; | ||
157 | timespec_add_ns(ts, nsecs + tomono.tv_nsec); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts) | ||
162 | { | ||
163 | struct vdso_data *vdata; | ||
164 | int ret = -1; | ||
165 | |||
166 | vdata = __get_datapage(); | ||
167 | if (vdata->cycle_count_offset == EMPTY_REG_OFFSET) | ||
168 | return clock_gettime_fallback(clkid, ts); | ||
169 | |||
170 | switch (clkid) { | ||
171 | case CLOCK_REALTIME_COARSE: | ||
172 | ret = do_realtime_coarse(ts, vdata); | ||
173 | break; | ||
174 | case CLOCK_MONOTONIC_COARSE: | ||
175 | ret = do_monotonic_coarse(ts, vdata); | ||
176 | break; | ||
177 | case CLOCK_REALTIME: | ||
178 | ret = do_realtime(ts, vdata); | ||
179 | break; | ||
180 | case CLOCK_MONOTONIC: | ||
181 | ret = do_monotonic(ts, vdata); | ||
182 | break; | ||
183 | default: | ||
184 | break; | ||
185 | } | ||
186 | |||
187 | if (ret) | ||
188 | ret = clock_gettime_fallback(clkid, ts); | ||
189 | |||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | static notrace int clock_getres_fallback(clockid_t _clk_id, | ||
194 | struct timespec *_res) | ||
195 | { | ||
196 | register clockid_t clk_id asm("$r0") = _clk_id; | ||
197 | register struct timespec *res asm("$r1") = _res; | ||
198 | register int ret asm("$r0"); | ||
199 | |||
200 | asm volatile ("movi $r15, %3\n" | ||
201 | "syscall 0x0\n" | ||
202 | :"=r" (ret) | ||
203 | :"r"(clk_id), "r"(res), "i"(__NR_clock_getres) | ||
204 | :"$r15", "memory"); | ||
205 | |||
206 | return ret; | ||
207 | } | ||
208 | |||
209 | notrace int __vdso_clock_getres(clockid_t clk_id, struct timespec *res) | ||
210 | { | ||
211 | if (res == NULL) | ||
212 | return 0; | ||
213 | switch (clk_id) { | ||
214 | case CLOCK_REALTIME: | ||
215 | case CLOCK_MONOTONIC: | ||
216 | case CLOCK_MONOTONIC_RAW: | ||
217 | res->tv_sec = 0; | ||
218 | res->tv_nsec = CLOCK_REALTIME_RES; | ||
219 | break; | ||
220 | case CLOCK_REALTIME_COARSE: | ||
221 | case CLOCK_MONOTONIC_COARSE: | ||
222 | res->tv_sec = 0; | ||
223 | res->tv_nsec = CLOCK_COARSE_RES; | ||
224 | break; | ||
225 | default: | ||
226 | return clock_getres_fallback(clk_id, res); | ||
227 | } | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static notrace inline int gettimeofday_fallback(struct timeval *_tv, | ||
232 | struct timezone *_tz) | ||
233 | { | ||
234 | register struct timeval *tv asm("$r0") = _tv; | ||
235 | register struct timezone *tz asm("$r1") = _tz; | ||
236 | register int ret asm("$r0"); | ||
237 | |||
238 | asm volatile ("movi $r15, %3\n" | ||
239 | "syscall 0x0\n" | ||
240 | :"=r" (ret) | ||
241 | :"r"(tv), "r"(tz), "i"(__NR_gettimeofday) | ||
242 | :"$r15", "memory"); | ||
243 | |||
244 | return ret; | ||
245 | } | ||
246 | |||
247 | notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz) | ||
248 | { | ||
249 | struct timespec ts; | ||
250 | struct vdso_data *vdata; | ||
251 | int ret; | ||
252 | |||
253 | vdata = __get_datapage(); | ||
254 | |||
255 | if (vdata->cycle_count_offset == EMPTY_REG_OFFSET) | ||
256 | return gettimeofday_fallback(tv, tz); | ||
257 | |||
258 | ret = do_realtime(&ts, vdata); | ||
259 | |||
260 | if (tv) { | ||
261 | tv->tv_sec = ts.tv_sec; | ||
262 | tv->tv_usec = ts.tv_nsec / 1000; | ||
263 | } | ||
264 | if (tz) { | ||
265 | tz->tz_minuteswest = vdata->tz_minuteswest; | ||
266 | tz->tz_dsttime = vdata->tz_dsttime; | ||
267 | } | ||
268 | |||
269 | return ret; | ||
270 | } | ||
diff --git a/arch/nds32/kernel/vdso/note.S b/arch/nds32/kernel/vdso/note.S new file mode 100644 index 000000000000..0aeaa19b05f0 --- /dev/null +++ b/arch/nds32/kernel/vdso/note.S | |||
@@ -0,0 +1,11 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2012 ARM Limited | ||
3 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | |||
5 | #include <linux/uts.h> | ||
6 | #include <linux/version.h> | ||
7 | #include <linux/elfnote.h> | ||
8 | |||
9 | ELFNOTE_START(Linux, 0, "a") | ||
10 | .long LINUX_VERSION_CODE | ||
11 | ELFNOTE_END | ||
diff --git a/arch/nds32/kernel/vdso/sigreturn.S b/arch/nds32/kernel/vdso/sigreturn.S new file mode 100644 index 000000000000..67e4d1d1612a --- /dev/null +++ b/arch/nds32/kernel/vdso/sigreturn.S | |||
@@ -0,0 +1,19 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2012 ARM Limited | ||
3 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | |||
5 | #include <linux/linkage.h> | ||
6 | #include <asm/unistd.h> | ||
7 | |||
8 | .text | ||
9 | |||
10 | ENTRY(__kernel_rt_sigreturn) | ||
11 | .cfi_startproc | ||
12 | movi $r15, __NR_rt_sigreturn | ||
13 | /* | ||
14 | * The SWID of syscall should be __NR_rt_sigreturn to synchronize | ||
15 | * the unwinding scheme in gcc | ||
16 | */ | ||
17 | syscall __NR_rt_sigreturn | ||
18 | .cfi_endproc | ||
19 | ENDPROC(__kernel_rt_sigreturn) | ||
diff --git a/arch/nds32/kernel/vdso/vdso.S b/arch/nds32/kernel/vdso/vdso.S new file mode 100644 index 000000000000..16737c11e55b --- /dev/null +++ b/arch/nds32/kernel/vdso/vdso.S | |||
@@ -0,0 +1,18 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | // Copyright (C) 2012 ARM Limited | ||
3 | // Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | |||
5 | #include <linux/init.h> | ||
6 | #include <linux/linkage.h> | ||
7 | #include <linux/const.h> | ||
8 | #include <asm/page.h> | ||
9 | |||
10 | .globl vdso_start, vdso_end | ||
11 | .section .rodata | ||
12 | .balign PAGE_SIZE | ||
13 | vdso_start: | ||
14 | .incbin "arch/nds32/kernel/vdso/vdso.so" | ||
15 | .balign PAGE_SIZE | ||
16 | vdso_end: | ||
17 | |||
18 | .previous | ||
diff --git a/arch/nds32/kernel/vdso/vdso.lds.S b/arch/nds32/kernel/vdso/vdso.lds.S new file mode 100644 index 000000000000..36630352ee02 --- /dev/null +++ b/arch/nds32/kernel/vdso/vdso.lds.S | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * SPDX-License-Identifier: GPL-2.0 | ||
3 | * Copyright (C) 2005-2017 Andes Technology Corporation | ||
4 | */ | ||
5 | |||
6 | |||
7 | #include <linux/const.h> | ||
8 | #include <asm/page.h> | ||
9 | #include <asm/vdso.h> | ||
10 | |||
11 | OUTPUT_FORMAT("elf32-nds32le-linux", "elf32-nds32be-linux", "elf32-nds32le-linux") | ||
12 | OUTPUT_ARCH(nds32) | ||
13 | |||
14 | SECTIONS | ||
15 | { | ||
16 | . = SIZEOF_HEADERS; | ||
17 | |||
18 | .hash : { *(.hash) } :text | ||
19 | .gnu.hash : { *(.gnu.hash) } | ||
20 | .dynsym : { *(.dynsym) } | ||
21 | .dynstr : { *(.dynstr) } | ||
22 | .gnu.version : { *(.gnu.version) } | ||
23 | .gnu.version_d : { *(.gnu.version_d) } | ||
24 | .gnu.version_r : { *(.gnu.version_r) } | ||
25 | |||
26 | .note : { *(.note.*) } :text :note | ||
27 | |||
28 | |||
29 | .text : { *(.text*) } :text | ||
30 | |||
31 | .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr | ||
32 | .eh_frame : { KEEP (*(.eh_frame)) } :text | ||
33 | |||
34 | .dynamic : { *(.dynamic) } :text :dynamic | ||
35 | |||
36 | .rodata : { *(.rodata*) } :text | ||
37 | |||
38 | |||
39 | /DISCARD/ : { | ||
40 | *(.note.GNU-stack) | ||
41 | *(.data .data.* .gnu.linkonce.d.* .sdata*) | ||
42 | *(.bss .sbss .dynbss .dynsbss) | ||
43 | } | ||
44 | } | ||
45 | |||
46 | /* | ||
47 | * We must supply the ELF program headers explicitly to get just one | ||
48 | * PT_LOAD segment, and set the flags explicitly to make segments read-only. | ||
49 | */ | ||
50 | PHDRS | ||
51 | { | ||
52 | text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */ | ||
53 | dynamic PT_DYNAMIC FLAGS(4); /* PF_R */ | ||
54 | note PT_NOTE FLAGS(4); /* PF_R */ | ||
55 | eh_frame_hdr PT_GNU_EH_FRAME; | ||
56 | } | ||
57 | |||
58 | /* | ||
59 | * This controls what symbols we export from the DSO. | ||
60 | */ | ||
61 | VERSION | ||
62 | { | ||
63 | LINUX_4 { | ||
64 | global: | ||
65 | __kernel_rt_sigreturn; | ||
66 | __vdso_gettimeofday; | ||
67 | __vdso_clock_getres; | ||
68 | __vdso_clock_gettime; | ||
69 | local: *; | ||
70 | }; | ||
71 | } | ||
72 | |||
73 | /* | ||
74 | * Make the rt_sigreturn code visible to the kernel. | ||
75 | */ | ||
76 | VDSO_rt_sigtramp = __kernel_rt_sigreturn; | ||