1235537Sgber/*- 2235537Sgber * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org> 3235537Sgber * 4235537Sgber * Redistribution and use in source and binary forms, with or without 5235537Sgber * modification, are permitted provided that the following conditions 6235537Sgber * are met: 7235537Sgber * 1. Redistributions of source code must retain the above copyright 8235537Sgber * notice, this list of conditions and the following disclaimer. 9235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 10235537Sgber * notice, this list of conditions and the following disclaimer in the 11235537Sgber * documentation and/or other materials provided with the distribution. 12235537Sgber * 13235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23235537Sgber * SUCH DAMAGE. 24235537Sgber */ 25235537Sgber 26235537Sgber#include <sys/cdefs.h> 27235537Sgber__FBSDID("$FreeBSD$"); 28235537Sgber 29235537Sgber#include <sys/elf.h> 30235537Sgber#include <sys/time.h> 31235537Sgber#include <sys/vdso.h> 32235537Sgber#include <errno.h> 33235537Sgber#include <time.h> 34235537Sgber#include <machine/atomic.h> 35235537Sgber#include "libc_private.h" 36235537Sgber 37235537Sgberstatic u_int 38235537Sgbertc_delta(const struct vdso_timehands *th) 39235537Sgber{ 40235537Sgber 41235537Sgber return ((__vdso_gettc(th) - th->th_offset_count) & 42235537Sgber th->th_counter_mask); 43235537Sgber} 44235537Sgber 45235537Sgberstatic int 46235537Sgberbinuptime(struct bintime *bt, struct vdso_timekeep *tk, int abs) 47235537Sgber{ 48235537Sgber struct vdso_timehands *th; 49235537Sgber uint32_t curr, gen; 50235537Sgber 51235537Sgber do { 52235537Sgber if (!tk->tk_enabled) 53235537Sgber return (ENOSYS); 54235537Sgber 55235537Sgber /* 56235537Sgber * XXXKIB. The load of tk->tk_current should use 57235537Sgber * atomic_load_acq_32 to provide load barrier. But 58235537Sgber * since tk points to r/o mapped page, x86 59235537Sgber * implementation of atomic_load_acq faults. 60235537Sgber */ 61235537Sgber curr = tk->tk_current; 62235537Sgber rmb(); 63235537Sgber th = &tk->tk_th[curr]; 64235537Sgber if (th->th_algo != VDSO_TH_ALGO_1) 65235537Sgber return (ENOSYS); 66235537Sgber gen = th->th_gen; 67235537Sgber *bt = th->th_offset; 68235537Sgber bintime_addx(bt, th->th_scale * tc_delta(th)); 69235537Sgber if (abs) 70235537Sgber bintime_add(bt, &th->th_boottime); 71235537Sgber 72235537Sgber /* 73235537Sgber * Barrier for load of both tk->tk_current and th->th_gen. 74235537Sgber */ 75235537Sgber rmb(); 76235537Sgber } while (curr != tk->tk_current || gen == 0 || gen != th->th_gen); 77235537Sgber return (0); 78235537Sgber} 79235537Sgber 80235537Sgberstatic struct vdso_timekeep *tk; 81235537Sgber 82235537Sgber#pragma weak __vdso_gettimeofday 83235537Sgberint 84235537Sgber__vdso_gettimeofday(struct timeval *tv, struct timezone *tz) 85235537Sgber{ 86235537Sgber struct bintime bt; 87235537Sgber int error; 88235537Sgber 89235537Sgber if (tz != NULL) 90235537Sgber return (ENOSYS); 91235537Sgber if (tk == NULL) { 92235537Sgber error = __vdso_gettimekeep(&tk); 93235537Sgber if (error != 0 || tk == NULL) 94235537Sgber return (ENOSYS); 95235537Sgber } 96235537Sgber if (tk->tk_ver != VDSO_TK_VER_CURR) 97235537Sgber return (ENOSYS); 98235537Sgber error = binuptime(&bt, tk, 1); 99235537Sgber if (error != 0) 100235537Sgber return (error); 101235537Sgber bintime2timeval(&bt, tv); 102235537Sgber return (0); 103235537Sgber} 104235537Sgber 105235537Sgber#pragma weak __vdso_clock_gettime 106235537Sgberint 107235537Sgber__vdso_clock_gettime(clockid_t clock_id, struct timespec *ts) 108235537Sgber{ 109235537Sgber struct bintime bt; 110235537Sgber int abs, error; 111235537Sgber 112235537Sgber if (tk == NULL) { 113235537Sgber error = _elf_aux_info(AT_TIMEKEEP, &tk, sizeof(tk)); 114235537Sgber if (error != 0 || tk == NULL) 115235537Sgber return (ENOSYS); 116235537Sgber } 117235537Sgber if (tk->tk_ver != VDSO_TK_VER_CURR) 118235537Sgber return (ENOSYS); 119235537Sgber switch (clock_id) { 120235537Sgber case CLOCK_REALTIME: 121235537Sgber case CLOCK_REALTIME_PRECISE: 122235537Sgber case CLOCK_REALTIME_FAST: 123235537Sgber case CLOCK_SECOND: 124235537Sgber abs = 1; 125235537Sgber break; 126235537Sgber case CLOCK_MONOTONIC: 127235537Sgber case CLOCK_MONOTONIC_PRECISE: 128235537Sgber case CLOCK_MONOTONIC_FAST: 129235537Sgber case CLOCK_UPTIME: 130235537Sgber case CLOCK_UPTIME_PRECISE: 131235537Sgber case CLOCK_UPTIME_FAST: 132235537Sgber abs = 0; 133235537Sgber break; 134235537Sgber default: 135235537Sgber return (ENOSYS); 136235537Sgber } 137235537Sgber error = binuptime(&bt, tk, abs); 138235537Sgber if (error != 0) 139235537Sgber return (error); 140235537Sgber bintime2timespec(&bt, ts); 141 if (clock_id == CLOCK_SECOND) 142 ts->tv_nsec = 0; 143 return (0); 144} 145