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