1 /*
2 * Check HDIO_* ioctl decoding.
3 *
4 * Copyright (c) 2020-2021 The strace developers.
5 * All rights reserved.
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "tests.h"
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <linux/hdreg.h>
16 #include <sys/ioctl.h>
17 #include "xlat.h"
18
19 #include "xlat/hdio_busstates.h"
20 #include "xlat/hdio_ide_nice.h"
21
22 static const char *errstr;
23
24 static long
25 do_ioctl(kernel_ulong_t cmd, kernel_ulong_t arg)
26 {
27 long rc = ioctl(-1, cmd, arg);
28
29 errstr = sprintrc(rc);
30
31 #ifdef INJECT_RETVAL
32 if (rc != INJECT_RETVAL)
33 error_msg_and_fail("Got a return value of %ld != %ld",
34 rc, (long) INJECT_RETVAL);
35
36 static char inj_errstr[4096];
37
38 snprintf(inj_errstr, sizeof(inj_errstr), "%s (INJECTED)", errstr);
39 errstr = inj_errstr;
40 #endif
41
42 return rc;
43 }
44
45 static long
46 do_ioctl_ptr(kernel_ulong_t cmd, const void *arg)
47 {
48 return do_ioctl(cmd, (uintptr_t) arg);
49 }
50
51 int
52 main(int argc, char *argv[])
53 {
54 #ifdef INJECT_RETVAL
55 unsigned long num_skip;
56 bool locked = false;
57
58 if (argc < 2)
59 error_msg_and_fail("Usage: %s NUM_SKIP", argv[0]);
60
61 num_skip = strtoul(argv[1], NULL, 0);
62
63 for (size_t i = 0; i < num_skip; i++) {
64 long ret = ioctl(-1, HDIO_GET_QDMA, 0);
65
66 printf("ioctl(-1, %s, 0) = %s%s\n",
67 XLAT_STR(HDIO_GET_QDMA), sprintrc(ret),
68 ret == INJECT_RETVAL ? " (INJECTED)" : "");
69
70 if (ret != INJECT_RETVAL)
71 continue;
72
73 locked = true;
74 break;
75 }
76
77 if (!locked)
78 error_msg_and_fail("Hasn't locked on ioctl(-1"
79 ", HDIO_GET_QDMA, 0) returning %d",
80 INJECT_RETVAL);
81 #endif
82
83 long rc;
84
85 /* Decoding is not supported */
86 static const struct {
87 uint32_t cmd;
88 const char *str;
89 } unsupp_cmds[] = {
90 { ARG_STR(HDIO_GET_QDMA) },
91 { ARG_STR(HDIO_SET_XFER) },
92 { ARG_STR(HDIO_TRISTATE_HWIF) },
93 { ARG_STR(HDIO_DRIVE_TASKFILE) },
94 { ARG_STR(HDIO_DRIVE_TASK) },
95 { ARG_STR(HDIO_SET_QDMA) },
96 #ifdef MPERS_IS_m32
97 { ARG_STR(HDIO_GET_UNMASKINTR) },
98 { ARG_STR(HDIO_GET_MULTCOUNT) },
99 { ARG_STR(HDIO_OBSOLETE_IDENTITY) },
100 { ARG_STR(HDIO_GET_KEEPSETTINGS) },
101 { ARG_STR(HDIO_GET_32BIT) },
102 { ARG_STR(HDIO_GET_NOWERR) },
103 { ARG_STR(HDIO_GET_DMA) },
104 { ARG_STR(HDIO_GET_NICE) },
105 { ARG_STR(HDIO_GET_IDENTITY) },
106 { ARG_STR(HDIO_GET_WCACHE) },
107 { ARG_STR(HDIO_GET_ACOUSTIC) },
108 { ARG_STR(HDIO_GET_ADDRESS) },
109 { ARG_STR(HDIO_GET_BUSSTATE) },
110 { ARG_STR(HDIO_DRIVE_RESET) },
111 { ARG_STR(HDIO_SET_MULTCOUNT) },
112 { ARG_STR(HDIO_SET_UNMASKINTR) },
113 { ARG_STR(HDIO_SET_KEEPSETTINGS) },
114 { ARG_STR(HDIO_SET_32BIT) },
115 { ARG_STR(HDIO_SET_NOWERR) },
116 { ARG_STR(HDIO_SET_DMA) },
117 { ARG_STR(HDIO_SET_PIO_MODE) },
118 { ARG_STR(HDIO_SCAN_HWIF) },
119 { ARG_STR(HDIO_UNREGISTER_HWIF) },
120 { ARG_STR(HDIO_SET_NICE) },
121 { ARG_STR(HDIO_SET_WCACHE) },
122 { ARG_STR(HDIO_SET_ACOUSTIC) },
123 { ARG_STR(HDIO_SET_BUSSTATE) },
124 { ARG_STR(HDIO_SET_ADDRESS) },
125 #endif /* MPERS_IS_m32 */
126 };
127
128 for (size_t i = 0; i < ARRAY_SIZE(unsupp_cmds); i++) {
129 do_ioctl(unsupp_cmds[i].cmd, 0);
130 printf("ioctl(-1, " XLAT_FMT ", 0) = %s\n",
131 XLAT_SEL(unsupp_cmds[i].cmd, unsupp_cmds[i].str),
132 errstr);
133
134 do_ioctl(unsupp_cmds[i].cmd,
135 (unsigned long) 0xbadc0deddeadc0deULL);
136 printf("ioctl(-1, " XLAT_FMT ", %#lx) = %s\n",
137 XLAT_SEL(unsupp_cmds[i].cmd, unsupp_cmds[i].str),
138 (unsigned long) 0xbadc0deddeadc0deULL, errstr);
139 }
140
141
142 /* HDIO_GETGEO */
143 do_ioctl(HDIO_GETGEO, 0);
144 printf("ioctl(-1, %s, NULL) = %s\n",
145 XLAT_STR(HDIO_GETGEO), errstr);
146
147 TAIL_ALLOC_OBJECT_CONST_PTR(struct hd_geometry, p_hd_geom);
148
149 p_hd_geom->heads = 0xca;
150 p_hd_geom->sectors = 0xfe;
151 p_hd_geom->cylinders = 0xbabe;
152 p_hd_geom->start = (unsigned long) 0xbadc0deddeadfaceULL;
153
154 do_ioctl_ptr(HDIO_GETGEO, (char *) p_hd_geom + 1);
155 printf("ioctl(-1, %s, %p) = %s\n",
156 XLAT_STR(HDIO_GETGEO), (char *) p_hd_geom + 1, errstr);
157
158 rc = do_ioctl_ptr(HDIO_GETGEO, p_hd_geom);
159 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_GETGEO));
160 if (rc >= 0) {
161 printf("{heads=202, sectors=254, cylinders=47806, start=%lu}",
162 (unsigned long) 0xbadc0deddeadfaceULL);
163 } else {
164 printf("%p", p_hd_geom);
165 }
166 printf(") = %s\n", errstr);
167
168 /* HDIO_DRIVE_CMD */
169 do_ioctl(HDIO_DRIVE_CMD, 0);
170 printf("ioctl(-1, %s, NULL) = %s\n",
171 XLAT_STR(HDIO_DRIVE_CMD), errstr);
172
173 TAIL_ALLOC_OBJECT_CONST_PTR(struct hd_drive_cmd_hdr, p_hd_drive_cmd);
174 struct hd_drive_cmd_hdr *p_hd_drive_cmd2 =
175 tail_alloc(sizeof(*p_hd_drive_cmd2) + 16);
176 struct hd_drive_cmd_hdr *p_hd_drive_cmd3 =
177 tail_alloc(sizeof(*p_hd_drive_cmd3) + DEFAULT_STRLEN + 1);
178
179 fill_memory(p_hd_drive_cmd2, sizeof(*p_hd_drive_cmd2) + 16);
180 fill_memory(p_hd_drive_cmd3,
181 sizeof(*p_hd_drive_cmd3) + DEFAULT_STRLEN + 1);
182
183 p_hd_drive_cmd->command = 0xca;
184 p_hd_drive_cmd->sector_number = 0xff;
185 p_hd_drive_cmd->feature = 0xee;
186 p_hd_drive_cmd->sector_count = 0;
187
188 do_ioctl_ptr(HDIO_DRIVE_CMD, (char *) p_hd_drive_cmd + 1);
189 printf("ioctl(-1, %s, %p) = %s\n",
190 XLAT_STR(HDIO_DRIVE_CMD), (char *) p_hd_drive_cmd + 1, errstr);
191
192 for (size_t i = 0; i < 2; i++) {
193 p_hd_drive_cmd->sector_count = i;
194
195 rc = do_ioctl_ptr(HDIO_DRIVE_CMD, p_hd_drive_cmd);
196 printf("ioctl(-1, %s, {command=" XLAT_FMT ", sector_number=255"
197 ", feature=238, sector_count=%zu",
198 XLAT_STR(HDIO_DRIVE_CMD),
199 XLAT_SEL(0xca, "ATA_CMD_WRITE"), i);
200 if (rc >= 0) {
201 printf("} => {status=0xca, error=255, nsector=238");
202 if (i)
203 printf(", buf=%p", p_hd_drive_cmd + 1);
204 }
205 printf("}) = %s\n", errstr);
206 }
207
208 rc = do_ioctl_ptr(HDIO_DRIVE_CMD, p_hd_drive_cmd2);
209 printf("ioctl(-1, %s, {command=0x80" NRAW(" /* ATA_CMD_??? */")
210 ", sector_number=129, feature=130, sector_count=131",
211 XLAT_STR(HDIO_DRIVE_CMD));
212 if (rc >= 0) {
213 printf("} => {status=0x80, error=129, nsector=130, buf=%p",
214 p_hd_drive_cmd2 + 1);
215 }
216 printf("}) = %s\n", errstr);
217
218 rc = do_ioctl_ptr(HDIO_DRIVE_CMD, p_hd_drive_cmd3);
219 printf("ioctl(-1, %s, {command=0x80" NRAW(" /* ATA_CMD_??? */")
220 ", sector_number=129, feature=130, sector_count=131",
221 XLAT_STR(HDIO_DRIVE_CMD));
222 if (rc >= 0) {
223 printf("} => {status=0x80, error=129, nsector=130, buf=");
224 print_quoted_hex(p_hd_drive_cmd3 + 1, DEFAULT_STRLEN);
225 printf("...");
226 }
227 printf("}) = %s\n", errstr);
228
229 /* HDIO compat has never been supported by the kernel. */
230 #ifndef MPERS_IS_m32
231
232 void *const efault = tail_alloc(1) + 1;
233
234 /* HDIO_DRIVE_RESET */
235 printf("ioctl(-1, %s, NULL) = %s\n",
236 XLAT_STR(HDIO_DRIVE_RESET), errstr);
237 do_ioctl_ptr(HDIO_DRIVE_RESET, 0);
238
239 printf("ioctl(-1, %s, %p) = %s\n",
240 XLAT_STR(HDIO_DRIVE_RESET), efault, errstr);
241 do_ioctl(HDIO_DRIVE_RESET, (uintptr_t) efault);
242
243 printf("ioctl(-1, %s, [%#x, %#x, %#x]) = %s\n",
244 XLAT_STR(HDIO_DRIVE_RESET),
245 (unsigned int) 0xdeadbeef,
246 (unsigned int) 0xAAAAAAAA,
247 (unsigned int) 0xbeefbeef,
248 errstr);
249 int drive_reset_args[3] = {0xdeadbeef, 0xAAAAAAAA, 0xbeefbeef};
250 do_ioctl_ptr(HDIO_DRIVE_RESET, &drive_reset_args);
251
252 /* HDIO_SCAN_HWIF */
253 printf("ioctl(-1, %s, NULL) = %s\n", XLAT_STR(HDIO_SCAN_HWIF), errstr);
254 do_ioctl_ptr(HDIO_SCAN_HWIF, 0);
255
256 printf("ioctl(-1, %s, %p) = %s\n",
257 XLAT_STR(HDIO_SCAN_HWIF), efault, errstr);
258 do_ioctl(HDIO_SCAN_HWIF, (uintptr_t) efault);
259
260 printf("ioctl(-1, %s, [%#x, %#x, %#x]) = %s\n",
261 XLAT_STR(HDIO_SCAN_HWIF),
262 (unsigned int) 0xdeadbeef,
263 (unsigned int) 0xAAAAAAAA,
264 (unsigned int) 0xbeefbeef,
265 errstr);
266 int scan_hwif_args[3] = {0xdeadbeef, 0xAAAAAAAA, 0xbeefbeef};
267 do_ioctl_ptr(HDIO_SCAN_HWIF, &scan_hwif_args);
268
269 /* Getters of the form ioctl(..., ..., &val) */
270 static const struct {
271 uint32_t cmd;
272 const char *str;
273 } getter_cmds[] = {
274 { ARG_STR(HDIO_GET_32BIT) },
275 { ARG_STR(HDIO_GET_ACOUSTIC) },
276 { ARG_STR(HDIO_GET_ADDRESS) },
277 { ARG_STR(HDIO_GET_DMA) },
278 { ARG_STR(HDIO_GET_KEEPSETTINGS) },
279 { ARG_STR(HDIO_GET_MULTCOUNT) },
280 { ARG_STR(HDIO_GET_NOWERR) },
281 { ARG_STR(HDIO_GET_UNMASKINTR) },
282 { ARG_STR(HDIO_GET_WCACHE) },
283 };
284
285 for (size_t i = 0; i < ARRAY_SIZE(getter_cmds); i++) {
286 kernel_ulong_t val = (kernel_ulong_t) 0xfacefeeddeadbeefULL;
287
288 rc = do_ioctl_ptr(getter_cmds[i].cmd, &val);
289 printf("ioctl(-1, " XLAT_FMT ", ",
290 XLAT_SEL(getter_cmds[i].cmd, getter_cmds[i].str));
291 if (rc >= 0) {
292 printf("[%llu]", (unsigned long long) val);
293 } else {
294 printf("%p", &val);
295 }
296 printf(") = %s\n", errstr);
297 }
298
299 /* Setters of the form ioctl(..., ..., val) */
300 static const struct {
301 uint32_t cmd;
302 const char *str;
303 } setter_cmds[] = {
304 { ARG_STR(HDIO_SET_32BIT) },
305 { ARG_STR(HDIO_SET_ACOUSTIC) },
306 { ARG_STR(HDIO_SET_ADDRESS) },
307 { ARG_STR(HDIO_SET_DMA) },
308 { ARG_STR(HDIO_SET_KEEPSETTINGS) },
309 { ARG_STR(HDIO_SET_MULTCOUNT) },
310 { ARG_STR(HDIO_SET_NOWERR) },
311 { ARG_STR(HDIO_SET_PIO_MODE) },
312 { ARG_STR(HDIO_SET_UNMASKINTR) },
313 { ARG_STR(HDIO_SET_WCACHE) },
314 { ARG_STR(HDIO_UNREGISTER_HWIF) },
315 };
316
317 for (size_t i = 0; i < ARRAY_SIZE(setter_cmds); i++) {
318 unsigned long val = (unsigned long) 0xfacefeeddeadbeefULL;
319
320 rc = do_ioctl(setter_cmds[i].cmd, val);
321 printf("ioctl(-1, " XLAT_FMT ", %llu) = %s\n",
322 XLAT_SEL(setter_cmds[i].cmd, setter_cmds[i].str),
323 (unsigned long long) val, errstr);
324 }
325
326 /* HDIO_OBSOLETE_IDENTITY */
327 do_ioctl_ptr(HDIO_OBSOLETE_IDENTITY, NULL);
328 printf("ioctl(-1, %s, NULL) = %s\n",
329 XLAT_STR(HDIO_OBSOLETE_IDENTITY), errstr);
330
331 char obsolete_identity[142];
332 rc = do_ioctl_ptr(HDIO_OBSOLETE_IDENTITY, obsolete_identity);
333
334 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_OBSOLETE_IDENTITY));
335
336 if (rc >= 0) {
337 print_quoted_memory(obsolete_identity, DEFAULT_STRLEN);
338 printf("...");
339 } else {
340 printf("%p", obsolete_identity);
341 }
342 printf(") = %s\n", errstr);
343
344 /* HDIO_GET_IDENTITY */
345 do_ioctl_ptr(HDIO_GET_IDENTITY, NULL);
346 printf("ioctl(-1, %s, NULL) = %s\n",
347 XLAT_STR(HDIO_GET_IDENTITY), errstr);
348
349 char identity[512];
350 rc = do_ioctl_ptr(HDIO_GET_IDENTITY, identity);
351
352 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_GET_IDENTITY));
353
354 if (rc >= 0) {
355 print_quoted_memory(identity, DEFAULT_STRLEN);
356 printf("...");
357 } else {
358 printf("%p", identity);
359 }
360 printf(") = %s\n", errstr);
361
362 /* HDIO_GET_NICE */
363 kernel_ulong_t nice_val = (kernel_ulong_t) 0xfacefeedded1ffffULL;
364
365 rc = do_ioctl_ptr(HDIO_GET_NICE, &nice_val);
366 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_GET_NICE));
367 if (rc >= 0) {
368 printf("[");
369 # if XLAT_RAW
370 printf("%#llx", (unsigned long long) nice_val);
371 # else
372 # if XLAT_VERBOSE
373 printf("%#llx /* ", (unsigned long long) nice_val);
374 # endif
375 printflags(hdio_ide_nice, nice_val, "IDE_NICE_???");
376 # if XLAT_VERBOSE
377 printf(" */");
378 # endif
379 # endif
380 printf("]");
381 } else {
382 printf("%p", &nice_val);
383 }
384 printf(") = %s\n", errstr);
385
386 /* HDIO_SET_NICE */
387 nice_val = (unsigned long) 0xfacefeedded2ffffULL;
388 do_ioctl(HDIO_SET_NICE, nice_val);
389 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_SET_NICE));
390 # if XLAT_RAW
391 printf("%#llx", (unsigned long long) nice_val);
392 # else
393 # if XLAT_VERBOSE
394 printf("%#llx /* ", (unsigned long long) nice_val);
395 # endif
396 printflags(hdio_ide_nice, nice_val, "IDE_NICE_???");
397 # if XLAT_VERBOSE
398 printf(" */");
399 # endif
400 # endif
401 printf(") = %s\n", errstr);
402
403 /* HDIO_GET_BUSSTATE */
404 kernel_ulong_t busstate_value = (kernel_ulong_t) 0xfacefeedded3ffffULL;
405
406 rc = do_ioctl_ptr(HDIO_GET_BUSSTATE, &busstate_value);
407 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_GET_BUSSTATE));
408 if (rc >= 0) {
409 printf("[");
410 printxval(hdio_busstates, busstate_value, "BUSSTATE_???");
411 printf("]");
412 } else {
413 printf("%p", &busstate_value);
414 }
415 printf(") = %s\n", errstr);
416
417
418 /* HDIO_SET_BUSSTATE */
419 busstate_value = (unsigned long) 0xfacefeedded4ffffULL;
420
421 do_ioctl(HDIO_SET_BUSSTATE, busstate_value);
422 printf("ioctl(-1, %s, ", XLAT_STR(HDIO_SET_BUSSTATE));
423 printxval(hdio_busstates, busstate_value, "BUSSTATE_???");
424 printf(") = %s\n", errstr);
425
426 #endif /* !MPERS_IS_m32 */
427
428 puts("+++ exited with 0 +++");
429 return 0;
430 }