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