1 /*
2 * Check decoding of RTC ioctl commands.
3 *
4 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2016-2022 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/ioctl.h>
16 #include <linux/types.h>
17 #include <linux/rtc.h>
18
19 #ifndef RTC_VL_READ
20 # define RTC_VL_READ _IOR('p', 0x13, unsigned int)
21 #endif
22 #ifndef RTC_VL_CLR
23 # define RTC_VL_CLR _IO ('p', 0x14)
24 #endif
25
26 #ifndef RTC_PARAM_GET
27 struct rtc_param {
28 __u64 param;
29 union {
30 __u64 uvalue;
31 __s64 svalue;
32 __u64 ptr;
33 };
34 __u32 index;
35 __u32 __pad;
36 };
37 # define RTC_PARAM_GET _IOW('p', 0x13, struct rtc_param)
38 #endif /* !TC_PARAM_GET */
39 #ifndef RTC_PARAM_SET
40 # define RTC_PARAM_SET _IOW('p', 0x14, struct rtc_param)
41 #endif
42
43 static const unsigned long lmagic = (unsigned long) 0xdeadbeefbadc0dedULL;
44
45 static const char *errstr;
46
47 static int
48 do_ioctl(kernel_ulong_t cmd, kernel_ulong_t arg)
49 {
50 int rc = ioctl(-1, cmd, arg);
51 errstr = sprintrc(rc);
52
53 #ifdef INJECT_RETVAL
54 if (rc != INJECT_RETVAL)
55 error_msg_and_fail("Return value [%d] does not match"
56 " expectations [%d]", rc, INJECT_RETVAL);
57
58 static char inj_errstr[4096];
59
60 snprintf(inj_errstr, sizeof(inj_errstr), "%s (INJECTED)", errstr);
61 errstr = inj_errstr;
62 #endif
63
64 return rc;
65 }
66
67 static int
68 do_ioctl_ptr(kernel_ulong_t cmd, const void *arg)
69 {
70 return do_ioctl(cmd, (uintptr_t) arg);
71 }
72
73 #ifdef INJECT_RETVAL
74 static void
75 skip_ioctls(int argc, const char *argv[])
76 {
77 if (argc < 2)
78 error_msg_and_fail("Usage: %s NUM_SKIP", argv[0]);
79
80 unsigned long num_skip = strtoul(argv[1], NULL, 0);
81
82 for (size_t i = 0; i < num_skip; ++i) {
83 int rc = ioctl(-1, RTC_AIE_OFF, 0);
84 printf("ioctl(-1, RTC_AIE_OFF) = %s%s\n", sprintrc(rc),
85 rc == INJECT_RETVAL ? " (INJECTED)" : "");
86 if (rc == INJECT_RETVAL)
87 return;
88 }
89
90 error_msg_and_fail("Issued %lu ioctl syscalls but failed"
91 " to detect an injected return code %d",
92 num_skip, INJECT_RETVAL);
93 }
94 #endif /* INJECT_RETVAL */
95
96 static void
97 print_rtc_time(const struct rtc_time *rt)
98 {
99 printf("{tm_sec=%d, tm_min=%d, tm_hour=%d"
100 ", tm_mday=%d, tm_mon=%d, tm_year=%d",
101 rt->tm_sec, rt->tm_min, rt->tm_hour,
102 rt->tm_mday, rt->tm_mon, rt->tm_year);
103 #if VERBOSE
104 printf(", tm_wday=%d, tm_yday=%d, tm_isdst=%d}",
105 rt->tm_wday, rt->tm_yday, rt->tm_isdst);
106 #else
107 printf(", ...}");
108 #endif
109 }
110
111 int
112 main(int argc, const char *argv[])
113 {
114 #ifdef INJECT_RETVAL
115 skip_ioctls(argc, argv);
116 #endif
117
118 static const struct {
119 unsigned int cmd;
120 const char *str;
121 } noarg_cmds[] = {
122 { ARG_STR(RTC_AIE_OFF) },
123 #ifdef HPPA
124 { RTC_AIE_ON, "PA_PERF_ON or RTC_AIE_ON" },
125 #else
126 { ARG_STR(RTC_AIE_ON) },
127 #endif
128 { ARG_STR(RTC_PIE_OFF) },
129 { ARG_STR(RTC_PIE_ON) },
130 { RTC_UIE_OFF, "PHN_NOT_OH or RTC_UIE_OFF" },
131 { ARG_STR(RTC_UIE_ON) },
132 { ARG_STR(RTC_VL_CLR), },
133 { ARG_STR(RTC_WIE_OFF) },
134 { ARG_STR(RTC_WIE_ON) },
135 }, long_cmds[] = {
136 { ARG_STR(RTC_EPOCH_SET) },
137 { ARG_STR(RTC_IRQP_SET) },
138 }, plong_cmds[] = {
139 { ARG_STR(RTC_EPOCH_READ) },
140 { ARG_STR(RTC_IRQP_READ) },
141 }, ptr_cmds[] = {
142 { ARG_STR(RTC_ALM_READ) },
143 { ARG_STR(RTC_ALM_SET) },
144 { ARG_STR(RTC_EPOCH_READ) },
145 { ARG_STR(RTC_IRQP_READ) },
146 { ARG_STR(RTC_PLL_GET) },
147 { ARG_STR(RTC_PLL_SET) },
148 { ARG_STR(RTC_RD_TIME) },
149 { ARG_STR(RTC_SET_TIME) },
150 { ARG_STR(RTC_VL_READ) },
151 { ARG_STR(RTC_WKALM_RD) },
152 { ARG_STR(RTC_WKALM_SET) },
153 }, r_time_cmds[] = {
154 { ARG_STR(RTC_ALM_READ) },
155 { ARG_STR(RTC_RD_TIME) },
156 }, w_time_cmds[] = {
157 { ARG_STR(RTC_ALM_SET) },
158 { ARG_STR(RTC_SET_TIME) },
159 }, r_wkalrm_cmds[] = {
160 { ARG_STR(RTC_WKALM_RD) },
161 }, w_wkalrm_cmds[] = {
162 { ARG_STR(RTC_WKALM_SET) },
163 }, r_pll_cmds[] = {
164 { ARG_STR(RTC_PLL_GET) },
165 }, w_pll_cmds[] = {
166 { ARG_STR(RTC_PLL_SET) },
167 };
168
169 for (size_t i = 0; i < ARRAY_SIZE(noarg_cmds); ++i) {
170 do_ioctl(noarg_cmds[i].cmd, lmagic);
171 printf("ioctl(-1, %s) = %s\n",
172 noarg_cmds[i].str, errstr);
173 }
174
175 for (size_t i = 0; i < ARRAY_SIZE(long_cmds); ++i) {
176 do_ioctl(long_cmds[i].cmd, lmagic);
177 printf("ioctl(-1, %s, %lu) = %s\n",
178 long_cmds[i].str, lmagic, errstr);
179 }
180
181 TAIL_ALLOC_OBJECT_CONST_PTR(unsigned long, plong);
182 *plong = lmagic;
183
184 for (size_t i = 0; i < ARRAY_SIZE(plong_cmds); ++i) {
185 if (do_ioctl_ptr(plong_cmds[i].cmd, plong) < 0) {
186 printf("ioctl(-1, %s, %p) = %s\n",
187 plong_cmds[i].str, plong, errstr);
188 } else {
189 printf("ioctl(-1, %s, [%lu]) = %s\n",
190 plong_cmds[i].str, *plong, errstr);
191 }
192 }
193
194 void *const efault = tail_alloc(1);
195
196 for (size_t i = 0; i < ARRAY_SIZE(ptr_cmds); ++i) {
197 do_ioctl(ptr_cmds[i].cmd, 0);
198 printf("ioctl(-1, %s, NULL) = %s\n",
199 ptr_cmds[i].str, errstr);
200 do_ioctl_ptr(ptr_cmds[i].cmd, efault);
201 printf("ioctl(-1, %s, %p) = %s\n",
202 ptr_cmds[i].str, efault, errstr);
203 }
204
205 TAIL_ALLOC_OBJECT_CONST_PTR(struct rtc_time, rt);
206 fill_memory(rt, sizeof(*rt));
207
208 for (size_t i = 0; i < ARRAY_SIZE(w_time_cmds); ++i) {
209 do_ioctl_ptr(w_time_cmds[i].cmd, rt);
210 printf("ioctl(-1, %s, ", w_time_cmds[i].str);
211 print_rtc_time(rt);
212 printf(") = %s\n", errstr);
213 }
214
215 for (size_t i = 0; i < ARRAY_SIZE(r_time_cmds); ++i) {
216 if (do_ioctl_ptr(r_time_cmds[i].cmd, rt) < 0) {
217 printf("ioctl(-1, %s, %p) = %s\n",
218 r_time_cmds[i].str, rt, errstr);
219 } else {
220 printf("ioctl(-1, %s, ", r_time_cmds[i].str);
221 print_rtc_time(rt);
222 printf(") = %s\n", errstr);
223 }
224 }
225
226 TAIL_ALLOC_OBJECT_CONST_PTR(struct rtc_wkalrm, wk);
227 fill_memory(wk, sizeof(*wk));
228
229 for (size_t i = 0; i < ARRAY_SIZE(w_wkalrm_cmds); ++i) {
230 do_ioctl_ptr(w_wkalrm_cmds[i].cmd, wk);
231 printf("ioctl(-1, %s, {enabled=%hhu, pending=%hhu, time=",
232 w_wkalrm_cmds[i].str, wk->enabled, wk->pending);
233 print_rtc_time(&wk->time);
234 printf("}) = %s\n", errstr);
235 }
236
237 for (size_t i = 0; i < ARRAY_SIZE(r_wkalrm_cmds); ++i) {
238 if (do_ioctl_ptr(r_wkalrm_cmds[i].cmd, wk) < 0) {
239 printf("ioctl(-1, %s, %p) = %s\n",
240 r_wkalrm_cmds[i].str, wk, errstr);
241 } else {
242 printf("ioctl(-1, %s, {enabled=%hhu, pending=%hhu, time=",
243 r_wkalrm_cmds[i].str, wk->enabled, wk->pending);
244 print_rtc_time(&wk->time);
245 printf("}) = %s\n", errstr);
246 }
247 }
248
249 TAIL_ALLOC_OBJECT_CONST_PTR(struct rtc_pll_info, pll);
250 fill_memory(pll, sizeof(*pll));
251
252 for (size_t i = 0; i < ARRAY_SIZE(w_pll_cmds); ++i) {
253 do_ioctl_ptr(w_pll_cmds[i].cmd, pll);
254 printf("ioctl(-1, %s, {pll_ctrl=%d, pll_value=%d"
255 ", pll_max=%d, pll_min=%d, pll_posmult=%d"
256 ", pll_negmult=%d, pll_clock=%ld}) = %s\n",
257 w_pll_cmds[i].str, pll->pll_ctrl, pll->pll_value,
258 pll->pll_max, pll->pll_min, pll->pll_posmult,
259 pll->pll_negmult, pll->pll_clock, errstr);
260 }
261
262 for (size_t i = 0; i < ARRAY_SIZE(r_pll_cmds); ++i) {
263 if (do_ioctl_ptr(r_pll_cmds[i].cmd, pll) < 0) {
264 printf("ioctl(-1, %s, %p) = %s\n",
265 r_pll_cmds[i].str, pll, errstr);
266 } else {
267 printf("ioctl(-1, %s, {pll_ctrl=%d, pll_value=%d"
268 ", pll_max=%d, pll_min=%d, pll_posmult=%d"
269 ", pll_negmult=%d, pll_clock=%ld}) = %s\n",
270 r_pll_cmds[i].str, pll->pll_ctrl, pll->pll_value,
271 pll->pll_max, pll->pll_min, pll->pll_posmult,
272 pll->pll_negmult, pll->pll_clock, errstr);
273 }
274 }
275
276 static const struct strval32 vl_vecs[] = {
277 { ARG_STR(0) },
278 { ARG_XLAT_KNOWN(0x10, "RTC_VL_BACKUP_SWITCH") },
279 { ARG_XLAT_KNOWN(0xbeef, "RTC_VL_DATA_INVALID"
280 "|RTC_VL_BACKUP_LOW"
281 "|RTC_VL_BACKUP_EMPTY"
282 "|RTC_VL_ACCURACY_LOW|0xbee0") },
283 { ARG_XLAT_UNKNOWN(0xbadc0de0, "RTC_VL_???") },
284 };
285 TAIL_ALLOC_OBJECT_CONST_PTR(unsigned int, pint);
286
287 for (size_t i = 0; i < ARRAY_SIZE(vl_vecs); i++) {
288 *pint = vl_vecs[i].val;
289
290 if (do_ioctl_ptr(RTC_VL_READ, pint) < 0) {
291 printf("ioctl(-1, RTC_VL_READ, %p) = %s\n",
292 pint, errstr);
293 } else {
294 printf("ioctl(-1, RTC_VL_READ, [%s]) = %s\n",
295 vl_vecs[i].str, errstr);
296 }
297 }
298
299 do_ioctl(_IO(0x70, 0x40), lmagic);
300 printf("ioctl(-1, %s, %#lx) = %s\n", "NVRAM_INIT", lmagic, errstr);
301
302 static const struct strval32 param_cmds[] = {
303 { ARG_STR(RTC_PARAM_GET) },
304 { ARG_STR(RTC_PARAM_SET) },
305 };
306 static const struct {
307 struct rtc_param val;
308 const char *get_in;
309 const char *get_out;
310 const char *set;
311 } param_vecs[] = {
312 { { 0 },
313 "{param=RTC_PARAM_FEATURES, index=0}",
314 "{uvalue=0}",
315 "{param=RTC_PARAM_FEATURES, uvalue=0, index=0}" },
316 { { .param = 0, .uvalue = (__u64) 0xdeadfacebeeffeedULL,
317 .index= 0xfacecafe, .__pad = 0xbadc0ded },
318 "{param=RTC_PARAM_FEATURES, index=4207856382"
319 ", __pad=0xbadc0ded}",
320 "{uvalue=1<<RTC_FEATURE_ALARM|1<<RTC_FEATURE_NEED_WEEK_DAY"
321 "|1<<RTC_FEATURE_ALARM_RES_2S|1<<RTC_FEATURE_CORRECTION"
322 "|1<<RTC_FEATURE_BACKUP_SWITCH_MODE"
323 "|1<<RTC_FEATURE_ALARM_WAKEUP_ONLY|0xdeadfacebeeffe00"
324 ", __pad=0xbadc0ded}",
325 "{param=RTC_PARAM_FEATURES, uvalue=1<<RTC_FEATURE_ALARM"
326 "|1<<RTC_FEATURE_NEED_WEEK_DAY|1<<RTC_FEATURE_ALARM_RES_2S"
327 "|1<<RTC_FEATURE_CORRECTION|1<<RTC_FEATURE_BACKUP_SWITCH_MODE"
328 "|1<<RTC_FEATURE_ALARM_WAKEUP_ONLY|0xdeadfacebeeffe00"
329 ", index=4207856382, __pad=0xbadc0ded}" },
330 { { .param = 0, .uvalue = 0xbeef00, .__pad = 1 },
331 "{param=RTC_PARAM_FEATURES, index=0, __pad=0x1}",
332 "{uvalue=0xbeef00 /* 1<<RTC_FEATURE_??? */, __pad=0x1}",
333 "{param=RTC_PARAM_FEATURES"
334 ", uvalue=0xbeef00 /* 1<<RTC_FEATURE_??? */, index=0"
335 ", __pad=0x1}" },
336 { { .param = 1 },
337 "{param=RTC_PARAM_CORRECTION, index=0}",
338 "{svalue=0}",
339 "{param=RTC_PARAM_CORRECTION, svalue=0, index=0}" },
340 { { .param = 1, .svalue = (__s64) 0xfacefeeddeadcafeULL,
341 .index = 0xdeffaced, .__pad = 0xcafeface },
342 "{param=RTC_PARAM_CORRECTION, index=3741297901"
343 ", __pad=0xcafeface}",
344 "{svalue=-374081421428536578, __pad=0xcafeface}",
345 "{param=RTC_PARAM_CORRECTION, svalue=-374081421428536578"
346 ", index=3741297901, __pad=0xcafeface}" },
347 { { .param = 1, .svalue = -1337, .index = 0x42 },
348 "{param=RTC_PARAM_CORRECTION, index=66}",
349 "{svalue=-1337}",
350 "{param=RTC_PARAM_CORRECTION, svalue=-1337, index=66}" },
351 { { .param = 2 },
352 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, index=0}",
353 "{uvalue=RTC_BSM_DISABLED}",
354 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, uvalue=RTC_BSM_DISABLED"
355 ", index=0}" },
356 { { .param = 2, .uvalue = 3, .index = 0xdecaffed,
357 .__pad = 0xfacebeef },
358 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, index=3737845741"
359 ", __pad=0xfacebeef}",
360 "{uvalue=RTC_BSM_STANDBY, __pad=0xfacebeef}",
361 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, uvalue=RTC_BSM_STANDBY"
362 ", index=3737845741, __pad=0xfacebeef}" },
363 { { .param = 2, .uvalue = 4, .__pad = 23 },
364 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, index=0, __pad=0x17}",
365 "{uvalue=0x4 /* RTC_BSM_??? */, __pad=0x17}",
366 "{param=RTC_PARAM_BACKUP_SWITCH_MODE"
367 ", uvalue=0x4 /* RTC_BSM_??? */, index=0, __pad=0x17}" },
368 { { .param = 2, .uvalue = (__u64) 0xface1e55beefcafeULL,
369 .index = 42 },
370 "{param=RTC_PARAM_BACKUP_SWITCH_MODE, index=42}",
371 "{uvalue=0xface1e55beefcafe /* RTC_BSM_??? */}",
372 "{param=RTC_PARAM_BACKUP_SWITCH_MODE"
373 ", uvalue=0xface1e55beefcafe /* RTC_BSM_??? */"
374 ", index=42}" },
375 { { .param = 3 },
376 "{param=0x3 /* RTC_PARAM_??? */, index=0}",
377 "{uvalue=0}",
378 "{param=0x3 /* RTC_PARAM_??? */, uvalue=0, index=0}" },
379 { { .param = (__u64) 0xbeeffacedeadc0deULL,
380 .uvalue = (__u64) 0xdefc0dedbadfacedULL,
381 .index = 3141592653, .__pad = 2718281828 },
382 "{param=0xbeeffacedeadc0de /* RTC_PARAM_??? */"
383 ", index=3141592653, __pad=0xa205b064}",
384 "{uvalue=0xdefc0dedbadfaced, __pad=0xa205b064}",
385 "{param=0xbeeffacedeadc0de /* RTC_PARAM_??? */"
386 ", uvalue=0xdefc0dedbadfaced, index=3141592653"
387 ", __pad=0xa205b064}" },
388 };
389 TAIL_ALLOC_OBJECT_CONST_PTR(struct rtc_param, pparam);
390
391 for (size_t i = 0; i < ARRAY_SIZE(param_cmds); i++) {
392 do_ioctl(param_cmds[i].val, 0);
393 printf("ioctl(-1, %s, NULL) = %s\n",
394 param_cmds[i].str, errstr);
395
396 do_ioctl_ptr(param_cmds[i].val, pparam + 1);
397 printf("ioctl(-1, %s, %p) = %s\n",
398 param_cmds[i].str, pparam + 1, errstr);
399 }
400
401 for (size_t i = 0; i < ARRAY_SIZE(param_vecs); i++) {
402 *pparam = param_vecs[i].val;
403
404 int ret = do_ioctl_ptr(RTC_PARAM_GET, pparam);
405 printf("ioctl(-1, RTC_PARAM_GET, %s => ", param_vecs[i].get_in);
406 if (ret < 0)
407 printf("%p", pparam);
408 else
409 printf("%s", param_vecs[i].get_out);
410 printf(") = %s\n", errstr);
411
412 do_ioctl_ptr(RTC_PARAM_SET, pparam);
413 printf("ioctl(-1, RTC_PARAM_SET, %s) = %s\n",
414 param_vecs[i].set, errstr);
415 }
416
417 puts("+++ exited with 0 +++");
418 return 0;
419 }