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>
40int 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 */
47int 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
64long 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
98long 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
108long invalid_freq[NUM_FREQ_INVALID] = {
109	LONG_MAX,
110	LONG_MIN,
111};
112
113int 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");
185out:
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
195int 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
230int 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
250int 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, 1))
257		return -1;
258
259	if (set_offset(-NSEC_PER_SEC + 1, 1))
260		return -1;
261
262	if (set_offset(-NSEC_PER_SEC - 1, 1))
263		return -1;
264
265	if (set_offset(5 * NSEC_PER_SEC, 1))
266		return -1;
267
268	if (set_offset(-5 * NSEC_PER_SEC, 1))
269		return -1;
270
271	if (set_offset(5 * NSEC_PER_SEC + NSEC_PER_SEC / 2, 1))
272		return -1;
273
274	if (set_offset(-5 * NSEC_PER_SEC - NSEC_PER_SEC / 2, 1))
275		return -1;
276
277	if (set_offset(USEC_PER_SEC - 1, 0))
278		return -1;
279
280	if (set_offset(-USEC_PER_SEC + 1, 0))
281		return -1;
282
283	if (set_offset(-USEC_PER_SEC - 1, 0))
284		return -1;
285
286	if (set_offset(5 * USEC_PER_SEC, 0))
287		return -1;
288
289	if (set_offset(-5 * USEC_PER_SEC, 0))
290		return -1;
291
292	if (set_offset(5 * USEC_PER_SEC + USEC_PER_SEC / 2, 0))
293		return -1;
294
295	if (set_offset(-5 * USEC_PER_SEC - USEC_PER_SEC / 2, 0))
296		return -1;
297
298	/* Test invalid values */
299	if (set_bad_offset(0, -1, 1))
300		return -1;
301	if (set_bad_offset(0, -1, 0))
302		return -1;
303	if (set_bad_offset(0, 2 * NSEC_PER_SEC, 1))
304		return -1;
305	if (set_bad_offset(0, 2 * USEC_PER_SEC, 0))
306		return -1;
307	if (set_bad_offset(0, NSEC_PER_SEC, 1))
308		return -1;
309	if (set_bad_offset(0, USEC_PER_SEC, 0))
310		return -1;
311	if (set_bad_offset(0, -NSEC_PER_SEC, 1))
312		return -1;
313	if (set_bad_offset(0, -USEC_PER_SEC, 0))
314		return -1;
315
316	printf("[OK]\n");
317	return 0;
318}
319
320int 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