libevent: updated to 2.0.22
[tomato.git] / release / src-rt-6.x.4708 / router / rc / mtd.c
blob0568695d4cbfbc96d39d253cf99504151742b67d
1 /*
3 Copyright 2005, Broadcom Corporation
4 All Rights Reserved.
6 THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
7 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
8 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
9 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
14 Modified for Tomato Firmware
15 Portions, Copyright (C) 2006-2009 Jonathan Zarate
19 #include "rc.h"
21 #include <limits.h>
22 #include <sys/sysmacros.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <error.h>
28 #include <sys/ioctl.h>
29 #include <sys/sysinfo.h>
30 #include <sys/mman.h>
31 #ifdef LINUX26
32 #include <linux/compiler.h>
33 #include <mtd/mtd-user.h>
34 #else
35 #include <linux/mtd/mtd.h>
36 #endif
37 #include <stdint.h>
39 #include <trxhdr.h>
40 #include <bcmutils.h>
42 #ifdef TCONFIG_BCMARM
43 #include <bcmendian.h>
44 #include <bcmnvram.h>
45 #include <shutils.h>
46 #endif
47 // #define DEBUG_SIMULATE
50 struct code_header {
51 char magic[4];
52 char res1[4];
53 char fwdate[3];
54 char fwvern[3];
55 char id[4];
56 char hw_ver;
57 char res2;
58 unsigned short flags;
59 unsigned char res3[10];
60 } ;
62 // -----------------------------------------------------------------------------
64 static uint32 *crc_table = NULL;
66 static void crc_done(void)
68 free(crc_table);
69 crc_table = NULL;
72 // -----------------------------------------------------------------------------
74 #ifdef TCONFIG_BCMARM
75 static int mtd_open_old(const char *mtdname, mtd_info_t *mi)
76 #else
77 static int mtd_open(const char *mtdname, mtd_info_t *mi)
78 #endif
80 char path[256];
81 int part;
82 int size;
83 int f;
85 if (mtd_getinfo(mtdname, &part, &size)) {
86 sprintf(path, MTD_DEV(%d), part);
87 if ((f = open(path, O_RDWR|O_SYNC)) >= 0) {
88 if ((mi) && ioctl(f, MEMGETINFO, mi) != 0) {
89 close(f);
90 return -1;
92 return f;
95 return -1;
98 static int _unlock_erase(const char *mtdname, int erase)
100 int mf;
101 mtd_info_t mi;
102 erase_info_t ei;
103 int r, ret, skipbb;
105 if (!wait_action_idle(5)) return 0;
106 set_action(ACT_ERASE_NVRAM);
108 r = 0;
109 skipbb = 0;
111 #ifdef TCONFIG_BCMARM
112 if ((mf = mtd_open_old(mtdname, &mi)) >= 0) {
113 #else
114 if ((mf = mtd_open(mtdname, &mi)) >= 0) {
115 #endif
116 r = 1;
117 #if 1
118 ei.length = mi.erasesize;
119 for (ei.start = 0; ei.start < mi.size; ei.start += mi.erasesize) {
120 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, (ei.start + ei.length) - 1);
121 fflush(stdout);
123 if (!skipbb) {
124 loff_t offset = ei.start;
126 if ((ret = ioctl(mf, MEMGETBADBLOCK, &offset)) > 0) {
127 printf("Skipping bad block at 0x%08x\n", ei.start);
128 continue;
129 } else if (ret < 0) {
130 if (errno == EOPNOTSUPP) {
131 skipbb = 1; // Not supported by this device
132 } else {
133 perror("MEMGETBADBLOCK");
134 r = 0;
135 break;
139 if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
140 // perror("MEMUNLOCK");
141 // r = 0;
142 // break;
144 if (erase) {
145 if (ioctl(mf, MEMERASE, &ei) != 0) {
146 perror("MEMERASE");
147 r = 0;
148 break;
152 #else
153 ei.start = 0;
154 ei.length = mi.size;
156 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, ei.length - 1);
157 fflush(stdout);
159 if (ioctl(mf, MEMUNLOCK, &ei) != 0) {
160 perror("MEMUNLOCK");
161 r = 0;
163 else if (erase) {
164 if (ioctl(mf, MEMERASE, &ei) != 0) {
165 perror("MEMERASE");
166 r = 0;
169 #endif
171 // checkme:
172 char buf[2];
173 read(mf, &buf, sizeof(buf));
174 close(mf);
177 set_action(ACT_IDLE);
179 if (r) printf("\"%s\" successfully %s.\n", mtdname, erase ? "erased" : "unlocked");
180 else printf("\nError %sing MTD\n", erase ? "eras" : "unlock");
182 sleep(1);
183 return r;
186 int mtd_unlock(const char *mtdname)
188 return _unlock_erase(mtdname, 0);
191 #ifdef TCONFIG_BCMARM
192 int mtd_erase_old(const char *mtdname)
193 #else
194 int mtd_erase(const char *mtdname)
195 #endif
197 return _unlock_erase(mtdname, 1);
200 #ifdef TCONFIG_BCMARM
201 int mtd_unlock_erase_main_old(int argc, char *argv[])
202 #else
203 int mtd_unlock_erase_main(int argc, char *argv[])
204 #endif
206 char c;
207 char *dev = NULL;
209 while ((c = getopt(argc, argv, "d:")) != -1) {
210 switch (c) {
211 case 'd':
212 dev = optarg;
213 break;
217 if (!dev) {
218 usage_exit(argv[0], "-d part");
221 return _unlock_erase(dev, strstr(argv[0], "erase") ? 1 : 0);
224 #ifdef TCONFIG_BCMARM
225 int mtd_write_main_old(int argc, char *argv[])
226 #else
227 int mtd_write_main(int argc, char *argv[])
228 #endif
230 int mf = -1;
231 mtd_info_t mi;
232 erase_info_t ei;
233 #ifdef TCONFIG_BCMARM
234 uint32 sig;
235 struct code_header cth;
236 uint32 crc;
237 #endif
238 FILE *f;
239 unsigned char *buf = NULL, *p, *bounce_buf = NULL;
240 const char *error;
241 long filelen = 0, n, wlen, unit_len;
242 struct sysinfo si;
243 uint32 ofs;
244 char c;
245 int web = 0;
246 char *iname = NULL;
247 char *dev = NULL;
248 char msg_buf[2048];
249 int alloc = 0, bounce = 0, fd;
250 #ifdef DEBUG_SIMULATE
251 FILE *of;
252 #endif
254 while ((c = getopt(argc, argv, "i:d:w")) != -1) {
255 switch (c) {
256 case 'i':
257 iname = optarg;
258 break;
259 case 'd':
260 dev = optarg;
261 break;
262 case 'w':
263 web = 1;
264 break;
268 if ((iname == NULL) || (dev == NULL)) {
269 usage_exit(argv[0], "-i file -d part");
272 if (!wait_action_idle(10)) {
273 printf("System is busy\n");
274 return 1;
277 set_action(ACT_WEB_UPGRADE);
279 if ((f = fopen(iname, "r")) == NULL) {
280 error = "Error opening input file";
281 goto ERROR;
284 fd = fileno(f);
285 fseek( f, 0, SEEK_END);
286 filelen = ftell(f);
287 fseek( f, 0, SEEK_SET);
288 _dprintf("file len=0x%x\n", filelen);
290 #ifdef TCONFIG_BCMARM
291 if ((mf = mtd_open_old(dev, &mi)) < 0) {
292 #else
293 if ((mf = mtd_open(dev, &mi)) < 0) {
294 #endif
295 snprintf(msg_buf, sizeof(msg_buf), "Error opening MTD device. (errno %d (%s))", errno, strerror(errno));
296 error = msg_buf;
297 goto ERROR;
300 if (mi.erasesize < sizeof(struct trx_header)) {
301 error = "Error obtaining MTD information";
302 goto ERROR;
305 _dprintf("mtd size=%x, erasesize=%x, writesize=%x, type=%x\n", mi.size, mi.erasesize, mi.writesize, mi.type);
307 unit_len = ROUNDUP(filelen, mi.erasesize);
308 if (unit_len > mi.size) {
309 error = "File is too big to fit in MTD";
310 goto ERROR;
313 if ((buf = mmap(0, filelen, PROT_READ, MAP_SHARED, fd, 0)) == (unsigned char*)MAP_FAILED) {
314 _dprintf("mmap %x bytes fail!. errno %d (%s).\n", filelen, errno, strerror(errno));
315 alloc = 1;
318 sysinfo(&si);
319 if (alloc) {
320 if ((si.freeram * si.mem_unit) <= (unit_len + (4096 * 1024)))
321 unit_len = mi.erasesize;
324 if (mi.type == MTD_UBIVOLUME) {
325 if (!(bounce_buf = malloc(mi.writesize))) {
326 error = "Not enough memory";
327 goto ERROR;
330 _dprintf("freeram=%lx unit_len=%lx filelen=%lx mi.erasesize=%x mi.writesize=%x\n",
331 si.freeram, unit_len, filelen, mi.erasesize, mi.writesize);
333 if (alloc && !(buf = malloc(unit_len))) {
334 error = "Not enough memory";
335 goto ERROR;
338 #ifdef DEBUG_SIMULATE
339 if ((of = fopen("/mnt/out.bin", "w")) == NULL) {
340 error = "Error creating test file";
341 goto ERROR;
343 #endif
345 for (ei.start = ofs = 0, ei.length = unit_len, n = 0, error = NULL, p = buf;
346 ofs < filelen;
347 ofs += n, ei.start += unit_len)
349 wlen = n = MIN(unit_len, filelen - ofs);
350 if (mi.type == MTD_UBIVOLUME) {
351 if (n >= mi.writesize) {
352 n &= ~(mi.writesize - 1);
353 wlen = n;
354 } else {
355 if (!alloc)
356 memcpy(bounce_buf, p, n);
357 bounce = 1;
358 p = bounce_buf;
359 wlen = ROUNDUP(n, mi.writesize);
363 if (alloc && safe_fread(p, 1, n, f) != n) {
364 error = "Error reading file";
365 break;
368 _dprintf("ofs=%x n=%lx/%lx ei.start=%x ei.length=%x\n", ofs, n, wlen, ei.start, ei.length);
370 #ifdef DEBUG_SIMULATE
371 if (fwrite(p, 1, wlen, of) != wlen) {
372 fclose(of);
373 error = "Error writing to test file";
374 break;
376 #else
377 if (ei.start == ofs) {
378 ioctl(mf, MEMUNLOCK, &ei);
379 if (ioctl(mf, MEMERASE, &ei) != 0) {
380 snprintf(msg_buf, sizeof(msg_buf), "Error erasing MTD block. (errno %d (%s))", errno, strerror(errno));
381 error = msg_buf;
382 break;
385 if (write(mf, p, wlen) != wlen) {
386 snprintf(msg_buf, sizeof(msg_buf), "Error writing to MTD device. (errno %d (%s))", errno, strerror(errno));
387 error = msg_buf;
388 break;
390 #endif
392 if (!(alloc || bounce))
393 p += n;
396 #ifdef DEBUG_SIMULATE
397 fclose(of);
398 #endif
400 ERROR:
401 if (!alloc)
402 munmap((void*) buf, filelen);
403 else
404 free(buf);
406 if (bounce_buf)
407 free(bounce_buf);
409 if (mf >= 0) {
410 // dummy read to ensure chip(s) are out of lock/suspend state
411 read(mf, &n, sizeof(n));
412 close(mf);
414 if (f) fclose(f);
416 crc_done();
418 set_action(ACT_IDLE);
420 _dprintf("%s\n", error ? error : "Image successfully flashed");
421 return (error ? 1 : 0);
424 #ifdef TCONFIG_BCMARM
427 * Open an MTD device
428 * @param mtd path to or partition name of MTD device
429 * @param flags open() flags
430 * @return return value of open()
433 mtd_open(const char *mtd, int flags)
435 FILE *fp;
436 char dev[PATH_MAX];
437 int i;
439 if ((fp = fopen("/proc/mtd", "r"))) {
440 while (fgets(dev, sizeof(dev), fp)) {
441 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
442 #ifdef LINUX26
443 snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
444 #else
445 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
446 #endif
447 fclose(fp);
448 return open(dev, flags);
451 fclose(fp);
454 return open(mtd, flags);
458 * Erase an MTD device
459 * @param mtd path to or partition name of MTD device
460 * @return 0 on success and errno on failure
463 mtd_erase(const char *mtd)
465 int mtd_fd;
466 mtd_info_t mtd_info;
467 erase_info_t erase_info;
469 /* Open MTD device */
470 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) {
471 perror(mtd);
472 return errno;
475 /* Get sector size */
476 if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) {
477 perror(mtd);
478 close(mtd_fd);
479 return errno;
482 erase_info.length = mtd_info.erasesize;
484 for (erase_info.start = 0;
485 erase_info.start < mtd_info.size;
486 erase_info.start += mtd_info.erasesize) {
487 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
488 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) {
489 perror(mtd);
490 close(mtd_fd);
491 return errno;
495 close(mtd_fd);
496 return 0;
499 static char *
500 base64enc(const char *p, char *buf, int len)
502 char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
503 "0123456789+/";
504 char *s = buf;
506 while (*p) {
507 if (s >= buf+len-4)
508 break;
509 *(s++) = al[(*p >> 2) & 0x3F];
510 *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
511 *s = *(s+1) = '=';
512 *(s+2) = 0;
513 if (! *(++p)) break;
514 *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
515 if (! *(++p)) break;
516 *(s++) = al[*(p++) & 0x3F];
519 return buf;
522 enum {
523 METHOD_GET,
524 METHOD_POST
527 static int
528 wget(int method, const char *server, char *buf, size_t count, off_t offset)
530 char url[PATH_MAX] = { 0 }, *s;
531 char *host = url, *path = "", auth[128] = { 0 }, line[512];
532 unsigned short port = 80;
533 int fd;
534 FILE *fp;
535 struct sockaddr_in sin;
536 int chunked = 0, len = 0;
538 if (server == NULL || !strcmp(server, "")) {
539 _dprintf("wget: null server input\n");
540 return (0);
543 strncpy(url, server, sizeof(url));
545 /* Parse URL */
546 if (!strncmp(url, "http://", 7)) {
547 port = 80;
548 host = url + 7;
550 if ((s = strchr(host, '/'))) {
551 *s++ = '\0';
552 path = s;
554 if ((s = strchr(host, '@'))) {
555 *s++ = '\0';
556 base64enc(host, auth, sizeof(auth));
557 host = s;
559 if ((s = strchr(host, ':'))) {
560 *s++ = '\0';
561 port = atoi(s);
564 /* Open socket */
565 if (!inet_aton(host, &sin.sin_addr))
566 return 0;
567 sin.sin_family = AF_INET;
568 sin.sin_port = htons(port);
569 _dprintf("Connecting to %s:%u...\n", host, port);
570 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
571 connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
572 !(fp = fdopen(fd, "r+"))) {
573 perror(host);
574 if (fd >= 0)
575 close(fd);
576 return 0;
578 _dprintf("connected!\n");
580 /* Send HTTP request */
581 fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
582 fprintf(fp, "Host: %s\r\n", host);
583 fprintf(fp, "User-Agent: wget\r\n");
584 if (strlen(auth))
585 fprintf(fp, "Authorization: Basic %s\r\n", auth);
586 if (offset)
587 fprintf(fp, "Range: bytes=%ld-\r\n", offset);
588 if (method == METHOD_POST) {
589 fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
590 fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf));
591 fputs(buf, fp);
592 } else
593 fprintf(fp, "Connection: close\r\n\r\n");
595 /* Check HTTP response */
596 _dprintf("HTTP request sent, awaiting response...\n");
597 if (fgets(line, sizeof(line), fp)) {
598 _dprintf("%s", line);
599 for (s = line; *s && !isspace((int)*s); s++);
600 for (; isspace((int)*s); s++);
601 switch (atoi(s)) {
602 case 200: if (offset) goto done; else break;
603 case 206: if (offset) break; else goto done;
604 default: goto done;
607 /* Parse headers */
608 while (fgets(line, sizeof(line), fp)) {
609 _dprintf("%s", line);
610 for (s = line; *s == '\r'; s++);
611 if (*s == '\n')
612 break;
613 if (!strncasecmp(s, "Content-Length:", 15)) {
614 for (s += 15; isblank(*s); s++);
615 chomp(s);
616 len = atoi(s);
618 else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
619 for (s += 18; isblank(*s); s++);
620 chomp(s);
621 if (!strncasecmp(s, "chunked", 7))
622 chunked = 1;
626 if (chunked && fgets(line, sizeof(line), fp))
627 len = strtol(line, NULL, 16);
629 len = (len > count) ? count : len;
630 len = fread(buf, 1, len, fp);
632 done:
633 /* Close socket */
634 fflush(fp);
635 fclose(fp);
636 return len;
640 http_get(const char *server, char *buf, size_t count, off_t offset)
642 return wget(METHOD_GET, server, buf, count, offset);
646 * Write a file to an MTD device
647 * @param path file to write or a URL
648 * @param mtd path to or partition name of MTD device
649 * @return 0 on success and errno on failure
652 mtd_write(const char *path, const char *mtd)
654 int mtd_fd = -1;
655 mtd_info_t mtd_info;
656 erase_info_t erase_info;
658 struct sysinfo info;
659 struct trx_header trx;
660 unsigned long crc;
662 #ifdef CONFIG_FAILSAFE_UPGRADE
663 if (get_model() == MODEL_EA6700)
665 if (nvram_match("bootpartition", "1")) {
666 mtd = "linux";
667 nvram_set("bootpartition", "0");
668 nvram_commit();
669 } else {
670 mtd = "linux2";
671 nvram_set("bootpartition", "1");
672 nvram_commit();
675 #endif
676 FILE *fp;
677 char *buf = NULL;
678 long count, len, off;
679 int ret = -1;
682 if ((fp = fopen(path, "r")))
683 count = safe_fread(&trx, 1, sizeof(struct trx_header), fp);
684 else
685 count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0);
686 if (count < sizeof(struct trx_header)) {
687 fprintf(stderr, "%s: File is too small (%ld bytes)\n", path, count);
688 goto fail;
691 /* Open MTD device and get sector size */
692 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 ||
693 ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 ||
694 mtd_info.erasesize < sizeof(struct trx_header)) {
695 perror(mtd);
696 goto fail;
699 if (trx.magic != TRX_MAGIC ||
700 trx.len > mtd_info.size ||
701 trx.len < sizeof(struct trx_header)) {
702 fprintf(stderr, "%s: Bad trx header\n", path);
703 goto fail;
706 /* Allocate temporary buffer */
707 /* See if we have enough memory to store the whole file */
708 sysinfo(&info);
709 if (info.freeram >= trx.len) {
710 erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize);
711 if (!(buf = malloc(erase_info.length)))
712 erase_info.length = mtd_info.erasesize;
714 /* fallback to smaller buffer */
715 else {
716 erase_info.length = mtd_info.erasesize;
717 buf = NULL;
719 if (!buf && (!(buf = malloc(erase_info.length)))) {
720 perror("malloc");
721 goto fail;
724 /* Calculate CRC over header */
725 crc = hndcrc32((uint8 *) &trx.flag_version,
726 sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
727 CRC32_INIT_VALUE);
729 if (trx.flag_version & TRX_NO_HEADER)
730 trx.len -= sizeof(struct trx_header);
732 /* Write file or URL to MTD device */
733 for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) {
734 len = MIN(erase_info.length, trx.len - erase_info.start);
735 if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start)
736 count = off = 0;
737 else {
738 count = off = sizeof(struct trx_header);
739 memcpy(buf, &trx, sizeof(struct trx_header));
741 if (fp)
742 count += safe_fread(&buf[off], 1, len - off, fp);
743 else
744 count += http_get(path, &buf[off], len - off, erase_info.start + off);
745 if (count < len) {
746 fprintf(stderr, "%s: Truncated file (actual %ld expect %ld)\n", path,
747 count - off, len - off);
748 goto fail;
750 /* Update CRC */
751 crc = hndcrc32((uint8 *)&buf[off], count - off, crc);
752 /* Check CRC before writing if possible */
753 if (count == trx.len) {
754 if (crc != trx.crc32) {
755 fprintf(stderr, "%s: Bad CRC\n", path);
756 goto fail;
759 /* Do it */
760 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info);
761 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 ||
762 write(mtd_fd, buf, count) != count) {
763 perror(mtd);
764 goto fail;
768 #ifdef PLC
769 eval("gigle_util restart");
770 nvram_set("plc_pconfig_state", "2");
771 nvram_commit();
772 #endif
774 printf("%s: CRC OK - Image successfully flashed\n", mtd);
775 ret = 0;
777 fail:
778 if (buf) {
779 /* Dummy read to ensure chip(s) are out of lock/suspend state */
780 (void) read(mtd_fd, buf, 2);
781 free(buf);
784 if (mtd_fd >= 0)
785 close(mtd_fd);
786 if (fp)
787 fclose(fp);
788 return ret;
791 #endif