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 static int vdso_test_gettimeofday(void) 37 { 38 /* Find gettimeofday. */ 39 vdso_gettimeofday_t vdso_gettimeofday = 40 (vdso_gettimeofday_t)vdso_sym(version, name[0]); 41 42 if (!vdso_gettimeofday) { 43 printf("Could not find %s\n", name[0]); 44 return KSFT_SKIP; 45 } 46 47 struct timeval tv; 48 long ret = vdso_gettimeofday(&tv, 0); 49 50 if (ret == 0) { 51 printf("The time is %lld.%06lld\n", 52 (long long)tv.tv_sec, (long long)tv.tv_usec); 53 } else { 54 printf("%s failed\n", name[0]); 55 return KSFT_FAIL; 56 } 57 58 return KSFT_PASS; 59 } 60 61 static int vdso_test_clock_gettime(clockid_t clk_id) 62 { 63 /* Find clock_gettime. */ 64 vdso_clock_gettime_t vdso_clock_gettime = 65 (vdso_clock_gettime_t)vdso_sym(version, name[1]); 66 67 if (!vdso_clock_gettime) { 68 printf("Could not find %s\n", name[1]); 69 return KSFT_SKIP; 70 } 71 72 struct timespec ts; 73 long ret = vdso_clock_gettime(clk_id, &ts); 74 75 if (ret == 0) { 76 printf("The time is %lld.%06lld\n", 77 (long long)ts.tv_sec, (long long)ts.tv_nsec); 78 } else { 79 printf("%s failed\n", name[1]); 80 return KSFT_FAIL; 81 } 82 83 return KSFT_PASS; 84 } 85 86 static int 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 printf("Could not find %s\n", name[2]); 94 return KSFT_SKIP; 95 } 96 97 long ret = vdso_time(NULL); 98 99 if (ret > 0) { 100 printf("The time in hours since January 1, 1970 is %lld\n", 101 (long long)(ret / 3600)); 102 } else { 103 printf("%s failed\n", name[2]); 104 return KSFT_FAIL; 105 } 106 107 return KSFT_PASS; 108 } 109 110 static int vdso_test_clock_getres(clockid_t clk_id) 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 printf("Could not find %s\n", name[3]); 118 return KSFT_SKIP; 119 } 120 121 struct timespec ts, sys_ts; 122 long ret = vdso_clock_getres(clk_id, &ts); 123 124 if (ret == 0) { 125 printf("The resolution is %lld %lld\n", 126 (long long)ts.tv_sec, (long long)ts.tv_nsec); 127 } else { 128 printf("%s failed\n", name[3]); 129 return KSFT_FAIL; 130 } 131 132 ret = syscall(SYS_clock_getres, clk_id, &sys_ts); 133 134 if ((sys_ts.tv_sec != ts.tv_sec) || (sys_ts.tv_nsec != ts.tv_nsec)) { 135 printf("%s failed\n", name[3]); 136 return KSFT_FAIL; 137 } 138 139 return KSFT_PASS; 140 } 141 142 const char *vdso_clock_name[12] = { 143 "CLOCK_REALTIME", 144 "CLOCK_MONOTONIC", 145 "CLOCK_PROCESS_CPUTIME_ID", 146 "CLOCK_THREAD_CPUTIME_ID", 147 "CLOCK_MONOTONIC_RAW", 148 "CLOCK_REALTIME_COARSE", 149 "CLOCK_MONOTONIC_COARSE", 150 "CLOCK_BOOTTIME", 151 "CLOCK_REALTIME_ALARM", 152 "CLOCK_BOOTTIME_ALARM", 153 "CLOCK_SGI_CYCLE", 154 "CLOCK_TAI", 155 }; 156 157 /* 158 * This function calls vdso_test_clock_gettime and vdso_test_clock_getres 159 * with different values for clock_id. 160 */ 161 static inline int vdso_test_clock(clockid_t clock_id) 162 { 163 int ret0, ret1; 164 165 ret0 = vdso_test_clock_gettime(clock_id); 166 /* A skipped test is considered passed */ 167 if (ret0 == KSFT_SKIP) 168 ret0 = KSFT_PASS; 169 170 ret1 = vdso_test_clock_getres(clock_id); 171 /* A skipped test is considered passed */ 172 if (ret1 == KSFT_SKIP) 173 ret1 = KSFT_PASS; 174 175 ret0 += ret1; 176 177 printf("clock_id: %s", vdso_clock_name[clock_id]); 178 179 if (ret0 > 0) 180 printf(" [FAIL]\n"); 181 else 182 printf(" [PASS]\n"); 183 184 return ret0; 185 } 186 187 int main(int argc, char **argv) 188 { 189 unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); 190 int ret; 191 192 if (!sysinfo_ehdr) { 193 printf("AT_SYSINFO_EHDR is not present!\n"); 194 return KSFT_SKIP; 195 } 196 197 version = versions[VDSO_VERSION]; 198 name = (const char **)&names[VDSO_NAMES]; 199 200 printf("[vDSO kselftest] VDSO_VERSION: %s\n", version); 201 202 vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); 203 204 ret = vdso_test_gettimeofday(); 205 206 #if _POSIX_TIMERS > 0 207 208 #ifdef CLOCK_REALTIME 209 ret += vdso_test_clock(CLOCK_REALTIME); 210 #endif 211 212 #ifdef CLOCK_BOOTTIME 213 ret += vdso_test_clock(CLOCK_BOOTTIME); 214 #endif 215 216 #ifdef CLOCK_TAI 217 ret += vdso_test_clock(CLOCK_TAI); 218 #endif 219 220 #ifdef CLOCK_REALTIME_COARSE 221 ret += vdso_test_clock(CLOCK_REALTIME_COARSE); 222 #endif 223 224 #ifdef CLOCK_MONOTONIC 225 ret += vdso_test_clock(CLOCK_MONOTONIC); 226 #endif 227 228 #ifdef CLOCK_MONOTONIC_RAW 229 ret += vdso_test_clock(CLOCK_MONOTONIC_RAW); 230 #endif 231 232 #ifdef CLOCK_MONOTONIC_COARSE 233 ret += vdso_test_clock(CLOCK_MONOTONIC_COARSE); 234 #endif 235 236 #endif 237 238 ret += vdso_test_time(); 239 240 if (ret > 0) 241 return KSFT_FAIL; 242 243 return KSFT_PASS; 244 } 245