xref: /freebsd/sys/compat/linux/linux_vdso_gtod.inc (revision 9931033bbfbe56a037723638cf3712366c6d943f)
1*9931033bSDmitry Chagin/*-
2*9931033bSDmitry Chagin * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*9931033bSDmitry Chagin *
4*9931033bSDmitry Chagin * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org>
5*9931033bSDmitry Chagin * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org>
6*9931033bSDmitry Chagin *
7*9931033bSDmitry Chagin * Redistribution and use in source and binary forms, with or without
8*9931033bSDmitry Chagin * modification, are permitted provided that the following conditions
9*9931033bSDmitry Chagin * are met:
10*9931033bSDmitry Chagin * 1. Redistributions of source code must retain the above copyright
11*9931033bSDmitry Chagin *    notice, this list of conditions and the following disclaimer.
12*9931033bSDmitry Chagin * 2. Redistributions in binary form must reproduce the above copyright
13*9931033bSDmitry Chagin *    notice, this list of conditions and the following disclaimer in the
14*9931033bSDmitry Chagin *    documentation and/or other materials provided with the distribution.
15*9931033bSDmitry Chagin *
16*9931033bSDmitry Chagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*9931033bSDmitry Chagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*9931033bSDmitry Chagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*9931033bSDmitry Chagin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*9931033bSDmitry Chagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*9931033bSDmitry Chagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*9931033bSDmitry Chagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*9931033bSDmitry Chagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*9931033bSDmitry Chagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*9931033bSDmitry Chagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*9931033bSDmitry Chagin * SUCH DAMAGE.
27*9931033bSDmitry Chagin */
28*9931033bSDmitry Chagin
29*9931033bSDmitry Chagin
30*9931033bSDmitry Chaginstatic int
31*9931033bSDmitry Chagin__vdso_native_to_linux_timespec(struct l_timespec *lts,
32*9931033bSDmitry Chagin    struct timespec *nts)
33*9931033bSDmitry Chagin{
34*9931033bSDmitry Chagin
35*9931033bSDmitry Chagin#ifdef COMPAT_LINUX32
36*9931033bSDmitry Chagin	if (nts->tv_sec > INT_MAX || nts->tv_sec < INT_MIN)
37*9931033bSDmitry Chagin		return (LINUX_EOVERFLOW);
38*9931033bSDmitry Chagin#endif
39*9931033bSDmitry Chagin	lts->tv_sec = nts->tv_sec;
40*9931033bSDmitry Chagin	lts->tv_nsec = nts->tv_nsec;
41*9931033bSDmitry Chagin	return (0);
42*9931033bSDmitry Chagin}
43*9931033bSDmitry Chagin
44*9931033bSDmitry Chaginstatic int
45*9931033bSDmitry Chagin__vdso_native_to_linux_timeval(l_timeval *ltv,
46*9931033bSDmitry Chagin    struct timeval *ntv)
47*9931033bSDmitry Chagin{
48*9931033bSDmitry Chagin
49*9931033bSDmitry Chagin#ifdef COMPAT_LINUX32
50*9931033bSDmitry Chagin	if (ntv->tv_sec > INT_MAX || ntv->tv_sec < INT_MIN)
51*9931033bSDmitry Chagin		return (LINUX_EOVERFLOW);
52*9931033bSDmitry Chagin#endif
53*9931033bSDmitry Chagin	ltv->tv_sec = ntv->tv_sec;
54*9931033bSDmitry Chagin	ltv->tv_usec = ntv->tv_usec;
55*9931033bSDmitry Chagin	return (0);
56*9931033bSDmitry Chagin}
57*9931033bSDmitry Chagin
58*9931033bSDmitry Chagin
59*9931033bSDmitry Chagin#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
60*9931033bSDmitry Chaginstatic int
61*9931033bSDmitry Chagin__vdso_native_to_linux_timespec64(struct l_timespec64 *lts,
62*9931033bSDmitry Chagin    struct timespec *nts)
63*9931033bSDmitry Chagin{
64*9931033bSDmitry Chagin
65*9931033bSDmitry Chagin	lts->tv_sec = nts->tv_sec;
66*9931033bSDmitry Chagin	lts->tv_nsec = nts->tv_nsec;
67*9931033bSDmitry Chagin	return (0);
68*9931033bSDmitry Chagin}
69*9931033bSDmitry Chagin#endif
70*9931033bSDmitry Chagin
71*9931033bSDmitry Chaginstatic int
72*9931033bSDmitry Chagin__vdso_linux_to_native_clockid(clockid_t *n, clockid_t l)
73*9931033bSDmitry Chagin{
74*9931033bSDmitry Chagin
75*9931033bSDmitry Chagin	switch (l) {
76*9931033bSDmitry Chagin	case LINUX_CLOCK_REALTIME:
77*9931033bSDmitry Chagin		*n = CLOCK_REALTIME;
78*9931033bSDmitry Chagin		break;
79*9931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC:
80*9931033bSDmitry Chagin		*n = CLOCK_MONOTONIC;
81*9931033bSDmitry Chagin		break;
82*9931033bSDmitry Chagin	case LINUX_CLOCK_REALTIME_COARSE:
83*9931033bSDmitry Chagin		*n = CLOCK_REALTIME_FAST;
84*9931033bSDmitry Chagin		break;
85*9931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC_COARSE:
86*9931033bSDmitry Chagin	case LINUX_CLOCK_MONOTONIC_RAW:
87*9931033bSDmitry Chagin		*n = CLOCK_MONOTONIC_FAST;
88*9931033bSDmitry Chagin		break;
89*9931033bSDmitry Chagin	case LINUX_CLOCK_BOOTTIME:
90*9931033bSDmitry Chagin		*n = CLOCK_UPTIME;
91*9931033bSDmitry Chagin		break;
92*9931033bSDmitry Chagin	default:
93*9931033bSDmitry Chagin		return (LINUX_EINVAL);
94*9931033bSDmitry Chagin	}
95*9931033bSDmitry Chagin	return (0);
96*9931033bSDmitry Chagin}
97*9931033bSDmitry Chagin
98*9931033bSDmitry Chagin/*
99*9931033bSDmitry Chagin * The code below adapted from
100*9931033bSDmitry Chagin * lib/libc/sys/__vdso_gettimeofday.c
101*9931033bSDmitry Chagin */
102*9931033bSDmitry Chagin
103*9931033bSDmitry Chaginstatic inline void
104*9931033bSDmitry Chagin__vdso_gettimekeep(struct vdso_timekeep **tk)
105*9931033bSDmitry Chagin{
106*9931033bSDmitry Chagin
107*9931033bSDmitry Chagin	*tk = (struct vdso_timekeep *)kern_timekeep_base;
108*9931033bSDmitry Chagin}
109*9931033bSDmitry Chagin
110*9931033bSDmitry Chaginstatic int
111*9931033bSDmitry Chagintc_delta(const struct vdso_timehands *th, u_int *delta)
112*9931033bSDmitry Chagin{
113*9931033bSDmitry Chagin	int error;
114*9931033bSDmitry Chagin	u_int tc;
115*9931033bSDmitry Chagin
116*9931033bSDmitry Chagin	error = __vdso_gettc(th, &tc);
117*9931033bSDmitry Chagin	if (error == 0)
118*9931033bSDmitry Chagin		*delta = (tc - th->th_offset_count) & th->th_counter_mask;
119*9931033bSDmitry Chagin	return (error);
120*9931033bSDmitry Chagin}
121*9931033bSDmitry Chagin
122*9931033bSDmitry Chagin/*
123*9931033bSDmitry Chagin * Calculate the absolute or boot-relative time from the
124*9931033bSDmitry Chagin * machine-specific fast timecounter and the published timehands
125*9931033bSDmitry Chagin * structure read from the shared page.
126*9931033bSDmitry Chagin *
127*9931033bSDmitry Chagin * The lockless reading scheme is similar to the one used to read the
128*9931033bSDmitry Chagin * in-kernel timehands, see sys/kern/kern_tc.c:binuptime().  This code
129*9931033bSDmitry Chagin * is based on the kernel implementation.
130*9931033bSDmitry Chagin */
131*9931033bSDmitry Chaginstatic int
132*9931033bSDmitry Chaginfreebsd_binuptime(struct bintime *bt, struct vdso_timekeep *tk, bool abs)
133*9931033bSDmitry Chagin{
134*9931033bSDmitry Chagin	struct vdso_timehands *th;
135*9931033bSDmitry Chagin	uint32_t curr, gen;
136*9931033bSDmitry Chagin	uint64_t scale, x;
137*9931033bSDmitry Chagin	u_int delta, scale_bits;
138*9931033bSDmitry Chagin	int error;
139*9931033bSDmitry Chagin
140*9931033bSDmitry Chagin	do {
141*9931033bSDmitry Chagin		if (!tk->tk_enabled)
142*9931033bSDmitry Chagin			return (ENOSYS);
143*9931033bSDmitry Chagin
144*9931033bSDmitry Chagin		curr = atomic_load_acq_32(&tk->tk_current);
145*9931033bSDmitry Chagin		th = &tk->tk_th[curr];
146*9931033bSDmitry Chagin		gen = atomic_load_acq_32(&th->th_gen);
147*9931033bSDmitry Chagin		*bt = th->th_offset;
148*9931033bSDmitry Chagin		error = tc_delta(th, &delta);
149*9931033bSDmitry Chagin		if (error == EAGAIN)
150*9931033bSDmitry Chagin			continue;
151*9931033bSDmitry Chagin		if (error != 0)
152*9931033bSDmitry Chagin			return (error);
153*9931033bSDmitry Chagin		scale = th->th_scale;
154*9931033bSDmitry Chagin#ifdef _LP64
155*9931033bSDmitry Chagin		scale_bits = ffsl(scale);
156*9931033bSDmitry Chagin#else
157*9931033bSDmitry Chagin		scale_bits = ffsll(scale);
158*9931033bSDmitry Chagin#endif
159*9931033bSDmitry Chagin		if (__predict_false(scale_bits + fls(delta) > 63)) {
160*9931033bSDmitry Chagin			x = (scale >> 32) * delta;
161*9931033bSDmitry Chagin			scale &= 0xffffffff;
162*9931033bSDmitry Chagin			bt->sec += x >> 32;
163*9931033bSDmitry Chagin			bintime_addx(bt, x << 32);
164*9931033bSDmitry Chagin		}
165*9931033bSDmitry Chagin		bintime_addx(bt, scale * delta);
166*9931033bSDmitry Chagin		if (abs)
167*9931033bSDmitry Chagin			bintime_add(bt, &th->th_boottime);
168*9931033bSDmitry Chagin
169*9931033bSDmitry Chagin		/*
170*9931033bSDmitry Chagin		 * Ensure that the load of th_offset is completed
171*9931033bSDmitry Chagin		 * before the load of th_gen.
172*9931033bSDmitry Chagin		 */
173*9931033bSDmitry Chagin		atomic_thread_fence_acq();
174*9931033bSDmitry Chagin	} while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
175*9931033bSDmitry Chagin	return (0);
176*9931033bSDmitry Chagin}
177*9931033bSDmitry Chagin
178*9931033bSDmitry Chaginstatic int
179*9931033bSDmitry Chaginfreebsd_getnanouptime(struct bintime *bt, struct vdso_timekeep *tk)
180*9931033bSDmitry Chagin{
181*9931033bSDmitry Chagin	struct vdso_timehands *th;
182*9931033bSDmitry Chagin	uint32_t curr, gen;
183*9931033bSDmitry Chagin
184*9931033bSDmitry Chagin	do {
185*9931033bSDmitry Chagin		if (!tk->tk_enabled)
186*9931033bSDmitry Chagin			return (ENOSYS);
187*9931033bSDmitry Chagin
188*9931033bSDmitry Chagin		curr = atomic_load_acq_32(&tk->tk_current);
189*9931033bSDmitry Chagin		th = &tk->tk_th[curr];
190*9931033bSDmitry Chagin		gen = atomic_load_acq_32(&th->th_gen);
191*9931033bSDmitry Chagin		*bt = th->th_offset;
192*9931033bSDmitry Chagin
193*9931033bSDmitry Chagin		/*
194*9931033bSDmitry Chagin		 * Ensure that the load of th_offset is completed
195*9931033bSDmitry Chagin		 * before the load of th_gen.
196*9931033bSDmitry Chagin		 */
197*9931033bSDmitry Chagin		atomic_thread_fence_acq();
198*9931033bSDmitry Chagin	} while (curr != tk->tk_current || gen == 0 || gen != th->th_gen);
199*9931033bSDmitry Chagin	return (0);
200*9931033bSDmitry Chagin}
201*9931033bSDmitry Chagin
202*9931033bSDmitry Chaginstatic int
203*9931033bSDmitry Chaginfreebsd_gettimeofday(struct timeval *tv, struct timezone *tz)
204*9931033bSDmitry Chagin{
205*9931033bSDmitry Chagin	struct vdso_timekeep *tk;
206*9931033bSDmitry Chagin	struct bintime bt;
207*9931033bSDmitry Chagin	int error;
208*9931033bSDmitry Chagin
209*9931033bSDmitry Chagin	if (tz != NULL)
210*9931033bSDmitry Chagin		return (ENOSYS);
211*9931033bSDmitry Chagin	__vdso_gettimekeep(&tk);
212*9931033bSDmitry Chagin	if (tk == NULL)
213*9931033bSDmitry Chagin		return (ENOSYS);
214*9931033bSDmitry Chagin	if (tk->tk_ver != VDSO_TK_VER_CURR)
215*9931033bSDmitry Chagin		return (ENOSYS);
216*9931033bSDmitry Chagin	error = freebsd_binuptime(&bt, tk, true);
217*9931033bSDmitry Chagin	if (error == 0)
218*9931033bSDmitry Chagin		bintime2timeval(&bt, tv);
219*9931033bSDmitry Chagin	return (error);
220*9931033bSDmitry Chagin}
221*9931033bSDmitry Chagin
222*9931033bSDmitry Chaginstatic int
223*9931033bSDmitry Chaginfreebsd_clock_gettime(clockid_t clock_id, struct timespec *ts)
224*9931033bSDmitry Chagin{
225*9931033bSDmitry Chagin	struct vdso_timekeep *tk;
226*9931033bSDmitry Chagin	struct bintime bt;
227*9931033bSDmitry Chagin	int error;
228*9931033bSDmitry Chagin
229*9931033bSDmitry Chagin	__vdso_gettimekeep(&tk);
230*9931033bSDmitry Chagin	if (tk == NULL)
231*9931033bSDmitry Chagin		return (ENOSYS);
232*9931033bSDmitry Chagin	if (tk->tk_ver != VDSO_TK_VER_CURR)
233*9931033bSDmitry Chagin		return (ENOSYS);
234*9931033bSDmitry Chagin	switch (clock_id) {
235*9931033bSDmitry Chagin	case CLOCK_REALTIME:
236*9931033bSDmitry Chagin	case CLOCK_REALTIME_PRECISE:
237*9931033bSDmitry Chagin	case CLOCK_REALTIME_FAST:
238*9931033bSDmitry Chagin		error = freebsd_binuptime(&bt, tk, true);
239*9931033bSDmitry Chagin		break;
240*9931033bSDmitry Chagin	case CLOCK_MONOTONIC:
241*9931033bSDmitry Chagin	case CLOCK_MONOTONIC_PRECISE:
242*9931033bSDmitry Chagin	case CLOCK_UPTIME:
243*9931033bSDmitry Chagin	case CLOCK_UPTIME_PRECISE:
244*9931033bSDmitry Chagin		error = freebsd_binuptime(&bt, tk, false);
245*9931033bSDmitry Chagin		break;
246*9931033bSDmitry Chagin	case CLOCK_MONOTONIC_FAST:
247*9931033bSDmitry Chagin	case CLOCK_UPTIME_FAST:
248*9931033bSDmitry Chagin		error = freebsd_getnanouptime(&bt, tk);
249*9931033bSDmitry Chagin		break;
250*9931033bSDmitry Chagin	default:
251*9931033bSDmitry Chagin		error = ENOSYS;
252*9931033bSDmitry Chagin		break;
253*9931033bSDmitry Chagin	}
254*9931033bSDmitry Chagin	if (error == 0)
255*9931033bSDmitry Chagin		bintime2timespec(&bt, ts);
256*9931033bSDmitry Chagin	return (error);
257*9931033bSDmitry Chagin}
258*9931033bSDmitry Chagin
259*9931033bSDmitry Chagin/*
260*9931033bSDmitry Chagin * Linux vDSO interfaces
261*9931033bSDmitry Chagin *
262*9931033bSDmitry Chagin */
263*9931033bSDmitry Chaginint
264*9931033bSDmitry Chagin__vdso_clock_gettime(clockid_t clock_id, struct l_timespec *lts)
265*9931033bSDmitry Chagin{
266*9931033bSDmitry Chagin	struct timespec ts;
267*9931033bSDmitry Chagin	clockid_t which;
268*9931033bSDmitry Chagin	int error;
269*9931033bSDmitry Chagin
270*9931033bSDmitry Chagin	error = __vdso_linux_to_native_clockid(&which, clock_id);
271*9931033bSDmitry Chagin	if (error != 0)
272*9931033bSDmitry Chagin		return (__vdso_clock_gettime_fallback(clock_id, lts));
273*9931033bSDmitry Chagin	error = freebsd_clock_gettime(which, &ts);
274*9931033bSDmitry Chagin	if (error == 0)
275*9931033bSDmitry Chagin		return (-__vdso_native_to_linux_timespec(lts, &ts));
276*9931033bSDmitry Chagin	else
277*9931033bSDmitry Chagin		return (__vdso_clock_gettime_fallback(clock_id, lts));
278*9931033bSDmitry Chagin}
279*9931033bSDmitry Chagin
280*9931033bSDmitry Chaginint
281*9931033bSDmitry Chagin__vdso_gettimeofday(l_timeval *ltv, struct timezone *tz)
282*9931033bSDmitry Chagin{
283*9931033bSDmitry Chagin	struct timeval tv;
284*9931033bSDmitry Chagin	int error;
285*9931033bSDmitry Chagin
286*9931033bSDmitry Chagin	error = freebsd_gettimeofday(&tv, tz);
287*9931033bSDmitry Chagin	if (error != 0)
288*9931033bSDmitry Chagin		return (__vdso_gettimeofday_fallback(ltv, tz));
289*9931033bSDmitry Chagin	return (-__vdso_native_to_linux_timeval(ltv, &tv));
290*9931033bSDmitry Chagin}
291*9931033bSDmitry Chagin
292*9931033bSDmitry Chaginint
293*9931033bSDmitry Chagin__vdso_clock_getres(clockid_t clock_id, struct l_timespec *lts)
294*9931033bSDmitry Chagin{
295*9931033bSDmitry Chagin
296*9931033bSDmitry Chagin	return (__vdso_clock_getres_fallback(clock_id, lts));
297*9931033bSDmitry Chagin}
298*9931033bSDmitry Chagin
299*9931033bSDmitry Chagin#if defined(__i386__) || defined(COMPAT_LINUX32)
300*9931033bSDmitry Chaginint
301*9931033bSDmitry Chagin__vdso_clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
302*9931033bSDmitry Chagin{
303*9931033bSDmitry Chagin	struct timespec ts;
304*9931033bSDmitry Chagin	clockid_t which;
305*9931033bSDmitry Chagin	int error;
306*9931033bSDmitry Chagin
307*9931033bSDmitry Chagin	error = __vdso_linux_to_native_clockid(&which, clock_id);
308*9931033bSDmitry Chagin	if (error != 0)
309*9931033bSDmitry Chagin		return (__vdso_clock_gettime64_fallback(clock_id, lts));
310*9931033bSDmitry Chagin	error = freebsd_clock_gettime(which, &ts);
311*9931033bSDmitry Chagin	if (error == 0)
312*9931033bSDmitry Chagin		return(-__vdso_native_to_linux_timespec64(lts, &ts));
313*9931033bSDmitry Chagin	else
314*9931033bSDmitry Chagin		return(__vdso_clock_gettime64_fallback(clock_id, lts));
315*9931033bSDmitry Chagin}
316*9931033bSDmitry Chagin
317*9931033bSDmitry Chaginint clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts)
318*9931033bSDmitry Chagin    __attribute__((weak, alias("__vdso_clock_gettime64")));
319*9931033bSDmitry Chagin#endif
320*9931033bSDmitry Chagin
321*9931033bSDmitry Chagin#if defined(__amd64__) && !defined(COMPAT_LINUX32)
322*9931033bSDmitry Chaginint
323*9931033bSDmitry Chagin__vdso_getcpu(uint32_t *cpu, uint32_t *node, void *cache)
324*9931033bSDmitry Chagin{
325*9931033bSDmitry Chagin
326*9931033bSDmitry Chagin	return (__vdso_getcpu_fallback(cpu, node, cache));
327*9931033bSDmitry Chagin}
328*9931033bSDmitry Chagin#endif
329*9931033bSDmitry Chagin
330*9931033bSDmitry Chagin#if defined(__i386__) || defined(__amd64__)
331*9931033bSDmitry Chaginint
332*9931033bSDmitry Chagin__vdso_time(long *tm)
333*9931033bSDmitry Chagin{
334*9931033bSDmitry Chagin
335*9931033bSDmitry Chagin	return (__vdso_time_fallback(tm));
336*9931033bSDmitry Chagin}
337*9931033bSDmitry Chagin#endif
338