| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * vdso_full_test.c: Sample code to test all the timers. |
| * Copyright (c) 2019 Arm Ltd. |
| * |
| * Compile with: |
| * gcc -std=gnu99 vdso_full_test.c parse_vdso.c |
| * |
| */ |
| |
| #include <stdint.h> |
| #include <elf.h> |
| #include <stdio.h> |
| #include <time.h> |
| #include <sys/auxv.h> |
| #include <sys/time.h> |
| #define _GNU_SOURCE |
| #include <unistd.h> |
| #include <sys/syscall.h> |
| |
| #include "../kselftest.h" |
| #include "vdso_config.h" |
| #include "vdso_call.h" |
| #include "parse_vdso.h" |
| |
| static const char *version; |
| static const char **name; |
| |
| typedef long (*vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz); |
| typedef long (*vdso_clock_gettime_t)(clockid_t clk_id, struct timespec *ts); |
| typedef long (*vdso_clock_getres_t)(clockid_t clk_id, struct timespec *ts); |
| typedef time_t (*vdso_time_t)(time_t *t); |
| |
| const char *vdso_clock_name[12] = { |
| "CLOCK_REALTIME", |
| "CLOCK_MONOTONIC", |
| "CLOCK_PROCESS_CPUTIME_ID", |
| "CLOCK_THREAD_CPUTIME_ID", |
| "CLOCK_MONOTONIC_RAW", |
| "CLOCK_REALTIME_COARSE", |
| "CLOCK_MONOTONIC_COARSE", |
| "CLOCK_BOOTTIME", |
| "CLOCK_REALTIME_ALARM", |
| "CLOCK_BOOTTIME_ALARM", |
| "CLOCK_SGI_CYCLE", |
| "CLOCK_TAI", |
| }; |
| |
| static void vdso_test_gettimeofday(void) |
| { |
| /* Find gettimeofday. */ |
| vdso_gettimeofday_t vdso_gettimeofday = |
| (vdso_gettimeofday_t)vdso_sym(version, name[0]); |
| |
| if (!vdso_gettimeofday) { |
| ksft_print_msg("Couldn't find %s\n", name[0]); |
| ksft_test_result_skip("%s\n", name[0]); |
| return; |
| } |
| |
| struct timeval tv; |
| long ret = VDSO_CALL(vdso_gettimeofday, 2, &tv, 0); |
| |
| if (ret == 0) { |
| ksft_print_msg("The time is %lld.%06lld\n", |
| (long long)tv.tv_sec, (long long)tv.tv_usec); |
| ksft_test_result_pass("%s\n", name[0]); |
| } else { |
| ksft_test_result_fail("%s\n", name[0]); |
| } |
| } |
| |
| static void vdso_test_clock_gettime(clockid_t clk_id) |
| { |
| /* Find clock_gettime. */ |
| vdso_clock_gettime_t vdso_clock_gettime = |
| (vdso_clock_gettime_t)vdso_sym(version, name[1]); |
| |
| if (!vdso_clock_gettime) { |
| ksft_print_msg("Couldn't find %s\n", name[1]); |
| ksft_test_result_skip("%s %s\n", name[1], |
| vdso_clock_name[clk_id]); |
| return; |
| } |
| |
| struct timespec ts; |
| long ret = VDSO_CALL(vdso_clock_gettime, 2, clk_id, &ts); |
| |
| if (ret == 0) { |
| ksft_print_msg("The time is %lld.%06lld\n", |
| (long long)ts.tv_sec, (long long)ts.tv_nsec); |
| ksft_test_result_pass("%s %s\n", name[1], |
| vdso_clock_name[clk_id]); |
| } else { |
| ksft_test_result_fail("%s %s\n", name[1], |
| vdso_clock_name[clk_id]); |
| } |
| } |
| |
| static void vdso_test_time(void) |
| { |
| /* Find time. */ |
| vdso_time_t vdso_time = |
| (vdso_time_t)vdso_sym(version, name[2]); |
| |
| if (!vdso_time) { |
| ksft_print_msg("Couldn't find %s\n", name[2]); |
| ksft_test_result_skip("%s\n", name[2]); |
| return; |
| } |
| |
| long ret = VDSO_CALL(vdso_time, 1, NULL); |
| |
| if (ret > 0) { |
| ksft_print_msg("The time in hours since January 1, 1970 is %lld\n", |
| (long long)(ret / 3600)); |
| ksft_test_result_pass("%s\n", name[2]); |
| } else { |
| ksft_test_result_fail("%s\n", name[2]); |
| } |
| } |
| |
| static void vdso_test_clock_getres(clockid_t clk_id) |
| { |
| int clock_getres_fail = 0; |
| |
| /* Find clock_getres. */ |
| vdso_clock_getres_t vdso_clock_getres = |
| (vdso_clock_getres_t)vdso_sym(version, name[3]); |
| |
| if (!vdso_clock_getres) { |
| ksft_print_msg("Couldn't find %s\n", name[3]); |
| ksft_test_result_skip("%s %s\n", name[3], |
| vdso_clock_name[clk_id]); |
| return; |
| } |
| |
| struct timespec ts, sys_ts; |
| long ret = VDSO_CALL(vdso_clock_getres, 2, clk_id, &ts); |
| |
| if (ret == 0) { |
| ksft_print_msg("The vdso resolution is %lld %lld\n", |
| (long long)ts.tv_sec, (long long)ts.tv_nsec); |
| } else { |
| clock_getres_fail++; |
| } |
| |
| ret = syscall(SYS_clock_getres, clk_id, &sys_ts); |
| |
| ksft_print_msg("The syscall resolution is %lld %lld\n", |
| (long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec); |
| |
| if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) |
| clock_getres_fail++; |
| |
| if (clock_getres_fail > 0) { |
| ksft_test_result_fail("%s %s\n", name[3], |
| vdso_clock_name[clk_id]); |
| } else { |
| ksft_test_result_pass("%s %s\n", name[3], |
| vdso_clock_name[clk_id]); |
| } |
| } |
| |
| /* |
| * This function calls vdso_test_clock_gettime and vdso_test_clock_getres |
| * with different values for clock_id. |
| */ |
| static inline void vdso_test_clock(clockid_t clock_id) |
| { |
| ksft_print_msg("clock_id: %s\n", vdso_clock_name[clock_id]); |
| |
| vdso_test_clock_gettime(clock_id); |
| |
| vdso_test_clock_getres(clock_id); |
| } |
| |
| #define VDSO_TEST_PLAN 16 |
| |
| int main(int argc, char **argv) |
| { |
| unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); |
| |
| ksft_print_header(); |
| ksft_set_plan(VDSO_TEST_PLAN); |
| |
| if (!sysinfo_ehdr) { |
| ksft_print_msg("AT_SYSINFO_EHDR is not present!\n"); |
| return KSFT_SKIP; |
| } |
| |
| version = versions[VDSO_VERSION]; |
| name = (const char **)&names[VDSO_NAMES]; |
| |
| ksft_print_msg("[vDSO kselftest] VDSO_VERSION: %s\n", version); |
| |
| vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); |
| |
| vdso_test_gettimeofday(); |
| |
| #if _POSIX_TIMERS > 0 |
| |
| #ifdef CLOCK_REALTIME |
| vdso_test_clock(CLOCK_REALTIME); |
| #endif |
| |
| #ifdef CLOCK_BOOTTIME |
| vdso_test_clock(CLOCK_BOOTTIME); |
| #endif |
| |
| #ifdef CLOCK_TAI |
| vdso_test_clock(CLOCK_TAI); |
| #endif |
| |
| #ifdef CLOCK_REALTIME_COARSE |
| vdso_test_clock(CLOCK_REALTIME_COARSE); |
| #endif |
| |
| #ifdef CLOCK_MONOTONIC |
| vdso_test_clock(CLOCK_MONOTONIC); |
| #endif |
| |
| #ifdef CLOCK_MONOTONIC_RAW |
| vdso_test_clock(CLOCK_MONOTONIC_RAW); |
| #endif |
| |
| #ifdef CLOCK_MONOTONIC_COARSE |
| vdso_test_clock(CLOCK_MONOTONIC_COARSE); |
| #endif |
| |
| #endif |
| |
| vdso_test_time(); |
| |
| ksft_print_cnts(); |
| return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; |
| } |