source: MondoRescue/branches/3.3/mindi-busybox/miscutils/nandwrite.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 8.0 KB
RevLine 
[2725]1/*
2 * nandwrite and nanddump ported to busybox from mtd-utils
3 *
4 * Author: Baruch Siach <baruch@tkos.co.il>, Orex Computed Radiography
5 *
6 * Licensed under GPLv2, see file LICENSE in this source tree.
7 *
8 * TODO: add support for large (>4GB) MTD devices
9 */
10
11//config:config NANDWRITE
12//config: bool "nandwrite"
[3232]13//config: default y
14//config: select PLATFORM_LINUX
[2725]15//config: help
16//config: Write to the specified MTD device, with bad blocks awareness
17//config:
18//config:config NANDDUMP
19//config: bool "nanddump"
[3232]20//config: default y
21//config: select PLATFORM_LINUX
[2725]22//config: help
23//config: Dump the content of raw NAND chip
24
[3232]25//applet:IF_NANDWRITE(APPLET(nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP))
[3621]26//applet:IF_NANDDUMP(APPLET_ODDNAME(nanddump, nandwrite, BB_DIR_USR_SBIN, BB_SUID_DROP, nanddump))
[3232]27
28//kbuild:lib-$(CONFIG_NANDWRITE) += nandwrite.o
29//kbuild:lib-$(CONFIG_NANDDUMP) += nandwrite.o
30
[2725]31//usage:#define nandwrite_trivial_usage
[3621]32//usage: "[-np] [-s ADDR] MTD_DEVICE [FILE]"
[2725]33//usage:#define nandwrite_full_usage "\n\n"
[3621]34//usage: "Write to MTD_DEVICE\n"
35//usage: "\n -n Write without ecc"
[2725]36//usage: "\n -p Pad to page size"
37//usage: "\n -s ADDR Start address"
38
39//usage:#define nanddump_trivial_usage
[3621]40//usage: "[-no]" IF_LONG_OPTS(" [--bb=padbad|skipbad]") " [-s ADDR] [-l LEN] [-f FILE] MTD_DEVICE"
[2725]41//usage:#define nanddump_full_usage "\n\n"
[3621]42//usage: "Dump MTD_DEVICE\n"
43//usage: "\n -n Read without ecc"
[3232]44//usage: "\n -o Dump oob data"
[2725]45//usage: "\n -s ADDR Start address"
46//usage: "\n -l LEN Length"
47//usage: "\n -f FILE Dump to file ('-' for stdout)"
[3621]48//usage: IF_LONG_OPTS(
49//usage: "\n --bb=METHOD:"
50//usage: "\n skipbad: skip bad blocks"
51//usage: "\n padbad: substitute bad blocks by 0xff (default)"
52//usage: )
[2725]53
54#include "libbb.h"
55#include <mtd/mtd-user.h>
56
57#define IS_NANDDUMP (ENABLE_NANDDUMP && (!ENABLE_NANDWRITE || (applet_name[4] == 'd')))
58#define IS_NANDWRITE (ENABLE_NANDWRITE && (!ENABLE_NANDDUMP || (applet_name[4] != 'd')))
59
60#define OPT_p (1 << 0) /* nandwrite only */
61#define OPT_o (1 << 0) /* nanddump only */
[3621]62#define OPT_n (1 << 1)
63#define OPT_s (1 << 2)
[2725]64#define OPT_f (1 << 3)
65#define OPT_l (1 << 4)
[3621]66#define OPT_bb (1 << 5) /* must be the last one in the list */
[2725]67
[3621]68#define BB_PADBAD (1 << 0)
69#define BB_SKIPBAD (1 << 1)
70
[2725]71/* helper for writing out 0xff for bad blocks pad */
72static void dump_bad(struct mtd_info_user *meminfo, unsigned len, int oob)
73{
74 unsigned char buf[meminfo->writesize];
75 unsigned count;
76
[3621]77 /* round len to the next page only if len is not already on a page */
78 len = ((len - 1) | (meminfo->writesize - 1)) + 1;
[2725]79
80 memset(buf, 0xff, sizeof(buf));
81 for (count = 0; count < len; count += meminfo->writesize) {
82 xwrite(STDOUT_FILENO, buf, meminfo->writesize);
83 if (oob)
84 xwrite(STDOUT_FILENO, buf, meminfo->oobsize);
85 }
86}
87
88static unsigned next_good_eraseblock(int fd, struct mtd_info_user *meminfo,
89 unsigned block_offset)
90{
91 while (1) {
92 loff_t offs;
93
94 if (block_offset >= meminfo->size) {
95 if (IS_NANDWRITE)
96 bb_error_msg_and_die("not enough space in MTD device");
97 return block_offset; /* let the caller exit */
98 }
99 offs = block_offset;
100 if (xioctl(fd, MEMGETBADBLOCK, &offs) == 0)
101 return block_offset;
102 /* ioctl returned 1 => "bad block" */
103 if (IS_NANDWRITE)
104 printf("Skipping bad block at 0x%08x\n", block_offset);
105 block_offset += meminfo->erasesize;
106 }
107}
108
109int nandwrite_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
110int nandwrite_main(int argc UNUSED_PARAM, char **argv)
111{
112 /* Buffer for OOB data */
113 unsigned char *oobbuf;
114 unsigned opts;
[3621]115 unsigned bb_method = BB_SKIPBAD;
[2725]116 int fd;
117 ssize_t cnt;
118 unsigned mtdoffset, meminfo_writesize, blockstart, limit;
119 unsigned end_addr = ~0;
120 struct mtd_info_user meminfo;
121 struct mtd_oob_buf oob;
122 unsigned char *filebuf;
[3621]123 const char *opt_s = "0", *opt_f = "-", *opt_l, *opt_bb;
[2725]124
125 if (IS_NANDDUMP) {
126 opt_complementary = "=1";
[3621]127#if ENABLE_LONG_OPTS
128 applet_long_options =
129 "bb\0" Required_argument "\xff"; /* no short equivalent */
130#endif
131 opts = getopt32(argv, "ons:f:l:", &opt_s, &opt_f, &opt_l, &opt_bb);
[2725]132 } else { /* nandwrite */
133 opt_complementary = "-1:?2";
[3621]134 opts = getopt32(argv, "pns:", &opt_s);
[2725]135 }
136 argv += optind;
137
138 if (IS_NANDWRITE && argv[1])
139 opt_f = argv[1];
140 if (!LONE_DASH(opt_f)) {
141 int tmp_fd = xopen(opt_f,
142 IS_NANDDUMP ? O_WRONLY | O_TRUNC | O_CREAT : O_RDONLY
143 );
144 xmove_fd(tmp_fd, IS_NANDDUMP ? STDOUT_FILENO : STDIN_FILENO);
145 }
146
[3232]147 fd = xopen(argv[0], IS_NANDWRITE ? O_RDWR : O_RDONLY);
[2725]148 xioctl(fd, MEMGETINFO, &meminfo);
149
[3621]150 if (opts & OPT_n)
151 xioctl(fd, MTDFILEMODE, (void *)MTD_FILE_MODE_RAW);
152
[2725]153 mtdoffset = xstrtou(opt_s, 0);
154 if (IS_NANDDUMP && (opts & OPT_l)) {
155 unsigned length = xstrtou(opt_l, 0);
156 if (length < meminfo.size - mtdoffset)
157 end_addr = mtdoffset + length;
158 }
[3621]159 if (IS_NANDDUMP && (opts & OPT_bb)) {
160 if (strcmp("skipbad", opt_bb) == 0)
161 bb_method = BB_SKIPBAD;
162 else if (strcmp("padbad", opt_bb) == 0)
163 bb_method = BB_PADBAD;
164 else
165 bb_show_usage();
166 }
[2725]167
168 /* Pull it into a CPU register (hopefully) - smaller code that way */
169 meminfo_writesize = meminfo.writesize;
170
171 if (mtdoffset & (meminfo_writesize - 1))
172 bb_error_msg_and_die("start address is not page aligned");
173
174 filebuf = xmalloc(meminfo_writesize);
175 oobbuf = xmalloc(meminfo.oobsize);
176
177 oob.start = 0;
178 oob.length = meminfo.oobsize;
179 oob.ptr = oobbuf;
180
181 blockstart = mtdoffset & ~(meminfo.erasesize - 1);
182 if (blockstart != mtdoffset) {
183 unsigned tmp;
184 /* mtdoffset is in the middle of an erase block, verify that
185 * this block is OK. Advance mtdoffset only if this block is
186 * bad.
187 */
188 tmp = next_good_eraseblock(fd, &meminfo, blockstart);
189 if (tmp != blockstart) {
190 /* bad block(s), advance mtdoffset */
[3621]191 if (IS_NANDDUMP) {
192 if (bb_method == BB_PADBAD) {
193 int bad_len = MIN(tmp, end_addr) - mtdoffset;
194 dump_bad(&meminfo, bad_len, opts & OPT_o);
195 }
196 /* with option skipbad, increase the total length */
197 if (bb_method == BB_SKIPBAD) {
198 end_addr += (tmp - blockstart);
199 }
[2725]200 }
201 mtdoffset = tmp;
202 }
203 }
204
205 cnt = -1;
206 limit = MIN(meminfo.size, end_addr);
207 while (mtdoffset < limit) {
208 int input_fd = IS_NANDWRITE ? STDIN_FILENO : fd;
209 int output_fd = IS_NANDWRITE ? fd : STDOUT_FILENO;
210
211 blockstart = mtdoffset & ~(meminfo.erasesize - 1);
212 if (blockstart == mtdoffset) {
213 /* starting a new eraseblock */
214 mtdoffset = next_good_eraseblock(fd, &meminfo, blockstart);
215 if (IS_NANDWRITE)
216 printf("Writing at 0x%08x\n", mtdoffset);
[3621]217 else if (mtdoffset > blockstart) {
218 if (bb_method == BB_PADBAD) {
219 /* dump FF padded bad block */
220 int bad_len = MIN(mtdoffset, limit) - blockstart;
221 dump_bad(&meminfo, bad_len, opts & OPT_o);
222 } else if (bb_method == BB_SKIPBAD) {
223 /* for skipbad, increase the length */
224 if ((end_addr + mtdoffset - blockstart) > end_addr)
225 end_addr += (mtdoffset - blockstart);
226 else
227 end_addr = ~0;
228 limit = MIN(meminfo.size, end_addr);
229 }
[2725]230 }
231 if (mtdoffset >= limit)
232 break;
233 }
234 xlseek(fd, mtdoffset, SEEK_SET);
235
236 /* get some more data from input */
237 cnt = full_read(input_fd, filebuf, meminfo_writesize);
238 if (cnt == 0) {
239 /* even with -p, we do not pad past the end of input
240 * (-p only zero-pads last incomplete page)
241 */
242 break;
243 }
244 if (cnt < meminfo_writesize) {
245 if (IS_NANDDUMP)
246 bb_error_msg_and_die("short read");
247 if (!(opts & OPT_p))
248 bb_error_msg_and_die("input size is not rounded up to page size, "
249 "use -p to zero pad");
250 /* zero pad to end of write block */
251 memset(filebuf + cnt, 0, meminfo_writesize - cnt);
252 }
253 xwrite(output_fd, filebuf, meminfo_writesize);
254
[3232]255 if (IS_NANDDUMP && (opts & OPT_o)) {
[2725]256 /* Dump OOB data */
257 oob.start = mtdoffset;
258 xioctl(fd, MEMREADOOB, &oob);
259 xwrite(output_fd, oobbuf, meminfo.oobsize);
260 }
261
262 mtdoffset += meminfo_writesize;
263 if (cnt < meminfo_writesize)
264 break;
265 }
266
267 if (IS_NANDWRITE && cnt != 0) {
268 /* We filled entire MTD, but did we reach EOF on input? */
269 if (full_read(STDIN_FILENO, filebuf, meminfo_writesize) != 0) {
270 /* no */
271 bb_error_msg_and_die("not enough space in MTD device");
272 }
273 }
274
275 if (ENABLE_FEATURE_CLEAN_UP) {
276 free(filebuf);
277 close(fd);
278 }
279
280 return EXIT_SUCCESS;
281}
Note: See TracBrowser for help on using the repository browser.