19931033bSDmitry Chagin/*- 29931033bSDmitry Chagin * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 39931033bSDmitry Chagin * 49931033bSDmitry Chagin * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org> 59931033bSDmitry Chagin * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org> 69931033bSDmitry Chagin * 79931033bSDmitry Chagin * Redistribution and use in source and binary forms, with or without 89931033bSDmitry Chagin * modification, are permitted provided that the following conditions 99931033bSDmitry Chagin * are met: 109931033bSDmitry Chagin * 1. Redistributions of source code must retain the above copyright 119931033bSDmitry Chagin * notice, this list of conditions and the following disclaimer. 129931033bSDmitry Chagin * 2. Redistributions in binary form must reproduce the above copyright 139931033bSDmitry Chagin * notice, this list of conditions and the following disclaimer in the 149931033bSDmitry Chagin * documentation and/or other materials provided with the distribution. 159931033bSDmitry Chagin * 169931033bSDmitry Chagin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 179931033bSDmitry Chagin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 189931033bSDmitry Chagin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 199931033bSDmitry Chagin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 209931033bSDmitry Chagin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 219931033bSDmitry Chagin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 229931033bSDmitry Chagin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 239931033bSDmitry Chagin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 249931033bSDmitry Chagin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 259931033bSDmitry Chagin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 269931033bSDmitry Chagin * SUCH DAMAGE. 279931033bSDmitry Chagin */ 289931033bSDmitry Chagin 29f3379401SDmitry Chaginstatic int 30f3379401SDmitry Chaginfls(int mask) 31f3379401SDmitry Chagin{ 32f3379401SDmitry Chagin 33f3379401SDmitry Chagin if (mask == 0) 34f3379401SDmitry Chagin return (0); 35f3379401SDmitry Chagin return ((__builtin_clz(mask) ^ 0x1f) + 1); 36f3379401SDmitry Chagin} 37f3379401SDmitry Chagin 38f3379401SDmitry Chagin#ifdef _LP64 39f3379401SDmitry Chaginstatic int 40f3379401SDmitry Chaginffsl(long mask) 41f3379401SDmitry Chagin{ 42f3379401SDmitry Chagin int bit; 43f3379401SDmitry Chagin 44f3379401SDmitry Chagin if (mask == 0) 45f3379401SDmitry Chagin return (0); 46f3379401SDmitry Chagin for (bit = 1; !(mask & 1); bit++) 47f3379401SDmitry Chagin mask = (unsigned long)mask >> 1; 48f3379401SDmitry Chagin return (bit); 49f3379401SDmitry Chagin} 50f3379401SDmitry Chagin#else 51f3379401SDmitry Chaginstatic int 52f3379401SDmitry Chaginffsll(long long mask) 53f3379401SDmitry Chagin{ 54f3379401SDmitry Chagin int bit; 55f3379401SDmitry Chagin 56f3379401SDmitry Chagin if (mask == 0) 57f3379401SDmitry Chagin return (0); 58f3379401SDmitry Chagin for (bit = 1; !(mask & 1); bit++) 59f3379401SDmitry Chagin mask = (unsigned long long)mask >> 1; 60f3379401SDmitry Chagin return (bit); 61f3379401SDmitry Chagin} 62f3379401SDmitry Chagin#endif 639931033bSDmitry Chagin 649931033bSDmitry Chaginstatic int 659931033bSDmitry Chagin__vdso_native_to_linux_timespec(struct l_timespec *lts, 669931033bSDmitry Chagin struct timespec *nts) 679931033bSDmitry Chagin{ 689931033bSDmitry Chagin 699931033bSDmitry Chagin#ifdef COMPAT_LINUX32 709931033bSDmitry Chagin if (nts->tv_sec > INT_MAX || nts->tv_sec < INT_MIN) 719931033bSDmitry Chagin return (LINUX_EOVERFLOW); 729931033bSDmitry Chagin#endif 739931033bSDmitry Chagin lts->tv_sec = nts->tv_sec; 749931033bSDmitry Chagin lts->tv_nsec = nts->tv_nsec; 759931033bSDmitry Chagin return (0); 769931033bSDmitry Chagin} 779931033bSDmitry Chagin 789931033bSDmitry Chaginstatic int 799931033bSDmitry Chagin__vdso_native_to_linux_timeval(l_timeval *ltv, 809931033bSDmitry Chagin struct timeval *ntv) 819931033bSDmitry Chagin{ 829931033bSDmitry Chagin 839931033bSDmitry Chagin#ifdef COMPAT_LINUX32 849931033bSDmitry Chagin if (ntv->tv_sec > INT_MAX || ntv->tv_sec < INT_MIN) 859931033bSDmitry Chagin return (LINUX_EOVERFLOW); 869931033bSDmitry Chagin#endif 879931033bSDmitry Chagin ltv->tv_sec = ntv->tv_sec; 889931033bSDmitry Chagin ltv->tv_usec = ntv->tv_usec; 899931033bSDmitry Chagin return (0); 909931033bSDmitry Chagin} 919931033bSDmitry Chagin 929931033bSDmitry Chagin 939931033bSDmitry Chagin#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32)) 949931033bSDmitry Chaginstatic int 959931033bSDmitry Chagin__vdso_native_to_linux_timespec64(struct l_timespec64 *lts, 969931033bSDmitry Chagin struct timespec *nts) 979931033bSDmitry Chagin{ 989931033bSDmitry Chagin 999931033bSDmitry Chagin lts->tv_sec = nts->tv_sec; 1009931033bSDmitry Chagin lts->tv_nsec = nts->tv_nsec; 1019931033bSDmitry Chagin return (0); 1029931033bSDmitry Chagin} 1039931033bSDmitry Chagin#endif 1049931033bSDmitry Chagin 1059931033bSDmitry Chaginstatic int 1069931033bSDmitry Chagin__vdso_linux_to_native_clockid(clockid_t *n, clockid_t l) 1079931033bSDmitry Chagin{ 1089931033bSDmitry Chagin 1099931033bSDmitry Chagin switch (l) { 1109931033bSDmitry Chagin case LINUX_CLOCK_REALTIME: 1119931033bSDmitry Chagin *n = CLOCK_REALTIME; 1129931033bSDmitry Chagin break; 1139931033bSDmitry Chagin case LINUX_CLOCK_MONOTONIC: 1149931033bSDmitry Chagin *n = CLOCK_MONOTONIC; 1159931033bSDmitry Chagin break; 1169931033bSDmitry Chagin case LINUX_CLOCK_REALTIME_COARSE: 1179931033bSDmitry Chagin *n = CLOCK_REALTIME_FAST; 1189931033bSDmitry Chagin break; 1199931033bSDmitry Chagin case LINUX_CLOCK_MONOTONIC_COARSE: 1209931033bSDmitry Chagin case LINUX_CLOCK_MONOTONIC_RAW: 1219931033bSDmitry Chagin *n = CLOCK_MONOTONIC_FAST; 1229931033bSDmitry Chagin break; 1239931033bSDmitry Chagin case LINUX_CLOCK_BOOTTIME: 1249931033bSDmitry Chagin *n = CLOCK_UPTIME; 1259931033bSDmitry Chagin break; 1269931033bSDmitry Chagin default: 1279931033bSDmitry Chagin return (LINUX_EINVAL); 1289931033bSDmitry Chagin } 1299931033bSDmitry Chagin return (0); 1309931033bSDmitry Chagin} 1319931033bSDmitry Chagin 1329931033bSDmitry Chagin/* 1339931033bSDmitry Chagin * The code below adapted from 1349931033bSDmitry Chagin * lib/libc/sys/__vdso_gettimeofday.c 1359931033bSDmitry Chagin */ 1369931033bSDmitry Chagin 1379931033bSDmitry Chaginstatic inline void 1389931033bSDmitry Chagin__vdso_gettimekeep(struct vdso_timekeep **tk) 1399931033bSDmitry Chagin{ 1409931033bSDmitry Chagin 1419931033bSDmitry Chagin *tk = (struct vdso_timekeep *)kern_timekeep_base; 1429931033bSDmitry Chagin} 1439931033bSDmitry Chagin 1449931033bSDmitry Chaginstatic int 1459931033bSDmitry Chagintc_delta(const struct vdso_timehands *th, u_int *delta) 1469931033bSDmitry Chagin{ 1479931033bSDmitry Chagin int error; 1489931033bSDmitry Chagin u_int tc; 1499931033bSDmitry Chagin 1509931033bSDmitry Chagin error = __vdso_gettc(th, &tc); 1519931033bSDmitry Chagin if (error == 0) 1529931033bSDmitry Chagin *delta = (tc - th->th_offset_count) & th->th_counter_mask; 1539931033bSDmitry Chagin return (error); 1549931033bSDmitry Chagin} 1559931033bSDmitry Chagin 1569931033bSDmitry Chagin/* 1579931033bSDmitry Chagin * Calculate the absolute or boot-relative time from the 1589931033bSDmitry Chagin * machine-specific fast timecounter and the published timehands 1599931033bSDmitry Chagin * structure read from the shared page. 1609931033bSDmitry Chagin * 1619931033bSDmitry Chagin * The lockless reading scheme is similar to the one used to read the 1629931033bSDmitry Chagin * in-kernel timehands, see sys/kern/kern_tc.c:binuptime(). This code 1639931033bSDmitry Chagin * is based on the kernel implementation. 1649931033bSDmitry Chagin */ 1659931033bSDmitry Chaginstatic int 1669931033bSDmitry Chaginfreebsd_binuptime(struct bintime *bt, struct vdso_timekeep *tk, bool abs) 1679931033bSDmitry Chagin{ 1689931033bSDmitry Chagin struct vdso_timehands *th; 1699931033bSDmitry Chagin uint32_t curr, gen; 1709931033bSDmitry Chagin uint64_t scale, x; 1719931033bSDmitry Chagin u_int delta, scale_bits; 1729931033bSDmitry Chagin int error; 1739931033bSDmitry Chagin 1749931033bSDmitry Chagin do { 1759931033bSDmitry Chagin if (!tk->tk_enabled) 1769931033bSDmitry Chagin return (ENOSYS); 1779931033bSDmitry Chagin 1789931033bSDmitry Chagin curr = atomic_load_acq_32(&tk->tk_current); 1799931033bSDmitry Chagin th = &tk->tk_th[curr]; 1809931033bSDmitry Chagin gen = atomic_load_acq_32(&th->th_gen); 1819931033bSDmitry Chagin *bt = th->th_offset; 1829931033bSDmitry Chagin error = tc_delta(th, &delta); 1839931033bSDmitry Chagin if (error == EAGAIN) 1849931033bSDmitry Chagin continue; 1859931033bSDmitry Chagin if (error != 0) 1869931033bSDmitry Chagin return (error); 1879931033bSDmitry Chagin scale = th->th_scale; 1889931033bSDmitry Chagin#ifdef _LP64 1899931033bSDmitry Chagin scale_bits = ffsl(scale); 1909931033bSDmitry Chagin#else 1919931033bSDmitry Chagin scale_bits = ffsll(scale); 1929931033bSDmitry Chagin#endif 1939931033bSDmitry Chagin if (__predict_false(scale_bits + fls(delta) > 63)) { 1949931033bSDmitry Chagin x = (scale >> 32) * delta; 1959931033bSDmitry Chagin scale &= 0xffffffff; 1969931033bSDmitry Chagin bt->sec += x >> 32; 1979931033bSDmitry Chagin bintime_addx(bt, x << 32); 1989931033bSDmitry Chagin } 1999931033bSDmitry Chagin bintime_addx(bt, scale * delta); 2009931033bSDmitry Chagin if (abs) 2019931033bSDmitry Chagin bintime_add(bt, &th->th_boottime); 2029931033bSDmitry Chagin 2039931033bSDmitry Chagin /* 2049931033bSDmitry Chagin * Ensure that the load of th_offset is completed 2059931033bSDmitry Chagin * before the load of th_gen. 2069931033bSDmitry Chagin */ 2079931033bSDmitry Chagin atomic_thread_fence_acq(); 2089931033bSDmitry Chagin } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen); 2099931033bSDmitry Chagin return (0); 2109931033bSDmitry Chagin} 2119931033bSDmitry Chagin 2129931033bSDmitry Chaginstatic int 2139931033bSDmitry Chaginfreebsd_getnanouptime(struct bintime *bt, struct vdso_timekeep *tk) 2149931033bSDmitry Chagin{ 2159931033bSDmitry Chagin struct vdso_timehands *th; 2169931033bSDmitry Chagin uint32_t curr, gen; 2179931033bSDmitry Chagin 2189931033bSDmitry Chagin do { 2199931033bSDmitry Chagin if (!tk->tk_enabled) 2209931033bSDmitry Chagin return (ENOSYS); 2219931033bSDmitry Chagin 2229931033bSDmitry Chagin curr = atomic_load_acq_32(&tk->tk_current); 2239931033bSDmitry Chagin th = &tk->tk_th[curr]; 2249931033bSDmitry Chagin gen = atomic_load_acq_32(&th->th_gen); 2259931033bSDmitry Chagin *bt = th->th_offset; 2269931033bSDmitry Chagin 2279931033bSDmitry Chagin /* 2289931033bSDmitry Chagin * Ensure that the load of th_offset is completed 2299931033bSDmitry Chagin * before the load of th_gen. 2309931033bSDmitry Chagin */ 2319931033bSDmitry Chagin atomic_thread_fence_acq(); 2329931033bSDmitry Chagin } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen); 2339931033bSDmitry Chagin return (0); 2349931033bSDmitry Chagin} 2359931033bSDmitry Chagin 2369931033bSDmitry Chaginstatic int 2379931033bSDmitry Chaginfreebsd_gettimeofday(struct timeval *tv, struct timezone *tz) 2389931033bSDmitry Chagin{ 2399931033bSDmitry Chagin struct vdso_timekeep *tk; 2409931033bSDmitry Chagin struct bintime bt; 2419931033bSDmitry Chagin int error; 2429931033bSDmitry Chagin 2439931033bSDmitry Chagin if (tz != NULL) 2449931033bSDmitry Chagin return (ENOSYS); 2459931033bSDmitry Chagin __vdso_gettimekeep(&tk); 2469931033bSDmitry Chagin if (tk == NULL) 2479931033bSDmitry Chagin return (ENOSYS); 2489931033bSDmitry Chagin if (tk->tk_ver != VDSO_TK_VER_CURR) 2499931033bSDmitry Chagin return (ENOSYS); 2509931033bSDmitry Chagin error = freebsd_binuptime(&bt, tk, true); 2519931033bSDmitry Chagin if (error == 0) 2529931033bSDmitry Chagin bintime2timeval(&bt, tv); 2539931033bSDmitry Chagin return (error); 2549931033bSDmitry Chagin} 2559931033bSDmitry Chagin 2569931033bSDmitry Chaginstatic int 2579931033bSDmitry Chaginfreebsd_clock_gettime(clockid_t clock_id, struct timespec *ts) 2589931033bSDmitry Chagin{ 2599931033bSDmitry Chagin struct vdso_timekeep *tk; 2609931033bSDmitry Chagin struct bintime bt; 2619931033bSDmitry Chagin int error; 2629931033bSDmitry Chagin 2639931033bSDmitry Chagin __vdso_gettimekeep(&tk); 2649931033bSDmitry Chagin if (tk == NULL) 2659931033bSDmitry Chagin return (ENOSYS); 2669931033bSDmitry Chagin if (tk->tk_ver != VDSO_TK_VER_CURR) 2679931033bSDmitry Chagin return (ENOSYS); 2689931033bSDmitry Chagin switch (clock_id) { 2699931033bSDmitry Chagin case CLOCK_REALTIME: 2709931033bSDmitry Chagin case CLOCK_REALTIME_PRECISE: 2719931033bSDmitry Chagin case CLOCK_REALTIME_FAST: 2729931033bSDmitry Chagin error = freebsd_binuptime(&bt, tk, true); 2739931033bSDmitry Chagin break; 2749931033bSDmitry Chagin case CLOCK_MONOTONIC: 2759931033bSDmitry Chagin case CLOCK_MONOTONIC_PRECISE: 2769931033bSDmitry Chagin case CLOCK_UPTIME: 2779931033bSDmitry Chagin case CLOCK_UPTIME_PRECISE: 2789931033bSDmitry Chagin error = freebsd_binuptime(&bt, tk, false); 2799931033bSDmitry Chagin break; 2809931033bSDmitry Chagin case CLOCK_MONOTONIC_FAST: 2819931033bSDmitry Chagin case CLOCK_UPTIME_FAST: 2829931033bSDmitry Chagin error = freebsd_getnanouptime(&bt, tk); 2839931033bSDmitry Chagin break; 2849931033bSDmitry Chagin default: 2859931033bSDmitry Chagin error = ENOSYS; 2869931033bSDmitry Chagin break; 2879931033bSDmitry Chagin } 2889931033bSDmitry Chagin if (error == 0) 2899931033bSDmitry Chagin bintime2timespec(&bt, ts); 2909931033bSDmitry Chagin return (error); 2919931033bSDmitry Chagin} 2929931033bSDmitry Chagin 2939931033bSDmitry Chagin/* 2949931033bSDmitry Chagin * Linux vDSO interfaces 2959931033bSDmitry Chagin * 2969931033bSDmitry Chagin */ 2979931033bSDmitry Chaginint 2989931033bSDmitry Chagin__vdso_clock_gettime(clockid_t clock_id, struct l_timespec *lts) 2999931033bSDmitry Chagin{ 3009931033bSDmitry Chagin struct timespec ts; 3019931033bSDmitry Chagin clockid_t which; 3029931033bSDmitry Chagin int error; 3039931033bSDmitry Chagin 3049931033bSDmitry Chagin error = __vdso_linux_to_native_clockid(&which, clock_id); 3059931033bSDmitry Chagin if (error != 0) 3069931033bSDmitry Chagin return (__vdso_clock_gettime_fallback(clock_id, lts)); 3079931033bSDmitry Chagin error = freebsd_clock_gettime(which, &ts); 3089931033bSDmitry Chagin if (error == 0) 3099931033bSDmitry Chagin return (-__vdso_native_to_linux_timespec(lts, &ts)); 3109931033bSDmitry Chagin else 3119931033bSDmitry Chagin return (__vdso_clock_gettime_fallback(clock_id, lts)); 3129931033bSDmitry Chagin} 3139931033bSDmitry Chagin 3149931033bSDmitry Chaginint 3159931033bSDmitry Chagin__vdso_gettimeofday(l_timeval *ltv, struct timezone *tz) 3169931033bSDmitry Chagin{ 3179931033bSDmitry Chagin struct timeval tv; 3189931033bSDmitry Chagin int error; 3199931033bSDmitry Chagin 3209931033bSDmitry Chagin error = freebsd_gettimeofday(&tv, tz); 3219931033bSDmitry Chagin if (error != 0) 3229931033bSDmitry Chagin return (__vdso_gettimeofday_fallback(ltv, tz)); 3239931033bSDmitry Chagin return (-__vdso_native_to_linux_timeval(ltv, &tv)); 3249931033bSDmitry Chagin} 3259931033bSDmitry Chagin 3269931033bSDmitry Chaginint 3279931033bSDmitry Chagin__vdso_clock_getres(clockid_t clock_id, struct l_timespec *lts) 3289931033bSDmitry Chagin{ 3299931033bSDmitry Chagin 3309931033bSDmitry Chagin return (__vdso_clock_getres_fallback(clock_id, lts)); 3319931033bSDmitry Chagin} 3329931033bSDmitry Chagin 3339931033bSDmitry Chagin#if defined(__i386__) || defined(COMPAT_LINUX32) 3349931033bSDmitry Chaginint 3359931033bSDmitry Chagin__vdso_clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts) 3369931033bSDmitry Chagin{ 3379931033bSDmitry Chagin struct timespec ts; 3389931033bSDmitry Chagin clockid_t which; 3399931033bSDmitry Chagin int error; 3409931033bSDmitry Chagin 3419931033bSDmitry Chagin error = __vdso_linux_to_native_clockid(&which, clock_id); 3429931033bSDmitry Chagin if (error != 0) 3439931033bSDmitry Chagin return (__vdso_clock_gettime64_fallback(clock_id, lts)); 3449931033bSDmitry Chagin error = freebsd_clock_gettime(which, &ts); 3459931033bSDmitry Chagin if (error == 0) 3469931033bSDmitry Chagin return(-__vdso_native_to_linux_timespec64(lts, &ts)); 3479931033bSDmitry Chagin else 3489931033bSDmitry Chagin return(__vdso_clock_gettime64_fallback(clock_id, lts)); 3499931033bSDmitry Chagin} 3509931033bSDmitry Chagin 3519931033bSDmitry Chaginint clock_gettime64(clockid_t clock_id, struct l_timespec64 *lts) 3529931033bSDmitry Chagin __attribute__((weak, alias("__vdso_clock_gettime64"))); 3539931033bSDmitry Chagin#endif 3549931033bSDmitry Chagin 355*5a6a4fb2SDmitry Chagin#if defined(__i386__) || defined(__amd64__) 3569931033bSDmitry Chaginint 3579931033bSDmitry Chagin__vdso_getcpu(uint32_t *cpu, uint32_t *node, void *cache) 3589931033bSDmitry Chagin{ 359*5a6a4fb2SDmitry Chagin int ret; 3609931033bSDmitry Chagin 361*5a6a4fb2SDmitry Chagin if (node != NULL) 3629931033bSDmitry Chagin return (__vdso_getcpu_fallback(cpu, node, cache)); 363*5a6a4fb2SDmitry Chagin ret = __vdso_getcpu_try(); 364*5a6a4fb2SDmitry Chagin if (ret < 0) 365*5a6a4fb2SDmitry Chagin return (__vdso_getcpu_fallback(cpu, node, cache)); 366*5a6a4fb2SDmitry Chagin *cpu = ret; 367*5a6a4fb2SDmitry Chagin return (0); 3689931033bSDmitry Chagin} 3699931033bSDmitry Chagin#endif 3709931033bSDmitry Chagin 3719931033bSDmitry Chagin#if defined(__i386__) || defined(__amd64__) 3729931033bSDmitry Chaginint 3739931033bSDmitry Chagin__vdso_time(long *tm) 3749931033bSDmitry Chagin{ 3759931033bSDmitry Chagin 3769931033bSDmitry Chagin return (__vdso_time_fallback(tm)); 3779931033bSDmitry Chagin} 3789931033bSDmitry Chagin#endif 379