18a73a0a38ffe8a5026d6a2269b82f15b3187047
[librecmc/librecmc.git] / 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  *                        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
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  *
21  * $Id$
22  *
23  * The code is based on the linux-mtd examples.
24  */
25
26 #include <limits.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <signal.h>
32 #include <sys/ioctl.h>
33 #include <sys/syscall.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <error.h>
37 #include <time.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40 #include <sys/types.h>
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #include <sys/stat.h>
44 #include <sys/reboot.h>
45 #include <linux/reboot.h>
46 #include "mtd-api.h"
47 #include "mtd.h"
48
49 #ifdef target_brcm47xx
50 #define target_brcm 1
51 #endif
52
53 #define TRX_MAGIC       0x30524448      /* "HDR0" */
54 #define MAX_ARGS 8
55
56 #define DEBUG
57
58 #define JFFS2_DEFAULT_DIR       "" /* directory name without /, empty means root dir */
59
60 #define SYSTYPE_UNKNOWN     0
61 #define SYSTYPE_BROADCOM    1
62 /* to be continued */
63
64 struct trx_header {
65         uint32_t magic;         /* "HDR0" */
66         uint32_t len;           /* Length of file including header */
67         uint32_t crc32;         /* 32-bit CRC from flag_version to end of file */
68         uint32_t flag_version;  /* 0:15 flags, 16:31 version */
69         uint32_t offsets[3];    /* Offsets of partitions from start of header */
70 };
71
72 static char *buf = NULL;
73 static char *imagefile = NULL;
74 static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
75 static int buflen = 0;
76 int quiet;
77 int mtdsize = 0;
78 int erasesize = 0;
79
80 int mtd_open(const char *mtd)
81 {
82         FILE *fp;
83         char dev[PATH_MAX];
84         int i;
85         int ret;
86         int flags = O_RDWR | O_SYNC;
87
88         if ((fp = fopen("/proc/mtd", "r"))) {
89                 while (fgets(dev, sizeof(dev), fp)) {
90                         if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
91                                 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
92                                 if ((ret=open(dev, flags))<0) {
93                                         snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
94                                         ret=open(dev, flags);
95                                 }
96                                 fclose(fp);
97                                 return ret;
98                         }
99                 }
100                 fclose(fp);
101         }
102
103         return open(mtd, flags);
104 }
105
106 int mtd_check_open(const char *mtd)
107 {
108         struct mtd_info_user mtdInfo;
109         int fd;
110
111         fd = mtd_open(mtd);
112         if(fd < 0) {
113                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
114                 return 0;
115         }
116
117         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
118                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
119                 close(fd);
120                 return 0;
121         }
122         mtdsize = mtdInfo.size;
123         erasesize = mtdInfo.erasesize;
124
125         return fd;
126 }
127
128 int mtd_erase_block(int fd, int offset)
129 {
130         struct erase_info_user mtdEraseInfo;
131
132         mtdEraseInfo.start = offset;
133         mtdEraseInfo.length = erasesize;
134         ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
135         if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0) {
136                 fprintf(stderr, "Erasing mtd failed.\n");
137                 exit(1);
138         }
139 }
140
141 int mtd_write_buffer(int fd, const char *buf, int offset, int length)
142 {
143         lseek(fd, offset, SEEK_SET);
144         write(fd, buf, length);
145 }
146
147
148 #ifdef target_brcm
149 static int
150 image_check_brcm(int imagefd, const char *mtd)
151 {
152         struct trx_header *trx = (struct trx_header *) buf;
153         int fd;
154
155         if (strcmp(mtd, "linux") != 0)
156                 return 1;
157         
158         buflen = read(imagefd, buf, 32);
159         if (buflen < 32) {
160                 fprintf(stdout, "Could not get image header, file too small (%ld bytes)\n", buflen);
161                 return 0;
162         }
163
164         if (trx->magic != TRX_MAGIC || trx->len < sizeof(struct trx_header)) {
165                 if (quiet < 2) {
166                         fprintf(stderr, "Bad trx header\n");
167                         fprintf(stderr, "This is not the correct file format; refusing to flash.\n"
168                                         "Please specify the correct file or use -f to force.\n");
169                 }
170                 return 0;
171         }
172
173         /* check if image fits to mtd device */
174         fd = mtd_check_open(mtd);
175         if(fd < 0) {
176                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
177                 exit(1);
178         }
179
180         if(mtdsize < trx->len) {
181                 fprintf(stderr, "Image too big for partition: %s\n", mtd);
182                 close(fd);
183                 return 0;
184         }       
185         
186         close(fd);
187         return 1;
188 }
189 #endif /* target_brcm */
190
191 static int
192 image_check(int imagefd, const char *mtd)
193 {
194         int fd, systype;
195         size_t count;
196         char *c;
197         FILE *f;
198
199 #ifdef target_brcm
200         return image_check_brcm(imagefd, mtd);
201 #endif
202 }
203
204 static int mtd_check(const char *mtd)
205 {
206         int fd;
207
208         fd = mtd_check_open(mtd);
209         if (!fd)
210                 return 0;
211
212         if (!buf)
213                 buf = malloc(erasesize);
214
215         close(fd);
216         return 1;
217 }
218
219 static int
220 mtd_unlock(const char *mtd)
221 {
222         int fd;
223         struct erase_info_user mtdLockInfo;
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         if (quiet < 2) 
232                 fprintf(stderr, "Unlocking %s ...\n", mtd);
233
234         mtdLockInfo.start = 0;
235         mtdLockInfo.length = mtdsize;
236         if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
237                 close(fd);
238                 return 0;
239         }
240                 
241         close(fd);
242         return 0;
243 }
244
245 static int
246 mtd_erase(const char *mtd)
247 {
248         int fd;
249         struct erase_info_user mtdEraseInfo;
250
251         if (quiet < 2)
252                 fprintf(stderr, "Erasing %s ...\n", mtd);
253
254         fd = mtd_check_open(mtd);
255         if(fd <= 0) {
256                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
257                 exit(1);
258         }
259
260         mtdEraseInfo.length = erasesize;
261
262         for (mtdEraseInfo.start = 0;
263                  mtdEraseInfo.start < mtdsize;
264                  mtdEraseInfo.start += erasesize) {
265                 
266                 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
267                 if(ioctl(fd, MEMERASE, &mtdEraseInfo))
268                         fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
269         }               
270
271         close(fd);
272         return 0;
273
274 }
275
276 static int
277 mtd_refresh(const char *mtd)
278 {
279         int fd;
280
281         if (quiet < 2)
282                 fprintf(stderr, "Refreshing mtd partition %s ... ", mtd);
283
284         fd = mtd_check_open(mtd);
285         if(fd <= 0) {
286                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
287                 exit(1);
288         }
289
290         if (ioctl(fd, MTDREFRESH, NULL)) {
291                 fprintf(stderr, "Failed to refresh the MTD device\n");
292                 close(fd);
293                 exit(1);
294         }
295         close(fd);
296
297         if (quiet < 2)
298                 fprintf(stderr, "\n");
299
300         return 0;
301 }
302
303 static int
304 mtd_write(int imagefd, const char *mtd)
305 {
306         int fd, i, result;
307         size_t r, w, e;
308         struct erase_info_user mtdEraseInfo;
309         int ret = 0;
310
311         fd = mtd_check_open(mtd);
312         if(fd < 0) {
313                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
314                 exit(1);
315         }
316                 
317         if (quiet < 2)
318                 fprintf(stderr, "Writing from %s to %s ... ", imagefile, mtd);
319
320         r = w = e = 0;
321         if (!quiet)
322                 fprintf(stderr, " [ ]");
323
324         for (;;) {
325                 /* buffer may contain data already (from trx check) */
326                 r = read(imagefd, buf + buflen, erasesize - buflen);
327                 if (r < 0)
328                         break;
329
330                 buflen += r;
331
332                 if (jffs2file) {
333                         if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF)) == 0) {
334                                 if (!quiet)
335                                         fprintf(stderr, "\b\b\b   ");
336                                 if (quiet < 2)
337                                         fprintf(stderr, "\nAppending jffs2 data to from %s to %s...", jffs2file, mtd);
338                                 /* got an EOF marker - this is the place to add some jffs2 data */
339                                 mtd_replace_jffs2(fd, e, jffs2file);
340                                 goto done;
341                         }
342                         /* no EOF marker, make sure we figure out the last inode number
343                          * before appending some data */
344                         mtd_parse_jffs2data(buf, jffs2dir);
345                 }
346
347                 /* need to erase the next block before writing data to it */
348                 while (w + buflen > e) {
349                         if (!quiet)
350                                 fprintf(stderr, "\b\b\b[e]");
351
352                         mtd_erase_block(fd, e);
353
354                         /* erase the chunk */
355                         e += erasesize;
356                 }
357                 
358                 if (!quiet)
359                         fprintf(stderr, "\b\b\b[w]");
360                 
361                 if ((result = write(fd, buf, buflen)) < buflen) {
362                         if (result < 0) {
363                                 fprintf(stderr, "Error writing image.\n");
364                                 exit(1);
365                         } else {
366                                 fprintf(stderr, "Insufficient space.\n");
367                                 exit(1);
368                         }
369                 }
370                 w += buflen;
371
372                 /* not enough data - eof */
373                 if (buflen < erasesize)
374                         break;
375
376                 buflen = 0;
377         }
378         if (!quiet)
379                 fprintf(stderr, "\b\b\b\b");
380
381 done:
382         if (quiet < 2)
383                 fprintf(stderr, "\n");
384
385         close(fd);
386         return 0;
387 }
388
389 static void usage(void)
390 {
391         fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
392         "The device is in the format of mtdX (eg: mtd4) or its label.\n"
393         "mtd recognizes these commands:\n"
394         "        unlock                  unlock the device\n"
395         "        refresh                 refresh mtd partition\n"
396         "        erase                   erase all data on device\n"
397         "        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
398         "        jffs2write <file>       append <file> to the jffs2 partition on the device\n"
399         "Following options are available:\n"
400         "        -q                      quiet mode (once: no [w] on writing,\n"
401         "                                           twice: no status messages)\n"
402         "        -r                      reboot after successful command\n"
403         "        -f                      force write without trx checks\n"
404         "        -e <device>             erase <device> before executing the command\n"
405         "        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
406         "        -j <name>               integrate <file> into jffs2 data when writing an image\n"
407         "\n"
408         "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
409         "         mtd -r write linux.trx linux\n\n");
410         exit(1);
411 }
412
413 static void do_reboot(void)
414 {
415         fprintf(stderr, "Rebooting ...\n");
416         fflush(stderr);
417
418         /* try regular reboot method first */
419         system("/sbin/reboot");
420         sleep(2);
421
422         /* if we're still alive at this point, force the kernel to reboot */
423         syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
424 }
425
426 int main (int argc, char **argv)
427 {
428         int ch, i, boot, unlock, imagefd, force, unlocked;
429         char *erase[MAX_ARGS], *device;
430         enum {
431                 CMD_ERASE,
432                 CMD_WRITE,
433                 CMD_UNLOCK,
434                 CMD_REFRESH,
435                 CMD_JFFS2WRITE
436         } cmd;
437         
438         erase[0] = NULL;
439         boot = 0;
440         force = 0;
441         buflen = 0;
442         quiet = 0;
443
444         while ((ch = getopt(argc, argv, "frqe:d:j:")) != -1)
445                 switch (ch) {
446                         case 'f':
447                                 force = 1;
448                                 break;
449                         case 'r':
450                                 boot = 1;
451                                 break;
452                         case 'j':
453                                 jffs2file = optarg;
454                                 break;
455                         case 'q':
456                                 quiet++;
457                                 break;
458                         case 'e':
459                                 i = 0;
460                                 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
461                                         i++;
462                                         
463                                 erase[i++] = optarg;
464                                 erase[i] = NULL;
465                                 break;
466                         case 'd':
467                                 jffs2dir = optarg;
468                                 break;
469                         case '?':
470                         default:
471                                 usage();
472                 }
473         argc -= optind;
474         argv += optind;
475         
476         if (argc < 2)
477                 usage();
478
479         if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
480                 cmd = CMD_UNLOCK;
481                 device = argv[1];
482         } else if ((strcmp(argv[0], "refresh") == 0) && (argc == 2)) {
483                 cmd = CMD_REFRESH;
484                 device = argv[1];
485         } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
486                 cmd = CMD_ERASE;
487                 device = argv[1];
488         } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
489                 cmd = CMD_WRITE;
490                 device = argv[2];
491         
492                 if (strcmp(argv[1], "-") == 0) {
493                         imagefile = "<stdin>";
494                         imagefd = 0;
495                 } else {
496                         imagefile = argv[1];
497                         if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
498                                 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
499                                 exit(1);
500                         }
501                 }
502         
503                 if (!mtd_check(device)) {
504                         fprintf(stderr, "Can't open device for writing!\n");
505                         exit(1);
506                 }
507                 /* check trx file before erasing or writing anything */
508                 if (!image_check(imagefd, device) && !force) {
509                         fprintf(stderr, "Image check failed.\n");
510                         exit(1);
511                 }
512         } else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
513                 cmd = CMD_JFFS2WRITE;
514                 device = argv[2];
515         
516                 imagefile = argv[1];
517                 if (!mtd_check(device)) {
518                         fprintf(stderr, "Can't open device for writing!\n");
519                         exit(1);
520                 }
521         } else {
522                 usage();
523         }
524
525         sync();
526         
527         i = 0;
528         unlocked = 0;
529         while (erase[i] != NULL) {
530                 mtd_unlock(erase[i]);
531                 mtd_erase(erase[i]);
532                 if (strcmp(erase[i], device) == 0)
533                         unlocked = 1;
534                 i++;
535         }
536         
537                 
538         switch (cmd) {
539                 case CMD_UNLOCK:
540                         if (!unlocked)
541                                 mtd_unlock(device);
542                         break;
543                 case CMD_ERASE:
544                         if (!unlocked)
545                                 mtd_unlock(device);
546                         mtd_erase(device);
547                         break;
548                 case CMD_WRITE:
549                         if (!unlocked)
550                                 mtd_unlock(device);
551                         mtd_write(imagefd, device);
552                         break;
553                 case CMD_JFFS2WRITE:
554                         if (!unlocked)
555                                 mtd_unlock(device);
556                         mtd_write_jffs2(device, imagefile, jffs2dir);
557                         break;
558                 case CMD_REFRESH:
559                         mtd_refresh(device);
560                         break;
561         }
562
563         sync();
564         
565         if (boot)
566                 do_reboot();
567
568         return 0;
569 }