1/*
2 * Copyright (c) 2012 Mike Frysinger <vapier@gentoo.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 *    derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "defs.h"
28
29#include DEF_MPERS_TYPE(struct_mtd_oob_buf)
30
31#include <linux/ioctl.h>
32
33/* The mtd api changes quickly, so we have to keep a local copy */
34#include <linux/version.h>
35#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
36# include "mtd-abi.h"
37#else
38# include <mtd/mtd-abi.h>
39#endif
40
41typedef struct mtd_oob_buf struct_mtd_oob_buf;
42
43#include MPERS_DEFS
44
45#include "xlat/mtd_mode_options.h"
46#include "xlat/mtd_file_mode_options.h"
47#include "xlat/mtd_type_options.h"
48#include "xlat/mtd_flags_options.h"
49#include "xlat/mtd_otp_options.h"
50#include "xlat/mtd_nandecc_options.h"
51
52static void
53decode_erase_info_user(struct tcb *const tcp, const kernel_ulong_t addr)
54{
55	struct erase_info_user einfo;
56
57	tprints(", ");
58	if (umove_or_printaddr(tcp, addr, &einfo))
59		return;
60
61	tprintf("{start=%#x, length=%#x}", einfo.start, einfo.length);
62}
63
64static void
65decode_erase_info_user64(struct tcb *const tcp, const kernel_ulong_t addr)
66{
67	struct erase_info_user64 einfo64;
68
69	tprints(", ");
70	if (umove_or_printaddr(tcp, addr, &einfo64))
71		return;
72
73	tprintf("{start=%#" PRIx64 ", length=%#" PRIx64 "}",
74		(uint64_t) einfo64.start, (uint64_t) einfo64.length);
75}
76
77static void
78decode_mtd_oob_buf(struct tcb *const tcp, const kernel_ulong_t addr)
79{
80	struct_mtd_oob_buf mbuf;
81
82	tprints(", ");
83	if (umove_or_printaddr(tcp, addr, &mbuf))
84		return;
85
86	tprintf("{start=%#x, length=%#x, ptr=", mbuf.start, mbuf.length);
87	printaddr(ptr_to_kulong(mbuf.ptr));
88	tprints("}");
89}
90
91static void
92decode_mtd_oob_buf64(struct tcb *const tcp, const kernel_ulong_t addr)
93{
94	struct mtd_oob_buf64 mbuf64;
95
96	tprints(", ");
97	if (umove_or_printaddr(tcp, addr, &mbuf64))
98		return;
99
100	tprintf("{start=%#" PRIx64 ", length=%#x, usr_ptr=%#" PRIx64 "}",
101		(uint64_t) mbuf64.start, mbuf64.length,
102		(uint64_t) mbuf64.usr_ptr);
103}
104
105static void
106decode_otp_info(struct tcb *const tcp, const kernel_ulong_t addr)
107{
108	struct otp_info oinfo;
109
110	tprints(", ");
111	if (umove_or_printaddr(tcp, addr, &oinfo))
112		return;
113
114	tprintf("{start=%#x, length=%#x, locked=%u}",
115		oinfo.start, oinfo.length, oinfo.locked);
116}
117
118static void
119decode_otp_select(struct tcb *const tcp, const kernel_ulong_t addr)
120{
121	unsigned int i;
122
123	tprints(", ");
124	if (umove_or_printaddr(tcp, addr, &i))
125		return;
126
127	tprints("[");
128	printxval(mtd_otp_options, i, "MTD_OTP_???");
129	tprints("]");
130}
131
132static void
133decode_mtd_write_req(struct tcb *const tcp, const kernel_ulong_t addr)
134{
135	struct mtd_write_req mreq;
136
137	tprints(", ");
138	if (umove_or_printaddr(tcp, addr, &mreq))
139		return;
140
141	tprintf("{start=%#" PRIx64 ", len=%#" PRIx64
142		", ooblen=%#" PRIx64 ", usr_data=%#" PRIx64
143		", usr_oob=%#" PRIx64 ", mode=",
144		(uint64_t) mreq.start, (uint64_t) mreq.len,
145		(uint64_t) mreq.ooblen, (uint64_t) mreq.usr_data,
146		(uint64_t) mreq.usr_oob);
147	printxval(mtd_mode_options, mreq.mode, "MTD_OPS_???");
148	tprints("}");
149}
150
151static void
152decode_mtd_info_user(struct tcb *const tcp, const kernel_ulong_t addr)
153{
154	struct mtd_info_user minfo;
155
156	tprints(", ");
157	if (umove_or_printaddr(tcp, addr, &minfo))
158		return;
159
160	tprints("{type=");
161	printxval(mtd_type_options, minfo.type, "MTD_???");
162	tprints(", flags=");
163	printflags(mtd_flags_options, minfo.flags, "MTD_???");
164	tprintf(", size=%#x, erasesize=%#x, writesize=%#x, oobsize=%#x"
165		", padding=%#" PRIx64 "}",
166		minfo.size, minfo.erasesize, minfo.writesize, minfo.oobsize,
167		(uint64_t) minfo.padding);
168}
169
170static void
171decode_nand_oobinfo(struct tcb *const tcp, const kernel_ulong_t addr)
172{
173	struct nand_oobinfo ninfo;
174	unsigned int i, j;
175
176	tprints(", ");
177	if (umove_or_printaddr(tcp, addr, &ninfo))
178		return;
179
180	tprints("{useecc=");
181	printxval(mtd_nandecc_options, ninfo.useecc, "MTD_NANDECC_???");
182	tprintf(", eccbytes=%#x", ninfo.eccbytes);
183
184	tprints(", oobfree={");
185	for (i = 0; i < ARRAY_SIZE(ninfo.oobfree); ++i) {
186		if (i)
187			tprints("}, ");
188		tprints("{");
189		for (j = 0; j < ARRAY_SIZE(ninfo.oobfree[0]); ++j) {
190			if (j)
191				tprints(", ");
192			tprintf("%#x", ninfo.oobfree[i][j]);
193		}
194	}
195
196	tprints("}}, eccpos={");
197	for (i = 0; i < ARRAY_SIZE(ninfo.eccpos); ++i) {
198		if (i)
199			tprints(", ");
200		tprintf("%#x", ninfo.eccpos[i]);
201	}
202
203	tprints("}");
204}
205
206static void
207decode_nand_ecclayout_user(struct tcb *const tcp, const kernel_ulong_t addr)
208{
209	struct nand_ecclayout_user nlay;
210	unsigned int i;
211
212	tprints(", ");
213	if (umove_or_printaddr(tcp, addr, &nlay))
214		return;
215
216	tprintf("{eccbytes=%#x, eccpos={", nlay.eccbytes);
217	for (i = 0; i < ARRAY_SIZE(nlay.eccpos); ++i) {
218		if (i)
219			tprints(", ");
220		tprintf("%#x", nlay.eccpos[i]);
221	}
222	tprintf("}, oobavail=%#x, oobfree={", nlay.oobavail);
223	for (i = 0; i < ARRAY_SIZE(nlay.oobfree); ++i) {
224		if (i)
225			tprints(", ");
226		tprintf("{offset=%#x, length=%#x}",
227			nlay.oobfree[i].offset, nlay.oobfree[i].length);
228	}
229	tprints("}");
230}
231
232static void
233decode_mtd_ecc_stats(struct tcb *const tcp, const kernel_ulong_t addr)
234{
235	struct mtd_ecc_stats es;
236
237	tprints(", ");
238	if (umove_or_printaddr(tcp, addr, &es))
239		return;
240
241	tprintf("{corrected=%#x, failed=%#x, badblocks=%#x, bbtblocks=%#x}",
242		es.corrected, es.failed, es.badblocks, es.bbtblocks);
243}
244
245MPERS_PRINTER_DECL(int, mtd_ioctl, struct tcb *const tcp,
246		   const unsigned int code, const kernel_ulong_t arg)
247{
248	switch (code) {
249	case MEMERASE:
250	case MEMLOCK:
251	case MEMUNLOCK:
252	case MEMISLOCKED:
253		decode_erase_info_user(tcp, arg);
254		break;
255
256	case MEMERASE64:
257		decode_erase_info_user64(tcp, arg);
258		break;
259
260	case MEMWRITEOOB:
261	case MEMREADOOB:
262		decode_mtd_oob_buf(tcp, arg);
263		break;
264
265	case MEMWRITEOOB64:
266	case MEMREADOOB64:
267		decode_mtd_oob_buf64(tcp, arg);
268		break;
269
270	case MEMWRITE:
271		decode_mtd_write_req(tcp, arg);
272		break;
273
274	case OTPGETREGIONINFO:
275		if (entering(tcp))
276			return 0;
277		/* fall through */
278	case OTPLOCK:
279		decode_otp_info(tcp, arg);
280		break;
281
282	case OTPSELECT:
283		decode_otp_select(tcp, arg);
284		break;
285
286	case MTDFILEMODE:
287		tprints(", ");
288		printxval64(mtd_file_mode_options, arg, "MTD_FILE_MODE_???");
289		break;
290
291	case MEMGETBADBLOCK:
292	case MEMSETBADBLOCK:
293		tprints(", ");
294		printnum_int64(tcp, arg, "%" PRIu64);
295		break;
296
297	case MEMGETINFO:
298		if (entering(tcp))
299			return 0;
300		decode_mtd_info_user(tcp, arg);
301		break;
302
303	case MEMGETOOBSEL:
304		if (entering(tcp))
305			return 0;
306		decode_nand_oobinfo(tcp, arg);
307		break;
308
309	case ECCGETLAYOUT:
310		if (entering(tcp))
311			return 0;
312		decode_nand_ecclayout_user(tcp, arg);
313		break;
314
315	case ECCGETSTATS:
316		if (entering(tcp))
317			return 0;
318		decode_mtd_ecc_stats(tcp, arg);
319		break;
320
321	case OTPGETREGIONCOUNT:
322		if (entering(tcp))
323			return 0;
324		tprints(", ");
325		printnum_int(tcp, arg, "%u");
326		break;
327
328	case MEMGETREGIONCOUNT:
329		if (entering(tcp))
330			return 0;
331		tprints(", ");
332		printnum_int(tcp, arg, "%d");
333		break;
334
335	case MEMGETREGIONINFO:
336		if (entering(tcp)) {
337			struct region_info_user rinfo;
338
339			tprints(", ");
340			if (umove_or_printaddr(tcp, arg, &rinfo))
341				break;
342			tprintf("{regionindex=%#x", rinfo.regionindex);
343			return 0;
344		} else {
345			struct region_info_user rinfo;
346
347			if (!syserror(tcp) && !umove(tcp, arg, &rinfo))
348				tprintf(", offset=%#x"
349					", erasesize=%#x"
350					", numblocks=%#x}",
351					rinfo.offset,
352					rinfo.erasesize,
353					rinfo.numblocks);
354			tprints("}");
355			break;
356		}
357
358	default:
359		return RVAL_DECODED;
360	}
361
362	return RVAL_DECODED | 1;
363}
364