Root/package/mtd/src/mtd.c

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@openwrt.org>
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#include <limits.h>
25#include <unistd.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <stdint.h>
29#include <signal.h>
30#include <sys/ioctl.h>
31#include <sys/syscall.h>
32#include <fcntl.h>
33#include <errno.h>
34#include <error.h>
35#include <time.h>
36#include <string.h>
37#include <sys/ioctl.h>
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/mount.h>
41#include <sys/stat.h>
42#include <sys/reboot.h>
43#include <linux/reboot.h>
44#include "mtd-api.h"
45#include "fis.h"
46#include "mtd.h"
47#include "crc32.h"
48
49#define MAX_ARGS 8
50#define JFFS2_DEFAULT_DIR "" /* directory name without /, empty means root dir */
51
52#if __BYTE_ORDER == __BIG_ENDIAN
53#define STORE32_LE(X) ((((X) & 0x000000FF) << 24) | (((X) & 0x0000FF00) << 8) | (((X) & 0x00FF0000) >> 8) | (((X) & 0xFF000000) >> 24))
54#elif __BYTE_ORDER == __LITTLE_ENDIAN
55#define STORE32_LE(X) (X)
56#else
57#error unkown endianness!
58#endif
59
60ssize_t pread(int fd, void *buf, size_t count, off_t offset);
61ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
62
63#define TRX_MAGIC 0x30524448 /* "HDR0" */
64struct trx_header {
65    uint32_t magic; /* "HDR0" */
66    uint32_t len; /* Length of file including header */
67    uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
68    uint32_t flag_version; /* 0:15 flags, 16:31 version */
69    uint32_t offsets[3]; /* Offsets of partitions from start of header */
70};
71
72static char *buf = NULL;
73static char *imagefile = NULL;
74static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
75static int buflen = 0;
76int quiet;
77int mtdsize = 0;
78int erasesize = 0;
79
80int mtd_open(const char *mtd, bool block)
81{
82    FILE *fp;
83    char dev[PATH_MAX];
84    int i;
85    int ret;
86    int flags = O_RDWR | O_SYNC;
87
88    if ((fp = fopen("/proc/mtd", "r"))) {
89        while (fgets(dev, sizeof(dev), fp)) {
90            if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
91                snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
92                if ((ret=open(dev, flags))<0) {
93                    snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
94                    ret=open(dev, flags);
95                }
96                fclose(fp);
97                return ret;
98            }
99        }
100        fclose(fp);
101    }
102
103    return open(mtd, flags);
104}
105
106int mtd_check_open(const char *mtd)
107{
108    struct mtd_info_user mtdInfo;
109    int fd;
110
111    fd = mtd_open(mtd, false);
112    if(fd < 0) {
113        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
114        return -1;
115    }
116
117    if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
118        fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
119        close(fd);
120        return -1;
121    }
122    mtdsize = mtdInfo.size;
123    erasesize = mtdInfo.erasesize;
124
125    return fd;
126}
127
128int mtd_erase_block(int fd, int offset)
129{
130    struct erase_info_user mtdEraseInfo;
131
132    mtdEraseInfo.start = offset;
133    mtdEraseInfo.length = erasesize;
134    ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
135    if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0)
136        return -1;
137
138    return 0;
139}
140
141int mtd_write_buffer(int fd, const char *buf, int offset, int length)
142{
143    lseek(fd, offset, SEEK_SET);
144    write(fd, buf, length);
145    return 0;
146}
147
148
149static int
150image_check(int imagefd, const char *mtd)
151{
152    int ret = 1;
153#ifdef target_brcm
154    ret = trx_check(imagefd, mtd, buf, &buflen);
155#endif
156    return ret;
157}
158
159static int mtd_check(const char *mtd)
160{
161    char *next = NULL;
162    char *str = NULL;
163    int fd;
164
165    if (strchr(mtd, ':')) {
166        str = strdup(mtd);
167        mtd = str;
168    }
169
170    do {
171        next = strchr(mtd, ':');
172        if (next) {
173            *next = 0;
174            next++;
175        }
176
177        fd = mtd_check_open(mtd);
178        if (fd < 0)
179            return 0;
180
181        if (!buf)
182            buf = malloc(erasesize);
183
184        close(fd);
185        mtd = next;
186    } while (next);
187
188    if (str)
189        free(str);
190
191    return 1;
192}
193
194static int
195mtd_unlock(const char *mtd)
196{
197    struct erase_info_user mtdLockInfo;
198    char *next = NULL;
199    char *str = NULL;
200    int fd;
201
202    if (strchr(mtd, ':')) {
203        str = strdup(mtd);
204        mtd = str;
205    }
206
207    do {
208        next = strchr(mtd, ':');
209        if (next) {
210            *next = 0;
211            next++;
212        }
213
214        fd = mtd_check_open(mtd);
215        if(fd < 0) {
216            fprintf(stderr, "Could not open mtd device: %s\n", mtd);
217            exit(1);
218        }
219
220        if (quiet < 2)
221            fprintf(stderr, "Unlocking %s ...\n", mtd);
222
223        mtdLockInfo.start = 0;
224        mtdLockInfo.length = mtdsize;
225        ioctl(fd, MEMUNLOCK, &mtdLockInfo);
226        close(fd);
227        mtd = next;
228    } while (next);
229
230    if (str)
231        free(str);
232
233    return 0;
234}
235
236static int
237mtd_erase(const char *mtd)
238{
239    int fd;
240    struct erase_info_user mtdEraseInfo;
241
242    if (quiet < 2)
243        fprintf(stderr, "Erasing %s ...\n", mtd);
244
245    fd = mtd_check_open(mtd);
246    if(fd < 0) {
247        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
248        exit(1);
249    }
250
251    mtdEraseInfo.length = erasesize;
252
253    for (mtdEraseInfo.start = 0;
254         mtdEraseInfo.start < mtdsize;
255         mtdEraseInfo.start += erasesize) {
256
257        ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
258        if(ioctl(fd, MEMERASE, &mtdEraseInfo))
259            fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
260    }
261
262    close(fd);
263    return 0;
264
265}
266
267static int
268mtd_fixtrx(const char *mtd, size_t offset)
269{
270    int fd;
271    struct trx_header *trx;
272    char *buf;
273    ssize_t res;
274    size_t block_offset;
275
276    if (quiet < 2)
277        fprintf(stderr, "Trying to fix trx header in %s at 0x%x...\n", mtd, offset);
278
279    block_offset = offset & ~(erasesize - 1);
280    offset -= block_offset;
281
282    fd = mtd_check_open(mtd);
283    if(fd < 0) {
284        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
285        exit(1);
286    }
287
288    if (block_offset + erasesize > mtdsize) {
289        fprintf(stderr, "Offset too large, device size 0x%x\n", mtdsize);
290        exit(1);
291    }
292
293    buf = malloc(erasesize);
294    if (!buf) {
295        perror("malloc");
296        exit(1);
297    }
298
299    res = pread(fd, buf, erasesize, block_offset);
300    if (res != erasesize) {
301        perror("pread");
302        exit(1);
303    }
304
305    trx = (struct trx_header *) (buf + offset);
306    if (trx->magic != STORE32_LE(0x30524448)) {
307        fprintf(stderr, "No trx magic found\n");
308        exit(1);
309    }
310
311    if (trx->len == STORE32_LE(erasesize - offset)) {
312        if (quiet < 2)
313            fprintf(stderr, "Header already fixed, exiting\n");
314        close(fd);
315        return 0;
316    }
317
318    trx->len = STORE32_LE(erasesize - offset);
319
320    trx->crc32 = STORE32_LE(crc32buf((char*) &trx->flag_version, erasesize - offset - 3*4));
321    if (mtd_erase_block(fd, block_offset)) {
322        fprintf(stderr, "Can't erease block at 0x%x (%s)\n", block_offset, strerror(errno));
323        exit(1);
324    }
325
326    if (quiet < 2)
327        fprintf(stderr, "New crc32: 0x%x, rewriting block\n", trx->crc32);
328
329    if (pwrite(fd, buf, erasesize, block_offset) != erasesize) {
330        fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
331        exit(1);
332    }
333
334    if (quiet < 2)
335        fprintf(stderr, "Done.\n");
336
337    close (fd);
338    sync();
339    return 0;
340
341}
342
343static int
344mtd_refresh(const char *mtd)
345{
346    int fd;
347
348    if (quiet < 2)
349        fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
350
351    fd = mtd_check_open(mtd);
352    if(fd < 0) {
353        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
354        exit(1);
355    }
356
357    if (ioctl(fd, MTDREFRESH, NULL)) {
358        fprintf(stderr, "Failed to refresh the MTD device\n");
359        close(fd);
360        exit(1);
361    }
362    close(fd);
363
364    if (quiet < 2)
365        fprintf(stderr, "\n");
366
367    return 0;
368}
369
370static int
371mtd_write(int imagefd, const char *mtd, char *fis_layout)
372{
373    char *next = NULL;
374    char *str = NULL;
375    int fd, result;
376    ssize_t r, w, e;
377    uint32_t offset = 0;
378
379#ifdef FIS_SUPPORT
380    static struct fis_part new_parts[MAX_ARGS];
381    static struct fis_part old_parts[MAX_ARGS];
382    int n_new = 0, n_old = 0;
383
384    if (fis_layout) {
385        const char *tmp = mtd;
386        char *word, *brkt;
387        int ret;
388
389        memset(&old_parts, 0, sizeof(old_parts));
390        memset(&new_parts, 0, sizeof(new_parts));
391
392        do {
393            next = strchr(tmp, ':');
394            if (!next)
395                next = (char *) tmp + strlen(tmp);
396
397            memcpy(old_parts[n_old].name, tmp, next - tmp);
398
399            n_old++;
400            tmp = next + 1;
401        } while(*next);
402
403        for (word = strtok_r(fis_layout, ",", &brkt);
404             word;
405             word = strtok_r(NULL, ",", &brkt)) {
406
407            tmp = strtok(word, ":");
408            strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1);
409
410            tmp = strtok(NULL, ":");
411            if (!tmp)
412                goto next;
413
414            new_parts[n_new].size = strtoul(tmp, NULL, 0);
415
416            tmp = strtok(NULL, ":");
417            if (!tmp)
418                goto next;
419
420            new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16);
421next:
422            n_new++;
423        }
424        ret = fis_validate(old_parts, n_old, new_parts, n_new);
425        if (ret < 0) {
426            fprintf(stderr, "Failed to validate the new FIS partition table\n");
427            exit(1);
428        }
429        if (ret == 0)
430            fis_layout = NULL;
431    }
432#endif
433
434    if (strchr(mtd, ':')) {
435        str = strdup(mtd);
436        mtd = str;
437    }
438
439    r = 0;
440
441resume:
442    next = strchr(mtd, ':');
443    if (next) {
444        *next = 0;
445        next++;
446    }
447
448    fd = mtd_check_open(mtd);
449    if(fd < 0) {
450        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
451        exit(1);
452    }
453
454    if (quiet < 2)
455        fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
456
457    w = e = 0;
458    if (!quiet)
459        fprintf(stderr, " [ ]");
460
461    for (;;) {
462        /* buffer may contain data already (from trx check or last mtd partition write attempt) */
463        while (buflen < erasesize) {
464            r = read(imagefd, buf + buflen, erasesize - buflen);
465            if (r < 0) {
466                if ((errno == EINTR) || (errno == EAGAIN))
467                    continue;
468                else {
469                    perror("read");
470                    break;
471                }
472            }
473
474            if (r == 0)
475                break;
476
477            buflen += r;
478        }
479
480        if (buflen == 0)
481            break;
482
483        if (jffs2file) {
484            if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
485                if (!quiet)
486                    fprintf(stderr, "\b\b\b ");
487                if (quiet < 2)
488                    fprintf(stderr, "\nAppending jffs2 data from %s to %s...", jffs2file, mtd);
489                /* got an EOF marker - this is the place to add some jffs2 data */
490                mtd_replace_jffs2(mtd, fd, e, jffs2file);
491                goto done;
492            }
493            /* no EOF marker, make sure we figure out the last inode number
494             * before appending some data */
495            mtd_parse_jffs2data(buf, jffs2dir);
496        }
497
498        /* need to erase the next block before writing data to it */
499        while (w + buflen > e) {
500            if (!quiet)
501                fprintf(stderr, "\b\b\b[e]");
502
503
504            if (mtd_erase_block(fd, e) < 0) {
505                if (next) {
506                    if (w < e) {
507                        write(fd, buf + offset, e - w);
508                        offset = e - w;
509                    }
510                    w = 0;
511                    e = 0;
512                    close(fd);
513                    mtd = next;
514                    fprintf(stderr, "\b\b\b \n");
515                    goto resume;
516                } else {
517                    fprintf(stderr, "Failed to erase block\n");
518                    exit(1);
519                }
520            }
521
522            /* erase the chunk */
523            e += erasesize;
524        }
525
526        if (!quiet)
527            fprintf(stderr, "\b\b\b[w]");
528
529        if ((result = write(fd, buf + offset, buflen)) < buflen) {
530            if (result < 0) {
531                fprintf(stderr, "Error writing image.\n");
532                exit(1);
533            } else {
534                fprintf(stderr, "Insufficient space.\n");
535                exit(1);
536            }
537        }
538        w += buflen;
539
540        buflen = 0;
541        offset = 0;
542    }
543
544    if (!quiet)
545        fprintf(stderr, "\b\b\b\b ");
546
547done:
548    if (quiet < 2)
549        fprintf(stderr, "\n");
550
551#ifdef FIS_SUPPORT
552    if (fis_layout) {
553        if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
554            fprintf(stderr, "Failed to update the FIS partition table\n");
555    }
556#endif
557
558    close(fd);
559    return 0;
560}
561
562static void usage(void)
563{
564    fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n"
565    "The device is in the format of mtdX (eg: mtd4) or its label.\n"
566    "mtd recognizes these commands:\n"
567    " unlock unlock the device\n"
568    " refresh refresh mtd partition\n"
569    " erase erase all data on device\n"
570    " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
571    " jffs2write <file> append <file> to the jffs2 partition on the device\n"
572    " fixtrx fix the checksum in a trx header on first boot\n"
573    "Following options are available:\n"
574    " -q quiet mode (once: no [w] on writing,\n"
575    " twice: no status messages)\n"
576    " -r reboot after successful command\n"
577    " -f force write without trx checks\n"
578    " -e <device> erase <device> before executing the command\n"
579    " -d <name> directory for jffs2write, defaults to \"tmp\"\n"
580    " -j <name> integrate <file> into jffs2 data when writing an image\n"
581    " -o offset offset of the trx header in the partition (for fixtrx)\n"
582#ifdef FIS_SUPPORT
583    " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
584    " alter the fis partition table to create new partitions replacing\n"
585    " the partitions provided as argument to the write command\n"
586    " (only valid together with the write command)\n"
587#endif
588    "\n"
589    "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
590    " mtd -r write linux.trx linux\n\n");
591    exit(1);
592}
593
594static void do_reboot(void)
595{
596    fprintf(stderr, "Rebooting ...\n");
597    fflush(stderr);
598
599    /* try regular reboot method first */
600    system("/sbin/reboot");
601    sleep(2);
602
603    /* if we're still alive at this point, force the kernel to reboot */
604    syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
605}
606
607int main (int argc, char **argv)
608{
609    int ch, i, boot, imagefd = 0, force, unlocked;
610    char *erase[MAX_ARGS], *device = NULL;
611    char *fis_layout = NULL;
612    size_t offset = 0;
613    enum {
614        CMD_ERASE,
615        CMD_WRITE,
616        CMD_UNLOCK,
617        CMD_REFRESH,
618        CMD_JFFS2WRITE,
619        CMD_FIXTRX,
620    } cmd = -1;
621
622    erase[0] = NULL;
623    boot = 0;
624    force = 0;
625    buflen = 0;
626    quiet = 0;
627
628    while ((ch = getopt(argc, argv,
629#ifdef FIS_SUPPORT
630            "F:"
631#endif
632            "frqe:d:j:o:")) != -1)
633        switch (ch) {
634            case 'f':
635                force = 1;
636                break;
637            case 'r':
638                boot = 1;
639                break;
640            case 'j':
641                jffs2file = optarg;
642                break;
643            case 'q':
644                quiet++;
645                break;
646            case 'e':
647                i = 0;
648                while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
649                    i++;
650
651                erase[i++] = optarg;
652                erase[i] = NULL;
653                break;
654            case 'd':
655                jffs2dir = optarg;
656                break;
657            case 'o':
658                errno = 0;
659                offset = strtoul(optarg, 0, 0);
660                if (errno) {
661                    fprintf(stderr, "-o: illegal numeric string\n");
662                    usage();
663                }
664                break;
665#ifdef FIS_SUPPORT
666            case 'F':
667                fis_layout = optarg;
668                break;
669#endif
670            case '?':
671            default:
672                usage();
673        }
674    argc -= optind;
675    argv += optind;
676
677    if (argc < 2)
678        usage();
679
680    if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
681        cmd = CMD_UNLOCK;
682        device = argv[1];
683    } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
684        cmd = CMD_REFRESH;
685        device = argv[1];
686    } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
687        cmd = CMD_ERASE;
688        device = argv[1];
689    } else if ((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) {
690        cmd = CMD_FIXTRX;
691        device = argv[1];
692    } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
693        cmd = CMD_WRITE;
694        device = argv[2];
695
696        if (strcmp(argv[1], "-") == 0) {
697            imagefile = "<stdin>";
698            imagefd = 0;
699        } else {
700            imagefile = argv[1];
701            if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
702                fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
703                exit(1);
704            }
705        }
706
707        if (!mtd_check(device)) {
708            fprintf(stderr, "Can't open device for writing!\n");
709            exit(1);
710        }
711        /* check trx file before erasing or writing anything */
712        if (!image_check(imagefd, device) && !force) {
713            fprintf(stderr, "Image check failed.\n");
714            exit(1);
715        }
716    } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
717        cmd = CMD_JFFS2WRITE;
718        device = argv[2];
719
720        imagefile = argv[1];
721        if (!mtd_check(device)) {
722            fprintf(stderr, "Can't open device for writing!\n");
723            exit(1);
724        }
725    } else {
726        usage();
727    }
728
729    sync();
730
731    i = 0;
732    unlocked = 0;
733    while (erase[i] != NULL) {
734        mtd_unlock(erase[i]);
735        mtd_erase(erase[i]);
736        if (strcmp(erase[i], device) == 0)
737            unlocked = 1;
738        i++;
739    }
740
741    switch (cmd) {
742        case CMD_UNLOCK:
743            if (!unlocked)
744                mtd_unlock(device);
745            break;
746        case CMD_ERASE:
747            if (!unlocked)
748                mtd_unlock(device);
749            mtd_erase(device);
750            break;
751        case CMD_WRITE:
752            if (!unlocked)
753                mtd_unlock(device);
754            mtd_write(imagefd, device, fis_layout);
755            break;
756        case CMD_JFFS2WRITE:
757            if (!unlocked)
758                mtd_unlock(device);
759            mtd_write_jffs2(device, imagefile, jffs2dir);
760            break;
761        case CMD_REFRESH:
762            mtd_refresh(device);
763            break;
764        case CMD_FIXTRX:
765            mtd_fixtrx(device, offset);
766            break;
767    }
768
769    sync();
770
771    if (boot)
772        do_reboot();
773
774    return 0;
775}
776

Archive Download this file



interactive