(root)/
strace-6.5/
tests/
ioctl_mtd.c
       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  }