/* gettimeofday.c COPYRIGHT FUJITSU LIMITED 2016 */ #include #include #include #include extern int __kernel_gettimeofday(struct timeval *tv, void *tz); static inline void cpu_pause_for_vsyscall(void) { asm volatile ("yield" ::: "memory"); return; } static inline void calculate_time_from_tsc(struct timespec *ts, struct tod_data_s *tod_data) { long ver; unsigned long current_tsc; __time_t sec_delta; long ns_delta; for (;;) { while ((ver = ihk_atomic64_read(&tod_data->version)) & 1) { /* settimeofday() is in progress */ cpu_pause_for_vsyscall(); } rmb(); *ts = tod_data->origin; rmb(); if (ver == ihk_atomic64_read(&tod_data->version)) { break; } /* settimeofday() has intervened */ cpu_pause_for_vsyscall(); } current_tsc = rdtsc(); sec_delta = current_tsc / tod_data->clocks_per_sec; ns_delta = NS_PER_SEC * (current_tsc % tod_data->clocks_per_sec) / tod_data->clocks_per_sec; /* calc. of ns_delta overflows if clocks_per_sec exceeds 18.44 GHz */ ts->tv_sec += sec_delta; ts->tv_nsec += ns_delta; if (ts->tv_nsec >= NS_PER_SEC) { ts->tv_nsec -= NS_PER_SEC; ++ts->tv_sec; } return; } static inline struct tod_data_s *get_tod_data_addr(void) { unsigned long addr; asm volatile("adr %0, _tod_data\n" : "=r" (addr) : : "memory"); return (struct tod_data_s *)addr; } int __kernel_gettimeofday(struct timeval *tv, void *tz) { long ret; struct tod_data_s *tod_data; struct timespec ats; if(!tv && !tz) { /* nothing to do */ return 0; } tod_data = get_tod_data_addr(); /* DO it locally if supported */ if (!tz && tod_data->do_local) { calculate_time_from_tsc(&ats, tod_data); tv->tv_sec = ats.tv_sec; tv->tv_usec = ats.tv_nsec / 1000; return 0; } /* Otherwize syscall */ asm volatile("mov w8, %w1\n" "mov x0, %2\n" "mov x1, %3\n" "svc #0\n" "mov %0, x0\n" : "=r" (ret) : "r" (__NR_gettimeofday), "r"(tv), "r"(tz) : "memory"); if (ret) { *(int *)0 = 0; /* i.e. raise(SIGSEGV) */ } return (int)ret; } /* * The IDs of the various system clocks (for POSIX.1b interval timers): * @ref.impl include/uapi/linux/time.h */ // #define CLOCK_REALTIME 0 // #define CLOCK_MONOTONIC 1 // #define CLOCK_PROCESS_CPUTIME_ID 2 // #define CLOCK_THREAD_CPUTIME_ID 3 #define CLOCK_MONOTONIC_RAW 4 #define CLOCK_REALTIME_COARSE 5 #define CLOCK_MONOTONIC_COARSE 6 #define CLOCK_BOOTTIME 7 #define CLOCK_REALTIME_ALARM 8 #define CLOCK_BOOTTIME_ALARM 9 #define CLOCK_SGI_CYCLE 10 /* Hardware specific */ #define CLOCK_TAI 11 #define HIGH_RES_NSEC 1 /* nsec. */ #define CLOCK_REALTIME_RES HIGH_RES_NSEC #define CLOCK_COARSE_RES ((NS_PER_SEC+CONFIG_HZ/2)/CONFIG_HZ) /* 10,000,000 nsec*/ typedef int clockid_t; int __kernel_clock_gettime(clockid_t clk_id, struct timespec *tp) { long ret; struct tod_data_s *tod_data; struct timespec ats; if (!tp) { /* nothing to do */ return 0; } tod_data = get_tod_data_addr(); /* DO it locally if supported */ if (tod_data->do_local && clk_id == CLOCK_REALTIME) { calculate_time_from_tsc(&ats, tod_data); tp->tv_sec = ats.tv_sec; tp->tv_nsec = ats.tv_nsec; return 0; } /* Otherwize syscall */ asm volatile("mov w8, %w1\n" "mov x0, %2\n" "mov x1, %3\n" "svc #0\n" "mov %0, x0\n" : "=r" (ret) : "r" (__NR_clock_gettime), "r"(clk_id), "r"(tp) : "memory"); return (int)ret; } int __kernel_clock_getres(clockid_t clk_id, struct timespec *res) { long ret; if (!res) { /* nothing to do */ return 0; } switch (clk_id) { case CLOCK_REALTIME: case CLOCK_MONOTONIC: res->tv_sec = 0; res->tv_nsec = CLOCK_REALTIME_RES; return 0; break; case CLOCK_REALTIME_COARSE: case CLOCK_MONOTONIC_COARSE: res->tv_sec = 0; res->tv_nsec = CLOCK_COARSE_RES; return 0; break; default: break; } /* Otherwise syscall */ asm volatile("mov w8, %w1\n" "mov x0, %2\n" "mov x1, %3\n" "svc #0\n" "mov %0, x0\n" : "=r" (ret) : "r" (__NR_clock_getres), "r"(clk_id), "r"(res) : "memory"); return (int)ret; }