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                /* don't add it again */
413                jffs2file = NULL;
414
415                w += skip;
416                e += skip;
417                skip -= buflen;
418                buflen = 0;
419                offset = 0;
420                continue;
421            }
422            /* no EOF marker, make sure we figure out the last inode number
423             * before appending some data */
424            mtd_parse_jffs2data(buf, jffs2dir);
425        }
426
427        /* need to erase the next block before writing data to it */
428        if(!no_erase)
429        {
430            while (w + buflen > e) {
431                if (!quiet)
432                    fprintf(stderr, "\b\b\b[e]");
433
434
435                if (mtd_erase_block(fd, e) < 0) {
436                    if (next) {
437                        if (w < e) {
438                            write(fd, buf + offset, e - w);
439                            offset = e - w;
440                        }
441                        w = 0;
442                        e = 0;
443                        close(fd);
444                        mtd = next;
445                        fprintf(stderr, "\b\b\b \n");
446                        goto resume;
447                    } else {
448                        fprintf(stderr, "Failed to erase block\n");
449                        exit(1);
450                    }
451                }
452
453                /* erase the chunk */
454                e += erasesize;
455            }
456        }
457
458        if (!quiet)
459            fprintf(stderr, "\b\b\b[w]");
460
461        if ((result = write(fd, buf + offset, buflen)) < buflen) {
462            if (result < 0) {
463                fprintf(stderr, "Error writing image.\n");
464                exit(1);
465            } else {
466                fprintf(stderr, "Insufficient space.\n");
467                exit(1);
468            }
469        }
470        w += buflen;
471
472        buflen = 0;
473        offset = 0;
474    }
475
476    if (!quiet)
477        fprintf(stderr, "\b\b\b\b ");
478
479done:
480    if (quiet < 2)
481        fprintf(stderr, "\n");
482
483#ifdef FIS_SUPPORT
484    if (fis_layout) {
485        if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
486            fprintf(stderr, "Failed to update the FIS partition table\n");
487    }
488#endif
489
490    close(fd);
491    return 0;
492}
493
494static void usage(void)
495{
496    fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n"
497    "The device is in the format of mtdX (eg: mtd4) or its label.\n"
498    "mtd recognizes these commands:\n"
499    " unlock unlock the device\n"
500    " refresh refresh mtd partition\n"
501    " erase erase all data on device\n"
502    " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
503    " jffs2write <file> append <file> to the jffs2 partition on the device\n");
504    if (mtd_fixtrx) {
505        fprintf(stderr,
506    " fixtrx fix the checksum in a trx header on first boot\n");
507    }
508    fprintf(stderr,
509    "Following options are available:\n"
510    " -q quiet mode (once: no [w] on writing,\n"
511    " twice: no status messages)\n"
512    " -n write without first erasing the blocks\n"
513    " -r reboot after successful command\n"
514    " -f force write without trx checks\n"
515    " -e <device> erase <device> before executing the command\n"
516    " -d <name> directory for jffs2write, defaults to \"tmp\"\n"
517    " -j <name> integrate <file> into jffs2 data when writing an image\n");
518    if (mtd_fixtrx) {
519        fprintf(stderr,
520    " -o offset offset of the image header in the partition(for fixtrx)\n");
521    }
522    fprintf(stderr,
523#ifdef FIS_SUPPORT
524    " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
525    " alter the fis partition table to create new partitions replacing\n"
526    " the partitions provided as argument to the write command\n"
527    " (only valid together with the write command)\n"
528#endif
529    "\n"
530    "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
531    " mtd -r write linux.trx linux\n\n");
532    exit(1);
533}
534
535static void do_reboot(void)
536{
537    fprintf(stderr, "Rebooting ...\n");
538    fflush(stderr);
539
540    /* try regular reboot method first */
541    system("/sbin/reboot");
542    sleep(2);
543
544    /* if we're still alive at this point, force the kernel to reboot */
545    syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
546}
547
548int main (int argc, char **argv)
549{
550    int ch, i, boot, imagefd = 0, force, unlocked;
551    char *erase[MAX_ARGS], *device = NULL;
552    char *fis_layout = NULL;
553    size_t offset = 0;
554    enum {
555        CMD_ERASE,
556        CMD_WRITE,
557        CMD_UNLOCK,
558        CMD_REFRESH,
559        CMD_JFFS2WRITE,
560        CMD_FIXTRX,
561    } cmd = -1;
562
563    erase[0] = NULL;
564    boot = 0;
565    force = 0;
566    buflen = 0;
567    quiet = 0;
568  no_erase = 0;
569
570    while ((ch = getopt(argc, argv,
571#ifdef FIS_SUPPORT
572            "F:"
573#endif
574            "frnqe:d:j:o:")) != -1)
575        switch (ch) {
576            case 'f':
577                force = 1;
578                break;
579            case 'r':
580                boot = 1;
581                break;
582            case 'n':
583                no_erase = 1;
584                break;
585            case 'j':
586                jffs2file = optarg;
587                break;
588            case 'q':
589                quiet++;
590                break;
591            case 'e':
592                i = 0;
593                while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
594                    i++;
595
596                erase[i++] = optarg;
597                erase[i] = NULL;
598                break;
599            case 'd':
600                jffs2dir = optarg;
601                break;
602            case 'o':
603                if (!mtd_fixtrx) {
604                    fprintf(stderr, "-o: is not available on this platform\n");
605                    usage();
606                }
607                errno = 0;
608                offset = strtoul(optarg, 0, 0);
609                if (errno) {
610                    fprintf(stderr, "-o: illegal numeric string\n");
611                    usage();
612                }
613                break;
614#ifdef FIS_SUPPORT
615            case 'F':
616                fis_layout = optarg;
617                break;
618#endif
619            case '?':
620            default:
621                usage();
622        }
623    argc -= optind;
624    argv += optind;
625
626    if (argc < 2)
627        usage();
628
629    if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
630        cmd = CMD_UNLOCK;
631        device = argv[1];
632    } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
633        cmd = CMD_REFRESH;
634        device = argv[1];
635    } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
636        cmd = CMD_ERASE;
637        device = argv[1];
638    } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) {
639        cmd = CMD_FIXTRX;
640        device = argv[1];
641    } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
642        cmd = CMD_WRITE;
643        device = argv[2];
644
645        if (strcmp(argv[1], "-") == 0) {
646            imagefile = "<stdin>";
647            imagefd = 0;
648        } else {
649            imagefile = argv[1];
650            if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
651                fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
652                exit(1);
653            }
654        }
655
656        if (!mtd_check(device)) {
657            fprintf(stderr, "Can't open device for writing!\n");
658            exit(1);
659        }
660        /* check trx file before erasing or writing anything */
661        if (!image_check(imagefd, device) && !force) {
662            fprintf(stderr, "Image check failed.\n");
663            exit(1);
664        }
665    } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
666        cmd = CMD_JFFS2WRITE;
667        device = argv[2];
668
669        imagefile = argv[1];
670        if (!mtd_check(device)) {
671            fprintf(stderr, "Can't open device for writing!\n");
672            exit(1);
673        }
674    } else {
675        usage();
676    }
677
678    sync();
679
680    i = 0;
681    unlocked = 0;
682    while (erase[i] != NULL) {
683        mtd_unlock(erase[i]);
684        mtd_erase(erase[i]);
685        if (strcmp(erase[i], device) == 0)
686            unlocked = 1;
687        i++;
688    }
689
690    switch (cmd) {
691        case CMD_UNLOCK:
692            if (!unlocked)
693                mtd_unlock(device);
694            break;
695        case CMD_ERASE:
696            if (!unlocked)
697                mtd_unlock(device);
698            mtd_erase(device);
699            break;
700        case CMD_WRITE:
701            if (!unlocked)
702                mtd_unlock(device);
703            mtd_write(imagefd, device, fis_layout);
704            break;
705        case CMD_JFFS2WRITE:
706            if (!unlocked)
707                mtd_unlock(device);
708            mtd_write_jffs2(device, imagefile, jffs2dir);
709            break;
710        case CMD_REFRESH:
711            mtd_refresh(device);
712            break;
713        case CMD_FIXTRX:
714            if (mtd_fixtrx) {
715                mtd_fixtrx(device, offset);
716            }
717            break;
718    }
719
720    sync();
721
722    if (boot)
723        do_reboot();
724
725    return 0;
726}
727

Archive Download this file



interactive