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/mtd-user.h>
45#include "fis.h"
46#include "mtd.h"
47
48#ifndef MTDREFRESH
49#define MTDREFRESH _IO('M', 50)
50#endif
51
52#define MAX_ARGS 8
53#define JFFS2_DEFAULT_DIR "" /* directory name without /, empty means root dir */
54
55static char *buf = NULL;
56static char *imagefile = NULL;
57static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
58static int buflen = 0;
59int quiet;
60int no_erase;
61int mtdsize = 0;
62int erasesize = 0;
63
64int mtd_open(const char *mtd, bool block)
65{
66    FILE *fp;
67    char dev[PATH_MAX];
68    int i;
69    int ret;
70    int flags = O_RDWR | O_SYNC;
71
72    if ((fp = fopen("/proc/mtd", "r"))) {
73        while (fgets(dev, sizeof(dev), fp)) {
74            if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
75                snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
76                if ((ret=open(dev, flags))<0) {
77                    snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
78                    ret=open(dev, flags);
79                }
80                fclose(fp);
81                return ret;
82            }
83        }
84        fclose(fp);
85    }
86
87    return open(mtd, flags);
88}
89
90int mtd_check_open(const char *mtd)
91{
92    struct mtd_info_user mtdInfo;
93    int fd;
94
95    fd = mtd_open(mtd, false);
96    if(fd < 0) {
97        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
98        return -1;
99    }
100
101    if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
102        fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
103        close(fd);
104        return -1;
105    }
106    mtdsize = mtdInfo.size;
107    erasesize = mtdInfo.erasesize;
108
109    return fd;
110}
111
112int mtd_erase_block(int fd, int offset)
113{
114    struct erase_info_user mtdEraseInfo;
115
116    mtdEraseInfo.start = offset;
117    mtdEraseInfo.length = erasesize;
118    ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
119    if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0)
120        return -1;
121
122    return 0;
123}
124
125int mtd_write_buffer(int fd, const char *buf, int offset, int length)
126{
127    lseek(fd, offset, SEEK_SET);
128    write(fd, buf, length);
129    return 0;
130}
131
132
133static int
134image_check(int imagefd, const char *mtd)
135{
136    int ret = 1;
137    if (trx_check) {
138      ret = trx_check(imagefd, mtd, buf, &buflen);
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    fprintf(stderr,
524    "Following options are available:\n"
525    " -q quiet mode (once: no [w] on writing,\n"
526    " twice: no status messages)\n"
527    " -n write without first erasing the blocks\n"
528    " -r reboot after successful command\n"
529    " -f force write without trx checks\n"
530    " -e <device> erase <device> before executing the command\n"
531    " -d <name> directory for jffs2write, defaults to \"tmp\"\n"
532    " -j <name> integrate <file> into jffs2 data when writing an image\n"
533    " -p write beginning at partition offset\n");
534    if (mtd_fixtrx) {
535        fprintf(stderr,
536    " -o offset offset of the image header in the partition(for fixtrx)\n");
537    }
538    fprintf(stderr,
539#ifdef FIS_SUPPORT
540    " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
541    " alter the fis partition table to create new partitions replacing\n"
542    " the partitions provided as argument to the write command\n"
543    " (only valid together with the write command)\n"
544#endif
545    "\n"
546    "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
547    " mtd -r write linux.trx linux\n\n");
548    exit(1);
549}
550
551static void do_reboot(void)
552{
553    fprintf(stderr, "Rebooting ...\n");
554    fflush(stderr);
555
556    /* try regular reboot method first */
557    system("/sbin/reboot");
558    sleep(2);
559
560    /* if we're still alive at this point, force the kernel to reboot */
561    syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
562}
563
564int main (int argc, char **argv)
565{
566    int ch, i, boot, imagefd = 0, force, unlocked;
567    char *erase[MAX_ARGS], *device = NULL;
568    char *fis_layout = NULL;
569    size_t offset = 0, part_offset = 0;
570    enum {
571        CMD_ERASE,
572        CMD_WRITE,
573        CMD_UNLOCK,
574        CMD_REFRESH,
575        CMD_JFFS2WRITE,
576        CMD_FIXTRX,
577    } cmd = -1;
578
579    erase[0] = NULL;
580    boot = 0;
581    force = 0;
582    buflen = 0;
583    quiet = 0;
584    no_erase = 0;
585
586    while ((ch = getopt(argc, argv,
587#ifdef FIS_SUPPORT
588            "F:"
589#endif
590            "frnqe:d:j:p:o:")) != -1)
591        switch (ch) {
592            case 'f':
593                force = 1;
594                break;
595            case 'r':
596                boot = 1;
597                break;
598            case 'n':
599                no_erase = 1;
600                break;
601            case 'j':
602                jffs2file = optarg;
603                break;
604            case 'q':
605                quiet++;
606                break;
607            case 'e':
608                i = 0;
609                while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
610                    i++;
611
612                erase[i++] = optarg;
613                erase[i] = NULL;
614                break;
615            case 'd':
616                jffs2dir = optarg;
617                break;
618            case 'p':
619                errno = 0;
620                part_offset = strtoul(optarg, 0, 0);
621                if (errno) {
622                    fprintf(stderr, "-p: illegal numeric string\n");
623                    usage();
624                }
625                break;
626            case 'o':
627                if (!mtd_fixtrx) {
628                    fprintf(stderr, "-o: is not available on this platform\n");
629                    usage();
630                }
631                errno = 0;
632                offset = strtoul(optarg, 0, 0);
633                if (errno) {
634                    fprintf(stderr, "-o: illegal numeric string\n");
635                    usage();
636                }
637                break;
638#ifdef FIS_SUPPORT
639            case 'F':
640                fis_layout = optarg;
641                break;
642#endif
643            case '?':
644            default:
645                usage();
646        }
647    argc -= optind;
648    argv += optind;
649
650    if (argc < 2)
651        usage();
652
653    if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
654        cmd = CMD_UNLOCK;
655        device = argv[1];
656    } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
657        cmd = CMD_REFRESH;
658        device = argv[1];
659    } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
660        cmd = CMD_ERASE;
661        device = argv[1];
662    } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) {
663        cmd = CMD_FIXTRX;
664        device = argv[1];
665    } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
666        cmd = CMD_WRITE;
667        device = argv[2];
668
669        if (strcmp(argv[1], "-") == 0) {
670            imagefile = "<stdin>";
671            imagefd = 0;
672        } else {
673            imagefile = argv[1];
674            if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
675                fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
676                exit(1);
677            }
678        }
679
680        if (!mtd_check(device)) {
681            fprintf(stderr, "Can't open device for writing!\n");
682            exit(1);
683        }
684        /* check trx file before erasing or writing anything */
685        if (!image_check(imagefd, device) && !force) {
686            fprintf(stderr, "Image check failed.\n");
687            exit(1);
688        }
689    } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
690        cmd = CMD_JFFS2WRITE;
691        device = argv[2];
692
693        imagefile = argv[1];
694        if (!mtd_check(device)) {
695            fprintf(stderr, "Can't open device for writing!\n");
696            exit(1);
697        }
698    } else {
699        usage();
700    }
701
702    sync();
703
704    i = 0;
705    unlocked = 0;
706    while (erase[i] != NULL) {
707        mtd_unlock(erase[i]);
708        mtd_erase(erase[i]);
709        if (strcmp(erase[i], device) == 0)
710            unlocked = 1;
711        i++;
712    }
713
714    switch (cmd) {
715        case CMD_UNLOCK:
716            if (!unlocked)
717                mtd_unlock(device);
718            break;
719        case CMD_ERASE:
720            if (!unlocked)
721                mtd_unlock(device);
722            mtd_erase(device);
723            break;
724        case CMD_WRITE:
725            if (!unlocked)
726                mtd_unlock(device);
727            mtd_write(imagefd, device, fis_layout, part_offset);
728            break;
729        case CMD_JFFS2WRITE:
730            if (!unlocked)
731                mtd_unlock(device);
732            mtd_write_jffs2(device, imagefile, jffs2dir);
733            break;
734        case CMD_REFRESH:
735            mtd_refresh(device);
736            break;
737        case CMD_FIXTRX:
738            if (mtd_fixtrx) {
739                mtd_fixtrx(device, offset);
740            }
741            break;
742    }
743
744    sync();
745
746    if (boot)
747        do_reboot();
748
749    return 0;
750}
751

Archive Download this file



interactive