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)
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
298#ifdef FIS_SUPPORT
299    static struct fis_part new_parts[MAX_ARGS];
300    static struct fis_part old_parts[MAX_ARGS];
301    int n_new = 0, n_old = 0;
302
303    if (fis_layout) {
304        const char *tmp = mtd;
305        char *word, *brkt;
306        int ret;
307
308        memset(&old_parts, 0, sizeof(old_parts));
309        memset(&new_parts, 0, sizeof(new_parts));
310
311        do {
312            next = strchr(tmp, ':');
313            if (!next)
314                next = (char *) tmp + strlen(tmp);
315
316            memcpy(old_parts[n_old].name, tmp, next - tmp);
317
318            n_old++;
319            tmp = next + 1;
320        } while(*next);
321
322        for (word = strtok_r(fis_layout, ",", &brkt);
323             word;
324             word = strtok_r(NULL, ",", &brkt)) {
325
326            tmp = strtok(word, ":");
327            strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1);
328
329            tmp = strtok(NULL, ":");
330            if (!tmp)
331                goto next;
332
333            new_parts[n_new].size = strtoul(tmp, NULL, 0);
334
335            tmp = strtok(NULL, ":");
336            if (!tmp)
337                goto next;
338
339            new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16);
340next:
341            n_new++;
342        }
343        ret = fis_validate(old_parts, n_old, new_parts, n_new);
344        if (ret < 0) {
345            fprintf(stderr, "Failed to validate the new FIS partition table\n");
346            exit(1);
347        }
348        if (ret == 0)
349            fis_layout = NULL;
350    }
351#endif
352
353    if (strchr(mtd, ':')) {
354        str = strdup(mtd);
355        mtd = str;
356    }
357
358    r = 0;
359
360resume:
361    next = strchr(mtd, ':');
362    if (next) {
363        *next = 0;
364        next++;
365    }
366
367    fd = mtd_check_open(mtd);
368    if(fd < 0) {
369        fprintf(stderr, "Could not open mtd device: %s\n", mtd);
370        exit(1);
371    }
372
373    indicate_writing(mtd);
374
375    w = e = 0;
376    for (;;) {
377        /* buffer may contain data already (from trx check or last mtd partition write attempt) */
378        while (buflen < erasesize) {
379            r = read(imagefd, buf + buflen, erasesize - buflen);
380            if (r < 0) {
381                if ((errno == EINTR) || (errno == EAGAIN))
382                    continue;
383                else {
384                    perror("read");
385                    break;
386                }
387            }
388
389            if (r == 0)
390                break;
391
392            buflen += r;
393        }
394
395        if (buflen == 0)
396            break;
397
398        if (skip > 0) {
399            skip -= buflen;
400            buflen = 0;
401            if (skip <= 0)
402                indicate_writing(mtd);
403
404            continue;
405        }
406
407        if (jffs2file) {
408            if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
409                if (!quiet)
410                    fprintf(stderr, "\b\b\b ");
411                if (quiet < 2)
412                    fprintf(stderr, "\nAppending jffs2 data from %s to %s...", jffs2file, mtd);
413                /* got an EOF marker - this is the place to add some jffs2 data */
414                skip = mtd_replace_jffs2(mtd, fd, e, jffs2file);
415
416                /* don't add it again */
417                jffs2file = NULL;
418
419                w += skip;
420                e += skip;
421                skip -= buflen;
422                buflen = 0;
423                offset = 0;
424                continue;
425            }
426            /* no EOF marker, make sure we figure out the last inode number
427             * before appending some data */
428            mtd_parse_jffs2data(buf, jffs2dir);
429        }
430
431        /* need to erase the next block before writing data to it */
432        if(!no_erase)
433        {
434            while (w + buflen > e) {
435                if (!quiet)
436                    fprintf(stderr, "\b\b\b[e]");
437
438
439                if (mtd_erase_block(fd, e) < 0) {
440                    if (next) {
441                        if (w < e) {
442                            write(fd, buf + offset, e - w);
443                            offset = e - w;
444                        }
445                        w = 0;
446                        e = 0;
447                        close(fd);
448                        mtd = next;
449                        fprintf(stderr, "\b\b\b \n");
450                        goto resume;
451                    } else {
452                        fprintf(stderr, "Failed to erase block\n");
453                        exit(1);
454                    }
455                }
456
457                /* erase the chunk */
458                e += erasesize;
459            }
460        }
461
462        if (!quiet)
463            fprintf(stderr, "\b\b\b[w]");
464
465        if ((result = write(fd, buf + offset, buflen)) < buflen) {
466            if (result < 0) {
467                fprintf(stderr, "Error writing image.\n");
468                exit(1);
469            } else {
470                fprintf(stderr, "Insufficient space.\n");
471                exit(1);
472            }
473        }
474        w += buflen;
475
476        buflen = 0;
477        offset = 0;
478    }
479
480    if (!quiet)
481        fprintf(stderr, "\b\b\b\b ");
482
483done:
484    if (quiet < 2)
485        fprintf(stderr, "\n");
486
487#ifdef FIS_SUPPORT
488    if (fis_layout) {
489        if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
490            fprintf(stderr, "Failed to update the FIS partition table\n");
491    }
492#endif
493
494    close(fd);
495    return 0;
496}
497
498static void usage(void)
499{
500    fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n"
501    "The device is in the format of mtdX (eg: mtd4) or its label.\n"
502    "mtd recognizes these commands:\n"
503    " unlock unlock the device\n"
504    " refresh refresh mtd partition\n"
505    " erase erase all data on device\n"
506    " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
507    " jffs2write <file> append <file> to the jffs2 partition on the device\n");
508    if (mtd_fixtrx) {
509        fprintf(stderr,
510    " fixtrx fix the checksum in a trx header on first boot\n");
511    }
512    fprintf(stderr,
513    "Following options are available:\n"
514    " -q quiet mode (once: no [w] on writing,\n"
515    " twice: no status messages)\n"
516    " -n write without first erasing the blocks\n"
517    " -r reboot after successful command\n"
518    " -f force write without trx checks\n"
519    " -e <device> erase <device> before executing the command\n"
520    " -d <name> directory for jffs2write, defaults to \"tmp\"\n"
521    " -j <name> integrate <file> into jffs2 data when writing an image\n");
522    if (mtd_fixtrx) {
523        fprintf(stderr,
524    " -o offset offset of the image header in the partition(for fixtrx)\n");
525    }
526    fprintf(stderr,
527#ifdef FIS_SUPPORT
528    " -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
529    " alter the fis partition table to create new partitions replacing\n"
530    " the partitions provided as argument to the write command\n"
531    " (only valid together with the write command)\n"
532#endif
533    "\n"
534    "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
535    " mtd -r write linux.trx linux\n\n");
536    exit(1);
537}
538
539static void do_reboot(void)
540{
541    fprintf(stderr, "Rebooting ...\n");
542    fflush(stderr);
543
544    /* try regular reboot method first */
545    system("/sbin/reboot");
546    sleep(2);
547
548    /* if we're still alive at this point, force the kernel to reboot */
549    syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
550}
551
552int main (int argc, char **argv)
553{
554    int ch, i, boot, imagefd = 0, force, unlocked;
555    char *erase[MAX_ARGS], *device = NULL;
556    char *fis_layout = NULL;
557    size_t offset = 0;
558    enum {
559        CMD_ERASE,
560        CMD_WRITE,
561        CMD_UNLOCK,
562        CMD_REFRESH,
563        CMD_JFFS2WRITE,
564        CMD_FIXTRX,
565    } cmd = -1;
566
567    erase[0] = NULL;
568    boot = 0;
569    force = 0;
570    buflen = 0;
571    quiet = 0;
572  no_erase = 0;
573
574    while ((ch = getopt(argc, argv,
575#ifdef FIS_SUPPORT
576            "F:"
577#endif
578            "frnqe:d:j:o:")) != -1)
579        switch (ch) {
580            case 'f':
581                force = 1;
582                break;
583            case 'r':
584                boot = 1;
585                break;
586            case 'n':
587                no_erase = 1;
588                break;
589            case 'j':
590                jffs2file = optarg;
591                break;
592            case 'q':
593                quiet++;
594                break;
595            case 'e':
596                i = 0;
597                while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
598                    i++;
599
600                erase[i++] = optarg;
601                erase[i] = NULL;
602                break;
603            case 'd':
604                jffs2dir = optarg;
605                break;
606            case 'o':
607                if (!mtd_fixtrx) {
608                    fprintf(stderr, "-o: is not available on this platform\n");
609                    usage();
610                }
611                errno = 0;
612                offset = strtoul(optarg, 0, 0);
613                if (errno) {
614                    fprintf(stderr, "-o: illegal numeric string\n");
615                    usage();
616                }
617                break;
618#ifdef FIS_SUPPORT
619            case 'F':
620                fis_layout = optarg;
621                break;
622#endif
623            case '?':
624            default:
625                usage();
626        }
627    argc -= optind;
628    argv += optind;
629
630    if (argc < 2)
631        usage();
632
633    if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
634        cmd = CMD_UNLOCK;
635        device = argv[1];
636    } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
637        cmd = CMD_REFRESH;
638        device = argv[1];
639    } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
640        cmd = CMD_ERASE;
641        device = argv[1];
642    } else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) {
643        cmd = CMD_FIXTRX;
644        device = argv[1];
645    } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
646        cmd = CMD_WRITE;
647        device = argv[2];
648
649        if (strcmp(argv[1], "-") == 0) {
650            imagefile = "<stdin>";
651            imagefd = 0;
652        } else {
653            imagefile = argv[1];
654            if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
655                fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
656                exit(1);
657            }
658        }
659
660        if (!mtd_check(device)) {
661            fprintf(stderr, "Can't open device for writing!\n");
662            exit(1);
663        }
664        /* check trx file before erasing or writing anything */
665        if (!image_check(imagefd, device) && !force) {
666            fprintf(stderr, "Image check failed.\n");
667            exit(1);
668        }
669    } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
670        cmd = CMD_JFFS2WRITE;
671        device = argv[2];
672
673        imagefile = argv[1];
674        if (!mtd_check(device)) {
675            fprintf(stderr, "Can't open device for writing!\n");
676            exit(1);
677        }
678    } else {
679        usage();
680    }
681
682    sync();
683
684    i = 0;
685    unlocked = 0;
686    while (erase[i] != NULL) {
687        mtd_unlock(erase[i]);
688        mtd_erase(erase[i]);
689        if (strcmp(erase[i], device) == 0)
690            unlocked = 1;
691        i++;
692    }
693
694    switch (cmd) {
695        case CMD_UNLOCK:
696            if (!unlocked)
697                mtd_unlock(device);
698            break;
699        case CMD_ERASE:
700            if (!unlocked)
701                mtd_unlock(device);
702            mtd_erase(device);
703            break;
704        case CMD_WRITE:
705            if (!unlocked)
706                mtd_unlock(device);
707            mtd_write(imagefd, device, fis_layout);
708            break;
709        case CMD_JFFS2WRITE:
710            if (!unlocked)
711                mtd_unlock(device);
712            mtd_write_jffs2(device, imagefile, jffs2dir);
713            break;
714        case CMD_REFRESH:
715            mtd_refresh(device);
716            break;
717        case CMD_FIXTRX:
718            if (mtd_fixtrx) {
719                mtd_fixtrx(device, offset);
720            }
721            break;
722    }
723
724    sync();
725
726    if (boot)
727        do_reboot();
728
729    return 0;
730}
731

Archive Download this file



interactive