aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreentime Hu <greentime@andestech.com>2017-10-24 23:16:56 -0400
committerGreentime Hu <greentime@andestech.com>2018-02-21 21:44:33 -0500
commiteefacd1dfe38a3334d2e1c44fee0d7b0fdd5d252 (patch)
treeecc49e54c9a797a7f402c39da9a2d7f25cd1c104
parent1932fbe36e02f54223ac4c6779b92269ca8b9b60 (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.h24
-rw-r--r--arch/nds32/include/asm/vdso_datapage.h36
-rw-r--r--arch/nds32/include/asm/vdso_timer_info.h14
-rw-r--r--arch/nds32/kernel/vdso.c230
-rw-r--r--arch/nds32/kernel/vdso/Makefile82
-rw-r--r--arch/nds32/kernel/vdso/datapage.S21
-rwxr-xr-xarch/nds32/kernel/vdso/gen_vdso_offsets.sh15
-rw-r--r--arch/nds32/kernel/vdso/gettimeofday.c270
-rw-r--r--arch/nds32/kernel/vdso/note.S11
-rw-r--r--arch/nds32/kernel/vdso/sigreturn.S19
-rw-r--r--arch/nds32/kernel/vdso/vdso.S18
-rw-r--r--arch/nds32/kernel/vdso/vdso.lds.S76
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
11struct 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
4extern 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
9struct 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>
25extern struct cache_info L1_cache_info[2];
26extern char vdso_start, vdso_end;
27static unsigned long vdso_pages __ro_after_init;
28static unsigned long timer_mapping_base;
29
30struct 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 */
38static struct page *no_pages[] = { NULL };
39
40static union {
41 struct vdso_data data;
42 u8 page[PAGE_SIZE];
43} vdso_data_store __page_aligned_data;
44struct vdso_data *vdso_data = &vdso_data_store.data;
45static 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
55static 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
64static 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
92arch_initcall(vdso_init);
93
94unsigned 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
114int 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
191up_fail:
192 mm->context.vdso = NULL;
193 up_write(&mm->mmap_sem);
194 return ret;
195}
196
197static 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
203static 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
209void 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
226void 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
8obj-vdso := note.o datapage.o sigreturn.o gettimeofday.o
9
10# Build rules
11targets := $(obj-vdso) vdso.so vdso.so.dbg
12obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
13
14ccflags-y := -shared -fno-common -fno-builtin
15ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
16 $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
17ccflags-y += -fPIC -Wl,-shared -g
18
19# Disable gcov profiling for VDSO code
20GCOV_PROFILE := n
21
22
23obj-y += vdso.o
24extra-y += vdso.lds
25CPPFLAGS_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
41gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
42quiet_cmd_vdsosym = VDSOSYM $@
43define cmd_vdsosym
44 $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
45endef
46
47include/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
54sigreturn.o : sigreturn.S
55 $(call if_changed_dep,vdsoas)
56
57note.o : note.S
58 $(call if_changed_dep,vdsoas)
59
60datapage.o : datapage.S
61 $(call if_changed_dep,vdsoas)
62
63gettimeofday.o : gettimeofday.c FORCE
64 $(call if_changed_dep,vdsocc)
65
66# Actual build commands
67quiet_cmd_vdsold = VDSOL $@
68 cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
69quiet_cmd_vdsoas = VDSOA $@
70 cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
71quiet_cmd_vdsocc = VDSOA $@
72 cmd_vdsocc = $(CC) $(c_flags) -c -o $@ $<
73
74# Install commands for the unstripped file
75quiet_cmd_vdso_install = INSTALL $@
76 cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
77
78vdso.so: $(obj)/vdso.so.dbg
79 @mkdir -p $(MODLIB)/vdso
80 $(call cmd,vdso_install)
81
82vdso_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
7ENTRY(__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
13ENDPROC(__get_timerpage)
14
15ENTRY(__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
21ENDPROC(__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
13LC_ALL=C
14sed -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
19extern struct vdso_data *__get_datapage(void);
20extern struct vdso_data *__get_timerpage(void);
21
22static notrace unsigned int __vdso_read_begin(const struct vdso_data *vdata)
23{
24 u32 seq;
25repeat:
26 seq = READ_ONCE(vdata->seq_count);
27 if (seq & 1) {
28 cpu_relax();
29 goto repeat;
30 }
31 return seq;
32}
33
34static 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
44static 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
50static 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
66static 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
81static 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
103static 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
118static 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
136static 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
161notrace 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
193static 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
209notrace 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
231static 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
247notrace 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
9ELFNOTE_START(Linux, 0, "a")
10 .long LINUX_VERSION_CODE
11ELFNOTE_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
10ENTRY(__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
19ENDPROC(__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
13vdso_start:
14 .incbin "arch/nds32/kernel/vdso/vdso.so"
15 .balign PAGE_SIZE
16vdso_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
11OUTPUT_FORMAT("elf32-nds32le-linux", "elf32-nds32be-linux", "elf32-nds32le-linux")
12OUTPUT_ARCH(nds32)
13
14SECTIONS
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 */
50PHDRS
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 */
61VERSION
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 */
76VDSO_rt_sigtramp = __kernel_rt_sigreturn;