8cf13af1323cde6d4f6e31c4dbf53dfe28e8aee8
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / arch / arm / vdso / vgettimeofday.c
1 /*
2 * Userspace implementations of gettimeofday() and friends.
3 *
4 * Copyright (C) 2017 Cavium, Inc.
5 * Copyright (C) 2015 Mentor Graphics Corporation
6 * Copyright (C) 2012 ARM Limited
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Author: Will Deacon <will.deacon@arm.com>
21 * Rewriten from arch64 version into C by: Andrew Pinski <apinski@cavium.com>
22 * Reworked and rebased over arm version by: Mark Salyzyn <salyzyn@android.com>
23 */
24
25 #include <linux/compiler.h>
26 #include <linux/hrtimer.h>
27 #include <linux/time.h>
28 #include <asm/arch_timer.h>
29 #include <asm/barrier.h>
30 #include <asm/bug.h>
31 #include <asm/page.h>
32 #include <asm/unistd.h>
33 #include <asm/vdso_datapage.h>
34
35 #ifndef CONFIG_AEABI
36 #error This code depends on AEABI system call conventions
37 #endif
38
39 extern struct vdso_data *__get_datapage(void);
40
41 static notrace u32 vdso_read_begin(const struct vdso_data *vdata)
42 {
43 u32 seq;
44
45 do {
46 seq = READ_ONCE(vdata->tb_seq_count);
47
48 if ((seq & 1) == 0)
49 break;
50
51 cpu_relax();
52 } while (true);
53
54 smp_rmb(); /* Pairs with second smp_wmb in update_vsyscall */
55 return seq;
56 }
57
58 static notrace int vdso_read_retry(const struct vdso_data *vdata, u32 start)
59 {
60 u32 seq;
61
62 smp_rmb(); /* Pairs with first smp_wmb in update_vsyscall */
63 seq = READ_ONCE(vdata->tb_seq_count);
64 return seq != start;
65 }
66
67 static notrace long clock_gettime_fallback(clockid_t _clkid,
68 struct timespec *_ts)
69 {
70 register struct timespec *ts asm("r1") = _ts;
71 register clockid_t clkid asm("r0") = _clkid;
72 register long ret asm ("r0");
73 register long nr asm("r7") = __NR_clock_gettime;
74
75 asm volatile(
76 " swi #0\n"
77 : "=r" (ret)
78 : "r" (clkid), "r" (ts), "r" (nr)
79 : "memory");
80
81 return ret;
82 }
83
84 static notrace int do_realtime_coarse(struct timespec *ts,
85 struct vdso_data *vdata)
86 {
87 u32 seq;
88
89 do {
90 seq = vdso_read_begin(vdata);
91
92 ts->tv_sec = vdata->xtime_coarse_sec;
93 ts->tv_nsec = vdata->xtime_coarse_nsec;
94
95 } while (vdso_read_retry(vdata, seq));
96
97 return 0;
98 }
99
100 static notrace int do_monotonic_coarse(struct timespec *ts,
101 struct vdso_data *vdata)
102 {
103 struct timespec tomono;
104 u32 seq;
105
106 do {
107 seq = vdso_read_begin(vdata);
108
109 ts->tv_sec = vdata->xtime_coarse_sec;
110 ts->tv_nsec = vdata->xtime_coarse_nsec;
111
112 tomono.tv_sec = vdata->wtm_clock_sec;
113 tomono.tv_nsec = vdata->wtm_clock_nsec;
114
115 } while (vdso_read_retry(vdata, seq));
116
117 ts->tv_sec += tomono.tv_sec;
118 timespec_add_ns(ts, tomono.tv_nsec);
119
120 return 0;
121 }
122
123 #ifdef CONFIG_ARM_ARCH_TIMER
124
125 static notrace u64 get_ns(struct vdso_data *vdata)
126 {
127 u64 cycle_delta;
128 u64 cycle_now;
129 u64 nsec;
130
131 cycle_now = arch_counter_get_cntvct();
132
133 cycle_delta = (cycle_now - vdata->cs_cycle_last) & vdata->cs_mask;
134
135 nsec = (cycle_delta * vdata->cs_mono_mult) + vdata->xtime_clock_snsec;
136 nsec >>= vdata->cs_shift;
137
138 return nsec;
139 }
140
141 static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
142 {
143 u64 nsecs;
144 u32 seq;
145
146 do {
147 seq = vdso_read_begin(vdata);
148
149 if (vdata->use_syscall)
150 return -1;
151
152 ts->tv_sec = vdata->xtime_clock_sec;
153 nsecs = get_ns(vdata);
154
155 } while (vdso_read_retry(vdata, seq));
156
157 ts->tv_nsec = 0;
158 timespec_add_ns(ts, nsecs);
159
160 return 0;
161 }
162
163 static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
164 {
165 struct timespec tomono;
166 u64 nsecs;
167 u32 seq;
168
169 do {
170 seq = vdso_read_begin(vdata);
171
172 if (vdata->use_syscall)
173 return -1;
174
175 ts->tv_sec = vdata->xtime_clock_sec;
176 nsecs = get_ns(vdata);
177
178 tomono.tv_sec = vdata->wtm_clock_sec;
179 tomono.tv_nsec = vdata->wtm_clock_nsec;
180
181 } while (vdso_read_retry(vdata, seq));
182
183 ts->tv_sec += tomono.tv_sec;
184 ts->tv_nsec = 0;
185 timespec_add_ns(ts, nsecs + tomono.tv_nsec);
186
187 return 0;
188 }
189
190 #else /* CONFIG_ARM_ARCH_TIMER */
191
192 static notrace int do_realtime(struct timespec *ts, struct vdso_data *vdata)
193 {
194 return -1;
195 }
196
197 static notrace int do_monotonic(struct timespec *ts, struct vdso_data *vdata)
198 {
199 return -1;
200 }
201
202 #endif /* CONFIG_ARM_ARCH_TIMER */
203
204 notrace int __vdso_clock_gettime(clockid_t clkid, struct timespec *ts)
205 {
206 struct vdso_data *vdata;
207 int ret = -1;
208
209 vdata = __get_datapage();
210
211 switch (clkid) {
212 case CLOCK_REALTIME_COARSE:
213 ret = do_realtime_coarse(ts, vdata);
214 break;
215 case CLOCK_MONOTONIC_COARSE:
216 ret = do_monotonic_coarse(ts, vdata);
217 break;
218 case CLOCK_REALTIME:
219 ret = do_realtime(ts, vdata);
220 break;
221 case CLOCK_MONOTONIC:
222 ret = do_monotonic(ts, vdata);
223 break;
224 default:
225 break;
226 }
227
228 if (ret)
229 ret = clock_gettime_fallback(clkid, ts);
230
231 return ret;
232 }
233
234 static notrace long gettimeofday_fallback(struct timeval *_tv,
235 struct timezone *_tz)
236 {
237 register struct timezone *tz asm("r1") = _tz;
238 register struct timeval *tv asm("r0") = _tv;
239 register long ret asm ("r0");
240 register long nr asm("r7") = __NR_gettimeofday;
241
242 asm volatile(
243 " swi #0\n"
244 : "=r" (ret)
245 : "r" (tv), "r" (tz), "r" (nr)
246 : "memory");
247
248 return ret;
249 }
250
251 notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
252 {
253 struct timespec ts;
254 struct vdso_data *vdata;
255 int ret;
256
257 vdata = __get_datapage();
258
259 ret = do_realtime(&ts, vdata);
260 if (ret)
261 return gettimeofday_fallback(tv, tz);
262
263 if (tv) {
264 tv->tv_sec = ts.tv_sec;
265 tv->tv_usec = ts.tv_nsec / 1000;
266 }
267 if (tz) {
268 tz->tz_minuteswest = vdata->tz_minuteswest;
269 tz->tz_dsttime = vdata->tz_dsttime;
270 }
271
272 return ret;
273 }
274
275 /* Avoid unresolved references emitted by GCC */
276
277 void __aeabi_unwind_cpp_pr0(void)
278 {
279 }
280
281 void __aeabi_unwind_cpp_pr1(void)
282 {
283 }
284
285 void __aeabi_unwind_cpp_pr2(void)
286 {
287 }