1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * vdso_full_test.c: Sample code to test all the timers. 4 * Copyright (c) 2019 Arm Ltd. 5 * 6 * Compile with: 7 * gcc -std=gnu99 vdso_full_test.c parse_vdso.c 8 * 9 */ 10 11 #include <stdint.h> 12 #include <elf.h> 13 #include <stdio.h> 14 #include <time.h> 15 #include <sys/auxv.h> 16 #include <sys/time.h> 17 #define _GNU_SOURCE 18 #include <unistd.h> 19 #include <sys/syscall.h> 20 21 #include "../kselftest.h" 22 #include "vdso_config.h" 23 24 extern void *vdso_sym(const char *version, const char *name); 25 extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); 26 extern void vdso_init_from_auxv(void *auxv); 27 28 static const char *version; 29 static const char **name; 30 31 typedef long (*vdso_gettimeofday_t)(struct timeval *tv, struct timezone *tz); 32 typedef long (*vdso_clock_gettime_t)(clockid_t clk_id, struct timespec *ts); 33 typedef long (*vdso_clock_getres_t)(clockid_t clk_id, struct timespec *ts); 34 typedef time_t (*vdso_time_t)(time_t *t); 35 36 #define VDSO_TEST_PASS_MSG() "\n%s(): PASS\n", __func__ 37 #define VDSO_TEST_FAIL_MSG(x) "\n%s(): %s FAIL\n", __func__, x 38 #define VDSO_TEST_SKIP_MSG(x) "\n%s(): SKIP: Could not find %s\n", __func__, x 39 40 static void vdso_test_gettimeofday(void) 41 { 42 /* Find gettimeofday. */ 43 vdso_gettimeofday_t vdso_gettimeofday = 44 (vdso_gettimeofday_t)vdso_sym(version, name[0]); 45 46 if (!vdso_gettimeofday) { 47 ksft_test_result_skip(VDSO_TEST_SKIP_MSG(name[0])); 48 return; 49 } 50 51 struct timeval tv; 52 long ret = vdso_gettimeofday(&tv, 0); 53 54 if (ret == 0) { 55 ksft_print_msg("The time is %lld.%06lld\n", 56 (long long)tv.tv_sec, (long long)tv.tv_usec); 57 ksft_test_result_pass(VDSO_TEST_PASS_MSG()); 58 } else { 59 ksft_test_result_fail(VDSO_TEST_FAIL_MSG(name[0])); 60 } 61 } 62 63 static void vdso_test_clock_gettime(clockid_t clk_id) 64 { 65 /* Find clock_gettime. */ 66 vdso_clock_gettime_t vdso_clock_gettime = 67 (vdso_clock_gettime_t)vdso_sym(version, name[1]); 68 69 if (!vdso_clock_gettime) { 70 ksft_test_result_skip(VDSO_TEST_SKIP_MSG(name[1])); 71 return; 72 } 73 74 struct timespec ts; 75 long ret = vdso_clock_gettime(clk_id, &ts); 76 77 if (ret == 0) { 78 ksft_print_msg("The time is %lld.%06lld\n", 79 (long long)ts.tv_sec, (long long)ts.tv_nsec); 80 ksft_test_result_pass(VDSO_TEST_PASS_MSG()); 81 } else { 82 ksft_test_result_fail(VDSO_TEST_FAIL_MSG(name[1])); 83 } 84 } 85 86 static void vdso_test_time(void) 87 { 88 /* Find time. */ 89 vdso_time_t vdso_time = 90 (vdso_time_t)vdso_sym(version, name[2]); 91 92 if (!vdso_time) { 93 ksft_test_result_skip(VDSO_TEST_SKIP_MSG(name[2])); 94 return; 95 } 96 97 long ret = vdso_time(NULL); 98 99 if (ret > 0) { 100 ksft_print_msg("The time in hours since January 1, 1970 is %lld\n", 101 (long long)(ret / 3600)); 102 ksft_test_result_pass(VDSO_TEST_PASS_MSG()); 103 } else { 104 ksft_test_result_fail(VDSO_TEST_FAIL_MSG(name[2])); 105 } 106 } 107 108 static void vdso_test_clock_getres(clockid_t clk_id) 109 { 110 int clock_getres_fail = 0; 111 112 /* Find clock_getres. */ 113 vdso_clock_getres_t vdso_clock_getres = 114 (vdso_clock_getres_t)vdso_sym(version, name[3]); 115 116 if (!vdso_clock_getres) { 117 ksft_test_result_skip(VDSO_TEST_SKIP_MSG(name[3])); 118 return; 119 } 120 121 struct timespec ts, sys_ts; 122 long ret = vdso_clock_getres(clk_id, &ts); 123 124 if (ret == 0) { 125 ksft_print_msg("The vdso resolution is %lld %lld\n", 126 (long long)ts.tv_sec, (long long)ts.tv_nsec); 127 } else { 128 clock_getres_fail++; 129 } 130 131 ret = syscall(SYS_clock_getres, clk_id, &sys_ts); 132 133 ksft_print_msg("The syscall resolution is %lld %lld\n", 134 (long long)sys_ts.tv_sec, (long long)sys_ts.tv_nsec); 135 136 if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) 137 clock_getres_fail++; 138 139 if (clock_getres_fail > 0) { 140 ksft_test_result_fail(VDSO_TEST_FAIL_MSG(name[3])); 141 } else { 142 ksft_test_result_pass(VDSO_TEST_PASS_MSG()); 143 } 144 } 145 146 const char *vdso_clock_name[12] = { 147 "CLOCK_REALTIME", 148 "CLOCK_MONOTONIC", 149 "CLOCK_PROCESS_CPUTIME_ID", 150 "CLOCK_THREAD_CPUTIME_ID", 151 "CLOCK_MONOTONIC_RAW", 152 "CLOCK_REALTIME_COARSE", 153 "CLOCK_MONOTONIC_COARSE", 154 "CLOCK_BOOTTIME", 155 "CLOCK_REALTIME_ALARM", 156 "CLOCK_BOOTTIME_ALARM", 157 "CLOCK_SGI_CYCLE", 158 "CLOCK_TAI", 159 }; 160 161 /* 162 * This function calls vdso_test_clock_gettime and vdso_test_clock_getres 163 * with different values for clock_id. 164 */ 165 static inline void vdso_test_clock(clockid_t clock_id) 166 { 167 ksft_print_msg("\nclock_id: %s\n", vdso_clock_name[clock_id]); 168 169 vdso_test_clock_gettime(clock_id); 170 171 vdso_test_clock_getres(clock_id); 172 } 173 174 #define VDSO_TEST_PLAN 16 175 176 int main(int argc, char **argv) 177 { 178 unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); 179 180 ksft_print_header(); 181 ksft_set_plan(VDSO_TEST_PLAN); 182 183 if (!sysinfo_ehdr) { 184 printf("AT_SYSINFO_EHDR is not present!\n"); 185 return KSFT_SKIP; 186 } 187 188 version = versions[VDSO_VERSION]; 189 name = (const char **)&names[VDSO_NAMES]; 190 191 printf("[vDSO kselftest] VDSO_VERSION: %s\n", version); 192 193 vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); 194 195 vdso_test_gettimeofday(); 196 197 #if _POSIX_TIMERS > 0 198 199 #ifdef CLOCK_REALTIME 200 vdso_test_clock(CLOCK_REALTIME); 201 #endif 202 203 #ifdef CLOCK_BOOTTIME 204 vdso_test_clock(CLOCK_BOOTTIME); 205 #endif 206 207 #ifdef CLOCK_TAI 208 vdso_test_clock(CLOCK_TAI); 209 #endif 210 211 #ifdef CLOCK_REALTIME_COARSE 212 vdso_test_clock(CLOCK_REALTIME_COARSE); 213 #endif 214 215 #ifdef CLOCK_MONOTONIC 216 vdso_test_clock(CLOCK_MONOTONIC); 217 #endif 218 219 #ifdef CLOCK_MONOTONIC_RAW 220 vdso_test_clock(CLOCK_MONOTONIC_RAW); 221 #endif 222 223 #ifdef CLOCK_MONOTONIC_COARSE 224 vdso_test_clock(CLOCK_MONOTONIC_COARSE); 225 #endif 226 227 #endif 228 229 vdso_test_time(); 230 231 ksft_print_cnts(); 232 return ksft_get_fail_cnt() == 0 ? KSFT_PASS : KSFT_FAIL; 233 } 234