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

Archive Download this file



interactive