OpenWrt – Blame information for rev 3
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * mtd - simple memory technology device manipulation tool |
||
3 | * |
||
4 | * Copyright (C) 2005 Waldemar Brodkorb <wbx@dass-it.de>, |
||
5 | * Copyright (C) 2005-2009 Felix Fietkau <nbd@nbd.name> |
||
6 | * |
||
7 | * This program is free software; you can redistribute it and/or |
||
8 | * modify it under the terms of the GNU General Public License v2 |
||
9 | * as published by the Free Software Foundation. |
||
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 | * |
||
21 | * The code is based on the linux-mtd examples. |
||
22 | */ |
||
23 | |||
24 | #define _GNU_SOURCE |
||
25 | #include <byteswap.h> |
||
26 | #include <endian.h> |
||
27 | #include <limits.h> |
||
28 | #include <unistd.h> |
||
29 | #include <stdlib.h> |
||
30 | #include <stdio.h> |
||
31 | #include <stdint.h> |
||
32 | #include <signal.h> |
||
33 | #include <sys/ioctl.h> |
||
34 | #include <sys/syscall.h> |
||
35 | #include <fcntl.h> |
||
36 | #include <errno.h> |
||
37 | #include <time.h> |
||
38 | #include <string.h> |
||
39 | #include <sys/ioctl.h> |
||
40 | #include <sys/types.h> |
||
41 | #include <sys/param.h> |
||
42 | #include <sys/mount.h> |
||
43 | #include <sys/stat.h> |
||
44 | #include <sys/reboot.h> |
||
45 | #include <linux/reboot.h> |
||
46 | #include <mtd/mtd-user.h> |
||
47 | #include "fis.h" |
||
48 | #include "mtd.h" |
||
49 | |||
50 | #include <libubox/md5.h> |
||
51 | |||
52 | #define MAX_ARGS 8 |
||
53 | #define JFFS2_DEFAULT_DIR "" /* directory name without /, empty means root dir */ |
||
54 | |||
55 | #define TRX_MAGIC 0x48445230 /* "HDR0" */ |
||
56 | #define SEAMA_MAGIC 0x5ea3a417 |
||
57 | #define WRG_MAGIC 0x20040220 |
||
58 | #define WRGG03_MAGIC 0x20080321 |
||
59 | |||
60 | #if !defined(__BYTE_ORDER) |
||
61 | #error "Unknown byte order" |
||
62 | #endif |
||
63 | |||
64 | #if __BYTE_ORDER == __BIG_ENDIAN |
||
65 | #define cpu_to_be32(x) (x) |
||
66 | #define be32_to_cpu(x) (x) |
||
67 | #define le32_to_cpu(x) bswap_32(x) |
||
68 | #elif __BYTE_ORDER == __LITTLE_ENDIAN |
||
69 | #define cpu_to_be32(x) bswap_32(x) |
||
70 | #define be32_to_cpu(x) bswap_32(x) |
||
71 | #define le32_to_cpu(x) (x) |
||
72 | #else |
||
73 | #error "Unsupported endianness" |
||
74 | #endif |
||
75 | |||
76 | enum mtd_image_format { |
||
77 | MTD_IMAGE_FORMAT_UNKNOWN, |
||
78 | MTD_IMAGE_FORMAT_TRX, |
||
79 | MTD_IMAGE_FORMAT_SEAMA, |
||
80 | MTD_IMAGE_FORMAT_WRG, |
||
81 | MTD_IMAGE_FORMAT_WRGG03, |
||
82 | }; |
||
83 | |||
84 | static char *buf = NULL; |
||
85 | static char *imagefile = NULL; |
||
86 | static enum mtd_image_format imageformat = MTD_IMAGE_FORMAT_UNKNOWN; |
||
87 | static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR; |
||
88 | static int buflen = 0; |
||
89 | int quiet; |
||
90 | int no_erase; |
||
91 | int mtdsize = 0; |
||
92 | int erasesize = 0; |
||
93 | int jffs2_skip_bytes=0; |
||
94 | int mtdtype = 0; |
||
95 | |||
96 | int mtd_open(const char *mtd, bool block) |
||
97 | { |
||
98 | FILE *fp; |
||
99 | char dev[PATH_MAX]; |
||
100 | int i; |
||
101 | int ret; |
||
102 | int flags = O_RDWR | O_SYNC; |
||
103 | char name[PATH_MAX]; |
||
104 | |||
105 | snprintf(name, sizeof(name), "\"%s\"", mtd); |
||
106 | if ((fp = fopen("/proc/mtd", "r"))) { |
||
107 | while (fgets(dev, sizeof(dev), fp)) { |
||
108 | if (sscanf(dev, "mtd%d:", &i) && strstr(dev, name)) { |
||
109 | snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i); |
||
110 | if ((ret=open(dev, flags))<0) { |
||
111 | snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i); |
||
112 | ret=open(dev, flags); |
||
113 | } |
||
114 | fclose(fp); |
||
115 | return ret; |
||
116 | } |
||
117 | } |
||
118 | fclose(fp); |
||
119 | } |
||
120 | |||
121 | return open(mtd, flags); |
||
122 | } |
||
123 | |||
124 | int mtd_check_open(const char *mtd) |
||
125 | { |
||
126 | struct mtd_info_user mtdInfo; |
||
127 | int fd; |
||
128 | |||
129 | fd = mtd_open(mtd, false); |
||
130 | if(fd < 0) { |
||
131 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
132 | return -1; |
||
133 | } |
||
134 | |||
135 | if(ioctl(fd, MEMGETINFO, &mtdInfo)) { |
||
136 | fprintf(stderr, "Could not get MTD device info from %s\n", mtd); |
||
137 | close(fd); |
||
138 | return -1; |
||
139 | } |
||
140 | mtdsize = mtdInfo.size; |
||
141 | erasesize = mtdInfo.erasesize; |
||
142 | mtdtype = mtdInfo.type; |
||
143 | |||
144 | return fd; |
||
145 | } |
||
146 | |||
147 | int mtd_block_is_bad(int fd, int offset) |
||
148 | { |
||
149 | int r = 0; |
||
150 | loff_t o = offset; |
||
151 | |||
152 | if (mtdtype == MTD_NANDFLASH) |
||
153 | { |
||
154 | r = ioctl(fd, MEMGETBADBLOCK, &o); |
||
155 | if (r < 0) |
||
156 | { |
||
157 | fprintf(stderr, "Failed to get erase block status\n"); |
||
158 | exit(1); |
||
159 | } |
||
160 | } |
||
161 | return r; |
||
162 | } |
||
163 | |||
164 | int mtd_erase_block(int fd, int offset) |
||
165 | { |
||
166 | struct erase_info_user mtdEraseInfo; |
||
167 | |||
168 | mtdEraseInfo.start = offset; |
||
169 | mtdEraseInfo.length = erasesize; |
||
170 | ioctl(fd, MEMUNLOCK, &mtdEraseInfo); |
||
171 | if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) |
||
172 | return -1; |
||
173 | |||
174 | return 0; |
||
175 | } |
||
176 | |||
177 | int mtd_write_buffer(int fd, const char *buf, int offset, int length) |
||
178 | { |
||
179 | lseek(fd, offset, SEEK_SET); |
||
180 | write(fd, buf, length); |
||
181 | return 0; |
||
182 | } |
||
183 | |||
184 | static int |
||
185 | image_check(int imagefd, const char *mtd) |
||
186 | { |
||
187 | uint32_t magic; |
||
188 | int ret = 1; |
||
189 | int bufread; |
||
190 | |||
191 | while (buflen < sizeof(magic)) { |
||
192 | bufread = read(imagefd, buf + buflen, sizeof(magic) - buflen); |
||
193 | if (bufread < 1) |
||
194 | break; |
||
195 | |||
196 | buflen += bufread; |
||
197 | } |
||
198 | |||
199 | if (buflen < sizeof(magic)) { |
||
200 | fprintf(stdout, "Could not get image magic\n"); |
||
201 | return 0; |
||
202 | } |
||
203 | |||
204 | magic = ((uint32_t *)buf)[0]; |
||
205 | |||
206 | if (be32_to_cpu(magic) == TRX_MAGIC) |
||
207 | imageformat = MTD_IMAGE_FORMAT_TRX; |
||
208 | else if (be32_to_cpu(magic) == SEAMA_MAGIC) |
||
209 | imageformat = MTD_IMAGE_FORMAT_SEAMA; |
||
210 | else if (le32_to_cpu(magic) == WRG_MAGIC) |
||
211 | imageformat = MTD_IMAGE_FORMAT_WRG; |
||
212 | else if (le32_to_cpu(magic) == WRGG03_MAGIC) |
||
213 | imageformat = MTD_IMAGE_FORMAT_WRGG03; |
||
214 | |||
215 | switch (imageformat) { |
||
216 | case MTD_IMAGE_FORMAT_TRX: |
||
217 | if (trx_check) |
||
218 | ret = trx_check(imagefd, mtd, buf, &buflen); |
||
219 | break; |
||
220 | case MTD_IMAGE_FORMAT_SEAMA: |
||
221 | case MTD_IMAGE_FORMAT_WRG: |
||
222 | case MTD_IMAGE_FORMAT_WRGG03: |
||
223 | break; |
||
224 | default: |
||
225 | #ifdef target_brcm |
||
226 | if (!strcmp(mtd, "firmware")) |
||
227 | ret = 0; |
||
228 | #endif |
||
229 | break; |
||
230 | } |
||
231 | |||
232 | return ret; |
||
233 | } |
||
234 | |||
235 | static int mtd_check(const char *mtd) |
||
236 | { |
||
237 | char *next = NULL; |
||
238 | char *str = NULL; |
||
239 | int fd; |
||
240 | |||
241 | if (strchr(mtd, ':')) { |
||
242 | str = strdup(mtd); |
||
243 | mtd = str; |
||
244 | } |
||
245 | |||
246 | do { |
||
247 | next = strchr(mtd, ':'); |
||
248 | if (next) { |
||
249 | *next = 0; |
||
250 | next++; |
||
251 | } |
||
252 | |||
253 | fd = mtd_check_open(mtd); |
||
254 | if (fd < 0) |
||
255 | return 0; |
||
256 | |||
257 | if (!buf) |
||
258 | buf = malloc(erasesize); |
||
259 | |||
260 | close(fd); |
||
261 | mtd = next; |
||
262 | } while (next); |
||
263 | |||
264 | if (str) |
||
265 | free(str); |
||
266 | |||
267 | return 1; |
||
268 | } |
||
269 | |||
270 | static int |
||
271 | mtd_unlock(const char *mtd) |
||
272 | { |
||
273 | struct erase_info_user mtdLockInfo; |
||
274 | char *next = NULL; |
||
275 | char *str = NULL; |
||
276 | int fd; |
||
277 | |||
278 | if (strchr(mtd, ':')) { |
||
279 | str = strdup(mtd); |
||
280 | mtd = str; |
||
281 | } |
||
282 | |||
283 | do { |
||
284 | next = strchr(mtd, ':'); |
||
285 | if (next) { |
||
286 | *next = 0; |
||
287 | next++; |
||
288 | } |
||
289 | |||
290 | fd = mtd_check_open(mtd); |
||
291 | if(fd < 0) { |
||
292 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
293 | exit(1); |
||
294 | } |
||
295 | |||
296 | if (quiet < 2) |
||
297 | fprintf(stderr, "Unlocking %s ...\n", mtd); |
||
298 | |||
299 | mtdLockInfo.start = 0; |
||
300 | mtdLockInfo.length = mtdsize; |
||
301 | ioctl(fd, MEMUNLOCK, &mtdLockInfo); |
||
302 | close(fd); |
||
303 | mtd = next; |
||
304 | } while (next); |
||
305 | |||
306 | if (str) |
||
307 | free(str); |
||
308 | |||
309 | return 0; |
||
310 | } |
||
311 | |||
312 | static int |
||
313 | mtd_erase(const char *mtd) |
||
314 | { |
||
315 | int fd; |
||
316 | struct erase_info_user mtdEraseInfo; |
||
317 | |||
318 | if (quiet < 2) |
||
319 | fprintf(stderr, "Erasing %s ...\n", mtd); |
||
320 | |||
321 | fd = mtd_check_open(mtd); |
||
322 | if(fd < 0) { |
||
323 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
324 | exit(1); |
||
325 | } |
||
326 | |||
327 | mtdEraseInfo.length = erasesize; |
||
328 | |||
329 | for (mtdEraseInfo.start = 0; |
||
330 | mtdEraseInfo.start < mtdsize; |
||
331 | mtdEraseInfo.start += erasesize) { |
||
332 | if (mtd_block_is_bad(fd, mtdEraseInfo.start)) { |
||
333 | if (!quiet) |
||
334 | fprintf(stderr, "\nSkipping bad block at 0x%x ", mtdEraseInfo.start); |
||
335 | } else { |
||
336 | ioctl(fd, MEMUNLOCK, &mtdEraseInfo); |
||
337 | if(ioctl(fd, MEMERASE, &mtdEraseInfo)) |
||
338 | fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start); |
||
339 | } |
||
340 | } |
||
341 | |||
342 | close(fd); |
||
343 | return 0; |
||
344 | |||
345 | } |
||
346 | |||
347 | static int |
||
348 | mtd_dump(const char *mtd, int part_offset, int size) |
||
349 | { |
||
350 | int ret = 0, offset = 0; |
||
351 | int fd; |
||
352 | char *buf; |
||
353 | |||
354 | if (quiet < 2) |
||
355 | fprintf(stderr, "Dumping %s ...\n", mtd); |
||
356 | |||
357 | fd = mtd_check_open(mtd); |
||
358 | if(fd < 0) { |
||
359 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
360 | return -1; |
||
361 | } |
||
362 | |||
363 | if (!size) |
||
364 | size = mtdsize; |
||
365 | |||
366 | if (part_offset) |
||
367 | lseek(fd, part_offset, SEEK_SET); |
||
368 | |||
369 | buf = malloc(erasesize); |
||
370 | if (!buf) |
||
371 | return -1; |
||
372 | |||
373 | do { |
||
374 | int len = (size > erasesize) ? (erasesize) : (size); |
||
375 | int rlen = read(fd, buf, len); |
||
376 | |||
377 | if (rlen < 0) { |
||
378 | if (errno == EINTR) |
||
379 | continue; |
||
380 | ret = -1; |
||
381 | goto out; |
||
382 | } |
||
383 | if (!rlen || rlen != len) |
||
384 | break; |
||
385 | if (mtd_block_is_bad(fd, offset)) { |
||
386 | fprintf(stderr, "skipping bad block at 0x%08x\n", offset); |
||
387 | } else { |
||
388 | size -= rlen; |
||
389 | write(1, buf, rlen); |
||
390 | } |
||
391 | offset += rlen; |
||
392 | } while (size > 0); |
||
393 | |||
394 | out: |
||
395 | close(fd); |
||
396 | return ret; |
||
397 | } |
||
398 | |||
399 | static int |
||
400 | mtd_verify(const char *mtd, char *file) |
||
401 | { |
||
402 | uint32_t f_md5[4], m_md5[4]; |
||
403 | struct stat s; |
||
404 | md5_ctx_t ctx; |
||
405 | int ret = 0; |
||
406 | int fd; |
||
407 | |||
408 | if (quiet < 2) |
||
409 | fprintf(stderr, "Verifying %s against %s ...\n", mtd, file); |
||
410 | |||
411 | if (stat(file, &s) || md5sum(file, f_md5) < 0) { |
||
412 | fprintf(stderr, "Failed to hash %s\n", file); |
||
413 | return -1; |
||
414 | } |
||
415 | |||
416 | fd = mtd_check_open(mtd); |
||
417 | if(fd < 0) { |
||
418 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
419 | return -1; |
||
420 | } |
||
421 | |||
422 | md5_begin(&ctx); |
||
423 | do { |
||
424 | char buf[256]; |
||
425 | int len = (s.st_size > sizeof(buf)) ? (sizeof(buf)) : (s.st_size); |
||
426 | int rlen = read(fd, buf, len); |
||
427 | |||
428 | if (rlen < 0) { |
||
429 | if (errno == EINTR) |
||
430 | continue; |
||
431 | ret = -1; |
||
432 | goto out; |
||
433 | } |
||
434 | if (!rlen) |
||
435 | break; |
||
436 | md5_hash(buf, rlen, &ctx); |
||
437 | s.st_size -= rlen; |
||
438 | } while (s.st_size > 0); |
||
439 | |||
440 | md5_end(m_md5, &ctx); |
||
441 | |||
442 | fprintf(stderr, "%08x%08x%08x%08x - %s\n", m_md5[0], m_md5[1], m_md5[2], m_md5[3], mtd); |
||
443 | fprintf(stderr, "%08x%08x%08x%08x - %s\n", f_md5[0], f_md5[1], f_md5[2], f_md5[3], file); |
||
444 | |||
445 | ret = memcmp(f_md5, m_md5, sizeof(m_md5)); |
||
446 | if (!ret) |
||
447 | fprintf(stderr, "Success\n"); |
||
448 | else |
||
449 | fprintf(stderr, "Failed\n"); |
||
450 | |||
451 | out: |
||
452 | close(fd); |
||
453 | return ret; |
||
454 | } |
||
455 | |||
456 | static void |
||
457 | indicate_writing(const char *mtd) |
||
458 | { |
||
459 | if (quiet < 2) |
||
460 | fprintf(stderr, "\nWriting from %s to %s ... ", imagefile, mtd); |
||
461 | |||
462 | if (!quiet) |
||
463 | fprintf(stderr, " [ ]"); |
||
464 | } |
||
465 | |||
466 | static int |
||
467 | mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset) |
||
468 | { |
||
469 | char *next = NULL; |
||
470 | char *str = NULL; |
||
471 | int fd, result; |
||
472 | ssize_t r, w, e; |
||
473 | ssize_t skip = 0; |
||
474 | uint32_t offset = 0; |
||
475 | int jffs2_replaced = 0; |
||
476 | int skip_bad_blocks = 0; |
||
477 | |||
478 | #ifdef FIS_SUPPORT |
||
479 | static struct fis_part new_parts[MAX_ARGS]; |
||
480 | static struct fis_part old_parts[MAX_ARGS]; |
||
481 | int n_new = 0, n_old = 0; |
||
482 | |||
483 | if (fis_layout) { |
||
484 | const char *tmp = mtd; |
||
485 | char *word, *brkt; |
||
486 | int ret; |
||
487 | |||
488 | memset(&old_parts, 0, sizeof(old_parts)); |
||
489 | memset(&new_parts, 0, sizeof(new_parts)); |
||
490 | |||
491 | do { |
||
492 | next = strchr(tmp, ':'); |
||
493 | if (!next) |
||
494 | next = (char *) tmp + strlen(tmp); |
||
495 | |||
496 | memcpy(old_parts[n_old].name, tmp, next - tmp); |
||
497 | |||
498 | n_old++; |
||
499 | tmp = next + 1; |
||
500 | } while(*next); |
||
501 | |||
502 | for (word = strtok_r(fis_layout, ",", &brkt); |
||
503 | word; |
||
504 | word = strtok_r(NULL, ",", &brkt)) { |
||
505 | |||
506 | tmp = strtok(word, ":"); |
||
507 | strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1); |
||
508 | |||
509 | tmp = strtok(NULL, ":"); |
||
510 | if (!tmp) |
||
511 | goto next; |
||
512 | |||
513 | new_parts[n_new].size = strtoul(tmp, NULL, 0); |
||
514 | |||
515 | tmp = strtok(NULL, ":"); |
||
516 | if (!tmp) |
||
517 | goto next; |
||
518 | |||
519 | new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16); |
||
520 | next: |
||
521 | n_new++; |
||
522 | } |
||
523 | ret = fis_validate(old_parts, n_old, new_parts, n_new); |
||
524 | if (ret < 0) { |
||
525 | fprintf(stderr, "Failed to validate the new FIS partition table\n"); |
||
526 | exit(1); |
||
527 | } |
||
528 | if (ret == 0) |
||
529 | fis_layout = NULL; |
||
530 | } |
||
531 | #endif |
||
532 | |||
533 | if (strchr(mtd, ':')) { |
||
534 | str = strdup(mtd); |
||
535 | mtd = str; |
||
536 | } |
||
537 | |||
538 | r = 0; |
||
539 | |||
540 | resume: |
||
541 | next = strchr(mtd, ':'); |
||
542 | if (next) { |
||
543 | *next = 0; |
||
544 | next++; |
||
545 | } |
||
546 | |||
547 | fd = mtd_check_open(mtd); |
||
548 | if(fd < 0) { |
||
549 | fprintf(stderr, "Could not open mtd device: %s\n", mtd); |
||
550 | exit(1); |
||
551 | } |
||
552 | if (part_offset > 0) { |
||
553 | fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset); |
||
554 | lseek(fd, part_offset, SEEK_SET); |
||
555 | } |
||
556 | |||
557 | indicate_writing(mtd); |
||
558 | |||
559 | w = e = 0; |
||
560 | for (;;) { |
||
561 | /* buffer may contain data already (from trx check or last mtd partition write attempt) */ |
||
562 | while (buflen < erasesize) { |
||
563 | r = read(imagefd, buf + buflen, erasesize - buflen); |
||
564 | if (r < 0) { |
||
565 | if ((errno == EINTR) || (errno == EAGAIN)) |
||
566 | continue; |
||
567 | else { |
||
568 | perror("read"); |
||
569 | break; |
||
570 | } |
||
571 | } |
||
572 | |||
573 | if (r == 0) |
||
574 | break; |
||
575 | |||
576 | buflen += r; |
||
577 | } |
||
578 | |||
579 | if (buflen == 0) |
||
580 | break; |
||
581 | |||
582 | if (buflen < erasesize) { |
||
583 | /* Pad block to eraseblock size */ |
||
584 | memset(&buf[buflen], 0xff, erasesize - buflen); |
||
585 | buflen = erasesize; |
||
586 | } |
||
587 | |||
588 | if (skip > 0) { |
||
589 | skip -= buflen; |
||
590 | buflen = 0; |
||
591 | if (skip <= 0) |
||
592 | indicate_writing(mtd); |
||
593 | |||
594 | continue; |
||
595 | } |
||
596 | |||
597 | if (jffs2file && w >= jffs2_skip_bytes) { |
||
598 | if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) { |
||
599 | if (!quiet) |
||
600 | fprintf(stderr, "\b\b\b "); |
||
601 | if (quiet < 2) |
||
602 | fprintf(stderr, "\nAppending jffs2 data from %s to %s..\n.", jffs2file, mtd); |
||
603 | /* got an EOF marker - this is the place to add some jffs2 data */ |
||
604 | skip = mtd_replace_jffs2(mtd, fd, e, jffs2file); |
||
605 | jffs2_replaced = 1; |
||
606 | |||
607 | /* don't add it again */ |
||
608 | jffs2file = NULL; |
||
609 | |||
610 | w += skip; |
||
611 | e += skip; |
||
612 | skip -= buflen; |
||
613 | buflen = 0; |
||
614 | offset = 0; |
||
615 | continue; |
||
616 | } |
||
617 | /* no EOF marker, make sure we figure out the last inode number |
||
618 | * before appending some data */ |
||
619 | mtd_parse_jffs2data(buf, jffs2dir); |
||
620 | } |
||
621 | |||
622 | /* need to erase the next block before writing data to it */ |
||
623 | if(!no_erase) |
||
624 | { |
||
625 | while (w + buflen > e - skip_bad_blocks) { |
||
626 | if (!quiet) |
||
627 | fprintf(stderr, "\b\b\b[e]"); |
||
628 | |||
629 | if (mtd_block_is_bad(fd, e)) { |
||
630 | if (!quiet) |
||
631 | fprintf(stderr, "\nSkipping bad block at 0x%08zx ", e); |
||
632 | |||
633 | skip_bad_blocks += erasesize; |
||
634 | e += erasesize; |
||
635 | |||
636 | // Move the file pointer along over the bad block. |
||
637 | lseek(fd, erasesize, SEEK_CUR); |
||
638 | continue; |
||
639 | } |
||
640 | |||
641 | if (mtd_erase_block(fd, e + part_offset) < 0) { |
||
642 | if (next) { |
||
643 | if (w < e) { |
||
644 | write(fd, buf + offset, e - w); |
||
645 | offset = e - w; |
||
646 | } |
||
647 | w = 0; |
||
648 | e = 0; |
||
649 | close(fd); |
||
650 | mtd = next; |
||
651 | fprintf(stderr, "\b\b\b \n"); |
||
652 | goto resume; |
||
653 | } else { |
||
654 | fprintf(stderr, "Failed to erase block\n"); |
||
655 | exit(1); |
||
656 | } |
||
657 | } |
||
658 | |||
659 | /* erase the chunk */ |
||
660 | e += erasesize; |
||
661 | } |
||
662 | } |
||
663 | |||
664 | if (!quiet) |
||
665 | fprintf(stderr, "\b\b\b[w]"); |
||
666 | |||
667 | if ((result = write(fd, buf + offset, buflen)) < buflen) { |
||
668 | if (result < 0) { |
||
669 | fprintf(stderr, "Error writing image.\n"); |
||
670 | exit(1); |
||
671 | } else { |
||
672 | fprintf(stderr, "Insufficient space.\n"); |
||
673 | exit(1); |
||
674 | } |
||
675 | } |
||
676 | w += buflen; |
||
677 | |||
678 | buflen = 0; |
||
679 | offset = 0; |
||
680 | } |
||
681 | |||
682 | if (jffs2_replaced) { |
||
683 | switch (imageformat) { |
||
684 | case MTD_IMAGE_FORMAT_TRX: |
||
685 | if (trx_fixup) |
||
686 | trx_fixup(fd, mtd); |
||
687 | break; |
||
688 | case MTD_IMAGE_FORMAT_SEAMA: |
||
689 | if (mtd_fixseama) |
||
690 | mtd_fixseama(mtd, 0, 0); |
||
691 | break; |
||
692 | case MTD_IMAGE_FORMAT_WRG: |
||
693 | if (mtd_fixwrg) |
||
694 | mtd_fixwrg(mtd, 0, 0); |
||
695 | break; |
||
696 | case MTD_IMAGE_FORMAT_WRGG03: |
||
697 | if (mtd_fixwrgg) |
||
698 | mtd_fixwrgg(mtd, 0, 0); |
||
699 | break; |
||
700 | default: |
||
701 | break; |
||
702 | } |
||
703 | } |
||
704 | |||
705 | if (!quiet) |
||
706 | fprintf(stderr, "\b\b\b\b "); |
||
707 | |||
708 | if (quiet < 2) |
||
709 | fprintf(stderr, "\n"); |
||
710 | |||
711 | #ifdef FIS_SUPPORT |
||
712 | if (fis_layout) { |
||
713 | if (fis_remap(old_parts, n_old, new_parts, n_new) < 0) |
||
714 | fprintf(stderr, "Failed to update the FIS partition table\n"); |
||
715 | } |
||
716 | #endif |
||
717 | |||
718 | close(fd); |
||
719 | return 0; |
||
720 | } |
||
721 | |||
722 | static void usage(void) |
||
723 | { |
||
724 | fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n" |
||
725 | "The device is in the format of mtdX (eg: mtd4) or its label.\n" |
||
726 | "mtd recognizes these commands:\n" |
||
727 | " unlock unlock the device\n" |
||
728 | " refresh refresh mtd partition\n" |
||
729 | " erase erase all data on device\n" |
||
730 | " verify <imagefile>|- verify <imagefile> (use - for stdin) to device\n" |
||
731 | " write <imagefile>|- write <imagefile> (use - for stdin) to device\n" |
||
732 | " jffs2write <file> append <file> to the jffs2 partition on the device\n"); |
||
733 | if (mtd_resetbc) { |
||
734 | fprintf(stderr, |
||
735 | " resetbc <device> reset the uboot boot counter\n"); |
||
736 | } |
||
737 | if (mtd_fixtrx) { |
||
738 | fprintf(stderr, |
||
739 | " fixtrx fix the checksum in a trx header on first boot\n"); |
||
740 | } |
||
741 | if (mtd_fixseama) { |
||
742 | fprintf(stderr, |
||
743 | " fixseama fix the checksum in a seama header on first boot\n"); |
||
744 | } |
||
745 | if (mtd_fixwrg) { |
||
746 | fprintf(stderr, |
||
747 | " fixwrg fix the checksum in a wrg header on first boot\n"); |
||
748 | } |
||
749 | if (mtd_fixwrgg) { |
||
750 | fprintf(stderr, |
||
751 | " fixwrgg fix the checksum in a wrgg header on first boot\n"); |
||
752 | } |
||
753 | fprintf(stderr, |
||
754 | "Following options are available:\n" |
||
755 | " -q quiet mode (once: no [w] on writing,\n" |
||
756 | " twice: no status messages)\n" |
||
757 | " -n write without first erasing the blocks\n" |
||
758 | " -r reboot after successful command\n" |
||
759 | " -f force write without trx checks\n" |
||
760 | " -e <device> erase <device> before executing the command\n" |
||
761 | " -d <name> directory for jffs2write, defaults to \"tmp\"\n" |
||
762 | " -j <name> integrate <file> into jffs2 data when writing an image\n" |
||
763 | " -s <number> skip the first n bytes when appending data to the jffs2 partiton, defaults to \"0\"\n" |
||
764 | " -p <number> write beginning at partition offset\n" |
||
765 | " -l <length> the length of data that we want to dump\n"); |
||
766 | if (mtd_fixtrx) { |
||
767 | fprintf(stderr, |
||
768 | " -o offset offset of the image header in the partition(for fixtrx)\n"); |
||
769 | } |
||
770 | if (mtd_fixtrx || mtd_fixseama || mtd_fixwrg || mtd_fixwrgg) { |
||
771 | fprintf(stderr, |
||
772 | " -c datasize amount of data to be used for checksum calculation (for fixtrx / fixseama / fixwrg / fixwrgg)\n"); |
||
773 | } |
||
774 | fprintf(stderr, |
||
775 | #ifdef FIS_SUPPORT |
||
776 | " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n" |
||
777 | " alter the fis partition table to create new partitions replacing\n" |
||
778 | " the partitions provided as argument to the write command\n" |
||
779 | " (only valid together with the write command)\n" |
||
780 | #endif |
||
781 | "\n" |
||
782 | "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n" |
||
783 | " mtd -r write linux.trx linux\n\n"); |
||
784 | exit(1); |
||
785 | } |
||
786 | |||
787 | static void do_reboot(void) |
||
788 | { |
||
789 | fprintf(stderr, "Rebooting ...\n"); |
||
790 | fflush(stderr); |
||
791 | |||
792 | /* try regular reboot method first */ |
||
793 | system("/sbin/reboot"); |
||
794 | sleep(2); |
||
795 | |||
796 | /* if we're still alive at this point, force the kernel to reboot */ |
||
797 | syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL); |
||
798 | } |
||
799 | |||
800 | int main (int argc, char **argv) |
||
801 | { |
||
802 | int ch, i, boot, imagefd = 0, force, unlocked; |
||
803 | char *erase[MAX_ARGS], *device = NULL; |
||
804 | char *fis_layout = NULL; |
||
805 | size_t offset = 0, data_size = 0, part_offset = 0, dump_len = 0; |
||
806 | enum { |
||
807 | CMD_ERASE, |
||
808 | CMD_WRITE, |
||
809 | CMD_UNLOCK, |
||
810 | CMD_JFFS2WRITE, |
||
811 | CMD_FIXTRX, |
||
812 | CMD_FIXSEAMA, |
||
813 | CMD_FIXWRG, |
||
814 | CMD_FIXWRGG, |
||
815 | CMD_VERIFY, |
||
816 | CMD_DUMP, |
||
817 | CMD_RESETBC, |
||
818 | } cmd = -1; |
||
819 | |||
820 | erase[0] = NULL; |
||
821 | boot = 0; |
||
822 | force = 0; |
||
823 | buflen = 0; |
||
824 | quiet = 0; |
||
825 | no_erase = 0; |
||
826 | |||
827 | while ((ch = getopt(argc, argv, |
||
828 | #ifdef FIS_SUPPORT |
||
829 | "F:" |
||
830 | #endif |
||
3 | office | 831 | "frnqe:d:s:j:p:o:c:l:")) != -1) |
1 | office | 832 | switch (ch) { |
833 | case 'f': |
||
834 | force = 1; |
||
835 | break; |
||
836 | case 'r': |
||
837 | boot = 1; |
||
838 | break; |
||
839 | case 'n': |
||
840 | no_erase = 1; |
||
841 | break; |
||
842 | case 'j': |
||
843 | jffs2file = optarg; |
||
844 | break; |
||
845 | case 's': |
||
846 | errno = 0; |
||
847 | jffs2_skip_bytes = strtoul(optarg, 0, 0); |
||
848 | if (errno) { |
||
849 | fprintf(stderr, "-s: illegal numeric string\n"); |
||
850 | usage(); |
||
851 | } |
||
852 | break; |
||
853 | case 'q': |
||
854 | quiet++; |
||
855 | break; |
||
856 | case 'e': |
||
857 | i = 0; |
||
858 | while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS)) |
||
859 | i++; |
||
860 | |||
861 | erase[i++] = optarg; |
||
862 | erase[i] = NULL; |
||
863 | break; |
||
864 | case 'd': |
||
865 | jffs2dir = optarg; |
||
866 | break; |
||
867 | case 'p': |
||
868 | errno = 0; |
||
869 | part_offset = strtoul(optarg, 0, 0); |
||
870 | if (errno) { |
||
871 | fprintf(stderr, "-p: illegal numeric string\n"); |
||
872 | usage(); |
||
873 | } |
||
874 | break; |
||
875 | case 'l': |
||
876 | errno = 0; |
||
877 | dump_len = strtoul(optarg, 0, 0); |
||
878 | if (errno) { |
||
879 | fprintf(stderr, "-l: illegal numeric string\n"); |
||
880 | usage(); |
||
881 | } |
||
882 | break; |
||
883 | case 'o': |
||
884 | errno = 0; |
||
885 | offset = strtoul(optarg, 0, 0); |
||
886 | if (errno) { |
||
887 | fprintf(stderr, "-o: illegal numeric string\n"); |
||
888 | usage(); |
||
889 | } |
||
890 | break; |
||
891 | case 'c': |
||
892 | errno = 0; |
||
893 | data_size = strtoul(optarg, 0, 0); |
||
894 | if (errno) { |
||
895 | fprintf(stderr, "-c: illegal numeric string\n"); |
||
896 | usage(); |
||
897 | } |
||
898 | break; |
||
899 | #ifdef FIS_SUPPORT |
||
900 | case 'F': |
||
901 | fis_layout = optarg; |
||
902 | break; |
||
903 | #endif |
||
904 | case '?': |
||
905 | default: |
||
906 | usage(); |
||
907 | } |
||
908 | argc -= optind; |
||
909 | argv += optind; |
||
910 | |||
911 | if (argc < 2) |
||
912 | usage(); |
||
913 | |||
914 | if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) { |
||
915 | cmd = CMD_UNLOCK; |
||
916 | device = argv[1]; |
||
917 | } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) { |
||
918 | cmd = CMD_ERASE; |
||
919 | device = argv[1]; |
||
920 | } else if (((strcmp(argv[0], "resetbc") == 0) && (argc == 2)) && mtd_resetbc) { |
||
921 | cmd = CMD_RESETBC; |
||
922 | device = argv[1]; |
||
923 | } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) { |
||
924 | cmd = CMD_FIXTRX; |
||
925 | device = argv[1]; |
||
926 | } else if (((strcmp(argv[0], "fixseama") == 0) && (argc == 2)) && mtd_fixseama) { |
||
927 | cmd = CMD_FIXSEAMA; |
||
928 | device = argv[1]; |
||
929 | } else if (((strcmp(argv[0], "fixwrg") == 0) && (argc == 2)) && mtd_fixwrg) { |
||
930 | cmd = CMD_FIXWRG; |
||
931 | device = argv[1]; |
||
932 | } else if (((strcmp(argv[0], "fixwrgg") == 0) && (argc == 2)) && mtd_fixwrgg) { |
||
933 | cmd = CMD_FIXWRGG; |
||
934 | device = argv[1]; |
||
935 | } else if ((strcmp(argv[0], "verify") == 0) && (argc == 3)) { |
||
936 | cmd = CMD_VERIFY; |
||
937 | imagefile = argv[1]; |
||
938 | device = argv[2]; |
||
939 | } else if ((strcmp(argv[0], "dump") == 0) && (argc == 2)) { |
||
940 | cmd = CMD_DUMP; |
||
941 | device = argv[1]; |
||
942 | } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) { |
||
943 | cmd = CMD_WRITE; |
||
944 | device = argv[2]; |
||
945 | |||
946 | if (strcmp(argv[1], "-") == 0) { |
||
947 | imagefile = "<stdin>"; |
||
948 | imagefd = 0; |
||
949 | } else { |
||
950 | imagefile = argv[1]; |
||
951 | if ((imagefd = open(argv[1], O_RDONLY)) < 0) { |
||
952 | fprintf(stderr, "Couldn't open image file: %s!\n", imagefile); |
||
953 | exit(1); |
||
954 | } |
||
955 | } |
||
956 | |||
957 | if (!mtd_check(device)) { |
||
958 | fprintf(stderr, "Can't open device for writing!\n"); |
||
959 | exit(1); |
||
960 | } |
||
961 | /* check trx file before erasing or writing anything */ |
||
962 | if (!image_check(imagefd, device) && !force) { |
||
963 | fprintf(stderr, "Image check failed.\n"); |
||
964 | exit(1); |
||
965 | } |
||
966 | } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) { |
||
967 | cmd = CMD_JFFS2WRITE; |
||
968 | device = argv[2]; |
||
969 | |||
970 | imagefile = argv[1]; |
||
971 | if (!mtd_check(device)) { |
||
972 | fprintf(stderr, "Can't open device for writing!\n"); |
||
973 | exit(1); |
||
974 | } |
||
975 | } else { |
||
976 | usage(); |
||
977 | } |
||
978 | |||
979 | sync(); |
||
980 | |||
981 | i = 0; |
||
982 | unlocked = 0; |
||
983 | while (erase[i] != NULL) { |
||
984 | mtd_unlock(erase[i]); |
||
985 | mtd_erase(erase[i]); |
||
986 | if (strcmp(erase[i], device) == 0) |
||
987 | unlocked = 1; |
||
988 | i++; |
||
989 | } |
||
990 | |||
991 | switch (cmd) { |
||
992 | case CMD_UNLOCK: |
||
993 | if (!unlocked) |
||
994 | mtd_unlock(device); |
||
995 | break; |
||
996 | case CMD_VERIFY: |
||
997 | mtd_verify(device, imagefile); |
||
998 | break; |
||
999 | case CMD_DUMP: |
||
1000 | mtd_dump(device, offset, dump_len); |
||
1001 | break; |
||
1002 | case CMD_ERASE: |
||
1003 | if (!unlocked) |
||
1004 | mtd_unlock(device); |
||
1005 | mtd_erase(device); |
||
1006 | break; |
||
1007 | case CMD_WRITE: |
||
1008 | if (!unlocked) |
||
1009 | mtd_unlock(device); |
||
1010 | mtd_write(imagefd, device, fis_layout, part_offset); |
||
1011 | break; |
||
1012 | case CMD_JFFS2WRITE: |
||
1013 | if (!unlocked) |
||
1014 | mtd_unlock(device); |
||
1015 | mtd_write_jffs2(device, imagefile, jffs2dir); |
||
1016 | break; |
||
1017 | case CMD_FIXTRX: |
||
1018 | if (mtd_fixtrx) { |
||
1019 | mtd_fixtrx(device, offset, data_size); |
||
1020 | } |
||
1021 | break; |
||
1022 | case CMD_RESETBC: |
||
1023 | if (mtd_resetbc) { |
||
1024 | mtd_resetbc(device); |
||
1025 | } |
||
1026 | break; |
||
1027 | case CMD_FIXSEAMA: |
||
1028 | if (mtd_fixseama) |
||
1029 | mtd_fixseama(device, 0, data_size); |
||
1030 | break; |
||
1031 | case CMD_FIXWRG: |
||
1032 | if (mtd_fixwrg) |
||
1033 | mtd_fixwrg(device, 0, data_size); |
||
1034 | break; |
||
1035 | case CMD_FIXWRGG: |
||
1036 | if (mtd_fixwrgg) |
||
1037 | mtd_fixwrgg(device, 0, data_size); |
||
1038 | break; |
||
1039 | } |
||
1040 | |||
1041 | sync(); |
||
1042 | |||
1043 | if (boot) |
||
1044 | do_reboot(); |
||
1045 | |||
1046 | return 0; |
||
1047 | } |