1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2019 Linaro Limited. All rights reserved.
4f08c3bdfSopenharmony_ci * Author: Rafael David Tinoco <rafael.tinoco@linaro.org>
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci#include "config.h"
8f08c3bdfSopenharmony_ci#include "tst_test.h"
9f08c3bdfSopenharmony_ci#include "tst_timer.h"
10f08c3bdfSopenharmony_ci#include "tst_safe_clocks.h"
11f08c3bdfSopenharmony_ci#include "lapi/syscalls.h"
12f08c3bdfSopenharmony_ci#include "lapi/posix_clocks.h"
13f08c3bdfSopenharmony_ci#include <time.h>
14f08c3bdfSopenharmony_ci#include <pwd.h>
15f08c3bdfSopenharmony_ci#include <sys/timex.h>
16f08c3bdfSopenharmony_ci#include <sys/types.h>
17f08c3bdfSopenharmony_ci#include <asm/posix_types.h>
18f08c3bdfSopenharmony_ci#include "lapi/timex.h"
19f08c3bdfSopenharmony_ci
20f08c3bdfSopenharmony_ci#ifndef __kernel_timex
21f08c3bdfSopenharmony_cistruct __kernel_old_timex {
22f08c3bdfSopenharmony_ci	unsigned int modes;	/* mode selector */
23f08c3bdfSopenharmony_ci	__kernel_long_t offset;	/* time offset (usec) */
24f08c3bdfSopenharmony_ci	__kernel_long_t freq;	/* frequency offset (scaled ppm) */
25f08c3bdfSopenharmony_ci	__kernel_long_t maxerror;/* maximum error (usec) */
26f08c3bdfSopenharmony_ci	__kernel_long_t esterror;/* estimated error (usec) */
27f08c3bdfSopenharmony_ci	int status;		/* clock command/status */
28f08c3bdfSopenharmony_ci	__kernel_long_t constant;/* pll time constant */
29f08c3bdfSopenharmony_ci	__kernel_long_t precision;/* clock precision (usec) (read only) */
30f08c3bdfSopenharmony_ci	__kernel_long_t tolerance;/* clock frequency tolerance (ppm)
31f08c3bdfSopenharmony_ci				   * (read only)
32f08c3bdfSopenharmony_ci				   */
33f08c3bdfSopenharmony_ci	struct __kernel_old_timeval time;	/* (read only, except for ADJ_SETOFFSET) */
34f08c3bdfSopenharmony_ci	__kernel_long_t tick;	/* (modified) usecs between clock ticks */
35f08c3bdfSopenharmony_ci
36f08c3bdfSopenharmony_ci	__kernel_long_t ppsfreq;/* pps frequency (scaled ppm) (ro) */
37f08c3bdfSopenharmony_ci	__kernel_long_t jitter; /* pps jitter (us) (ro) */
38f08c3bdfSopenharmony_ci	int shift;              /* interval duration (s) (shift) (ro) */
39f08c3bdfSopenharmony_ci	__kernel_long_t stabil;            /* pps stability (scaled ppm) (ro) */
40f08c3bdfSopenharmony_ci	__kernel_long_t jitcnt; /* jitter limit exceeded (ro) */
41f08c3bdfSopenharmony_ci	__kernel_long_t calcnt; /* calibration intervals (ro) */
42f08c3bdfSopenharmony_ci	__kernel_long_t errcnt; /* calibration errors (ro) */
43f08c3bdfSopenharmony_ci	__kernel_long_t stbcnt; /* stability limit exceeded (ro) */
44f08c3bdfSopenharmony_ci
45f08c3bdfSopenharmony_ci	int tai;		/* TAI offset (ro) */
46f08c3bdfSopenharmony_ci
47f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32; int  :32;
48f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32; int  :32;
49f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32;
50f08c3bdfSopenharmony_ci};
51f08c3bdfSopenharmony_ci
52f08c3bdfSopenharmony_cistruct __kernel_timex_timeval {
53f08c3bdfSopenharmony_ci	__kernel_time64_t       tv_sec;
54f08c3bdfSopenharmony_ci	long long		tv_usec;
55f08c3bdfSopenharmony_ci};
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_cistruct __kernel_timex {
58f08c3bdfSopenharmony_ci	unsigned int modes;	/* mode selector */
59f08c3bdfSopenharmony_ci	int :32;            /* pad */
60f08c3bdfSopenharmony_ci	long long offset;	/* time offset (usec) */
61f08c3bdfSopenharmony_ci	long long freq;	/* frequency offset (scaled ppm) */
62f08c3bdfSopenharmony_ci	long long maxerror;/* maximum error (usec) */
63f08c3bdfSopenharmony_ci	long long esterror;/* estimated error (usec) */
64f08c3bdfSopenharmony_ci	int status;		/* clock command/status */
65f08c3bdfSopenharmony_ci	int :32;            /* pad */
66f08c3bdfSopenharmony_ci	long long constant;/* pll time constant */
67f08c3bdfSopenharmony_ci	long long precision;/* clock precision (usec) (read only) */
68f08c3bdfSopenharmony_ci	long long tolerance;/* clock frequency tolerance (ppm)
69f08c3bdfSopenharmony_ci				   * (read only)
70f08c3bdfSopenharmony_ci				   */
71f08c3bdfSopenharmony_ci	struct __kernel_timex_timeval time;	/* (read only, except for ADJ_SETOFFSET) */
72f08c3bdfSopenharmony_ci	long long tick;	/* (modified) usecs between clock ticks */
73f08c3bdfSopenharmony_ci
74f08c3bdfSopenharmony_ci	long long ppsfreq;/* pps frequency (scaled ppm) (ro) */
75f08c3bdfSopenharmony_ci	long long jitter; /* pps jitter (us) (ro) */
76f08c3bdfSopenharmony_ci	int shift;              /* interval duration (s) (shift) (ro) */
77f08c3bdfSopenharmony_ci	int :32;            /* pad */
78f08c3bdfSopenharmony_ci	long long stabil;            /* pps stability (scaled ppm) (ro) */
79f08c3bdfSopenharmony_ci	long long jitcnt; /* jitter limit exceeded (ro) */
80f08c3bdfSopenharmony_ci	long long calcnt; /* calibration intervals (ro) */
81f08c3bdfSopenharmony_ci	long long errcnt; /* calibration errors (ro) */
82f08c3bdfSopenharmony_ci	long long stbcnt; /* stability limit exceeded (ro) */
83f08c3bdfSopenharmony_ci
84f08c3bdfSopenharmony_ci	int tai;		/* TAI offset (ro) */
85f08c3bdfSopenharmony_ci
86f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32; int  :32;
87f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32; int  :32;
88f08c3bdfSopenharmony_ci	int  :32; int  :32; int  :32;
89f08c3bdfSopenharmony_ci};
90f08c3bdfSopenharmony_ci#endif
91f08c3bdfSopenharmony_ci
92f08c3bdfSopenharmony_cienum tst_timex_type {
93f08c3bdfSopenharmony_ci	TST_KERN_OLD_TIMEX,
94f08c3bdfSopenharmony_ci	TST_KERN_TIMEX
95f08c3bdfSopenharmony_ci};
96f08c3bdfSopenharmony_ci
97f08c3bdfSopenharmony_cistruct tst_timex {
98f08c3bdfSopenharmony_ci	enum tst_timex_type type;
99f08c3bdfSopenharmony_ci	union tx{
100f08c3bdfSopenharmony_ci		struct __kernel_old_timex kern_old_timex;
101f08c3bdfSopenharmony_ci		struct __kernel_timex kern_timex;
102f08c3bdfSopenharmony_ci	} tx;
103f08c3bdfSopenharmony_ci};
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_cistatic inline void *tst_timex_get(struct tst_timex *t)
106f08c3bdfSopenharmony_ci{
107f08c3bdfSopenharmony_ci	switch (t->type) {
108f08c3bdfSopenharmony_ci	case TST_KERN_OLD_TIMEX:
109f08c3bdfSopenharmony_ci		return &t->tx.kern_old_timex;
110f08c3bdfSopenharmony_ci	case TST_KERN_TIMEX:
111f08c3bdfSopenharmony_ci		return &t->tx.kern_timex;
112f08c3bdfSopenharmony_ci	default:
113f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid type: %d", t->type);
114f08c3bdfSopenharmony_ci		return NULL;
115f08c3bdfSopenharmony_ci	}
116f08c3bdfSopenharmony_ci}
117f08c3bdfSopenharmony_ci
118f08c3bdfSopenharmony_cistatic inline int sys_clock_adjtime(clockid_t clk_id, void *timex)
119f08c3bdfSopenharmony_ci{
120f08c3bdfSopenharmony_ci	return tst_syscall(__NR_clock_adjtime, clk_id, timex);
121f08c3bdfSopenharmony_ci}
122f08c3bdfSopenharmony_ci
123f08c3bdfSopenharmony_cistatic inline int sys_clock_adjtime64(clockid_t clk_id, void *timex)
124f08c3bdfSopenharmony_ci{
125f08c3bdfSopenharmony_ci	return tst_syscall(__NR_clock_adjtime64, clk_id, timex);
126f08c3bdfSopenharmony_ci}
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci#define TIMEX_SHOW(tx, mode, fmt)					\
129f08c3bdfSopenharmony_ci	tst_res(TINFO,  "%s\n"						\
130f08c3bdfSopenharmony_ci			"             mode: %u\n"			\
131f08c3bdfSopenharmony_ci			"           offset: "fmt"\n"			\
132f08c3bdfSopenharmony_ci			"        frequency: "fmt"\n"			\
133f08c3bdfSopenharmony_ci			"         maxerror: "fmt"\n"			\
134f08c3bdfSopenharmony_ci			"         esterror: "fmt"\n"			\
135f08c3bdfSopenharmony_ci			"           status: %d (0x%x)\n"		\
136f08c3bdfSopenharmony_ci			"    time_constant: "fmt"\n"			\
137f08c3bdfSopenharmony_ci			"        precision: "fmt"\n"			\
138f08c3bdfSopenharmony_ci			"        tolerance: "fmt"\n"			\
139f08c3bdfSopenharmony_ci			"             tick: "fmt"\n"			\
140f08c3bdfSopenharmony_ci			"         raw time: "fmt"(s) "fmt"(us)",	\
141f08c3bdfSopenharmony_ci			mode,						\
142f08c3bdfSopenharmony_ci			tx.modes,					\
143f08c3bdfSopenharmony_ci			tx.offset,					\
144f08c3bdfSopenharmony_ci			tx.freq,					\
145f08c3bdfSopenharmony_ci			tx.maxerror,					\
146f08c3bdfSopenharmony_ci			tx.esterror,					\
147f08c3bdfSopenharmony_ci			tx.status,					\
148f08c3bdfSopenharmony_ci			tx.status,					\
149f08c3bdfSopenharmony_ci			tx.constant,					\
150f08c3bdfSopenharmony_ci			tx.precision,					\
151f08c3bdfSopenharmony_ci			tx.tolerance,					\
152f08c3bdfSopenharmony_ci			tx.tick,					\
153f08c3bdfSopenharmony_ci			tx.time.tv_sec,					\
154f08c3bdfSopenharmony_ci			tx.time.tv_usec)
155f08c3bdfSopenharmony_ci
156f08c3bdfSopenharmony_cistatic inline void timex_show(const char *mode, struct tst_timex *timex)
157f08c3bdfSopenharmony_ci{
158f08c3bdfSopenharmony_ci	switch (timex->type) {
159f08c3bdfSopenharmony_ci	case TST_KERN_OLD_TIMEX:
160f08c3bdfSopenharmony_ci		TIMEX_SHOW(timex->tx.kern_old_timex, mode, "%ld");
161f08c3bdfSopenharmony_ci		return;
162f08c3bdfSopenharmony_ci	case TST_KERN_TIMEX:
163f08c3bdfSopenharmony_ci		TIMEX_SHOW(timex->tx.kern_timex, mode, "%lld");
164f08c3bdfSopenharmony_ci		return;
165f08c3bdfSopenharmony_ci	default:
166f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid type: %d", timex->type);
167f08c3bdfSopenharmony_ci	}
168f08c3bdfSopenharmony_ci}
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci#undef TIMEX_SHOW
171f08c3bdfSopenharmony_ci
172f08c3bdfSopenharmony_ci#define ADJ_MODES	0
173f08c3bdfSopenharmony_ci
174f08c3bdfSopenharmony_ci#define SELECT_FIELD(tx, field)						\
175f08c3bdfSopenharmony_ci{									\
176f08c3bdfSopenharmony_ci	switch (field) {						\
177f08c3bdfSopenharmony_ci	case ADJ_MODES:							\
178f08c3bdfSopenharmony_ci		return &tx.modes;					\
179f08c3bdfSopenharmony_ci	case ADJ_OFFSET:						\
180f08c3bdfSopenharmony_ci		return &tx.offset;					\
181f08c3bdfSopenharmony_ci	case ADJ_FREQUENCY:						\
182f08c3bdfSopenharmony_ci		return &tx.freq;					\
183f08c3bdfSopenharmony_ci	case ADJ_MAXERROR:						\
184f08c3bdfSopenharmony_ci		return &tx.maxerror;					\
185f08c3bdfSopenharmony_ci	case ADJ_ESTERROR:						\
186f08c3bdfSopenharmony_ci		return &tx.esterror;					\
187f08c3bdfSopenharmony_ci	case ADJ_TIMECONST:						\
188f08c3bdfSopenharmony_ci		return &tx.constant;					\
189f08c3bdfSopenharmony_ci	case ADJ_TICK:							\
190f08c3bdfSopenharmony_ci		return &tx.tick;					\
191f08c3bdfSopenharmony_ci	case ADJ_STATUS:						\
192f08c3bdfSopenharmony_ci		return &tx.status;					\
193f08c3bdfSopenharmony_ci	default:							\
194f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid type: %d", timex->type);	\
195f08c3bdfSopenharmony_ci		return NULL;						\
196f08c3bdfSopenharmony_ci	}								\
197f08c3bdfSopenharmony_ci}
198f08c3bdfSopenharmony_ci
199f08c3bdfSopenharmony_cistatic inline void *timex_get_field(struct tst_timex *timex, unsigned int field)
200f08c3bdfSopenharmony_ci{
201f08c3bdfSopenharmony_ci	switch (timex->type) {
202f08c3bdfSopenharmony_ci	case TST_KERN_OLD_TIMEX:
203f08c3bdfSopenharmony_ci		SELECT_FIELD(timex->tx.kern_old_timex, field);
204f08c3bdfSopenharmony_ci	case TST_KERN_TIMEX:
205f08c3bdfSopenharmony_ci		SELECT_FIELD(timex->tx.kern_timex, field);
206f08c3bdfSopenharmony_ci	default:
207f08c3bdfSopenharmony_ci		tst_brk(TBROK, "Invalid type: %d", timex->type);
208f08c3bdfSopenharmony_ci		return NULL;
209f08c3bdfSopenharmony_ci	}
210f08c3bdfSopenharmony_ci}
211f08c3bdfSopenharmony_ci
212f08c3bdfSopenharmony_ci#undef SELECT_FIELD
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_ci#define TIMEX_GET_SET_FIELD_TYPE(type_libc, type_kern)			\
215f08c3bdfSopenharmony_cistatic inline type_kern							\
216f08c3bdfSopenharmony_citimex_get_field_##type_libc(struct tst_timex *timex, unsigned int field) \
217f08c3bdfSopenharmony_ci{									\
218f08c3bdfSopenharmony_ci	switch (timex->type) {						\
219f08c3bdfSopenharmony_ci	case TST_KERN_OLD_TIMEX:						\
220f08c3bdfSopenharmony_ci		return *((type_libc*)timex_get_field(timex, field));	\
221f08c3bdfSopenharmony_ci	case TST_KERN_TIMEX:						\
222f08c3bdfSopenharmony_ci		return *((type_kern*)timex_get_field(timex, field));	\
223f08c3bdfSopenharmony_ci	default:							\
224f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Invalid type: %d", timex->type);	\
225f08c3bdfSopenharmony_ci		return 0;						\
226f08c3bdfSopenharmony_ci	}								\
227f08c3bdfSopenharmony_ci}									\
228f08c3bdfSopenharmony_ci									\
229f08c3bdfSopenharmony_cistatic inline void							\
230f08c3bdfSopenharmony_citimex_set_field_##type_libc(struct tst_timex *timex, unsigned int field, \
231f08c3bdfSopenharmony_ci			    type_kern value)				\
232f08c3bdfSopenharmony_ci{									\
233f08c3bdfSopenharmony_ci	switch (timex->type) {						\
234f08c3bdfSopenharmony_ci	case TST_KERN_OLD_TIMEX:						\
235f08c3bdfSopenharmony_ci		*((type_libc*)timex_get_field(timex, field)) = value;	\
236f08c3bdfSopenharmony_ci		return;							\
237f08c3bdfSopenharmony_ci	case TST_KERN_TIMEX:						\
238f08c3bdfSopenharmony_ci		*((type_kern*)timex_get_field(timex, field)) = value;	\
239f08c3bdfSopenharmony_ci		return;							\
240f08c3bdfSopenharmony_ci	default:							\
241f08c3bdfSopenharmony_ci		tst_res(TFAIL, "Invalid type: %d", timex->type);	\
242f08c3bdfSopenharmony_ci	}								\
243f08c3bdfSopenharmony_ci}
244f08c3bdfSopenharmony_ci
245f08c3bdfSopenharmony_ciTIMEX_GET_SET_FIELD_TYPE(uint, uint);
246f08c3bdfSopenharmony_ciTIMEX_GET_SET_FIELD_TYPE(long, long long);
247f08c3bdfSopenharmony_ci
248f08c3bdfSopenharmony_ci#undef TIMEX_GET_SET_FIELD_TYPE
249