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/*
8f08c3bdfSopenharmony_ci * clock_adjtime() syscall might have as execution path:
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci *   1) a regular POSIX clock (only REALTIME clock implements adjtime())
11f08c3bdfSopenharmony_ci *      - will behave exactly like adjtimex() system call.
12f08c3bdfSopenharmony_ci *      - only one being tested here.
13f08c3bdfSopenharmony_ci *
14f08c3bdfSopenharmony_ci *   2) a dynamic POSIX clock (which ops are implemented by PTP clocks)
15f08c3bdfSopenharmony_ci *      - will trigger the PTP clock driver function "adjtime()"
16f08c3bdfSopenharmony_ci *      - different implementations from one PTP clock to another
17f08c3bdfSopenharmony_ci *      - might return EOPNOTSUPP (like ptp_kvm_caps, for example)
18f08c3bdfSopenharmony_ci *      - no entry point for clock_adjtime(), missing "CLOCK_PTP" model
19f08c3bdfSopenharmony_ci *
20f08c3bdfSopenharmony_ci * so it is sane to check possible adjustments:
21f08c3bdfSopenharmony_ci *
22f08c3bdfSopenharmony_ci *    - ADJ_OFFSET     - usec or nsec, kernel adjusts time gradually by offset
23f08c3bdfSopenharmony_ci *                       (-512000 < offset < 512000)
24f08c3bdfSopenharmony_ci *    - ADJ_FREQUENCY  - system clock frequency offset
25f08c3bdfSopenharmony_ci *    - ADJ_MAXERROR   - maximum error (usec)
26f08c3bdfSopenharmony_ci *    - ADJ_ESTERROR   - estimated time error in us
27f08c3bdfSopenharmony_ci *    - ADJ_STATUS     - clock command/status of ntp implementation
28f08c3bdfSopenharmony_ci *    - ADJ_TIMECONST  - PLL stiffness (jitter dependent) + poll int for PLL
29f08c3bdfSopenharmony_ci *    - ADJ_TICK       - us between clock ticks
30f08c3bdfSopenharmony_ci *                       (>= 900000/HZ, <= 1100000/HZ)
31f08c3bdfSopenharmony_ci *
32f08c3bdfSopenharmony_ci * and also the standalone ones (using .offset variable):
33f08c3bdfSopenharmony_ci *
34f08c3bdfSopenharmony_ci *    - ADJ_OFFSET_SINGLESHOT - behave like adjtime()
35f08c3bdfSopenharmony_ci *    - ADJ_OFFSET_SS_READ - ret remaining time for completion after SINGLESHOT
36f08c3bdfSopenharmony_ci *
37f08c3bdfSopenharmony_ci * For ADJ_STATUS, consider the following flags:
38f08c3bdfSopenharmony_ci *
39f08c3bdfSopenharmony_ci *      rw  STA_PLL - enable phase-locked loop updates (ADJ_OFFSET)
40f08c3bdfSopenharmony_ci *      rw  STA_PPSFREQ - enable PPS (pulse-per-second) freq discipline
41f08c3bdfSopenharmony_ci *      rw  STA_PPSTIME - enable PPS time discipline
42f08c3bdfSopenharmony_ci *      rw  STA_FLL - select freq-locked loop mode.
43f08c3bdfSopenharmony_ci *      rw  STA_INS - ins leap sec after the last sec of UTC day (all days)
44f08c3bdfSopenharmony_ci *      rw  STA_DEL - del leap sec at last sec of UTC day (all days)
45f08c3bdfSopenharmony_ci *      rw  STA_UNSYNC - clock unsynced
46f08c3bdfSopenharmony_ci *      rw  STA_FREQHOLD - hold freq. ADJ_OFFSET made w/out auto small adjs
47f08c3bdfSopenharmony_ci *      ro  STA_PPSSIGNAL - valid PPS (pulse-per-second) signal is present
48f08c3bdfSopenharmony_ci *      ro  STA_PPSJITTER - PPS signal jitter exceeded.
49f08c3bdfSopenharmony_ci *      ro  STA_PPSWANDER - PPS signal wander exceeded.
50f08c3bdfSopenharmony_ci *      ro  STA_PPSERROR - PPS signal calibration error.
51f08c3bdfSopenharmony_ci *      ro  STA_CLOKERR - clock HW fault.
52f08c3bdfSopenharmony_ci *      ro  STA_NANO - 0 = us, 1 = ns (set = ADJ_NANO, cl = ADJ_MICRO)
53f08c3bdfSopenharmony_ci *      rw  STA_MODE - mode: 0 = phased locked loop. 1 = freq locked loop
54f08c3bdfSopenharmony_ci *      ro  STA_CLK - clock source. unused.
55f08c3bdfSopenharmony_ci */
56f08c3bdfSopenharmony_ci
57f08c3bdfSopenharmony_ci#include "clock_adjtime.h"
58f08c3bdfSopenharmony_ci
59f08c3bdfSopenharmony_cistatic long hz;
60f08c3bdfSopenharmony_cistatic struct tst_timex saved, ttxc;
61f08c3bdfSopenharmony_cistatic int supported;
62f08c3bdfSopenharmony_ci
63f08c3bdfSopenharmony_cistruct test_case {
64f08c3bdfSopenharmony_ci	unsigned int modes;
65f08c3bdfSopenharmony_ci	long highlimit;
66f08c3bdfSopenharmony_ci	long delta;
67f08c3bdfSopenharmony_ci};
68f08c3bdfSopenharmony_ci
69f08c3bdfSopenharmony_cistruct test_case tc[] = {
70f08c3bdfSopenharmony_ci	{
71f08c3bdfSopenharmony_ci	 .modes = ADJ_OFFSET_SINGLESHOT,
72f08c3bdfSopenharmony_ci	},
73f08c3bdfSopenharmony_ci	{
74f08c3bdfSopenharmony_ci	 .modes = ADJ_OFFSET_SS_READ,
75f08c3bdfSopenharmony_ci	},
76f08c3bdfSopenharmony_ci	{
77f08c3bdfSopenharmony_ci	 .modes = ADJ_ALL,
78f08c3bdfSopenharmony_ci	},
79f08c3bdfSopenharmony_ci	{
80f08c3bdfSopenharmony_ci	 .modes = ADJ_OFFSET,
81f08c3bdfSopenharmony_ci	 .highlimit = 500000,
82f08c3bdfSopenharmony_ci	 .delta = 10000,
83f08c3bdfSopenharmony_ci	},
84f08c3bdfSopenharmony_ci	{
85f08c3bdfSopenharmony_ci	 .modes = ADJ_FREQUENCY,
86f08c3bdfSopenharmony_ci	 .delta = 100,
87f08c3bdfSopenharmony_ci	},
88f08c3bdfSopenharmony_ci	{
89f08c3bdfSopenharmony_ci	 .modes = ADJ_MAXERROR,
90f08c3bdfSopenharmony_ci	 .delta = 100,
91f08c3bdfSopenharmony_ci	},
92f08c3bdfSopenharmony_ci	{
93f08c3bdfSopenharmony_ci	 .modes = ADJ_ESTERROR,
94f08c3bdfSopenharmony_ci	 .delta = 100,
95f08c3bdfSopenharmony_ci	},
96f08c3bdfSopenharmony_ci	{
97f08c3bdfSopenharmony_ci	 .modes = ADJ_TIMECONST,
98f08c3bdfSopenharmony_ci	 .delta = 1,
99f08c3bdfSopenharmony_ci	},
100f08c3bdfSopenharmony_ci	{
101f08c3bdfSopenharmony_ci	 .modes = ADJ_TICK,
102f08c3bdfSopenharmony_ci	 .highlimit = 1100000,
103f08c3bdfSopenharmony_ci	 .delta = 1000,
104f08c3bdfSopenharmony_ci	},
105f08c3bdfSopenharmony_ci};
106f08c3bdfSopenharmony_ci
107f08c3bdfSopenharmony_cistatic struct test_variants {
108f08c3bdfSopenharmony_ci	int (*clock_adjtime)(clockid_t clk_id, void *timex);
109f08c3bdfSopenharmony_ci	enum tst_timex_type type;
110f08c3bdfSopenharmony_ci	char *desc;
111f08c3bdfSopenharmony_ci} variants[] = {
112f08c3bdfSopenharmony_ci#if (__NR_clock_adjtime != __LTP__NR_INVALID_SYSCALL)
113f08c3bdfSopenharmony_ci	{.clock_adjtime = sys_clock_adjtime, .type = TST_KERN_OLD_TIMEX, .desc = "syscall with old kernel spec"},
114f08c3bdfSopenharmony_ci#endif
115f08c3bdfSopenharmony_ci
116f08c3bdfSopenharmony_ci#if (__NR_clock_adjtime64 != __LTP__NR_INVALID_SYSCALL)
117f08c3bdfSopenharmony_ci	{.clock_adjtime = sys_clock_adjtime64, .type = TST_KERN_TIMEX, .desc = "syscall time64 with kernel spec"},
118f08c3bdfSopenharmony_ci#endif
119f08c3bdfSopenharmony_ci};
120f08c3bdfSopenharmony_ci
121f08c3bdfSopenharmony_cistatic void verify_clock_adjtime(unsigned int i)
122f08c3bdfSopenharmony_ci{
123f08c3bdfSopenharmony_ci	struct test_variants *tv = &variants[tst_variant];
124f08c3bdfSopenharmony_ci	struct tst_timex verify;
125f08c3bdfSopenharmony_ci	long long val;
126f08c3bdfSopenharmony_ci	int rval;
127f08c3bdfSopenharmony_ci
128f08c3bdfSopenharmony_ci	memset(&ttxc, 0, sizeof(ttxc));
129f08c3bdfSopenharmony_ci	memset(&verify, 0, sizeof(verify));
130f08c3bdfSopenharmony_ci
131f08c3bdfSopenharmony_ci	ttxc.type = verify.type = tv->type;
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci	rval = tv->clock_adjtime(CLOCK_REALTIME, tst_timex_get(&ttxc));
134f08c3bdfSopenharmony_ci	if (rval < 0) {
135f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "clock_adjtime() failed %i", rval);
136f08c3bdfSopenharmony_ci		return;
137f08c3bdfSopenharmony_ci	}
138f08c3bdfSopenharmony_ci
139f08c3bdfSopenharmony_ci	timex_show("GET", &ttxc);
140f08c3bdfSopenharmony_ci
141f08c3bdfSopenharmony_ci	timex_set_field_uint(&ttxc, ADJ_MODES, tc[i].modes);
142f08c3bdfSopenharmony_ci
143f08c3bdfSopenharmony_ci	if (tc[i].delta) {
144f08c3bdfSopenharmony_ci		val = timex_get_field_long(&ttxc, tc[i].modes);
145f08c3bdfSopenharmony_ci		val += tc[i].delta;
146f08c3bdfSopenharmony_ci
147f08c3bdfSopenharmony_ci		/* fix limits, if existent, so no errors occur */
148f08c3bdfSopenharmony_ci		if (tc[i].highlimit && val >= tc[i].highlimit)
149f08c3bdfSopenharmony_ci			val = tc[i].highlimit;
150f08c3bdfSopenharmony_ci
151f08c3bdfSopenharmony_ci		timex_set_field_long(&ttxc, tc[i].modes, val);
152f08c3bdfSopenharmony_ci	}
153f08c3bdfSopenharmony_ci
154f08c3bdfSopenharmony_ci	rval = tv->clock_adjtime(CLOCK_REALTIME, tst_timex_get(&ttxc));
155f08c3bdfSopenharmony_ci	if (rval < 0) {
156f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "clock_adjtime() failed %i", rval);
157f08c3bdfSopenharmony_ci		return;
158f08c3bdfSopenharmony_ci	}
159f08c3bdfSopenharmony_ci
160f08c3bdfSopenharmony_ci	timex_show("SET", &ttxc);
161f08c3bdfSopenharmony_ci
162f08c3bdfSopenharmony_ci	rval = tv->clock_adjtime(CLOCK_REALTIME, tst_timex_get(&verify));
163f08c3bdfSopenharmony_ci	if (rval < 0) {
164f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "clock_adjtime() failed %i", rval);
165f08c3bdfSopenharmony_ci		return;
166f08c3bdfSopenharmony_ci	}
167f08c3bdfSopenharmony_ci
168f08c3bdfSopenharmony_ci	timex_show("VERIFY", &verify);
169f08c3bdfSopenharmony_ci
170f08c3bdfSopenharmony_ci	if (tc[i].delta &&
171f08c3bdfSopenharmony_ci	    timex_get_field_long(&ttxc, tc[i].modes) !=
172f08c3bdfSopenharmony_ci	    timex_get_field_long(&verify, tc[i].modes)) {
173f08c3bdfSopenharmony_ci		tst_res(TFAIL, "clock_adjtime(): could not set value (mode=%x)",
174f08c3bdfSopenharmony_ci			tc[i].modes);
175f08c3bdfSopenharmony_ci	}
176f08c3bdfSopenharmony_ci
177f08c3bdfSopenharmony_ci	tst_res(TPASS, "clock_adjtime(): success (mode=%x)", tc[i].modes);
178f08c3bdfSopenharmony_ci}
179f08c3bdfSopenharmony_ci
180f08c3bdfSopenharmony_cistatic void setup(void)
181f08c3bdfSopenharmony_ci{
182f08c3bdfSopenharmony_ci	struct test_variants *tv = &variants[tst_variant];
183f08c3bdfSopenharmony_ci	size_t i;
184f08c3bdfSopenharmony_ci	int rval;
185f08c3bdfSopenharmony_ci
186f08c3bdfSopenharmony_ci	tst_res(TINFO, "Testing variant: %s", tv->desc);
187f08c3bdfSopenharmony_ci
188f08c3bdfSopenharmony_ci	saved.type = tv->type;
189f08c3bdfSopenharmony_ci	rval = tv->clock_adjtime(CLOCK_REALTIME, tst_timex_get(&saved));
190f08c3bdfSopenharmony_ci	if (rval < 0) {
191f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "clock_adjtime() failed %i", rval);
192f08c3bdfSopenharmony_ci		return;
193f08c3bdfSopenharmony_ci	}
194f08c3bdfSopenharmony_ci
195f08c3bdfSopenharmony_ci	supported = 1;
196f08c3bdfSopenharmony_ci
197f08c3bdfSopenharmony_ci	if (rval != TIME_OK && rval != TIME_ERROR) {
198f08c3bdfSopenharmony_ci		timex_show("SAVE_STATUS", &saved);
199f08c3bdfSopenharmony_ci		tst_brk(TBROK | TERRNO, "clock has on-going leap changes, "
200f08c3bdfSopenharmony_ci				"returned: %i", rval);
201f08c3bdfSopenharmony_ci	}
202f08c3bdfSopenharmony_ci
203f08c3bdfSopenharmony_ci	hz = SAFE_SYSCONF(_SC_CLK_TCK);
204f08c3bdfSopenharmony_ci
205f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tc); i++) {
206f08c3bdfSopenharmony_ci
207f08c3bdfSopenharmony_ci		/* fix high and low limits by dividing it per HZ value */
208f08c3bdfSopenharmony_ci
209f08c3bdfSopenharmony_ci		if (tc[i].modes == ADJ_TICK)
210f08c3bdfSopenharmony_ci			tc[i].highlimit /= hz;
211f08c3bdfSopenharmony_ci
212f08c3bdfSopenharmony_ci		/* fix usec as being test default resolution */
213f08c3bdfSopenharmony_ci
214f08c3bdfSopenharmony_ci		if (timex_get_field_uint(&saved, ADJ_MODES) & ADJ_NANO) {
215f08c3bdfSopenharmony_ci			if (tc[i].modes == ADJ_OFFSET) {
216f08c3bdfSopenharmony_ci				tc[i].highlimit *= 1000;
217f08c3bdfSopenharmony_ci				tc[i].delta *= 1000;
218f08c3bdfSopenharmony_ci			}
219f08c3bdfSopenharmony_ci		}
220f08c3bdfSopenharmony_ci	}
221f08c3bdfSopenharmony_ci}
222f08c3bdfSopenharmony_ci
223f08c3bdfSopenharmony_cistatic void cleanup(void)
224f08c3bdfSopenharmony_ci{
225f08c3bdfSopenharmony_ci	struct test_variants *tv = &variants[tst_variant];
226f08c3bdfSopenharmony_ci	unsigned int modes = ADJ_ALL;
227f08c3bdfSopenharmony_ci	int rval;
228f08c3bdfSopenharmony_ci
229f08c3bdfSopenharmony_ci	if (supported == 0)
230f08c3bdfSopenharmony_ci		return;
231f08c3bdfSopenharmony_ci
232f08c3bdfSopenharmony_ci	/* restore clock resolution based on original status flag */
233f08c3bdfSopenharmony_ci
234f08c3bdfSopenharmony_ci	if (timex_get_field_uint(&saved, ADJ_STATUS) & STA_NANO)
235f08c3bdfSopenharmony_ci		modes |= ADJ_NANO;
236f08c3bdfSopenharmony_ci	else
237f08c3bdfSopenharmony_ci		modes |= ADJ_MICRO;
238f08c3bdfSopenharmony_ci
239f08c3bdfSopenharmony_ci	timex_set_field_uint(&saved, ADJ_MODES, modes);
240f08c3bdfSopenharmony_ci
241f08c3bdfSopenharmony_ci	/* restore original clock flags */
242f08c3bdfSopenharmony_ci
243f08c3bdfSopenharmony_ci	rval = tv->clock_adjtime(CLOCK_REALTIME, tst_timex_get(&saved));
244f08c3bdfSopenharmony_ci	if (rval < 0) {
245f08c3bdfSopenharmony_ci		tst_res(TFAIL | TERRNO, "clock_adjtime() failed %i", rval);
246f08c3bdfSopenharmony_ci		return;
247f08c3bdfSopenharmony_ci	}
248f08c3bdfSopenharmony_ci}
249f08c3bdfSopenharmony_ci
250f08c3bdfSopenharmony_cistatic struct tst_test test = {
251f08c3bdfSopenharmony_ci	.test = verify_clock_adjtime,
252f08c3bdfSopenharmony_ci	.setup = setup,
253f08c3bdfSopenharmony_ci	.cleanup = cleanup,
254f08c3bdfSopenharmony_ci	.tcnt = ARRAY_SIZE(tc),
255f08c3bdfSopenharmony_ci	.test_variants = ARRAY_SIZE(variants),
256f08c3bdfSopenharmony_ci	.needs_root = 1,
257f08c3bdfSopenharmony_ci	.restore_wallclock = 1,
258f08c3bdfSopenharmony_ci};
259