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