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

Archive Download this file



interactive