1 /*
2 * Check decoding of MTD ioctl commands.
3 *
4 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
5 * Copyright (c) 2016-2021 The strace developers.
6 * All rights reserved.
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 #include "tests.h"
12
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/ioctl.h>
19 #include <linux/ioctl.h>
20 #include <linux/version.h>
21 #include <mtd/mtd-abi.h>
22
23 static const char *errstr;
24
25 static int
26 do_ioctl(kernel_ulong_t cmd, kernel_ulong_t arg)
27 {
28 int rc = ioctl(-1, cmd, arg);
29 errstr = sprintrc(rc);
30
31 #ifdef INJECT_RETVAL
32 if (rc != INJECT_RETVAL)
33 error_msg_and_fail("Return value [%d] does not match"
34 " expectations [%d]", rc, 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 int
46 do_ioctl_ptr(kernel_ulong_t cmd, const void *arg)
47 {
48 return do_ioctl(cmd, (uintptr_t) arg);
49 }
50
51 #ifdef INJECT_RETVAL
52 static void
53 skip_ioctls(int argc, const char *argv[])
54 {
55 if (argc < 2)
56 error_msg_and_fail("Usage: %s NUM_SKIP", argv[0]);
57
58 unsigned long num_skip = strtoul(argv[1], NULL, 0);
59
60 for (size_t i = 0; i < num_skip; ++i) {
61 int rc = ioctl(-1, MEMWRITE, 0);
62
63 printf("ioctl(-1, MEMWRITE, NULL) = %s%s\n", sprintrc(rc),
64 rc == INJECT_RETVAL ? " (INJECTED)" : "");
65
66 if (rc == INJECT_RETVAL)
67 return;
68 }
69
70 error_msg_and_fail("Issued %lu ioctl syscalls but failed"
71 " to detect an injected return code %d",
72 num_skip, INJECT_RETVAL);
73 }
74 #endif /* INJECT_RETVAL */
75
76 int
77 main(int argc, const char *argv[])
78 {
79 #ifdef INJECT_RETVAL
80 skip_ioctls(argc, argv);
81 #endif
82
83 static const struct {
84 uint32_t cmd;
85 const char *str;
86 } ptr_cmds[] = {
87 { ARG_STR(ECCGETLAYOUT) },
88 { ARG_STR(ECCGETSTATS) },
89 { ARG_STR(MEMERASE) },
90 { ARG_STR(MEMERASE64) },
91 { ARG_STR(MEMGETBADBLOCK) },
92 { ARG_STR(MEMGETINFO) },
93 { ARG_STR(MEMGETOOBSEL) },
94 { ARG_STR(MEMGETREGIONCOUNT) },
95 { ARG_STR(MEMISLOCKED) },
96 { ARG_STR(MEMLOCK) },
97 { ARG_STR(MEMREADOOB) },
98 { ARG_STR(MEMREADOOB64) },
99 { ARG_STR(MEMSETBADBLOCK) },
100 { ARG_STR(MEMUNLOCK) },
101 { ARG_STR(MEMWRITE) },
102 { ARG_STR(MEMWRITEOOB) },
103 { ARG_STR(MEMWRITEOOB64) },
104 { ARG_STR(OTPGETREGIONCOUNT) },
105 { ARG_STR(OTPGETREGIONINFO) },
106 { ARG_STR(OTPLOCK) },
107 { ARG_STR(OTPSELECT) },
108 },
109 eiu_cmds[] = {
110 { ARG_STR(MEMERASE) },
111 { ARG_STR(MEMLOCK) },
112 { ARG_STR(MEMUNLOCK) },
113 { ARG_STR(MEMISLOCKED) },
114 };
115
116 for (size_t i = 0; i < ARRAY_SIZE(ptr_cmds); ++i) {
117 do_ioctl(ptr_cmds[i].cmd, 0);
118 if (_IOC_DIR(ptr_cmds[i].cmd) == _IOC_WRITE)
119 printf("ioctl(-1, MIXER_WRITE(%u) or %s, NULL) = %s\n",
120 (unsigned int) _IOC_NR(ptr_cmds[i].cmd),
121 ptr_cmds[i].str, errstr);
122 else if (_IOC_DIR(ptr_cmds[i].cmd) == _IOC_READ)
123 printf("ioctl(-1, MIXER_READ(%u) or %s, NULL) = %s\n",
124 (unsigned int) _IOC_NR(ptr_cmds[i].cmd),
125 ptr_cmds[i].str, errstr);
126 else
127 printf("ioctl(-1, %s, NULL) = %s\n",
128 ptr_cmds[i].str, errstr);
129 }
130
131 do_ioctl(MTDFILEMODE, MTD_FILE_MODE_NORMAL);
132 printf("ioctl(-1, MTDFILEMODE, MTD_FILE_MODE_NORMAL) = %s\n", errstr);
133
134 TAIL_ALLOC_OBJECT_CONST_PTR(int, opt);
135 *opt = MTD_OTP_OFF;
136 do_ioctl_ptr(OTPSELECT, opt);
137 printf("ioctl(-1, MIXER_READ(%u) or OTPSELECT, [MTD_OTP_OFF]) = %s\n",
138 (unsigned int) _IOC_NR(OTPSELECT), errstr);
139
140 TAIL_ALLOC_OBJECT_CONST_PTR(uint64_t, v64);
141 fill_memory(v64, sizeof(*v64));
142
143 do_ioctl_ptr(MEMGETBADBLOCK, v64);
144 printf("ioctl(-1, MIXER_WRITE(%u) or MEMGETBADBLOCK, [%" PRIu64 "])"
145 " = %s\n",
146 (unsigned int) _IOC_NR(MEMGETBADBLOCK), *v64, errstr);
147
148 do_ioctl_ptr(MEMSETBADBLOCK, v64);
149 printf("ioctl(-1, MIXER_WRITE(%u) or MEMSETBADBLOCK, [%" PRIu64 "])"
150 " = %s\n",
151 (unsigned int) _IOC_NR(MEMSETBADBLOCK), *v64, errstr);
152
153 if (do_ioctl(MEMGETREGIONINFO, 0) < 0) {
154 printf("ioctl(-1, %s, NULL) = %s\n",
155 "MEMGETREGIONINFO"
156 #ifdef __i386__
157 " or MTRRIOC_GET_PAGE_ENTRY"
158 #endif
159 , errstr);
160 } else {
161 printf("ioctl(-1, %s, NULL) = %s\n",
162 "MEMGETREGIONINFO"
163 #ifdef __i386__
164 " or MTRRIOC_GET_PAGE_ENTRY"
165 #endif
166 , errstr);
167 }
168
169 TAIL_ALLOC_OBJECT_CONST_PTR(struct region_info_user, riu);
170 fill_memory(riu, sizeof(*riu));
171 if (do_ioctl_ptr(MEMGETREGIONINFO, riu) < 0) {
172 printf("ioctl(-1, %s, {regionindex=%#x}) = %s\n",
173 "MEMGETREGIONINFO"
174 #ifdef __i386__
175 " or MTRRIOC_GET_PAGE_ENTRY"
176 #endif
177 , riu->regionindex, errstr);
178 } else {
179 printf("ioctl(-1, %s, {regionindex=%#x, offset=%#x"
180 ", erasesize=%#x, numblocks=%#x}) = %s\n",
181 "MEMGETREGIONINFO"
182 #ifdef __i386__
183 " or MTRRIOC_GET_PAGE_ENTRY"
184 #endif
185 , riu->regionindex, riu->offset,
186 riu->erasesize, riu->numblocks, errstr);
187 }
188
189 TAIL_ALLOC_OBJECT_CONST_PTR(struct erase_info_user, eiu);
190 fill_memory(eiu, sizeof(*eiu));
191
192 for (size_t i = 0; i < ARRAY_SIZE(eiu_cmds); ++i) {
193 do_ioctl_ptr(eiu_cmds[i].cmd, eiu);
194 printf("ioctl(-1, MIXER_%s(%u) or %s"
195 ", {start=%#x, length=%#x}) = %s\n",
196 (_IOC_DIR(eiu_cmds[i].cmd) == _IOC_READ)
197 ? "READ" : "WRITE",
198 (unsigned int) _IOC_NR(eiu_cmds[i].cmd),
199 eiu_cmds[i].str, eiu->start, eiu->length, errstr);
200 }
201
202 TAIL_ALLOC_OBJECT_CONST_PTR(struct erase_info_user64, eiu64);
203 fill_memory(eiu64, sizeof(*eiu64));
204 do_ioctl_ptr(MEMERASE64, eiu64);
205 printf("ioctl(-1, MIXER_WRITE(%u) or %s, {start=%#llx, length=%#llx})"
206 " = %s\n",
207 (unsigned int) _IOC_NR(MEMERASE64), "MEMERASE64",
208 (unsigned long long) eiu64->start,
209 (unsigned long long) eiu64->length, errstr);
210
211 TAIL_ALLOC_OBJECT_CONST_PTR(struct mtd_oob_buf, oob);
212 fill_memory(oob, sizeof(*oob));
213
214 do_ioctl_ptr(MEMWRITEOOB, oob);
215 printf("ioctl(-1, MEMWRITEOOB, {start=%#x, length=%#x, ptr=%p})"
216 " = %s\n", oob->start, oob->length, oob->ptr, errstr);
217
218 do_ioctl_ptr(MEMREADOOB, oob);
219 printf("ioctl(-1, MEMREADOOB, {start=%#x, length=%#x, ptr=%p})"
220 " = %s\n", oob->start, oob->length, oob->ptr, errstr);
221
222 TAIL_ALLOC_OBJECT_CONST_PTR(struct mtd_oob_buf64, oob64);
223 fill_memory(oob64, sizeof(*oob64));
224
225 do_ioctl_ptr(MEMWRITEOOB64, oob64);
226 printf("ioctl(-1, MEMWRITEOOB64"
227 ", {start=%#llx, length=%#x, usr_ptr=%#llx}) = %s\n",
228 (unsigned long long) oob64->start, oob64->length,
229 (unsigned long long) oob64->usr_ptr, errstr);
230
231 do_ioctl_ptr(MEMREADOOB64, oob64);
232 printf("ioctl(-1, MEMREADOOB64"
233 ", {start=%#llx, length=%#x, usr_ptr=%#llx}) = %s\n",
234 (unsigned long long) oob64->start, oob64->length,
235 (unsigned long long) oob64->usr_ptr, errstr);
236
237
238 TAIL_ALLOC_OBJECT_CONST_PTR(struct otp_info, oi);
239 fill_memory(oi, sizeof(*oi));
240 do_ioctl_ptr(OTPLOCK, oi);
241 printf("ioctl(-1, MIXER_READ(%u) or OTPLOCK"
242 ", {start=%#x, length=%#x, locked=%u}) = %s\n",
243 (unsigned int) _IOC_NR(OTPLOCK),
244 oi->start, oi->length, oi->locked, errstr);
245
246 TAIL_ALLOC_OBJECT_CONST_PTR(struct mtd_write_req, wr);
247 fill_memory(wr, sizeof(*wr));
248 wr->mode = MTD_OPS_PLACE_OOB;
249 do_ioctl_ptr(MEMWRITE, wr);
250 printf("ioctl(-1, MEMWRITE, {start=%#llx, len=%#llx, ooblen=%#llx"
251 ", usr_data=%#llx, usr_oob=%#llx, mode=MTD_OPS_PLACE_OOB})"
252 " = %s\n",
253 (unsigned long long) wr->start,
254 (unsigned long long) wr->len,
255 (unsigned long long) wr->ooblen,
256 (unsigned long long) wr->usr_data,
257 (unsigned long long) wr->usr_oob,
258 errstr);
259
260 TAIL_ALLOC_OBJECT_CONST_PTR(struct mtd_info_user, minfo);
261 fill_memory(minfo, sizeof(*minfo));
262 minfo->type = MTD_ABSENT;
263 minfo->flags = MTD_WRITEABLE;
264 if (do_ioctl_ptr(MEMGETINFO, minfo) <0 ) {
265 printf("ioctl(-1, MIXER_READ(%u) or MEMGETINFO, %p) = %s\n",
266 (unsigned int) _IOC_NR(MEMGETINFO), minfo, errstr);
267 } else {
268 printf("ioctl(-1, MIXER_READ(%u) or MEMGETINFO"
269 ", {type=MTD_ABSENT, flags=MTD_WRITEABLE, size=%#x"
270 ", erasesize=%#x, writesize=%#x, oobsize=%#x"
271 ", padding=%#jx}) = %s\n",
272 (unsigned int) _IOC_NR(MEMGETINFO),
273 minfo->size, minfo->erasesize,
274 minfo->writesize, minfo->oobsize,
275 (uintmax_t) minfo->padding, errstr);
276 }
277
278 TAIL_ALLOC_OBJECT_CONST_PTR(struct nand_oobinfo, ninfo);
279 fill_memory(ninfo, sizeof(*ninfo));
280 ninfo->useecc = MTD_NANDECC_OFF;
281 if (do_ioctl_ptr(MEMGETOOBSEL, ninfo) < 0) {
282 printf("ioctl(-1, MIXER_READ(%u) or MEMGETOOBSEL, %p) = %s\n",
283 (unsigned int) _IOC_NR(MEMGETOOBSEL), ninfo, errstr);
284 } else {
285 printf("ioctl(-1, MIXER_READ(%u) or MEMGETOOBSEL"
286 ", {useecc=MTD_NANDECC_OFF, eccbytes=%#x, oobfree=[",
287 (unsigned int) _IOC_NR(MEMGETOOBSEL), ninfo->eccbytes);
288 for (unsigned int i = 0; i < ARRAY_SIZE(ninfo->oobfree); ++i)
289 printf("%s[%#x, %#x]", i ? ", " : "",
290 ninfo->oobfree[i][0], ninfo->oobfree[i][1]);
291 printf("], eccpos=[");
292 for (unsigned int i = 0; i < ARRAY_SIZE(ninfo->eccpos); ++i)
293 printf("%s%#x", i ? ", " : "", ninfo->eccpos[i]);
294 printf("]}) = %s\n", errstr);
295 }
296
297 TAIL_ALLOC_OBJECT_CONST_PTR(struct nand_ecclayout_user, nlay);
298 fill_memory(nlay, sizeof(*nlay));
299 if (do_ioctl_ptr(ECCGETLAYOUT, nlay) < 0) {
300 printf("ioctl(-1, MIXER_READ(%u) or ECCGETLAYOUT, %p) = %s\n",
301 (unsigned int) _IOC_NR(ECCGETLAYOUT), nlay, errstr);
302 } else {
303 printf("ioctl(-1, MIXER_READ(%u) or ECCGETLAYOUT"
304 ", {eccbytes=%#x, eccpos=[",
305 (unsigned int) _IOC_NR(ECCGETLAYOUT), nlay->eccbytes);
306 for (unsigned int i = 0; i < DEFAULT_STRLEN; ++i)
307 printf("%s%#x", i ? ", " : "", nlay->eccpos[i]);
308 printf(", ...], oobavail=%#x, oobfree=[", nlay->oobavail);
309 for (unsigned int i = 0; i < ARRAY_SIZE(nlay->oobfree); ++i)
310 printf("%s{offset=%#x, length=%#x}", i ? ", " : "",
311 nlay->oobfree[i].offset,
312 nlay->oobfree[i].length);
313 printf("]}) = %s\n", errstr);
314 }
315
316 TAIL_ALLOC_OBJECT_CONST_PTR(struct mtd_ecc_stats, es);
317 fill_memory(es, sizeof(*es));
318 if (do_ioctl_ptr(ECCGETSTATS, es) <0 ) {
319 printf("ioctl(-1, MIXER_READ(%u) or ECCGETSTATS, %p) = %s\n",
320 (unsigned int) _IOC_NR(ECCGETSTATS), es, errstr);
321 } else {
322 printf("ioctl(-1, MIXER_READ(%u) or ECCGETSTATS"
323 ", {corrected=%#x, failed=%#x, badblocks=%#x"
324 ", bbtblocks=%#x}) = %s\n",
325 (unsigned int) _IOC_NR(ECCGETSTATS),
326 es->corrected, es->failed,
327 es->badblocks, es->bbtblocks, errstr);
328 }
329
330 static const unsigned long lmagic =
331 (unsigned long) 0xdeadbeefbadc0dedULL;
332
333 do_ioctl(_IOC(_IOC_READ|_IOC_WRITE, 0x4d, 0xfe, 0xff), lmagic);
334 printf("ioctl(-1, %s, %#lx) = %s\n",
335 "_IOC(_IOC_READ|_IOC_WRITE, 0x4d, 0xfe, 0xff)",
336 lmagic, errstr);
337
338 puts("+++ exited with 0 +++");
339 return 0;
340 }