7d1e36010c5734af7265e385bbd05b980c3e7613
[openwrt/openwrt.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 <sys/ioctl.h>
32 #include <sys/syscall.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <error.h>
36 #include <time.h>
37 #include <string.h>
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #include <sys/stat.h>
43 #include <sys/reboot.h>
44 #include <linux/reboot.h>
45
46 #include "mtd.h"
47
48 #define TRX_MAGIC 0x30524448 /* "HDR0" */
49 #define BUFSIZE (16 * 1024)
50 #define MAX_ARGS 8
51
52 #define DEBUG
53
54 #define SYSTYPE_UNKNOWN 0
55 #define SYSTYPE_BROADCOM 1
56 /* to be continued */
57
58 struct trx_header {
59 uint32_t magic; /* "HDR0" */
60 uint32_t len; /* Length of file including header */
61 uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
62 uint32_t flag_version; /* 0:15 flags, 16:31 version */
63 uint32_t offsets[3]; /* Offsets of partitions from start of header */
64 };
65
66 char buf[BUFSIZE];
67 int buflen;
68 int quiet;
69
70 #ifdef target_brcm
71 int
72 image_check_brcm(int imagefd, const char *mtd)
73 {
74 struct trx_header *trx = (struct trx_header *) buf;
75 struct mtd_info_user mtdInfo;
76 int fd;
77
78 if (strcmp(mtd, "linux") != 0)
79 return 1;
80
81 buflen = read(imagefd, buf, 32);
82 if (buflen < 32) {
83 fprintf(stdout, "Could not get image header, file too small (%ld bytes)\n", buflen);
84 return 0;
85 }
86
87 switch(trx->magic) {
88 case 0x47343557: /* W54G */
89 case 0x53343557: /* W54S */
90 case 0x73343557: /* W54s */
91 case 0x46343557: /* W54F */
92 case 0x55343557: /* W54U */
93 /* ignore the first 32 bytes */
94 buflen = read(imagefd, buf, sizeof(struct trx_header));
95 break;
96 }
97
98 if (trx->magic != TRX_MAGIC || trx->len < sizeof(struct trx_header)) {
99 if (quiet < 2) {
100 fprintf(stderr, "Bad trx header\n");
101 fprintf(stderr, "If this is a firmware in bin format, like some of the\n"
102 "original firmware files are, you need to convert it to trx.\n");
103 }
104 return 0;
105 }
106
107 /* check if image fits to mtd device */
108 fd = mtd_open(mtd, O_RDWR | O_SYNC);
109 if(fd < 0) {
110 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
111 exit(1);
112 }
113
114 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
115 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
116 exit(1);
117 }
118
119 if(mtdInfo.size < trx->len) {
120 fprintf(stderr, "Image too big for partition: %s\n", mtd);
121 close(fd);
122 return 0;
123 }
124
125 close(fd);
126 return 1;
127 }
128 #endif /* target_brcm */
129
130 int
131 image_check(int imagefd, const char *mtd)
132 {
133 int fd, systype;
134 size_t count;
135 char *c;
136 FILE *f;
137
138 #ifdef target_brcm
139 return image_check_brcm(imagefd, mtd);
140 #endif
141 }
142
143 int mtd_check(char *mtd)
144 {
145 struct mtd_info_user mtdInfo;
146 int fd;
147
148 fd = mtd_open(mtd, O_RDWR | O_SYNC);
149 if(fd < 0) {
150 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
151 return 0;
152 }
153
154 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
155 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
156 close(fd);
157 return 0;
158 }
159
160 close(fd);
161 return 1;
162 }
163
164 int
165 mtd_unlock(const char *mtd)
166 {
167 int fd;
168 struct mtd_info_user mtdInfo;
169 struct erase_info_user mtdLockInfo;
170
171 fd = mtd_open(mtd, O_RDWR | O_SYNC);
172 if(fd < 0) {
173 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
174 exit(1);
175 }
176
177 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
178 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
179 close(fd);
180 exit(1);
181 }
182
183 mtdLockInfo.start = 0;
184 mtdLockInfo.length = mtdInfo.size;
185 if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
186 close(fd);
187 return 0;
188 }
189
190 close(fd);
191 return 0;
192 }
193
194 int
195 mtd_open(const char *mtd, int flags)
196 {
197 FILE *fp;
198 char dev[PATH_MAX];
199 int i;
200
201 if ((fp = fopen("/proc/mtd", "r"))) {
202 while (fgets(dev, sizeof(dev), fp)) {
203 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
204 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
205 fclose(fp);
206 return open(dev, flags);
207 }
208 }
209 fclose(fp);
210 }
211
212 return open(mtd, flags);
213 }
214
215 int
216 mtd_erase(const char *mtd)
217 {
218 int fd;
219 struct mtd_info_user mtdInfo;
220 struct erase_info_user mtdEraseInfo;
221
222 fd = mtd_open(mtd, O_RDWR | O_SYNC);
223 if(fd < 0) {
224 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
225 exit(1);
226 }
227
228 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
229 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
230 close(fd);
231 exit(1);
232 }
233
234 mtdEraseInfo.length = mtdInfo.erasesize;
235
236 for (mtdEraseInfo.start = 0;
237 mtdEraseInfo.start < mtdInfo.size;
238 mtdEraseInfo.start += mtdInfo.erasesize) {
239
240 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
241 if(ioctl(fd, MEMERASE, &mtdEraseInfo))
242 fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
243 }
244
245 close(fd);
246 return 0;
247
248 }
249
250 int
251 mtd_write(int imagefd, const char *mtd)
252 {
253 int fd, i, result;
254 size_t r, w, e;
255 struct mtd_info_user mtdInfo;
256 struct erase_info_user mtdEraseInfo;
257 int ret = 0;
258
259 fd = mtd_open(mtd, O_RDWR | O_SYNC);
260 if(fd < 0) {
261 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
262 exit(1);
263 }
264
265 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
266 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
267 close(fd);
268 exit(1);
269 }
270
271 r = w = e = 0;
272 if (!quiet)
273 fprintf(stderr, " [ ]");
274
275 for (;;) {
276 /* buffer may contain data already (from trx check) */
277 r = buflen;
278 r += read(imagefd, buf + buflen, BUFSIZE - buflen);
279 w += r;
280
281 /* EOF */
282 if (r <= 0) break;
283
284 /* need to erase the next block before writing data to it */
285 while (w > e) {
286 mtdEraseInfo.start = e;
287 mtdEraseInfo.length = mtdInfo.erasesize;
288
289 if (!quiet)
290 fprintf(stderr, "\b\b\b[e]");
291 /* erase the chunk */
292 if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
293 fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
294 exit(1);
295 }
296 e += mtdInfo.erasesize;
297 }
298
299 if (!quiet)
300 fprintf(stderr, "\b\b\b[w]");
301
302 if ((result = write(fd, buf, r)) < r) {
303 if (result < 0) {
304 fprintf(stderr, "Error writing image.\n");
305 exit(1);
306 } else {
307 fprintf(stderr, "Insufficient space.\n");
308 exit(1);
309 }
310 }
311
312 buflen = 0;
313 }
314 if (!quiet)
315 fprintf(stderr, "\b\b\b\b");
316
317 close(fd);
318 return 0;
319 }
320
321 void usage(void)
322 {
323 fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
324 "The device is in the format of mtdX (eg: mtd4) or its label.\n"
325 "mtd recognizes these commands:\n"
326 " unlock unlock the device\n"
327 " erase erase all data on device\n"
328 " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
329 "Following options are available:\n"
330 " -q quiet mode (once: no [w] on writing,\n"
331 " twice: no status messages)\n"
332 " -r reboot after successful command\n"
333 " -f force write without trx checks\n"
334 " -e <device> erase <device> before executing the command\n\n"
335 "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
336 " mtd -r write linux.trx linux\n\n");
337 exit(1);
338 }
339
340 int main (int argc, char **argv)
341 {
342 int ch, i, boot, unlock, imagefd, force, unlocked;
343 char *erase[MAX_ARGS], *device, *imagefile;
344 enum {
345 CMD_ERASE,
346 CMD_WRITE,
347 CMD_UNLOCK
348 } cmd;
349
350 erase[0] = NULL;
351 boot = 0;
352 force = 0;
353 buflen = 0;
354 quiet = 0;
355
356 while ((ch = getopt(argc, argv, "frqe:")) != -1)
357 switch (ch) {
358 case 'f':
359 force = 1;
360 break;
361 case 'r':
362 boot = 1;
363 break;
364 case 'q':
365 quiet++;
366 break;
367 case 'e':
368 i = 0;
369 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
370 i++;
371
372 erase[i++] = optarg;
373 erase[i] = NULL;
374 break;
375
376 case '?':
377 default:
378 usage();
379 }
380 argc -= optind;
381 argv += optind;
382
383 if (argc < 2)
384 usage();
385
386 if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
387 cmd = CMD_UNLOCK;
388 device = argv[1];
389 } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
390 cmd = CMD_ERASE;
391 device = argv[1];
392 } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
393 cmd = CMD_WRITE;
394 device = argv[2];
395
396 if (strcmp(argv[1], "-") == 0) {
397 imagefile = "<stdin>";
398 imagefd = 0;
399 } else {
400 imagefile = argv[1];
401 if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
402 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
403 exit(1);
404 }
405 }
406
407 /* check trx file before erasing or writing anything */
408 if (!image_check(imagefd, device)) {
409 if (!force) {
410 fprintf(stderr, "Image check failed.\n");
411 exit(1);
412 }
413 } else {
414 if (!mtd_check(device)) {
415 fprintf(stderr, "Can't open device for writing!\n");
416 exit(1);
417 }
418 }
419 } else {
420 usage();
421 }
422
423 sync();
424
425 i = 0;
426 unlocked = 0;
427 while (erase[i] != NULL) {
428 if (quiet < 2)
429 fprintf(stderr, "Unlocking %s ...\n", erase[i]);
430 mtd_unlock(erase[i]);
431 if (quiet < 2)
432 fprintf(stderr, "Erasing %s ...\n", erase[i]);
433 mtd_erase(erase[i]);
434 if (strcmp(erase[i], device) == 0)
435 unlocked = 1;
436 i++;
437 }
438
439 if (!unlocked) {
440 if (quiet < 2)
441 fprintf(stderr, "Unlocking %s ...\n", device);
442 mtd_unlock(device);
443 }
444
445 switch (cmd) {
446 case CMD_UNLOCK:
447 break;
448 case CMD_ERASE:
449 if (quiet < 2)
450 fprintf(stderr, "Erasing %s ...\n", device);
451 mtd_erase(device);
452 break;
453 case CMD_WRITE:
454 if (quiet < 2)
455 fprintf(stderr, "Writing from %s to %s ... ", imagefile, device);
456 mtd_write(imagefd, device);
457 if (quiet < 2)
458 fprintf(stderr, "\n");
459 break;
460 }
461
462 sync();
463
464 if (boot) {
465 fflush(stdout);
466 syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
467 }
468 return 0;
469 }