nandwrite.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. /*
  2. * nandwrite.c
  3. *
  4. * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  5. * 2003 Thomas Gleixner (tglx@linutronix.de)
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. * Overview:
  12. * This utility writes a binary image directly to a NAND flash
  13. * chip or NAND chips contained in DoC devices. This is the
  14. * "inverse operation" of nanddump.
  15. *
  16. * tglx: Major rewrite to handle bad blocks, write data with or without ECC
  17. * write oob data only on request
  18. *
  19. * Bug/ToDo:
  20. */
  21. #define _GNU_SOURCE
  22. #include <ctype.h>
  23. #include <errno.h>
  24. #include <fcntl.h>
  25. #include <stdbool.h>
  26. #include <stddef.h>
  27. #include <stdint.h>
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <time.h>
  32. #include <unistd.h>
  33. #include <sys/stat.h>
  34. #include <sys/ioctl.h>
  35. #include <sys/types.h>
  36. #include <getopt.h>
  37. #include <asm/types.h>
  38. #include "mtd/mtd-user.h"
  39. #define PROGRAM "nandwrite"
  40. #define VERSION "$Revision: 1.32 $"
  41. #define MAX_PAGE_SIZE 4096
  42. #define MAX_OOB_SIZE 128
  43. /*
  44. * Buffer array used for writing data
  45. */
  46. static unsigned char writebuf[MAX_PAGE_SIZE];
  47. static unsigned char oobbuf[MAX_OOB_SIZE];
  48. static unsigned char oobreadbuf[MAX_OOB_SIZE];
  49. // oob layouts to pass into the kernel as default
  50. static struct nand_oobinfo none_oobinfo = {
  51. .useecc = MTD_NANDECC_OFF,
  52. };
  53. static struct nand_oobinfo jffs2_oobinfo = {
  54. .useecc = MTD_NANDECC_PLACE,
  55. .eccbytes = 6,
  56. .eccpos = { 0, 1, 2, 3, 6, 7 }
  57. };
  58. static struct nand_oobinfo yaffs_oobinfo = {
  59. .useecc = MTD_NANDECC_PLACE,
  60. .eccbytes = 6,
  61. .eccpos = { 8, 9, 10, 13, 14, 15}
  62. };
  63. static struct nand_oobinfo autoplace_oobinfo = {
  64. .useecc = MTD_NANDECC_AUTOPLACE
  65. };
  66. static void display_help (void)
  67. {
  68. printf(
  69. "Usage: nandwrite [OPTION] MTD_DEVICE [INPUTFILE|-]\n"
  70. "Writes to the specified MTD device.\n"
  71. "\n"
  72. " -a, --autoplace Use auto oob layout\n"
  73. " -j, --jffs2 Force jffs2 oob layout (legacy support)\n"
  74. " -y, --yaffs Force yaffs oob layout (legacy support)\n"
  75. " -f, --forcelegacy Force legacy support on autoplacement-enabled mtd\n"
  76. " device\n"
  77. " -m, --markbad Mark blocks bad if write fails\n"
  78. " -n, --noecc Write without ecc\n"
  79. " -o, --oob Image contains oob data\n"
  80. " -s addr, --start=addr Set start address (default is 0)\n"
  81. " -p, --pad Pad to page size\n"
  82. " -b, --blockalign=1|2|4 Set multiple of eraseblocks to align to\n"
  83. " -q, --quiet Don't display progress messages\n"
  84. " --help Display this help and exit\n"
  85. " --version Output version information and exit\n"
  86. );
  87. exit (EXIT_SUCCESS);
  88. }
  89. static void display_version (void)
  90. {
  91. printf(PROGRAM " " VERSION "\n"
  92. "\n"
  93. "Copyright (C) 2003 Thomas Gleixner \n"
  94. "\n"
  95. PROGRAM " comes with NO WARRANTY\n"
  96. "to the extent permitted by law.\n"
  97. "\n"
  98. "You may redistribute copies of " PROGRAM "\n"
  99. "under the terms of the GNU General Public Licence.\n"
  100. "See the file `COPYING' for more information.\n");
  101. exit (EXIT_SUCCESS);
  102. }
  103. static const char *standard_input = "-";
  104. static const char *mtd_device, *img;
  105. static int mtdoffset = 0;
  106. static bool quiet = false;
  107. static bool writeoob = false;
  108. static bool autoplace = false;
  109. static bool markbad = false;
  110. static bool forcejffs2 = false;
  111. static bool forceyaffs = false;
  112. static bool forcelegacy = false;
  113. static bool noecc = false;
  114. static bool pad = false;
  115. static int blockalign = 1; /*default to using 16K block size */
  116. static void process_options (int argc, char * const argv[])
  117. {
  118. int error = 0;
  119. for (;;) {
  120. int option_index = 0;
  121. static const char *short_options = "ab:fjmnopqs:y";
  122. static const struct option long_options[] = {
  123. {"help", no_argument, 0, 0},
  124. {"version", no_argument, 0, 0},
  125. {"autoplace", no_argument, 0, 'a'},
  126. {"blockalign", required_argument, 0, 'b'},
  127. {"forcelegacy", no_argument, 0, 'f'},
  128. {"jffs2", no_argument, 0, 'j'},
  129. {"markbad", no_argument, 0, 'm'},
  130. {"noecc", no_argument, 0, 'n'},
  131. {"oob", no_argument, 0, 'o'},
  132. {"pad", no_argument, 0, 'p'},
  133. {"quiet", no_argument, 0, 'q'},
  134. {"start", required_argument, 0, 's'},
  135. {"yaffs", no_argument, 0, 'y'},
  136. {0, 0, 0, 0},
  137. };
  138. int c = getopt_long(argc, argv, short_options,
  139. long_options, &option_index);
  140. if (c == EOF) {
  141. break;
  142. }
  143. switch (c) {
  144. case 0:
  145. switch (option_index) {
  146. case 0:
  147. display_help();
  148. break;
  149. case 1:
  150. display_version();
  151. break;
  152. }
  153. break;
  154. case 'q':
  155. quiet = true;
  156. break;
  157. case 'a':
  158. autoplace = true;
  159. break;
  160. case 'j':
  161. forcejffs2 = true;
  162. break;
  163. case 'y':
  164. forceyaffs = true;
  165. break;
  166. case 'f':
  167. forcelegacy = true;
  168. break;
  169. case 'n':
  170. noecc = true;
  171. break;
  172. case 'm':
  173. markbad = true;
  174. break;
  175. case 'o':
  176. writeoob = true;
  177. break;
  178. case 'p':
  179. pad = true;
  180. break;
  181. case 's':
  182. mtdoffset = strtol (optarg, NULL, 0);
  183. break;
  184. case 'b':
  185. blockalign = atoi (optarg);
  186. break;
  187. case '?':
  188. error++;
  189. break;
  190. }
  191. }
  192. if (mtdoffset < 0) {
  193. fprintf(stderr, "Can't specify a negative device offset `%d'\n",
  194. mtdoffset);
  195. exit (EXIT_FAILURE);
  196. }
  197. argc -= optind;
  198. argv += optind;
  199. /*
  200. * There must be at least the MTD device node positional
  201. * argument remaining and, optionally, the input file.
  202. */
  203. if (argc < 1 || argc > 2 || error)
  204. display_help ();
  205. mtd_device = argv[0];
  206. /*
  207. * Standard input may be specified either explictly as "-" or
  208. * implicity by simply omitting the second of the two
  209. * positional arguments.
  210. */
  211. img = ((argc == 2) ? argv[1] : standard_input);
  212. }
  213. static void erase_buffer(void *buffer, size_t size)
  214. {
  215. const uint8_t kEraseByte = 0xff;
  216. if (buffer != NULL && size > 0) {
  217. memset(buffer, kEraseByte, size);
  218. }
  219. }
  220. /*
  221. * Main program
  222. */
  223. int main(int argc, char * const argv[])
  224. {
  225. int cnt = 0;
  226. int fd = -1;
  227. int ifd = -1;
  228. int imglen = 0, pagelen;
  229. bool baderaseblock = false;
  230. int blockstart = -1;
  231. struct mtd_info_user meminfo;
  232. struct mtd_oob_buf oob;
  233. loff_t offs;
  234. int ret, readlen;
  235. int oobinfochanged = 0;
  236. struct nand_oobinfo old_oobinfo;
  237. int readcnt = 0;
  238. process_options(argc, argv);
  239. erase_buffer(oobbuf, sizeof(oobbuf));
  240. if (pad && writeoob) {
  241. fprintf(stderr, "Can't pad when oob data is present.\n");
  242. exit (EXIT_FAILURE);
  243. }
  244. /* Open the device */
  245. if ((fd = open(mtd_device, O_RDWR)) == -1) {
  246. perror(mtd_device);
  247. exit (EXIT_FAILURE);
  248. }
  249. /* Fill in MTD device capability structure */
  250. if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
  251. perror("MEMGETINFO");
  252. close(fd);
  253. exit (EXIT_FAILURE);
  254. }
  255. /* Set erasesize to specified number of blocks - to match jffs2
  256. * (virtual) block size */
  257. meminfo.erasesize *= blockalign;
  258. /* Make sure device page sizes are valid */
  259. if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
  260. !(meminfo.oobsize == 8 && meminfo.writesize == 256) &&
  261. !(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
  262. !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) {
  263. fprintf(stderr, "Unknown flash (not normal NAND)\n");
  264. close(fd);
  265. exit (EXIT_FAILURE);
  266. }
  267. if (autoplace) {
  268. /* Read the current oob info */
  269. if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
  270. perror ("MEMGETOOBSEL");
  271. close (fd);
  272. exit (EXIT_FAILURE);
  273. }
  274. // autoplace ECC ?
  275. if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
  276. if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
  277. perror ("MEMSETOOBSEL");
  278. close (fd);
  279. exit (EXIT_FAILURE);
  280. }
  281. oobinfochanged = 1;
  282. }
  283. }
  284. if (noecc) {
  285. ret = ioctl(fd, MTDFILEMODE, (void *) MTD_MODE_RAW);
  286. if (ret == 0) {
  287. oobinfochanged = 2;
  288. } else {
  289. switch (errno) {
  290. case ENOTTY:
  291. if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
  292. perror ("MEMGETOOBSEL");
  293. close (fd);
  294. exit (EXIT_FAILURE);
  295. }
  296. if (ioctl (fd, MEMSETOOBSEL, &none_oobinfo) != 0) {
  297. perror ("MEMSETOOBSEL");
  298. close (fd);
  299. exit (EXIT_FAILURE);
  300. }
  301. oobinfochanged = 1;
  302. break;
  303. default:
  304. perror ("MTDFILEMODE");
  305. close (fd);
  306. exit (EXIT_FAILURE);
  307. }
  308. }
  309. }
  310. /*
  311. * force oob layout for jffs2 or yaffs ?
  312. * Legacy support
  313. */
  314. if (forcejffs2 || forceyaffs) {
  315. struct nand_oobinfo *oobsel = forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo;
  316. if (autoplace) {
  317. fprintf(stderr, "Autoplacement is not possible for legacy -j/-y options\n");
  318. goto restoreoob;
  319. }
  320. if ((old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE) && !forcelegacy) {
  321. fprintf(stderr, "Use -f option to enforce legacy placement on autoplacement enabled mtd device\n");
  322. goto restoreoob;
  323. }
  324. if (meminfo.oobsize == 8) {
  325. if (forceyaffs) {
  326. fprintf (stderr, "YAFSS cannot operate on 256 Byte page size");
  327. goto restoreoob;
  328. }
  329. /* Adjust number of ecc bytes */
  330. jffs2_oobinfo.eccbytes = 3;
  331. }
  332. if (ioctl (fd, MEMSETOOBSEL, oobsel) != 0) {
  333. perror ("MEMSETOOBSEL");
  334. goto restoreoob;
  335. }
  336. }
  337. oob.length = meminfo.oobsize;
  338. oob.ptr = noecc ? oobreadbuf : oobbuf;
  339. /* Determine if we are reading from standard input or from a file. */
  340. if (strcmp(img, standard_input) == 0) {
  341. ifd = STDIN_FILENO;
  342. } else {
  343. ifd = open(img, O_RDONLY);
  344. }
  345. if (ifd == -1) {
  346. perror(img);
  347. goto restoreoob;
  348. }
  349. /* For now, don't allow writing oob when reading from standard input. */
  350. if (ifd == STDIN_FILENO && writeoob) {
  351. fprintf(stderr, "Can't write oob when reading from standard input.\n");
  352. goto closeall;
  353. }
  354. pagelen = meminfo.writesize + ((writeoob) ? meminfo.oobsize : 0);
  355. /*
  356. * For the standard input case, the input size is merely an
  357. * invariant placeholder and is set to the write page
  358. * size. Otherwise, just use the input file size.
  359. *
  360. * TODO: Add support for the -l,--length=length option (see
  361. * previous discussion by Tommi Airikka <tommi.airikka@ericsson.com> at
  362. * <http://lists.infradead.org/pipermail/linux-mtd/2008-September/
  363. * 022913.html>
  364. */
  365. if (ifd == STDIN_FILENO) {
  366. imglen = pagelen;
  367. } else {
  368. imglen = lseek(ifd, 0, SEEK_END);
  369. lseek (ifd, 0, SEEK_SET);
  370. }
  371. // Check, if file is page-aligned
  372. if ((!pad) && ((imglen % pagelen) != 0)) {
  373. fprintf (stderr, "Input file is not page-aligned. Use the padding "
  374. "option.\n");
  375. goto closeall;
  376. }
  377. // Check, if length fits into device
  378. if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
  379. fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
  380. imglen, pagelen, meminfo.writesize, meminfo.size);
  381. perror ("Input file does not fit into device");
  382. goto closeall;
  383. }
  384. /*
  385. * Get data from input and write to the device while there is
  386. * still input to read and we are still within the device
  387. * bounds. Note that in the case of standard input, the input
  388. * length is simply a quasi-boolean flag whose values are page
  389. * length or zero.
  390. */
  391. while (imglen && (mtdoffset < meminfo.size)) {
  392. // new eraseblock , check for bad block(s)
  393. // Stay in the loop to be sure if the mtdoffset changes because
  394. // of a bad block, that the next block that will be written to
  395. // is also checked. Thus avoiding errors if the block(s) after the
  396. // skipped block(s) is also bad (number of blocks depending on
  397. // the blockalign
  398. while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
  399. blockstart = mtdoffset & (~meminfo.erasesize + 1);
  400. offs = blockstart;
  401. baderaseblock = false;
  402. if (!quiet)
  403. fprintf (stdout, "Writing data to block %d at offset 0x%x\n",
  404. blockstart / meminfo.erasesize, blockstart);
  405. /* Check all the blocks in an erase block for bad blocks */
  406. do {
  407. if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
  408. perror("ioctl(MEMGETBADBLOCK)");
  409. goto closeall;
  410. }
  411. if (ret == 1) {
  412. baderaseblock = true;
  413. if (!quiet)
  414. fprintf (stderr, "Bad block at %x, %u block(s) "
  415. "from %x will be skipped\n",
  416. (int) offs, blockalign, blockstart);
  417. }
  418. if (baderaseblock) {
  419. mtdoffset = blockstart + meminfo.erasesize;
  420. }
  421. offs += meminfo.erasesize / blockalign ;
  422. } while ( offs < blockstart + meminfo.erasesize );
  423. }
  424. readlen = meminfo.writesize;
  425. if (ifd != STDIN_FILENO) {
  426. int tinycnt = 0;
  427. if (pad && (imglen < readlen))
  428. {
  429. readlen = imglen;
  430. erase_buffer(writebuf + readlen, meminfo.writesize - readlen);
  431. }
  432. /* Read Page Data from input file */
  433. while(tinycnt < readlen) {
  434. cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
  435. if (cnt == 0) { // EOF
  436. break;
  437. } else if (cnt < 0) {
  438. perror ("File I/O error on input file");
  439. goto closeall;
  440. }
  441. tinycnt += cnt;
  442. }
  443. } else {
  444. int tinycnt = 0;
  445. while(tinycnt < readlen) {
  446. cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
  447. if (cnt == 0) { // EOF
  448. break;
  449. } else if (cnt < 0) {
  450. perror ("File I/O error on stdin");
  451. goto closeall;
  452. }
  453. tinycnt += cnt;
  454. }
  455. /* No padding needed - we are done */
  456. if (tinycnt == 0) {
  457. imglen = 0;
  458. break;
  459. }
  460. /* No more bytes - we are done after writing the remaining bytes */
  461. if (cnt == 0) {
  462. imglen = 0;
  463. }
  464. /* Padding */
  465. if (pad && (tinycnt < readlen)) {
  466. erase_buffer(writebuf + tinycnt, meminfo.writesize - tinycnt);
  467. }
  468. }
  469. if (writeoob) {
  470. int tinycnt = 0;
  471. while(tinycnt < readlen) {
  472. cnt = read(ifd, oobreadbuf + tinycnt, meminfo.oobsize - tinycnt);
  473. if (cnt == 0) { // EOF
  474. break;
  475. } else if (cnt < 0) {
  476. perror ("File I/O error on input file");
  477. goto closeall;
  478. }
  479. tinycnt += cnt;
  480. }
  481. if (!noecc) {
  482. int i, start, len;
  483. /*
  484. * We use autoplacement and have the oobinfo with the autoplacement
  485. * information from the kernel available
  486. *
  487. * Modified to support out of order oobfree segments,
  488. * such as the layout used by diskonchip.c
  489. */
  490. if (!oobinfochanged && (old_oobinfo.useecc == MTD_NANDECC_AUTOPLACE)) {
  491. for (i = 0;old_oobinfo.oobfree[i][1]; i++) {
  492. /* Set the reserved bytes to 0xff */
  493. start = old_oobinfo.oobfree[i][0];
  494. len = old_oobinfo.oobfree[i][1];
  495. memcpy(oobbuf + start,
  496. oobreadbuf + start,
  497. len);
  498. }
  499. } else {
  500. /* Set at least the ecc byte positions to 0xff */
  501. start = old_oobinfo.eccbytes;
  502. len = meminfo.oobsize - start;
  503. memcpy(oobbuf + start,
  504. oobreadbuf + start,
  505. len);
  506. }
  507. }
  508. /* Write OOB data first, as ecc will be placed in there*/
  509. oob.start = mtdoffset;
  510. if (ioctl(fd, MEMWRITEOOB, &oob) != 0) {
  511. perror ("ioctl(MEMWRITEOOB)");
  512. goto closeall;
  513. }
  514. imglen -= meminfo.oobsize;
  515. }
  516. /* Write out the Page data */
  517. if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
  518. int rewind_blocks;
  519. off_t rewind_bytes;
  520. erase_info_t erase;
  521. perror ("pwrite");
  522. /* Must rewind to blockstart if we can */
  523. rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
  524. rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
  525. if (writeoob)
  526. rewind_bytes += (rewind_blocks + 1) * meminfo.oobsize;
  527. if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
  528. perror("lseek");
  529. fprintf(stderr, "Failed to seek backwards to recover from write error\n");
  530. goto closeall;
  531. }
  532. erase.start = blockstart;
  533. erase.length = meminfo.erasesize;
  534. fprintf(stderr, "Erasing failed write from %08lx-%08lx\n",
  535. (long)erase.start, (long)erase.start+erase.length-1);
  536. if (ioctl(fd, MEMERASE, &erase) != 0) {
  537. perror("MEMERASE");
  538. goto closeall;
  539. }
  540. if (markbad) {
  541. loff_t bad_addr = mtdoffset & (~(meminfo.erasesize / blockalign) + 1);
  542. fprintf(stderr, "Marking block at %08lx bad\n", (long)bad_addr);
  543. if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
  544. perror("MEMSETBADBLOCK");
  545. /* But continue anyway */
  546. }
  547. }
  548. mtdoffset = blockstart + meminfo.erasesize;
  549. imglen += rewind_blocks * meminfo.writesize;
  550. continue;
  551. }
  552. if (ifd != STDIN_FILENO) {
  553. imglen -= readlen;
  554. }
  555. mtdoffset += meminfo.writesize;
  556. }
  557. closeall:
  558. close(ifd);
  559. restoreoob:
  560. if (oobinfochanged == 1) {
  561. if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
  562. perror ("MEMSETOOBSEL");
  563. close (fd);
  564. exit (EXIT_FAILURE);
  565. }
  566. }
  567. close(fd);
  568. if ((ifd != STDIN_FILENO) && (imglen > 0)) {
  569. perror ("Data was only partially written due to error\n");
  570. exit (EXIT_FAILURE);
  571. }
  572. /* Return happy */
  573. return EXIT_SUCCESS;
  574. }