1/*- 2 * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26#include <sys/cdefs.h> 27__FBSDID("$FreeBSD$"); 28 29#include <sys/elf.h> 30#include <sys/time.h> 31#include <sys/vdso.h> 32#include <errno.h> 33#include <time.h> 34#include <machine/atomic.h> 35#include "libc_private.h" 36 37static u_int 38tc_delta(const struct vdso_timehands *th) 39{ 40 41 return ((__vdso_gettc(th) - th->th_offset_count) & 42 th->th_counter_mask); 43} 44 45static int 46binuptime(struct bintime *bt, struct vdso_timekeep *tk, int abs) 47{ 48 struct vdso_timehands *th; 49 uint32_t curr, gen; 50 51 do { 52 if (!tk->tk_enabled) 53 return (ENOSYS); 54 55 /* 56 * XXXKIB. The load of tk->tk_current should use 57 * atomic_load_acq_32 to provide load barrier. But 58 * since tk points to r/o mapped page, x86 59 * implementation of atomic_load_acq faults. 60 */ 61 curr = tk->tk_current; 62 rmb(); 63 th = &tk->tk_th[curr]; 64 if (th->th_algo != VDSO_TH_ALGO_1) 65 return (ENOSYS); 66 gen = th->th_gen; 67 *bt = th->th_offset; 68 bintime_addx(bt, th->th_scale * tc_delta(th)); 69 if (abs) 70 bintime_add(bt, &th->th_boottime); 71 72 /* 73 * Barrier for load of both tk->tk_current and th->th_gen. 74 */ 75 rmb(); 76 } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen); 77 return (0); 78} 79 80static struct vdso_timekeep *tk; 81 82#pragma weak __vdso_gettimeofday 83int 84__vdso_gettimeofday(struct timeval *tv, struct timezone *tz) 85{ 86 struct bintime bt; 87 int error; 88 89 if (tz != NULL) 90 return (ENOSYS); 91 if (tk == NULL) { 92 error = __vdso_gettimekeep(&tk); 93 if (error != 0 || tk == NULL) 94 return (ENOSYS); 95 } 96 if (tk->tk_ver != VDSO_TK_VER_CURR) 97 return (ENOSYS); 98 error = binuptime(&bt, tk, 1); 99 if (error != 0) 100 return (error); 101 bintime2timeval(&bt, tv); 102 return (0); 103} 104 105#pragma weak __vdso_clock_gettime 106int 107__vdso_clock_gettime(clockid_t clock_id, struct timespec *ts) 108{ 109 struct bintime bt; 110 int abs, error; 111 112 if (tk == NULL) { 113 error = _elf_aux_info(AT_TIMEKEEP, &tk, sizeof(tk)); 114 if (error != 0 || tk == NULL) 115 return (ENOSYS); 116 } 117 if (tk->tk_ver != VDSO_TK_VER_CURR) 118 return (ENOSYS); 119 switch (clock_id) { 120 case CLOCK_REALTIME: 121 case CLOCK_REALTIME_PRECISE: 122 case CLOCK_REALTIME_FAST: 123 case CLOCK_SECOND: 124 abs = 1; 125 break; 126 case CLOCK_MONOTONIC: 127 case CLOCK_MONOTONIC_PRECISE: 128 case CLOCK_MONOTONIC_FAST: 129 case CLOCK_UPTIME: 130 case CLOCK_UPTIME_PRECISE: 131 case CLOCK_UPTIME_FAST: 132 abs = 0; 133 break; 134 default: 135 return (ENOSYS); 136 } 137 error = binuptime(&bt, tk, abs); 138 if (error != 0) 139 return (error); 140 bintime2timespec(&bt, ts); 141 if (clock_id == CLOCK_SECOND) 142 ts->tv_nsec = 0; 143 return (0); 144} 145