/*
* Copyright (c) 2012 Mike Frysinger <vapier@gentoo.org>
+ * Copyright (c) 2012-2017 The strace developers.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include "defs.h"
-#include <sys/ioctl.h>
+#ifdef HAVE_STRUCT_MTD_WRITE_REQ
-/* The mtd api changes quickly, so we have to keep a local copy */
-#include <linux/version.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
-# include "mtd-abi.h"
-#else
+# include DEF_MPERS_TYPE(struct_mtd_oob_buf)
+
+# include <linux/ioctl.h>
# include <mtd/mtd-abi.h>
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0)
-# include "ubi-user.h"
-#else
-# include <mtd/ubi-user.h>
-#endif
-
-#include "xlat/mtd_mode_options.h"
-#include "xlat/mtd_type_options.h"
-#include "xlat/mtd_flags_options.h"
-#include "xlat/mtd_otp_options.h"
-#include "xlat/mtd_nandecc_options.h"
-
-int mtd_ioctl(struct tcb *tcp, long code, long arg)
-{
- struct mtd_info_user minfo;
- struct erase_info_user einfo;
- struct erase_info_user64 einfo64;
- struct mtd_oob_buf mbuf;
- struct mtd_oob_buf64 mbuf64;
- struct region_info_user rinfo;
- struct otp_info oinfo;
- struct mtd_ecc_stats estat;
- struct mtd_write_req mreq;
- struct nand_oobinfo ninfo;
- struct nand_ecclayout_user nlay;
- unsigned int i, j;
- if (entering(tcp))
- return 0;
+typedef struct mtd_oob_buf struct_mtd_oob_buf;
- switch (code) {
+#endif /* HAVE_STRUCT_MTD_WRITE_REQ */
- case MEMGETINFO:
- if (!verbose(tcp) || umove(tcp, arg, &minfo) < 0)
- return 0;
+#include MPERS_DEFS
- tprints(", {type=");
- printxval(mtd_type_options, minfo.type, "MTD_???");
- tprints(", flags=");
- printflags(mtd_flags_options, minfo.flags, "MTD_???");
- tprintf(", size=%#" PRIx32 ", erasesize=%#" PRIx32,
- minfo.size, minfo.erasesize);
- tprintf(", writesize=%#" PRIx32 ", oobsize=%#" PRIx32,
- minfo.writesize, minfo.oobsize);
- tprintf(", padding=%#" PRIx64 "}",
- (uint64_t) minfo.padding);
- return 1;
+#ifdef HAVE_STRUCT_MTD_WRITE_REQ
- case MEMERASE:
- case MEMLOCK:
- case MEMUNLOCK:
- case MEMISLOCKED:
- if (!verbose(tcp) || umove(tcp, arg, &einfo) < 0)
- return 0;
-
- tprintf(", {start=%#" PRIx32 ", length=%#" PRIx32 "}",
- einfo.start, einfo.length);
- return 1;
+# include "xlat/mtd_mode_options.h"
+# include "xlat/mtd_file_mode_options.h"
+# include "xlat/mtd_type_options.h"
+# include "xlat/mtd_flags_options.h"
+# include "xlat/mtd_otp_options.h"
+# include "xlat/mtd_nandecc_options.h"
- case MEMERASE64:
- if (!verbose(tcp) || umove(tcp, arg, &einfo64) < 0)
- return 0;
+static void
+decode_erase_info_user(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct erase_info_user einfo;
- tprintf(", {start=%#" PRIx64 ", length=%#" PRIx64 "}",
- (uint64_t) einfo64.start, (uint64_t) einfo64.length);
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &einfo))
+ return;
- case MEMWRITEOOB:
- case MEMREADOOB:
- if (!verbose(tcp) || umove(tcp, arg, &mbuf) < 0)
- return 0;
+ tprintf("{start=%#x, length=%#x}", einfo.start, einfo.length);
+}
- tprintf(", {start=%#" PRIx32 ", length=%#" PRIx32 ", ptr=...}",
- mbuf.start, mbuf.length);
- return 1;
+static void
+decode_erase_info_user64(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct erase_info_user64 einfo64;
- case MEMWRITEOOB64:
- case MEMREADOOB64:
- if (!verbose(tcp) || umove(tcp, arg, &mbuf64) < 0)
- return 0;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &einfo64))
+ return;
- tprintf(", {start=%#" PRIx64 ", length=%#" PRIx64 ", ptr=...}",
- (uint64_t) mbuf64.start, (uint64_t) mbuf64.length);
- return 1;
+ tprintf("{start=%#" PRIx64 ", length=%#" PRIx64 "}",
+ (uint64_t) einfo64.start, (uint64_t) einfo64.length);
+}
- case MEMGETREGIONINFO:
- if (!verbose(tcp) || umove(tcp, arg, &rinfo) < 0)
- return 0;
+static void
+decode_mtd_oob_buf(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct_mtd_oob_buf mbuf;
- tprintf(", {offset=%#" PRIx32 ", erasesize=%#" PRIx32,
- rinfo.offset, rinfo.erasesize);
- tprintf(", numblocks=%#" PRIx32 ", regionindex=%#" PRIx32 "}",
- rinfo.numblocks, rinfo.regionindex);
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &mbuf))
+ return;
- case MEMGETOOBSEL:
- if (!verbose(tcp) || umove(tcp, arg, &ninfo) < 0)
- return 0;
+ tprintf("{start=%#x, length=%#x, ptr=", mbuf.start, mbuf.length);
+ printaddr(ptr_to_kulong(mbuf.ptr));
+ tprints("}");
+}
- tprints(", {useecc=");
- printxval(mtd_nandecc_options, ninfo.useecc, "MTD_NANDECC_???");
- tprintf(", eccbytes=%#" PRIx32, ninfo.eccbytes);
-
- tprints(", oobfree={");
- for (i = 0; i < ARRAY_SIZE(ninfo.oobfree); ++i) {
- if (i)
- tprints("}, ");
- tprints("{");
- for (j = 0; j < ARRAY_SIZE(ninfo.oobfree[0]); ++j) {
- if (j)
- tprints(", ");
- tprintf("%#" PRIx32, ninfo.oobfree[i][j]);
- }
- }
+static void
+decode_mtd_oob_buf64(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct mtd_oob_buf64 mbuf64;
- tprints("}}, eccpos={");
- for (i = 0; i < ARRAY_SIZE(ninfo.eccpos); ++i) {
- if (i)
- tprints(", ");
- tprintf("%#" PRIx32, ninfo.eccpos[i]);
- }
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &mbuf64))
+ return;
- tprints("}");
- return 1;
+ tprintf("{start=%#" PRIx64 ", length=%#x, usr_ptr=%#" PRIx64 "}",
+ (uint64_t) mbuf64.start, mbuf64.length,
+ (uint64_t) mbuf64.usr_ptr);
+}
- case OTPGETREGIONINFO:
- case OTPLOCK:
- if (!verbose(tcp) || umove(tcp, arg, &oinfo) < 0)
- return 0;
+static void
+decode_otp_info(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct otp_info oinfo;
- tprintf(", {start=%#" PRIx32 ", length=%#" PRIx32 ", locked=%" PRIu32 "}",
- oinfo.start, oinfo.length, oinfo.locked);
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &oinfo))
+ return;
- case ECCGETLAYOUT:
- if (!verbose(tcp) || umove(tcp, arg, &nlay) < 0)
- return 0;
+ tprintf("{start=%#x, length=%#x, locked=%u}",
+ oinfo.start, oinfo.length, oinfo.locked);
+}
- tprintf(", {eccbytes=%#" PRIx32 ", eccpos={", nlay.eccbytes);
- for (i = 0; i < ARRAY_SIZE(nlay.eccpos); ++i) {
- if (i)
- tprints(", ");
- tprintf("%#" PRIx32, nlay.eccpos[i]);
- }
- tprintf("}, oobavail=%#" PRIx32 ", oobfree={", nlay.oobavail);
- for (i = 0; i < ARRAY_SIZE(nlay.oobfree); ++i) {
- if (i)
- tprints(", ");
- tprintf("{offset=%#" PRIx32 ", length=%#" PRIx32 "}",
- nlay.oobfree[i].offset, nlay.oobfree[i].length);
- }
- tprints("}");
- return 1;
+static void
+decode_otp_select(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ unsigned int i;
- case ECCGETSTATS:
- if (!verbose(tcp) || umove(tcp, arg, &estat) < 0)
- return 0;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &i))
+ return;
- tprintf(", {corrected=%#" PRIx32 ", failed=%#" PRIx32,
- estat.corrected, estat.failed);
- tprintf(", badblocks=%#" PRIx32 ", bbtblocks=%#" PRIx32 "}",
- estat.badblocks, estat.bbtblocks);
- return 1;
+ tprints("[");
+ printxval(mtd_otp_options, i, "MTD_OTP_???");
+ tprints("]");
+}
- case MEMWRITE:
- if (!verbose(tcp) || umove(tcp, arg, &mreq) < 0)
- return 0;
+static void
+decode_mtd_write_req(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct mtd_write_req mreq;
- tprintf(", {start=%#" PRIx64 ", len=%#" PRIx64,
- (uint64_t) mreq.start, (uint64_t) mreq.len);
- tprintf(", ooblen=%#" PRIx64 ", usr_data=%#" PRIx64,
- (uint64_t) mreq.ooblen, (uint64_t) mreq.usr_data);
- tprintf(", usr_oob=%#" PRIx64 ", mode=",
- (uint64_t) mreq.usr_oob);
- printxval(mtd_mode_options, mreq.mode, "MTD_OPS_???");
- tprints(", padding=...}");
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &mreq))
+ return;
+
+ tprintf("{start=%#" PRIx64 ", len=%#" PRIx64
+ ", ooblen=%#" PRIx64 ", usr_data=%#" PRIx64
+ ", usr_oob=%#" PRIx64 ", mode=",
+ (uint64_t) mreq.start, (uint64_t) mreq.len,
+ (uint64_t) mreq.ooblen, (uint64_t) mreq.usr_data,
+ (uint64_t) mreq.usr_oob);
+ printxval(mtd_mode_options, mreq.mode, "MTD_OPS_???");
+ tprints("}");
+}
- case OTPSELECT:
- if (!verbose(tcp) || umove(tcp, arg, &i) < 0)
- return 0;
+static void
+decode_mtd_info_user(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct mtd_info_user minfo;
- tprints(", [");
- printxval(mtd_otp_options, i, "MTD_OTP_???");
- tprints("]");
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &minfo))
+ return;
+
+ tprints("{type=");
+ printxval(mtd_type_options, minfo.type, "MTD_???");
+ tprints(", flags=");
+ printflags(mtd_flags_options, minfo.flags, "MTD_???");
+ tprintf(", size=%#x, erasesize=%#x, writesize=%#x, oobsize=%#x"
+ ", padding=%#" PRIx64 "}",
+ minfo.size, minfo.erasesize, minfo.writesize, minfo.oobsize,
+ (uint64_t) minfo.padding);
+}
- case MEMGETBADBLOCK:
- case MEMSETBADBLOCK:
- if (!verbose(tcp))
- return 0;
+static void
+decode_nand_oobinfo(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct nand_oobinfo ninfo;
+ unsigned int i, j;
- tprints(", ");
- print_loff_t(tcp, arg);
- return 1;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &ninfo))
+ return;
+
+ tprints("{useecc=");
+ printxval(mtd_nandecc_options, ninfo.useecc, "MTD_NANDECC_???");
+ tprintf(", eccbytes=%#x", ninfo.eccbytes);
+
+ tprints(", oobfree={");
+ for (i = 0; i < ARRAY_SIZE(ninfo.oobfree); ++i) {
+ if (i)
+ tprints("}, ");
+ tprints("{");
+ for (j = 0; j < ARRAY_SIZE(ninfo.oobfree[0]); ++j) {
+ if (j)
+ tprints(", ");
+ tprintf("%#x", ninfo.oobfree[i][j]);
+ }
+ }
- case OTPGETREGIONCOUNT:
- if (!verbose(tcp) || umove(tcp, arg, &i) < 0)
- return 0;
+ tprints("}}, eccpos={");
+ for (i = 0; i < ARRAY_SIZE(ninfo.eccpos); ++i) {
+ if (i)
+ tprints(", ");
+ tprintf("%#x", ninfo.eccpos[i]);
+ }
- tprintf(", [%u]", i);
- return 1;
+ tprints("}");
+}
- case MTDFILEMODE:
- /* XXX: process return value as enum mtd_file_modes */
+static void
+decode_nand_ecclayout_user(struct tcb *const tcp, const kernel_ulong_t addr)
+{
+ struct nand_ecclayout_user nlay;
+ unsigned int i;
- case MEMGETREGIONCOUNT:
- /* These ones take simple args, so let default printer handle it */
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &nlay))
+ return;
- default:
- return 0;
+ tprintf("{eccbytes=%#x, eccpos={", nlay.eccbytes);
+ for (i = 0; i < ARRAY_SIZE(nlay.eccpos); ++i) {
+ if (i)
+ tprints(", ");
+ tprintf("%#x", nlay.eccpos[i]);
}
+ tprintf("}, oobavail=%#x, oobfree={", nlay.oobavail);
+ for (i = 0; i < ARRAY_SIZE(nlay.oobfree); ++i) {
+ if (i)
+ tprints(", ");
+ tprintf("{offset=%#x, length=%#x}",
+ nlay.oobfree[i].offset, nlay.oobfree[i].length);
+ }
+ tprints("}");
}
-#include "xlat/ubi_volume_types.h"
-#include "xlat/ubi_volume_props.h"
-
-int ubi_ioctl(struct tcb *tcp, long code, long arg)
+static void
+decode_mtd_ecc_stats(struct tcb *const tcp, const kernel_ulong_t addr)
{
- struct ubi_mkvol_req mkvol;
- struct ubi_rsvol_req rsvol;
- struct ubi_rnvol_req rnvol;
- struct ubi_attach_req attach;
- struct ubi_map_req map;
- struct ubi_set_vol_prop_req prop;
- /* 4*(n-1) + 3 for quotes and NUL */
- char vol_name[(UBI_MAX_VOLUME_NAME + 1) * 4];
- int ret;
-
- if (entering(tcp))
- return 0;
+ struct mtd_ecc_stats es;
+ tprints(", ");
+ if (umove_or_printaddr(tcp, addr, &es))
+ return;
+
+ tprintf("{corrected=%#x, failed=%#x, badblocks=%#x, bbtblocks=%#x}",
+ es.corrected, es.failed, es.badblocks, es.bbtblocks);
+}
+
+MPERS_PRINTER_DECL(int, mtd_ioctl, struct tcb *const tcp,
+ const unsigned int code, const kernel_ulong_t arg)
+{
switch (code) {
- case UBI_IOCMKVOL:
- if (!verbose(tcp) || umove(tcp, arg, &mkvol) < 0)
- return 0;
+ case MEMERASE:
+ case MEMLOCK:
+ case MEMUNLOCK:
+ case MEMISLOCKED:
+ decode_erase_info_user(tcp, arg);
+ break;
- tprintf(", {vol_id=%" PRIi32 ", alignment=%" PRIi32
- ", bytes=%" PRIi64 ", vol_type=", mkvol.vol_id,
- mkvol.alignment, (int64_t)mkvol.bytes);
- printxval(ubi_volume_types, mkvol.vol_type, "UBI_???_VOLUME");
- ret = string_quote(mkvol.name, vol_name, -1,
- CLAMP(mkvol.name_len, 0, UBI_MAX_VOLUME_NAME));
- tprintf(", name_len=%" PRIi16 ", name=%s%s",
- mkvol.name_len, vol_name, ret ? "..." : "");
- tprints("}");
- return 1;
-
- case UBI_IOCRSVOL:
- if (!verbose(tcp) || umove(tcp, arg, &rsvol) < 0)
- return 0;
+ case MEMERASE64:
+ decode_erase_info_user64(tcp, arg);
+ break;
- tprintf(", {vol_id=%" PRIi32 ", bytes=%" PRIi64 "}",
- rsvol.vol_id, (int64_t)rsvol.bytes);
- return 1;
+ case MEMWRITEOOB:
+ case MEMREADOOB:
+ decode_mtd_oob_buf(tcp, arg);
+ break;
- case UBI_IOCRNVOL: {
- __s32 c;
+ case MEMWRITEOOB64:
+ case MEMREADOOB64:
+ decode_mtd_oob_buf64(tcp, arg);
+ break;
+
+ case MEMWRITE:
+ decode_mtd_write_req(tcp, arg);
+ break;
- if (!verbose(tcp) || umove(tcp, arg, &rnvol) < 0)
+ case OTPGETREGIONINFO:
+ if (entering(tcp))
return 0;
+ /* fall through */
+ case OTPLOCK:
+ decode_otp_info(tcp, arg);
+ break;
- tprintf(", {count=%" PRIi32 ", ents=[", rnvol.count);
- for (c = 0; c < CLAMP(rnvol.count, 0, UBI_MAX_RNVOL); ++c) {
- if (c)
- tprints(", ");
- ret = string_quote(rnvol.ents[c].name, vol_name, -1,
- CLAMP(rnvol.ents[c].name_len, 0, UBI_MAX_VOLUME_NAME));
- tprintf("{vol_id=%" PRIi32 ", name_len=%" PRIi16
- ", name=%s%s}", rnvol.ents[c].vol_id,
- rnvol.ents[c].name_len, vol_name, ret ? "..." : "");
- }
- tprints("]}");
- return 1;
- }
+ case OTPSELECT:
+ decode_otp_select(tcp, arg);
+ break;
- case UBI_IOCVOLUP: {
- __s64 bytes;
+ case MTDFILEMODE:
+ tprints(", ");
+ printxval64(mtd_file_mode_options, arg, "MTD_FILE_MODE_???");
+ break;
- if (!verbose(tcp) || umove(tcp, arg, &bytes) < 0)
- return 0;
+ case MEMGETBADBLOCK:
+ case MEMSETBADBLOCK:
+ tprints(", ");
+ printnum_int64(tcp, arg, "%" PRIu64);
+ break;
- tprintf(", %" PRIi64, (int64_t)bytes);
- return 1;
- }
+ case MEMGETINFO:
+ if (entering(tcp))
+ return 0;
+ decode_mtd_info_user(tcp, arg);
+ break;
- case UBI_IOCATT:
- if (!verbose(tcp) || umove(tcp, arg, &attach) < 0)
+ case MEMGETOOBSEL:
+ if (entering(tcp))
return 0;
+ decode_nand_oobinfo(tcp, arg);
+ break;
- tprintf(", {ubi_num=%" PRIi32 ", mtd_num=%" PRIi32
- ", vid_hdr_offset=%" PRIi32
- ", max_beb_per1024=%" PRIi16 "}",
- attach.ubi_num, attach.mtd_num,
- attach.vid_hdr_offset, attach.max_beb_per1024);
- return 1;
+ case ECCGETLAYOUT:
+ if (entering(tcp))
+ return 0;
+ decode_nand_ecclayout_user(tcp, arg);
+ break;
- case UBI_IOCEBMAP:
- if (!verbose(tcp) || umove(tcp, arg, &map) < 0)
+ case ECCGETSTATS:
+ if (entering(tcp))
return 0;
+ decode_mtd_ecc_stats(tcp, arg);
+ break;
- tprintf(", {lnum=%" PRIi32 ", dtype=%" PRIi8 "}",
- map.lnum, map.dtype);
- return 1;
+ case OTPGETREGIONCOUNT:
+ if (entering(tcp))
+ return 0;
+ tprints(", ");
+ printnum_int(tcp, arg, "%u");
+ break;
- case UBI_IOCSETVOLPROP:
- if (!verbose(tcp) || umove(tcp, arg, &prop) < 0)
+ case MEMGETREGIONCOUNT:
+ if (entering(tcp))
return 0;
+ tprints(", ");
+ printnum_int(tcp, arg, "%d");
+ break;
- tprints(", {property=");
- printxval(ubi_volume_props, prop.property, "UBI_VOL_PROP_???");
- tprintf(", value=%#" PRIx64 "}", (uint64_t)prop.value);
- return 1;
+ case MEMGETREGIONINFO:
+ if (entering(tcp)) {
+ struct region_info_user rinfo;
- case UBI_IOCRMVOL:
- case UBI_IOCDET:
- case UBI_IOCEBER:
- case UBI_IOCEBCH:
- case UBI_IOCEBUNMAP:
- case UBI_IOCEBISMAP:
- /* These ones take simple args, so let default printer handle it */
+ tprints(", ");
+ if (umove_or_printaddr(tcp, arg, &rinfo))
+ break;
+ tprintf("{regionindex=%#x", rinfo.regionindex);
+ return 0;
+ } else {
+ struct region_info_user rinfo;
+
+ if (!syserror(tcp) && !umove(tcp, arg, &rinfo))
+ tprintf(", offset=%#x"
+ ", erasesize=%#x"
+ ", numblocks=%#x}",
+ rinfo.offset,
+ rinfo.erasesize,
+ rinfo.numblocks);
+ tprints("}");
+ break;
+ }
default:
- return 0;
+ return RVAL_DECODED;
}
+
+ return RVAL_IOCTL_DECODED;
}
+
+#endif /* HAVE_STRUCT_MTD_WRITE_REQ */