nand.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. /*
  2. * nand - simple nand memory technology device manipulation tool
  3. *
  4. * Copyright (C) 2010 Waldemar Brodkorb <wbx@openadk.org>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  19. *
  20. * The code is based on the mtd-utils nandwrite and flash_erase_all.
  21. */
  22. #define _GNU_SOURCE
  23. #include <ctype.h>
  24. #include <errno.h>
  25. #include <error.h>
  26. #include <err.h>
  27. #include <fcntl.h>
  28. #include <limits.h>
  29. #include <stdbool.h>
  30. #include <stddef.h>
  31. #include <stdint.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <time.h>
  36. #include <unistd.h>
  37. #include <sys/stat.h>
  38. #include <sys/mount.h>
  39. #include <sys/ioctl.h>
  40. #include <sys/types.h>
  41. #include <sys/syscall.h>
  42. #include <getopt.h>
  43. #include "mtd/mtd-user.h"
  44. #include <linux/reboot.h>
  45. int nand_open(const char *, int);
  46. int nand_erase(const char *);
  47. int nand_info(const char *);
  48. int nand_write(const char*, const char *, int);
  49. void usage(void) __attribute__((noreturn));
  50. #define MAX_PAGE_SIZE 4096
  51. #define MAX_OOB_SIZE 128
  52. static unsigned char writebuf[MAX_PAGE_SIZE];
  53. static unsigned char oobbuf[MAX_OOB_SIZE];
  54. static unsigned char oobreadbuf[MAX_OOB_SIZE];
  55. static struct nand_oobinfo autoplace_oobinfo = {
  56. .useecc = MTD_NANDECC_AUTOPLACE
  57. };
  58. static void erase_buffer(void *buffer, size_t size)
  59. {
  60. const uint8_t kEraseByte = 0xff;
  61. if (buffer != NULL && size > 0) {
  62. memset(buffer, kEraseByte, size);
  63. }
  64. }
  65. int nand_open(const char *nand, int flags) {
  66. FILE *fp;
  67. char dev[PATH_MAX];
  68. int i;
  69. if ((fp = fopen("/proc/mtd", "r"))) {
  70. while (fgets(dev, sizeof(dev), fp)) {
  71. if (sscanf(dev, "mtd%d:", &i) && strstr(dev, nand)) {
  72. snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
  73. fclose(fp);
  74. return open(dev, flags);
  75. }
  76. }
  77. fclose(fp);
  78. }
  79. return open(nand, flags);
  80. }
  81. int nand_info(const char *nand) {
  82. int fd, ret;
  83. mtd_info_t nandinfo;
  84. struct nand_oobinfo oobinfo;
  85. loff_t offset;
  86. if ((fd = nand_open(nand, O_RDONLY)) < 0) {
  87. fprintf(stderr, "nand: unable to open MTD device %s\n", nand);
  88. return 1;
  89. }
  90. if (ioctl(fd, MEMGETINFO, &nandinfo) != 0) {
  91. fprintf(stderr, "nand: unable to get MTD device info from %s\n", nand);
  92. return 1;
  93. }
  94. if (nandinfo.type == MTD_NANDFLASH) {
  95. fprintf(stdout, "MTD devise is NAND\n");
  96. } else {
  97. fprintf(stdout, "MTD devise is NOT NAND\n");
  98. return 1;
  99. }
  100. fprintf(stdout, "NAND block/erase size is: %u\n", nandinfo.erasesize);
  101. fprintf(stdout, "NAND page size is: %u\n", nandinfo.writesize);
  102. fprintf(stdout, "NAND OOB size is: %u\n", nandinfo.oobsize);
  103. fprintf(stdout, "NAND partition size is: %u\n", nandinfo.size);
  104. for (offset = 0; offset < nandinfo.size; offset += nandinfo.erasesize) {
  105. ret = ioctl(fd, MEMGETBADBLOCK, &offset);
  106. if (ret > 0) {
  107. printf("\nSkipping bad block at %llu\n", offset);
  108. continue;
  109. } else if (ret < 0) {
  110. if (errno == EOPNOTSUPP) {
  111. fprintf(stderr, "Bad block check not available\n");
  112. return 1;
  113. }
  114. }
  115. }
  116. if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) {
  117. fprintf(stderr, "Unable to get NAND oobinfo\n");
  118. return 1;
  119. }
  120. if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
  121. fprintf(stdout, "NAND device/driver supports autoplacement of OOB\n");
  122. }
  123. return 0;
  124. }
  125. int nand_erase(const char *nand) {
  126. mtd_info_t meminfo;
  127. struct nand_oobinfo oobinfo;
  128. int fd, clmpos, clmlen;
  129. erase_info_t erase;
  130. clmpos = 0;
  131. clmlen = 8;
  132. erase_buffer(oobbuf, sizeof(oobbuf));
  133. if ((fd = nand_open(nand, O_RDWR)) < 0) {
  134. fprintf(stderr, "nand: %s: unable to open MTD device\n", nand);
  135. return 1;
  136. }
  137. if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
  138. fprintf(stderr, "nand: %s: unable to get MTD device info\n", nand);
  139. return 1;
  140. }
  141. erase.length = meminfo.erasesize;
  142. for (erase.start = 0; erase.start < meminfo.size; erase.start += meminfo.erasesize) {
  143. if (ioctl(fd, MEMERASE, &erase) != 0) {
  144. fprintf(stderr, "\nnand: %s: MTD Erase failure: %s\n", nand, strerror(errno));
  145. continue;
  146. }
  147. struct mtd_oob_buf oob;
  148. if (ioctl(fd, MEMGETOOBSEL, &oobinfo) != 0) {
  149. fprintf(stderr, "Unable to get NAND oobinfo\n");
  150. return 1;
  151. }
  152. if (oobinfo.useecc != MTD_NANDECC_AUTOPLACE) {
  153. fprintf(stderr, "NAND device/driver does not support autoplacement of OOB\n");
  154. return 1;
  155. }
  156. if (!oobinfo.oobfree[0][1]) {
  157. fprintf(stderr, "Autoplacement selected and no empty space in oob\n");
  158. return 1;
  159. }
  160. clmpos = oobinfo.oobfree[0][0];
  161. clmlen = oobinfo.oobfree[0][1];
  162. if (clmlen > 8)
  163. clmlen = 8;
  164. //fprintf(stdout, "Using clmlen: %d clmpos: %d\n", clmlen, clmpos);
  165. oob.ptr = oobbuf;
  166. oob.start = erase.start + clmpos;
  167. oob.length = clmlen;
  168. if (ioctl (fd, MEMWRITEOOB, &oob) != 0) {
  169. fprintf(stderr, "\nnand: %s: MTD writeoob failure: %s\n", nand, strerror(errno));
  170. continue;
  171. }
  172. }
  173. return 0;
  174. }
  175. int nand_write(const char *img, const char *nand, int quiet) {
  176. static bool pad = true;
  177. static const char *standard_input = "-";
  178. static bool autoplace = true;
  179. static bool markbad = true;
  180. static int mtdoffset = 0;
  181. int cnt = 0;
  182. int fd = -1;
  183. int ifd = -1;
  184. int imglen = 0, pagelen;
  185. bool baderaseblock = false;
  186. int blockstart = -1;
  187. struct mtd_info_user meminfo;
  188. struct mtd_oob_buf oob;
  189. loff_t offs;
  190. int ret, readlen;
  191. int oobinfochanged = 0;
  192. struct nand_oobinfo old_oobinfo;
  193. erase_buffer(oobbuf, sizeof(oobbuf));
  194. /* Open the device */
  195. if ((fd = nand_open(nand, O_RDWR | O_SYNC)) == -1) {
  196. perror(nand);
  197. exit (EXIT_FAILURE);
  198. }
  199. /* Fill in MTD device capability structure */
  200. if (ioctl(fd, MEMGETINFO, &meminfo) != 0) {
  201. perror("MEMGETINFO");
  202. close(fd);
  203. exit (EXIT_FAILURE);
  204. }
  205. /* Make sure device page sizes are valid */
  206. if (!(meminfo.oobsize == 16 && meminfo.writesize == 512) &&
  207. !(meminfo.oobsize == 8 && meminfo.writesize == 256) &&
  208. !(meminfo.oobsize == 64 && meminfo.writesize == 2048) &&
  209. !(meminfo.oobsize == 128 && meminfo.writesize == 4096)) {
  210. fprintf(stderr, "Unknown flash (not normal NAND)\n");
  211. close(fd);
  212. exit (EXIT_FAILURE);
  213. }
  214. if (autoplace) {
  215. /* Read the current oob info */
  216. if (ioctl (fd, MEMGETOOBSEL, &old_oobinfo) != 0) {
  217. perror ("MEMGETOOBSEL");
  218. close (fd);
  219. exit (EXIT_FAILURE);
  220. }
  221. // autoplace ECC ?
  222. if (autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) {
  223. if (ioctl (fd, MEMSETOOBSEL, &autoplace_oobinfo) != 0) {
  224. perror ("MEMSETOOBSEL");
  225. close (fd);
  226. exit (EXIT_FAILURE);
  227. }
  228. oobinfochanged = 1;
  229. }
  230. }
  231. oob.length = meminfo.oobsize;
  232. oob.ptr = oobbuf;
  233. /* Determine if we are reading from standard input or from a file. */
  234. if (strcmp(img, standard_input) == 0) {
  235. ifd = STDIN_FILENO;
  236. } else {
  237. ifd = open(img, O_RDONLY);
  238. }
  239. if (ifd == -1) {
  240. perror(img);
  241. goto restoreoob;
  242. }
  243. pagelen = meminfo.writesize;
  244. /*
  245. * For the standard input case, the input size is merely an
  246. * invariant placeholder and is set to the write page
  247. * size. Otherwise, just use the input file size.
  248. */
  249. if (ifd == STDIN_FILENO) {
  250. imglen = pagelen;
  251. } else {
  252. imglen = lseek(ifd, 0, SEEK_END);
  253. lseek (ifd, 0, SEEK_SET);
  254. }
  255. // Check, if file is page-aligned
  256. if ((!pad) && ((imglen % pagelen) != 0)) {
  257. fprintf (stderr, "Input file is not page-aligned. Use the padding "
  258. "option.\n");
  259. goto closeall;
  260. }
  261. // Check, if length fits into device
  262. if ( ((imglen / pagelen) * meminfo.writesize) > (meminfo.size - mtdoffset)) {
  263. fprintf (stderr, "Image %d bytes, NAND page %d bytes, OOB area %u bytes, device size %u bytes\n",
  264. imglen, pagelen, meminfo.writesize, meminfo.size);
  265. perror ("Input file does not fit into device");
  266. goto closeall;
  267. }
  268. /*
  269. * Get data from input and write to the device while there is
  270. * still input to read and we are still within the device
  271. * bounds. Note that in the case of standard input, the input
  272. * length is simply a quasi-boolean flag whose values are page
  273. * length or zero.
  274. */
  275. while (imglen && (mtdoffset < meminfo.size)) {
  276. // new eraseblock , check for bad block(s)
  277. // Stay in the loop to be sure if the mtdoffset changes because
  278. // of a bad block, that the next block that will be written to
  279. // is also checked. Thus avoiding errors if the block(s) after the
  280. // skipped block(s) is also bad
  281. while (blockstart != (mtdoffset & (~meminfo.erasesize + 1))) {
  282. blockstart = mtdoffset & (~meminfo.erasesize + 1);
  283. offs = blockstart;
  284. baderaseblock = false;
  285. if (quiet < 2)
  286. fprintf (stdout, "Writing data to block %d at offset 0x%x\n",
  287. blockstart / meminfo.erasesize, blockstart);
  288. /* Check all the blocks in an erase block for bad blocks */
  289. do {
  290. if ((ret = ioctl(fd, MEMGETBADBLOCK, &offs)) < 0) {
  291. perror("ioctl(MEMGETBADBLOCK)");
  292. goto closeall;
  293. }
  294. if (ret == 1) {
  295. baderaseblock = true;
  296. if (!quiet)
  297. fprintf (stderr, "Bad block at %x "
  298. "from %x will be skipped\n",
  299. (int) offs, blockstart);
  300. }
  301. if (baderaseblock) {
  302. mtdoffset = blockstart + meminfo.erasesize;
  303. }
  304. offs += meminfo.erasesize;
  305. } while ( offs < blockstart + meminfo.erasesize );
  306. }
  307. readlen = meminfo.writesize;
  308. if (ifd != STDIN_FILENO) {
  309. int tinycnt = 0;
  310. if (pad && (imglen < readlen))
  311. {
  312. readlen = imglen;
  313. erase_buffer(writebuf + readlen, meminfo.writesize - readlen);
  314. }
  315. /* Read Page Data from input file */
  316. while(tinycnt < readlen) {
  317. cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
  318. if (cnt == 0) { // EOF
  319. break;
  320. } else if (cnt < 0) {
  321. perror ("File I/O error on input file");
  322. goto closeall;
  323. }
  324. tinycnt += cnt;
  325. }
  326. } else {
  327. int tinycnt = 0;
  328. while(tinycnt < readlen) {
  329. cnt = read(ifd, writebuf + tinycnt, readlen - tinycnt);
  330. if (cnt == 0) { // EOF
  331. break;
  332. } else if (cnt < 0) {
  333. perror ("File I/O error on stdin");
  334. goto closeall;
  335. }
  336. tinycnt += cnt;
  337. }
  338. /* No padding needed - we are done */
  339. if (tinycnt == 0) {
  340. imglen = 0;
  341. break;
  342. }
  343. /* No more bytes - we are done after writing the remaining bytes */
  344. if (cnt == 0) {
  345. imglen = 0;
  346. }
  347. /* Padding */
  348. if (pad && (tinycnt < readlen)) {
  349. erase_buffer(writebuf + tinycnt, meminfo.writesize - tinycnt);
  350. }
  351. }
  352. /* Write out the Page data */
  353. if (pwrite(fd, writebuf, meminfo.writesize, mtdoffset) != meminfo.writesize) {
  354. int rewind_blocks;
  355. off_t rewind_bytes;
  356. erase_info_t erase;
  357. perror ("pwrite");
  358. /* Must rewind to blockstart if we can */
  359. rewind_blocks = (mtdoffset - blockstart) / meminfo.writesize; /* Not including the one we just attempted */
  360. rewind_bytes = (rewind_blocks * meminfo.writesize) + readlen;
  361. if (lseek(ifd, -rewind_bytes, SEEK_CUR) == -1) {
  362. perror("lseek");
  363. fprintf(stderr, "Failed to seek backwards to recover from write error\n");
  364. goto closeall;
  365. }
  366. erase.start = blockstart;
  367. erase.length = meminfo.erasesize;
  368. fprintf(stderr, "Erasing failed write from %08lx-%08lx\n",
  369. (long)erase.start, (long)erase.start+erase.length-1);
  370. if (ioctl(fd, MEMERASE, &erase) != 0) {
  371. perror("MEMERASE");
  372. goto closeall;
  373. }
  374. if (markbad) {
  375. loff_t bad_addr = mtdoffset & (~(meminfo.erasesize) + 1);
  376. fprintf(stderr, "Marking block at %08lx bad\n", (long)bad_addr);
  377. if (ioctl(fd, MEMSETBADBLOCK, &bad_addr)) {
  378. perror("MEMSETBADBLOCK");
  379. /* But continue anyway */
  380. }
  381. }
  382. mtdoffset = blockstart + meminfo.erasesize;
  383. imglen += rewind_blocks * meminfo.writesize;
  384. continue;
  385. }
  386. if (ifd != STDIN_FILENO) {
  387. imglen -= readlen;
  388. }
  389. mtdoffset += meminfo.writesize;
  390. }
  391. closeall:
  392. close(ifd);
  393. restoreoob:
  394. if (oobinfochanged == 1) {
  395. if (ioctl (fd, MEMSETOOBSEL, &old_oobinfo) != 0) {
  396. perror ("MEMSETOOBSEL");
  397. close (fd);
  398. exit (EXIT_FAILURE);
  399. }
  400. }
  401. close(fd);
  402. if ((ifd != STDIN_FILENO) && (imglen > 0)) {
  403. perror ("Data was only partially written due to error\n");
  404. exit (EXIT_FAILURE);
  405. }
  406. /* Return happy */
  407. return EXIT_SUCCESS;
  408. }
  409. void
  410. usage(void)
  411. {
  412. fprintf(stderr, "Usage: nand [<options> ...] <command> [<arguments> ...] <device>\n\n"
  413. "The device is in the format of mtdX (eg: mtd4) or its label.\n"
  414. "nand recognises these commands:\n"
  415. " erase erase all data on device\n"
  416. " info print information about device\n"
  417. " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
  418. "Following options are available:\n"
  419. " -q quiet mode\n"
  420. " -r reboot after successful command\n"
  421. "Example: To write linux.img to mtd partition labeled as linux\n"
  422. " nand write linux.img linux\n\n");
  423. exit(1);
  424. }
  425. int main(int argc, char **argv) {
  426. int ch, quiet, boot;
  427. char *device;
  428. enum {
  429. CMD_INFO,
  430. CMD_ERASE,
  431. CMD_WRITE,
  432. } cmd;
  433. boot = 0;
  434. quiet = 0;
  435. while ((ch = getopt(argc, argv, "Fqr:")) != -1)
  436. switch (ch) {
  437. case 'F':
  438. quiet = 1;
  439. /* FALLTHROUGH */
  440. case 'q':
  441. quiet++;
  442. break;
  443. case 'r':
  444. boot = 1;
  445. break;
  446. case '?':
  447. default:
  448. usage();
  449. }
  450. argc -= optind;
  451. argv += optind;
  452. if (argc < 2)
  453. usage();
  454. if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
  455. cmd = CMD_ERASE;
  456. device = argv[1];
  457. } else if ((strcmp(argv[0], "info") == 0) && (argc == 2)) {
  458. cmd = CMD_INFO;
  459. device = argv[1];
  460. } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
  461. cmd = CMD_WRITE;
  462. device = argv[2];
  463. } else {
  464. usage();
  465. }
  466. sync();
  467. switch (cmd) {
  468. case CMD_INFO:
  469. if (quiet < 2)
  470. fprintf(stderr, "Info about %s ...\n", device);
  471. nand_info(device);
  472. break;
  473. case CMD_ERASE:
  474. if (quiet < 2)
  475. fprintf(stderr, "Erasing %s ...\n", device);
  476. nand_erase(device);
  477. break;
  478. case CMD_WRITE:
  479. if (quiet < 2)
  480. fprintf(stderr, "Writing from %s to %s ... ", argv[1], device);
  481. nand_erase(device);
  482. nand_write(argv[1], device, quiet);
  483. if (quiet < 2)
  484. fprintf(stderr, "\n");
  485. break;
  486. }
  487. sync();
  488. if (boot) {
  489. fprintf(stderr, "\nRebooting ... ");
  490. fflush(stdout);
  491. fflush(stderr);
  492. syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
  493. }
  494. return 0;
  495. }