1 | /* valid adjtimex test |
2 | * by: John Stultz <john.stultz@linaro.org> |
3 | * (C) Copyright Linaro 2015 |
4 | * Licensed under the GPLv2 |
5 | * |
6 | * This test validates adjtimex interface with valid |
7 | * and invalid test data. |
8 | * |
9 | * Usage: valid-adjtimex |
10 | * |
11 | * To build: |
12 | * $ gcc valid-adjtimex.c -o valid-adjtimex -lrt |
13 | * |
14 | * This program is free software: you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation, either version 2 of the License, or |
17 | * (at your option) any later version. |
18 | * |
19 | * This program is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. |
23 | */ |
24 | #include <stdio.h> |
25 | #include <stdlib.h> |
26 | #include <time.h> |
27 | #include <sys/time.h> |
28 | #include <sys/timex.h> |
29 | #include <string.h> |
30 | #include <signal.h> |
31 | #include <unistd.h> |
32 | #include "../kselftest.h" |
33 | |
34 | #define NSEC_PER_SEC 1000000000LL |
35 | #define USEC_PER_SEC 1000000LL |
36 | |
37 | #define ADJ_SETOFFSET 0x0100 |
38 | |
39 | #include <sys/syscall.h> |
40 | int clock_adjtime(clockid_t id, struct timex *tx) |
41 | { |
42 | return syscall(__NR_clock_adjtime, id, tx); |
43 | } |
44 | |
45 | |
46 | /* clear NTP time_status & time_state */ |
47 | int clear_time_state(void) |
48 | { |
49 | struct timex tx; |
50 | int ret; |
51 | |
52 | tx.modes = ADJ_STATUS; |
53 | tx.status = 0; |
54 | ret = adjtimex(&tx); |
55 | return ret; |
56 | } |
57 | |
58 | #define NUM_FREQ_VALID 32 |
59 | #define NUM_FREQ_OUTOFRANGE 4 |
60 | #define NUM_FREQ_INVALID 2 |
61 | |
62 | #define SHIFTED_PPM (1 << 16) |
63 | |
64 | long valid_freq[NUM_FREQ_VALID] = { |
65 | -499 * SHIFTED_PPM, |
66 | -450 * SHIFTED_PPM, |
67 | -400 * SHIFTED_PPM, |
68 | -350 * SHIFTED_PPM, |
69 | -300 * SHIFTED_PPM, |
70 | -250 * SHIFTED_PPM, |
71 | -200 * SHIFTED_PPM, |
72 | -150 * SHIFTED_PPM, |
73 | -100 * SHIFTED_PPM, |
74 | -75 * SHIFTED_PPM, |
75 | -50 * SHIFTED_PPM, |
76 | -25 * SHIFTED_PPM, |
77 | -10 * SHIFTED_PPM, |
78 | -5 * SHIFTED_PPM, |
79 | -1 * SHIFTED_PPM, |
80 | -1000, |
81 | 1 * SHIFTED_PPM, |
82 | 5 * SHIFTED_PPM, |
83 | 10 * SHIFTED_PPM, |
84 | 25 * SHIFTED_PPM, |
85 | 50 * SHIFTED_PPM, |
86 | 75 * SHIFTED_PPM, |
87 | 100 * SHIFTED_PPM, |
88 | 150 * SHIFTED_PPM, |
89 | 200 * SHIFTED_PPM, |
90 | 250 * SHIFTED_PPM, |
91 | 300 * SHIFTED_PPM, |
92 | 350 * SHIFTED_PPM, |
93 | 400 * SHIFTED_PPM, |
94 | 450 * SHIFTED_PPM, |
95 | 499 * SHIFTED_PPM, |
96 | }; |
97 | |
98 | long outofrange_freq[NUM_FREQ_OUTOFRANGE] = { |
99 | -1000 * SHIFTED_PPM, |
100 | -550 * SHIFTED_PPM, |
101 | 550 * SHIFTED_PPM, |
102 | 1000 * SHIFTED_PPM, |
103 | }; |
104 | |
105 | #define LONG_MAX (~0UL>>1) |
106 | #define LONG_MIN (-LONG_MAX - 1) |
107 | |
108 | long invalid_freq[NUM_FREQ_INVALID] = { |
109 | LONG_MAX, |
110 | LONG_MIN, |
111 | }; |
112 | |
113 | int validate_freq(void) |
114 | { |
115 | struct timex tx; |
116 | int ret, pass = 0; |
117 | int i; |
118 | |
119 | clear_time_state(); |
120 | |
121 | memset(&tx, 0, sizeof(struct timex)); |
122 | /* Set the leap second insert flag */ |
123 | |
124 | printf("Testing ADJ_FREQ... " ); |
125 | fflush(stdout); |
126 | for (i = 0; i < NUM_FREQ_VALID; i++) { |
127 | tx.modes = ADJ_FREQUENCY; |
128 | tx.freq = valid_freq[i]; |
129 | |
130 | ret = adjtimex(&tx); |
131 | if (ret < 0) { |
132 | printf("[FAIL]\n" ); |
133 | printf("Error: adjtimex(ADJ_FREQ, %ld - %ld ppm\n" , |
134 | valid_freq[i], valid_freq[i]>>16); |
135 | pass = -1; |
136 | goto out; |
137 | } |
138 | tx.modes = 0; |
139 | ret = adjtimex(&tx); |
140 | if (tx.freq != valid_freq[i]) { |
141 | printf("Warning: freq value %ld not what we set it (%ld)!\n" , |
142 | tx.freq, valid_freq[i]); |
143 | } |
144 | } |
145 | for (i = 0; i < NUM_FREQ_OUTOFRANGE; i++) { |
146 | tx.modes = ADJ_FREQUENCY; |
147 | tx.freq = outofrange_freq[i]; |
148 | |
149 | ret = adjtimex(&tx); |
150 | if (ret < 0) { |
151 | printf("[FAIL]\n" ); |
152 | printf("Error: adjtimex(ADJ_FREQ, %ld - %ld ppm\n" , |
153 | outofrange_freq[i], outofrange_freq[i]>>16); |
154 | pass = -1; |
155 | goto out; |
156 | } |
157 | tx.modes = 0; |
158 | ret = adjtimex(&tx); |
159 | if (tx.freq == outofrange_freq[i]) { |
160 | printf("[FAIL]\n" ); |
161 | printf("ERROR: out of range value %ld actually set!\n" , |
162 | tx.freq); |
163 | pass = -1; |
164 | goto out; |
165 | } |
166 | } |
167 | |
168 | |
169 | if (sizeof(long) == 8) { /* this case only applies to 64bit systems */ |
170 | for (i = 0; i < NUM_FREQ_INVALID; i++) { |
171 | tx.modes = ADJ_FREQUENCY; |
172 | tx.freq = invalid_freq[i]; |
173 | ret = adjtimex(&tx); |
174 | if (ret >= 0) { |
175 | printf("[FAIL]\n" ); |
176 | printf("Error: No failure on invalid ADJ_FREQUENCY %ld\n" , |
177 | invalid_freq[i]); |
178 | pass = -1; |
179 | goto out; |
180 | } |
181 | } |
182 | } |
183 | |
184 | printf("[OK]\n" ); |
185 | out: |
186 | /* reset freq to zero */ |
187 | tx.modes = ADJ_FREQUENCY; |
188 | tx.freq = 0; |
189 | ret = adjtimex(&tx); |
190 | |
191 | return pass; |
192 | } |
193 | |
194 | |
195 | int set_offset(long long offset, int use_nano) |
196 | { |
197 | struct timex tmx = {}; |
198 | int ret; |
199 | |
200 | tmx.modes = ADJ_SETOFFSET; |
201 | if (use_nano) { |
202 | tmx.modes |= ADJ_NANO; |
203 | |
204 | tmx.time.tv_sec = offset / NSEC_PER_SEC; |
205 | tmx.time.tv_usec = offset % NSEC_PER_SEC; |
206 | |
207 | if (offset < 0 && tmx.time.tv_usec) { |
208 | tmx.time.tv_sec -= 1; |
209 | tmx.time.tv_usec += NSEC_PER_SEC; |
210 | } |
211 | } else { |
212 | tmx.time.tv_sec = offset / USEC_PER_SEC; |
213 | tmx.time.tv_usec = offset % USEC_PER_SEC; |
214 | |
215 | if (offset < 0 && tmx.time.tv_usec) { |
216 | tmx.time.tv_sec -= 1; |
217 | tmx.time.tv_usec += USEC_PER_SEC; |
218 | } |
219 | } |
220 | |
221 | ret = clock_adjtime(CLOCK_REALTIME, &tmx); |
222 | if (ret < 0) { |
223 | printf("(sec: %ld usec: %ld) " , tmx.time.tv_sec, tmx.time.tv_usec); |
224 | printf("[FAIL]\n" ); |
225 | return -1; |
226 | } |
227 | return 0; |
228 | } |
229 | |
230 | int set_bad_offset(long sec, long usec, int use_nano) |
231 | { |
232 | struct timex tmx = {}; |
233 | int ret; |
234 | |
235 | tmx.modes = ADJ_SETOFFSET; |
236 | if (use_nano) |
237 | tmx.modes |= ADJ_NANO; |
238 | |
239 | tmx.time.tv_sec = sec; |
240 | tmx.time.tv_usec = usec; |
241 | ret = clock_adjtime(CLOCK_REALTIME, &tmx); |
242 | if (ret >= 0) { |
243 | printf("Invalid (sec: %ld usec: %ld) did not fail! " , tmx.time.tv_sec, tmx.time.tv_usec); |
244 | printf("[FAIL]\n" ); |
245 | return -1; |
246 | } |
247 | return 0; |
248 | } |
249 | |
250 | int validate_set_offset(void) |
251 | { |
252 | printf("Testing ADJ_SETOFFSET... " ); |
253 | fflush(stdout); |
254 | |
255 | /* Test valid values */ |
256 | if (set_offset(NSEC_PER_SEC - 1, use_nano: 1)) |
257 | return -1; |
258 | |
259 | if (set_offset(offset: -NSEC_PER_SEC + 1, use_nano: 1)) |
260 | return -1; |
261 | |
262 | if (set_offset(offset: -NSEC_PER_SEC - 1, use_nano: 1)) |
263 | return -1; |
264 | |
265 | if (set_offset(offset: 5 * NSEC_PER_SEC, use_nano: 1)) |
266 | return -1; |
267 | |
268 | if (set_offset(offset: -5 * NSEC_PER_SEC, use_nano: 1)) |
269 | return -1; |
270 | |
271 | if (set_offset(offset: 5 * NSEC_PER_SEC + NSEC_PER_SEC / 2, use_nano: 1)) |
272 | return -1; |
273 | |
274 | if (set_offset(offset: -5 * NSEC_PER_SEC - NSEC_PER_SEC / 2, use_nano: 1)) |
275 | return -1; |
276 | |
277 | if (set_offset(USEC_PER_SEC - 1, use_nano: 0)) |
278 | return -1; |
279 | |
280 | if (set_offset(offset: -USEC_PER_SEC + 1, use_nano: 0)) |
281 | return -1; |
282 | |
283 | if (set_offset(offset: -USEC_PER_SEC - 1, use_nano: 0)) |
284 | return -1; |
285 | |
286 | if (set_offset(offset: 5 * USEC_PER_SEC, use_nano: 0)) |
287 | return -1; |
288 | |
289 | if (set_offset(offset: -5 * USEC_PER_SEC, use_nano: 0)) |
290 | return -1; |
291 | |
292 | if (set_offset(offset: 5 * USEC_PER_SEC + USEC_PER_SEC / 2, use_nano: 0)) |
293 | return -1; |
294 | |
295 | if (set_offset(offset: -5 * USEC_PER_SEC - USEC_PER_SEC / 2, use_nano: 0)) |
296 | return -1; |
297 | |
298 | /* Test invalid values */ |
299 | if (set_bad_offset(sec: 0, usec: -1, use_nano: 1)) |
300 | return -1; |
301 | if (set_bad_offset(sec: 0, usec: -1, use_nano: 0)) |
302 | return -1; |
303 | if (set_bad_offset(sec: 0, usec: 2 * NSEC_PER_SEC, use_nano: 1)) |
304 | return -1; |
305 | if (set_bad_offset(sec: 0, usec: 2 * USEC_PER_SEC, use_nano: 0)) |
306 | return -1; |
307 | if (set_bad_offset(sec: 0, NSEC_PER_SEC, use_nano: 1)) |
308 | return -1; |
309 | if (set_bad_offset(sec: 0, USEC_PER_SEC, use_nano: 0)) |
310 | return -1; |
311 | if (set_bad_offset(sec: 0, usec: -NSEC_PER_SEC, use_nano: 1)) |
312 | return -1; |
313 | if (set_bad_offset(sec: 0, usec: -USEC_PER_SEC, use_nano: 0)) |
314 | return -1; |
315 | |
316 | printf("[OK]\n" ); |
317 | return 0; |
318 | } |
319 | |
320 | int main(int argc, char **argv) |
321 | { |
322 | if (validate_freq()) |
323 | return ksft_exit_fail(); |
324 | |
325 | if (validate_set_offset()) |
326 | return ksft_exit_fail(); |
327 | |
328 | return ksft_exit_pass(); |
329 | } |
330 | |