source: freewrt/package/mtd/mtd.c@ ca629a6

freewrt_1_0 freewrt_2_0
Last change on this file since ca629a6 was 2a24c7cd, checked in by Waldemar Brodkorb <wbx@…>, 19 years ago

sync with trunk

git-svn-id: svn://www.freewrt.org/branches/freewrt_1_0@2411 afb5a338-a214-0410-bd46-81f09a774fd1

  • Property mode set to 100644
File size: 12.0 KB
Line 
1/*
2 * mtd - simple memory technology device manipulation tool
3 *
4 * Copyright (c) 2006, 2007 Thorsten Glaser <tg@freewrt.org>
5 * Copyright (C) 2005 Waldemar Brodkorb <wbx@freewrt.org>,
6 * Felix Fietkau <nbd@openwrt.org>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 *
22 * The code is based on the linux-mtd examples.
23 */
24
25#include <sys/param.h>
26#include <sys/types.h>
27#include <sys/ioctl.h>
28#include <sys/mount.h>
29#include <sys/stat.h>
30#include <sys/reboot.h>
31#include <sys/ioctl.h>
32#include <sys/syscall.h>
33#include <limits.h>
34#include <unistd.h>
35#include <stdbool.h>
36#include <stdlib.h>
37#include <stdio.h>
38#include <stdint.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <error.h>
42#include <err.h>
43#include <time.h>
44#include <string.h>
45
46#include <linux/mtd/mtd.h>
47#include <linux/reboot.h>
48
49#define TRX_MAGIC 0x30524448 /* "HDR0" */
50#define BUFSIZE (16 * 1024)
51#define MAX_ARGS 8
52
53#define DEBUG
54
55#define SYSTYPE_UNKNOWN 0
56#define SYSTYPE_BROADCOM 1
57/* to be continued */
58
59struct trx_header {
60 uint32_t magic; /* "HDR0" */
61 uint32_t len; /* Length of file including header */
62 uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
63 uint32_t flag_version; /* 0:15 flags, 16:31 version */
64 uint32_t offsets[3]; /* Offsets of partitions from start of header */
65};
66
67int image_check_bcom(int, const char *);
68int image_check(int, const char *);
69int mtd_check(char *);
70int mtd_unlock(const char *);
71int mtd_open(const char *, int);
72int mtd_erase(const char *);
73int mtd_write(int, const char *, int, bool);
74void usage(void) __attribute__((noreturn));
75
76typedef char char_aligned_32bit __attribute__((aligned (4), may_alias));
77
78char_aligned_32bit buf[BUFSIZE];
79int buflen;
80
81int
82image_check_bcom(int imagefd, const char *mtd)
83{
84 struct trx_header *trx = (struct trx_header *) buf;
85 struct mtd_info_user mtdInfo;
86 int fd;
87
88 buflen = read(imagefd, buf, 32);
89 if (buflen < 32) {
90 fprintf(stdout, "Could not get image header, file too small (%d bytes)\n", buflen);
91 return 0;
92 }
93
94 switch(trx->magic) {
95 case 0x47343557: /* W54G */
96 case 0x53343557: /* W54S */
97 case 0x73343557: /* W54s */
98 case 0x46343557: /* W54F */
99 case 0x55343557: /* W54U */
100 /* ignore the first 32 bytes */
101 buflen = read(imagefd, buf, sizeof(struct trx_header));
102 break;
103 case 0x464c457f: /* ELF Netgear WGT634u */
104 fprintf(stderr, "found ELF header\n");
105 if (lseek(imagefd, 128*1024, SEEK_SET) == -1) {
106 if (errno != ESPIPE)
107 err(1, "lseek");
108 /*
109 * the lseek failed because stdin is a pipe,
110 * so fake it; we know we've already read
111 * 32 bytes in the above call, so just read
112 * in (128 * 1024 - 32) bytes now, modulo
113 * sizeof (buf). why is this 128 KiB btw,
114 * wouldn't it be better to parse the ELF
115 * header?
116 */
117 buflen = 128 * 1024 - 32;
118 while (buflen) {
119 ssize_t n;
120
121 n = read(imagefd, buf,
122 MIN((size_t)buflen, sizeof (buf)));
123 if (n < 0)
124 err(1, "read");
125 buflen -= n;
126 }
127 }
128 buflen = read(imagefd, buf, sizeof(struct trx_header));
129 break;
130 }
131
132 if (trx->magic != TRX_MAGIC || trx->len < sizeof(struct trx_header)) {
133 fprintf(stderr, "Bad trx header\n");
134 fprintf(stderr, "If this is a firmware in bin format, like some of the\n"
135 "original firmware files are, use following command to convert to trx:\n"
136 "dd if=firmware.bin of=firmware.trx bs=32 skip=1\n");
137 return 0;
138 }
139
140 /* check if image fits to mtd device */
141 fd = mtd_open(mtd, O_RDWR | O_SYNC);
142 if(fd < 0) {
143 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
144 exit(1);
145 }
146
147 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
148 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
149 exit(1);
150 }
151
152 if(mtdInfo.size < trx->len) {
153 fprintf(stderr, "Image too big for partition: %s\n", mtd);
154 close(fd);
155 return 0;
156 }
157
158 close(fd);
159 return 1;
160}
161
162int
163image_check(int imagefd, const char *mtd)
164{
165 int systype;
166 char *c;
167 FILE *f;
168
169 systype = SYSTYPE_UNKNOWN;
170 f = fopen("/proc/cpuinfo", "r");
171 while (!feof(f) && (fgets((char *)buf, BUFSIZE - 1, f) != NULL)) {
172 if ((memcmp(buf, "system type", 11) == 0) &&
173 (c = strchr((char *)buf, ':'))) {
174 c += 2;
175 if (strncmp(c, "Broadcom BCM947XX", 17) == 0)
176 systype = SYSTYPE_BROADCOM;
177 }
178 }
179 fclose(f);
180
181 switch(systype) {
182 case SYSTYPE_BROADCOM:
183 return image_check_bcom(imagefd, mtd);
184 default:
185 return 1;
186 }
187}
188
189int
190mtd_check(char *mtd)
191{
192 struct mtd_info_user mtdInfo;
193 int fd;
194
195 fd = mtd_open(mtd, O_RDWR | O_SYNC);
196 if(fd < 0) {
197 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
198 return 0;
199 }
200
201 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
202 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
203 close(fd);
204 return 0;
205 }
206
207 close(fd);
208 return 1;
209}
210
211int
212mtd_unlock(const char *mtd)
213{
214 int fd;
215 struct mtd_info_user mtdInfo;
216 struct erase_info_user mtdLockInfo;
217
218 fd = mtd_open(mtd, O_RDWR | O_SYNC);
219 if(fd < 0) {
220 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
221 exit(1);
222 }
223
224 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
225 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
226 close(fd);
227 exit(1);
228 }
229
230 mtdLockInfo.start = 0;
231 mtdLockInfo.length = mtdInfo.size;
232 if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
233 close(fd);
234 return 0;
235 }
236
237 close(fd);
238 return 0;
239}
240
241int
242mtd_open(const char *mtd, int flags)
243{
244 FILE *fp;
245 char dev[PATH_MAX];
246 int i;
247
248 if ((fp = fopen("/proc/mtd", "r"))) {
249 while (fgets(dev, sizeof(dev), fp)) {
250 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
251 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
252 fclose(fp);
253 return open(dev, flags);
254 }
255 }
256 fclose(fp);
257 }
258
259 return open(mtd, flags);
260}
261
262int
263mtd_erase(const char *mtd)
264{
265 int fd;
266 struct mtd_info_user mtdInfo;
267 struct erase_info_user mtdEraseInfo;
268
269 fd = mtd_open(mtd, O_RDWR | O_SYNC);
270 if(fd < 0) {
271 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
272 exit(1);
273 }
274
275 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
276 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
277 close(fd);
278 exit(1);
279 }
280
281 mtdEraseInfo.length = mtdInfo.erasesize;
282
283 for (mtdEraseInfo.start = 0;
284 mtdEraseInfo.start < mtdInfo.size;
285 mtdEraseInfo.start += mtdInfo.erasesize) {
286
287 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
288 if(ioctl(fd, MEMERASE, &mtdEraseInfo)) {
289 fprintf(stderr, "Could not erase MTD device: %s\n", mtd);
290 close(fd);
291 exit(1);
292 }
293 }
294
295 close(fd);
296 return 0;
297
298}
299
300int
301mtd_write(int imagefd, const char *mtd, int quiet, bool do_erase)
302{
303 int fd, result;
304 size_t r, w, e;
305 struct mtd_info_user mtdInfo;
306 struct erase_info_user mtdEraseInfo;
307
308 fd = mtd_open(mtd, O_RDWR | O_SYNC);
309 if(fd < 0) {
310 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
311 exit(1);
312 }
313
314 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
315 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
316 close(fd);
317 exit(1);
318 }
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 = buflen;
327 r += read(imagefd, buf + buflen, BUFSIZE - buflen);
328 w += r;
329
330 /* EOF */
331 if (r <= 0) break;
332
333 /* need to erase the next block before writing data to it */
334 while (do_erase && w > e) {
335 mtdEraseInfo.start = e;
336 mtdEraseInfo.length = mtdInfo.erasesize;
337
338 if (!quiet)
339 fprintf(stderr, "\b\b\b[e]");
340 /* erase the chunk */
341 if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
342 fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
343 exit(1);
344 }
345 e += mtdInfo.erasesize;
346 }
347
348 if (!quiet)
349 fprintf(stderr, "\b\b\b[w]");
350
351 if ((result = write(fd, buf, r)) < (ssize_t)r) {
352 if (result < 0) {
353 fprintf(stderr, "Error writing image.\n");
354 exit(1);
355 } else {
356 fprintf(stderr, "Insufficient space.\n");
357 exit(1);
358 }
359 }
360
361 buflen = 0;
362 }
363 if (!quiet)
364 fprintf(stderr, "\b\b\b\b");
365
366 close(fd);
367 return 0;
368}
369
370void
371usage(void)
372{
373 fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
374 "The device is in the format of mtdX (eg: mtd4) or its label.\n"
375 "mtd recognises these commands:\n"
376 " unlock unlock the device\n"
377 " erase erase all data on device\n"
378 " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
379 "Following options are available:\n"
380 " -q quiet mode (once: no [w] on writing,\n"
381 " twice: no status messages)\n"
382 " -r reboot after successful command\n"
383 " -f force write without trx checks\n"
384 " -e <device> erase <device> before executing the command\n\n"
385 "Example: To write linux.trx to mtd1 labeled as linux and reboot afterwards\n"
386 " mtd -r write linux.trx linux\n\n");
387 exit(1);
388}
389
390int
391main(int argc, char **argv)
392{
393 int ch, i, boot, imagefd = -1, force, quiet, unlocked;
394 char *erase[MAX_ARGS], *device;
395 const char *imagefile = NULL;
396 enum {
397 CMD_ERASE,
398 CMD_WRITE,
399 CMD_UNLOCK
400 } cmd;
401
402 erase[0] = NULL;
403 boot = 0;
404 force = 0;
405 buflen = 0;
406 quiet = 0;
407
408 while ((ch = getopt(argc, argv, "Ffrqe:")) != -1)
409 switch (ch) {
410 case 'F':
411 quiet = 1;
412 /* FALLTHROUGH */
413 case 'f':
414 force = 1;
415 break;
416 case 'r':
417 boot = 1;
418 break;
419 case 'q':
420 quiet++;
421 break;
422 case 'e':
423 i = 0;
424 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
425 i++;
426
427 erase[i++] = optarg;
428 erase[i] = NULL;
429 break;
430
431 case '?':
432 default:
433 usage();
434 }
435 argc -= optind;
436 argv += optind;
437
438 if (argc < 2)
439 usage();
440
441 if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
442 cmd = CMD_UNLOCK;
443 device = argv[1];
444 } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
445 cmd = CMD_ERASE;
446 device = argv[1];
447 } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
448 cmd = CMD_WRITE;
449 device = argv[2];
450
451 if (strcmp(argv[1], "-") == 0) {
452 imagefile = "<stdin>";
453 imagefd = 0;
454 } else {
455 imagefile = argv[1];
456 if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
457 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
458 exit(1);
459 }
460 }
461
462 /* check trx file before erasing or writing anything */
463 if (!(force && quiet)) if (!image_check(imagefd, device)) {
464 if ((quiet < 2) || !force)
465 fprintf(stderr, "TRX check failed!\n");
466 if (!force)
467 exit(1);
468 }
469 if (!mtd_check(device)) {
470 fprintf(stderr, "Can't open device for writing!\n");
471 exit(1);
472 }
473 } else {
474 usage();
475 }
476
477 sync();
478
479 i = 0;
480 unlocked = 0;
481 while (erase[i] != NULL) {
482 if (quiet < 2)
483 fprintf(stderr, "Unlocking %s ...\n", erase[i]);
484 mtd_unlock(erase[i]);
485 if (quiet < 2)
486 fprintf(stderr, "Erasing %s ...\n", erase[i]);
487 mtd_erase(erase[i]);
488 if (strcmp(erase[i], device) == 0)
489 /* this means that <device> is unlocked and erased */
490 unlocked = 1;
491 i++;
492 }
493
494 if (!unlocked) {
495 if (quiet < 2)
496 fprintf(stderr, "Unlocking %s ...\n", device);
497 mtd_unlock(device);
498 }
499
500 switch (cmd) {
501 case CMD_UNLOCK:
502 break;
503 case CMD_ERASE:
504 if (unlocked) {
505 fprintf(stderr, "Already erased: %s\n", device);
506 break;
507 }
508 if (quiet < 2)
509 fprintf(stderr, "Erasing %s ...\n", device);
510 mtd_erase(device);
511 break;
512 case CMD_WRITE:
513 if (quiet < 2)
514 fprintf(stderr, "Writing from %s to %s ... ", imagefile, device);
515 mtd_write(imagefd, device, quiet, (unlocked == 0));
516 if (quiet < 2)
517 fprintf(stderr, "\n");
518 break;
519 }
520
521 sync();
522
523 if (boot) {
524 if (quiet < 2)
525 fprintf(stderr, "\nRebooting ... ");
526 fflush(stdout);
527 fflush(stderr);
528 syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
529 }
530 return 0;
531}
Note: See TracBrowser for help on using the repository browser.