1/* 2 3 Copyright 2005, Broadcom Corporation 4 All Rights Reserved. 5 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. 10 11*/ 12/* 13 14 Modified for Tomato Firmware 15 Portions, Copyright (C) 2006-2009 Jonathan Zarate 16 17*/ 18 19#include "rc.h" 20 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> 38 39#include <trxhdr.h> 40#include <bcmutils.h> 41 42#ifdef RTCONFIG_BCMARM 43#include <bcmendian.h> 44#include <bcmnvram.h> 45#include <shutils.h> 46#endif 47// #define DEBUG_SIMULATE 48 49 50struct 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} ; 61 62// ----------------------------------------------------------------------------- 63 64static uint32 *crc_table = NULL; 65 66static void crc_done(void) 67{ 68 free(crc_table); 69 crc_table = NULL; 70} 71 72// ----------------------------------------------------------------------------- 73 74#ifdef RTCONFIG_BCMARM 75static int mtd_open_old(const char *mtdname, mtd_info_t *mi) 76#else 77static int mtd_open(const char *mtdname, mtd_info_t *mi) 78#endif 79{ 80 char path[256]; 81 int part; 82 int size; 83 int f; 84 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; 91 } 92 return f; 93 } 94 } 95 return -1; 96} 97 98static int _unlock_erase(const char *mtdname, int erase) 99{ 100 int mf; 101 mtd_info_t mi; 102 erase_info_t ei; 103 int r; 104 105 if (!wait_action_idle(5)) return 0; 106 set_action(ACT_ERASE_NVRAM); 107 108 r = 0; 109#ifdef RTCONFIG_BCMARM 110 if ((mf = mtd_open_old(mtdname, &mi)) >= 0) { 111#else 112 if ((mf = mtd_open(mtdname, &mi)) >= 0) { 113#endif 114 r = 1; 115#if 1 116 ei.length = mi.erasesize; 117 for (ei.start = 0; ei.start < mi.size; ei.start += mi.erasesize) { 118 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, (ei.start + ei.length) - 1); 119 fflush(stdout); 120 121 if (ioctl(mf, MEMUNLOCK, &ei) != 0) { 122// perror("MEMUNLOCK"); 123// r = 0; 124// break; 125 } 126 if (erase) { 127 if (ioctl(mf, MEMERASE, &ei) != 0) { 128 perror("MEMERASE"); 129 r = 0; 130 break; 131 } 132 } 133 } 134#else 135 ei.start = 0; 136 ei.length = mi.size; 137 138 printf("%sing 0x%x - 0x%x\n", erase ? "Eras" : "Unlock", ei.start, ei.length - 1); 139 fflush(stdout); 140 141 if (ioctl(mf, MEMUNLOCK, &ei) != 0) { 142 perror("MEMUNLOCK"); 143 r = 0; 144 } 145 else if (erase) { 146 if (ioctl(mf, MEMERASE, &ei) != 0) { 147 perror("MEMERASE"); 148 r = 0; 149 } 150 } 151#endif 152 153 // checkme: 154 char buf[2]; 155 read(mf, &buf, sizeof(buf)); 156 close(mf); 157 } 158 159 set_action(ACT_IDLE); 160 161 if (r) printf("\"%s\" successfully %s.\n", mtdname, erase ? "erased" : "unlocked"); 162 else printf("\nError %sing MTD\n", erase ? "eras" : "unlock"); 163 164 sleep(1); 165 return r; 166} 167 168int mtd_unlock(const char *mtdname) 169{ 170 return _unlock_erase(mtdname, 0); 171} 172 173#ifdef RTCONFIG_BCMARM 174int mtd_erase_old(const char *mtdname) 175#else 176int mtd_erase(const char *mtdname) 177#endif 178{ 179 return _unlock_erase(mtdname, 1); 180} 181 182#ifdef RTCONFIG_BCMARM 183int mtd_unlock_erase_main_old(int argc, char *argv[]) 184#else 185int mtd_unlock_erase_main(int argc, char *argv[]) 186#endif 187{ 188 char c; 189 char *dev = NULL; 190 191 while ((c = getopt(argc, argv, "d:")) != -1) { 192 switch (c) { 193 case 'd': 194 dev = optarg; 195 break; 196 } 197 } 198 199 if (!dev) { 200 usage_exit(argv[0], "-d part"); 201 } 202 203 return _unlock_erase(dev, strstr(argv[0], "erase") ? 1 : 0); 204} 205 206#ifdef RTCONFIG_BCMARM 207int mtd_write_main_old(int argc, char *argv[]) 208#else 209int mtd_write_main(int argc, char *argv[]) 210#endif 211{ 212 int mf = -1; 213 mtd_info_t mi; 214 erase_info_t ei; 215 FILE *f; 216 unsigned char *buf = NULL, *p, *bounce_buf = NULL; 217 const char *error; 218 long filelen = 0, n, wlen, unit_len; 219 struct sysinfo si; 220 uint32 ofs; 221 char c; 222 char *iname = NULL; 223 char *dev = NULL; 224 char msg_buf[2048]; 225 int alloc = 0, bounce = 0, fd; 226#ifdef DEBUG_SIMULATE 227 FILE *of; 228#endif 229 230 while ((c = getopt(argc, argv, "i:d:")) != -1) { 231 switch (c) { 232 case 'i': 233 iname = optarg; 234 break; 235 case 'd': 236 dev = optarg; 237 break; 238 } 239 } 240 241 if ((iname == NULL) || (dev == NULL)) { 242 usage_exit(argv[0], "-i file -d part"); 243 } 244 245 if (!wait_action_idle(10)) { 246 printf("System is busy\n"); 247 return 1; 248 } 249 250 set_action(ACT_WEB_UPGRADE); 251 252 if ((f = fopen(iname, "r")) == NULL) { 253 error = "Error opening input file"; 254 goto ERROR; 255 } 256 257 fd = fileno(f); 258 fseek( f, 0, SEEK_END); 259 filelen = ftell(f); 260 fseek( f, 0, SEEK_SET); 261 _dprintf("file len=0x%x\n", filelen); 262 263#ifdef RTCONFIG_BCMARM 264 if ((mf = mtd_open_old(dev, &mi)) < 0) { 265#else 266 if ((mf = mtd_open(dev, &mi)) < 0) { 267#endif 268 snprintf(msg_buf, sizeof(msg_buf), "Error opening MTD device. (errno %d (%s))", errno, strerror(errno)); 269 error = msg_buf; 270 goto ERROR; 271 } 272 273 if (mi.erasesize < sizeof(struct trx_header)) { 274 error = "Error obtaining MTD information"; 275 goto ERROR; 276 } 277 278 _dprintf("mtd size=%x, erasesize=%x, writesize=%x, type=%x\n", mi.size, mi.erasesize, mi.writesize, mi.type); 279 280 unit_len = ROUNDUP(filelen, mi.erasesize); 281 if (unit_len > mi.size) { 282 error = "File is too big to fit in MTD"; 283 goto ERROR; 284 } 285 286 if ((buf = mmap(0, filelen, PROT_READ, MAP_SHARED, fd, 0)) == (unsigned char*)MAP_FAILED) { 287 _dprintf("mmap %x bytes fail!. errno %d (%s).\n", filelen, errno, strerror(errno)); 288 alloc = 1; 289 } 290 291 sysinfo(&si); 292 if (alloc) { 293 if ((si.freeram * si.mem_unit) <= (unit_len + (4096 * 1024))) 294 unit_len = mi.erasesize; 295 } 296 297 if (mi.type == MTD_UBIVOLUME) { 298 if (!(bounce_buf = malloc(mi.writesize))) { 299 error = "Not enough memory"; 300 goto ERROR; 301 } 302 } 303 _dprintf("freeram=%lx unit_len=%lx filelen=%lx mi.erasesize=%x mi.writesize=%x\n", 304 si.freeram, unit_len, filelen, mi.erasesize, mi.writesize); 305 306 if (alloc && !(buf = malloc(unit_len))) { 307 error = "Not enough memory"; 308 goto ERROR; 309 } 310 311#ifdef DEBUG_SIMULATE 312 if ((of = fopen("/mnt/out.bin", "w")) == NULL) { 313 error = "Error creating test file"; 314 goto ERROR; 315 } 316#endif 317 318 for (ei.start = ofs = 0, ei.length = unit_len, n = 0, error = NULL, p = buf; 319 ofs < filelen; 320 ofs += n, ei.start += unit_len) 321 { 322 wlen = n = MIN(unit_len, filelen - ofs); 323 if (mi.type == MTD_UBIVOLUME) { 324 if (n >= mi.writesize) { 325 n &= ~(mi.writesize - 1); 326 wlen = n; 327 } else { 328 if (!alloc) 329 memcpy(bounce_buf, p, n); 330 bounce = 1; 331 p = bounce_buf; 332 wlen = ROUNDUP(n, mi.writesize); 333 } 334 } 335 336 if (alloc && safe_fread(p, 1, n, f) != n) { 337 error = "Error reading file"; 338 break; 339 } 340 341 _dprintf("ofs=%x n=%lx/%lx ei.start=%x ei.length=%x\n", ofs, n, wlen, ei.start, ei.length); 342 343#ifdef DEBUG_SIMULATE 344 if (fwrite(p, 1, wlen, of) != wlen) { 345 fclose(of); 346 error = "Error writing to test file"; 347 break; 348 } 349#else 350 if (ei.start == ofs) { 351 ioctl(mf, MEMUNLOCK, &ei); 352 if (ioctl(mf, MEMERASE, &ei) != 0) { 353 snprintf(msg_buf, sizeof(msg_buf), "Error erasing MTD block. (errno %d (%s))", errno, strerror(errno)); 354 error = msg_buf; 355 break; 356 } 357 } 358 if (write(mf, p, wlen) != wlen) { 359 snprintf(msg_buf, sizeof(msg_buf), "Error writing to MTD device. (errno %d (%s))", errno, strerror(errno)); 360 error = msg_buf; 361 break; 362 } 363#endif 364 365 if (!(alloc || bounce)) 366 p += n; 367 } 368 369#ifdef DEBUG_SIMULATE 370 fclose(of); 371#endif 372 373ERROR: 374 if (!alloc) 375 munmap((void*) buf, filelen); 376 else 377 free(buf); 378 379 if (bounce_buf) 380 free(bounce_buf); 381 382 if (mf >= 0) { 383 // dummy read to ensure chip(s) are out of lock/suspend state 384 read(mf, &n, sizeof(n)); 385 close(mf); 386 } 387 if (f) fclose(f); 388 389 crc_done(); 390 391 set_action(ACT_IDLE); 392 393 _dprintf("%s\n", error ? error : "Image successfully flashed"); 394 return (error ? 1 : 0); 395} 396 397#ifdef RTCONFIG_BCMARM 398 399/* 400 * Open an MTD device 401 * @param mtd path to or partition name of MTD device 402 * @param flags open() flags 403 * @return return value of open() 404 */ 405int 406mtd_open(const char *mtd, int flags) 407{ 408 FILE *fp; 409 char dev[PATH_MAX]; 410 int i; 411 412 if ((fp = fopen("/proc/mtd", "r"))) { 413 while (fgets(dev, sizeof(dev), fp)) { 414 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) { 415#ifdef LINUX26 416 snprintf(dev, sizeof(dev), "/dev/mtd%d", i); 417#else 418 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i); 419#endif 420 fclose(fp); 421 return open(dev, flags); 422 } 423 } 424 fclose(fp); 425 } 426 427 return open(mtd, flags); 428} 429 430/* 431 * Erase an MTD device 432 * @param mtd path to or partition name of MTD device 433 * @return 0 on success and errno on failure 434 */ 435int 436mtd_erase(const char *mtd) 437{ 438 int mtd_fd; 439 mtd_info_t mtd_info; 440 erase_info_t erase_info; 441#ifdef RTAC87U 442 char erase_err[255] = {0}; 443#endif 444 445 /* Open MTD device */ 446 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0) { 447 perror(mtd); 448 return errno; 449 } 450 451 /* Get sector size */ 452 if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0) { 453 perror(mtd); 454 close(mtd_fd); 455 return errno; 456 } 457 458 erase_info.length = mtd_info.erasesize; 459 460 for (erase_info.start = 0; 461 erase_info.start < mtd_info.size; 462 erase_info.start += mtd_info.erasesize) { 463 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info); 464 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0) { 465 perror(mtd); 466 close(mtd_fd); 467#ifdef RTAC87U 468 sprintf(erase_err, "logger -t ATE mtd_erase failed: [%d]", errno); 469 system(erase_err); 470#endif 471 return errno; 472 } 473 } 474 475 close(mtd_fd); 476#ifdef RTAC87U 477 sprintf(erase_err, "logger -t ATE mtd_erase OK:[%d]", errno); 478 system(erase_err); 479#endif 480 return 0; 481} 482 483static char * 484base64enc(const char *p, char *buf, int len) 485{ 486 char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 487 "0123456789+/"; 488 char *s = buf; 489 490 while (*p) { 491 if (s >= buf+len-4) 492 break; 493 *(s++) = al[(*p >> 2) & 0x3F]; 494 *(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)]; 495 *s = *(s+1) = '='; 496 *(s+2) = 0; 497 if (! *(++p)) break; 498 *(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)]; 499 if (! *(++p)) break; 500 *(s++) = al[*(p++) & 0x3F]; 501 } 502 503 return buf; 504} 505 506enum { 507 METHOD_GET, 508 METHOD_POST 509}; 510 511static int 512wget(int method, const char *server, char *buf, size_t count, off_t offset) 513{ 514 char url[PATH_MAX] = { 0 }, *s; 515 char *host = url, *path = "", auth[128] = { 0 }, line[512]; 516 unsigned short port = 80; 517 int fd; 518 FILE *fp; 519 struct sockaddr_in sin; 520 int chunked = 0, len = 0; 521 522 if (server == NULL || !strcmp(server, "")) { 523 _dprintf("wget: null server input\n"); 524 return (0); 525 } 526 527 strncpy(url, server, sizeof(url)); 528 529 /* Parse URL */ 530 if (!strncmp(url, "http://", 7)) { 531 port = 80; 532 host = url + 7; 533 } 534 if ((s = strchr(host, '/'))) { 535 *s++ = '\0'; 536 path = s; 537 } 538 if ((s = strchr(host, '@'))) { 539 *s++ = '\0'; 540 base64enc(host, auth, sizeof(auth)); 541 host = s; 542 } 543 if ((s = strchr(host, ':'))) { 544 *s++ = '\0'; 545 port = atoi(s); 546 } 547 548 /* Open socket */ 549 if (!inet_aton(host, &sin.sin_addr)) 550 return 0; 551 sin.sin_family = AF_INET; 552 sin.sin_port = htons(port); 553 _dprintf("Connecting to %s:%u...\n", host, port); 554 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 || 555 connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 || 556 !(fp = fdopen(fd, "r+"))) { 557 perror(host); 558 if (fd >= 0) 559 close(fd); 560 return 0; 561 } 562 _dprintf("connected!\n"); 563 564 /* Send HTTP request */ 565 fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path); 566 fprintf(fp, "Host: %s\r\n", host); 567 fprintf(fp, "User-Agent: wget\r\n"); 568 if (strlen(auth)) 569 fprintf(fp, "Authorization: Basic %s\r\n", auth); 570 if (offset) 571 fprintf(fp, "Range: bytes=%ld-\r\n", offset); 572 if (method == METHOD_POST) { 573 fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n"); 574 fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf)); 575 fputs(buf, fp); 576 } else 577 fprintf(fp, "Connection: close\r\n\r\n"); 578 579 /* Check HTTP response */ 580 _dprintf("HTTP request sent, awaiting response...\n"); 581 if (fgets(line, sizeof(line), fp)) { 582 _dprintf("%s", line); 583 for (s = line; *s && !isspace((int)*s); s++); 584 for (; isspace((int)*s); s++); 585 switch (atoi(s)) { 586 case 200: if (offset) goto done; else break; 587 case 206: if (offset) break; else goto done; 588 default: goto done; 589 } 590 } 591 /* Parse headers */ 592 while (fgets(line, sizeof(line), fp)) { 593 _dprintf("%s", line); 594 for (s = line; *s == '\r'; s++); 595 if (*s == '\n') 596 break; 597 if (!strncasecmp(s, "Content-Length:", 15)) { 598 for (s += 15; isblank(*s); s++); 599 chomp(s); 600 len = atoi(s); 601 } 602 else if (!strncasecmp(s, "Transfer-Encoding:", 18)) { 603 for (s += 18; isblank(*s); s++); 604 chomp(s); 605 if (!strncasecmp(s, "chunked", 7)) 606 chunked = 1; 607 } 608 } 609 610 if (chunked && fgets(line, sizeof(line), fp)) 611 len = strtol(line, NULL, 16); 612 613 len = (len > count) ? count : len; 614 len = fread(buf, 1, len, fp); 615 616done: 617 /* Close socket */ 618 fflush(fp); 619 fclose(fp); 620 return len; 621} 622 623int 624http_get(const char *server, char *buf, size_t count, off_t offset) 625{ 626 return wget(METHOD_GET, server, buf, count, offset); 627} 628 629/* 630 * Write a file to an MTD device 631 * @param path file to write or a URL 632 * @param mtd path to or partition name of MTD device 633 * @return 0 on success and errno on failure 634 */ 635int 636mtd_write(const char *path, const char *mtd) 637{ 638 int mtd_fd = -1; 639 mtd_info_t mtd_info; 640 erase_info_t erase_info; 641 642 struct sysinfo info; 643 struct trx_header trx; 644 unsigned long crc; 645 646 FILE *fp; 647 char *buf = NULL; 648 long count, len, off; 649 int ret = -1; 650 651 /* Examine TRX header */ 652 if ((fp = fopen(path, "r"))) 653 count = safe_fread(&trx, 1, sizeof(struct trx_header), fp); 654 else 655 count = http_get(path, (char *) &trx, sizeof(struct trx_header), 0); 656 if (count < sizeof(struct trx_header)) { 657 fprintf(stderr, "%s: File is too small (%ld bytes)\n", path, count); 658 goto fail; 659 } 660 661 /* Open MTD device and get sector size */ 662 if ((mtd_fd = mtd_open(mtd, O_RDWR)) < 0 || 663 ioctl(mtd_fd, MEMGETINFO, &mtd_info) != 0 || 664 mtd_info.erasesize < sizeof(struct trx_header)) { 665 perror(mtd); 666 goto fail; 667 } 668 669 if (trx.magic != TRX_MAGIC || 670 trx.len > mtd_info.size || 671 trx.len < sizeof(struct trx_header)) { 672 fprintf(stderr, "%s: Bad trx header\n", path); 673 goto fail; 674 } 675 676 /* Allocate temporary buffer */ 677 /* See if we have enough memory to store the whole file */ 678 sysinfo(&info); 679 if (info.freeram >= trx.len) { 680 erase_info.length = ROUNDUP(trx.len, mtd_info.erasesize); 681 if (!(buf = malloc(erase_info.length))) 682 erase_info.length = mtd_info.erasesize; 683 } 684 /* fallback to smaller buffer */ 685 else { 686 erase_info.length = mtd_info.erasesize; 687 buf = NULL; 688 } 689 if (!buf && (!(buf = malloc(erase_info.length)))) { 690 perror("malloc"); 691 goto fail; 692 } 693 694 /* Calculate CRC over header */ 695 crc = hndcrc32((uint8 *) &trx.flag_version, 696 sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version), 697 CRC32_INIT_VALUE); 698 699 if (trx.flag_version & TRX_NO_HEADER) 700 trx.len -= sizeof(struct trx_header); 701 702 /* Write file or URL to MTD device */ 703 for (erase_info.start = 0; erase_info.start < trx.len; erase_info.start += count) { 704 len = MIN(erase_info.length, trx.len - erase_info.start); 705 if ((trx.flag_version & TRX_NO_HEADER) || erase_info.start) 706 count = off = 0; 707 else { 708 count = off = sizeof(struct trx_header); 709 memcpy(buf, &trx, sizeof(struct trx_header)); 710 } 711 if (fp) 712 count += safe_fread(&buf[off], 1, len - off, fp); 713 else 714 count += http_get(path, &buf[off], len - off, erase_info.start + off); 715 if (count < len) { 716 fprintf(stderr, "%s: Truncated file (actual %ld expect %ld)\n", path, 717 count - off, len - off); 718 goto fail; 719 } 720 /* Update CRC */ 721 crc = hndcrc32((uint8 *)&buf[off], count - off, crc); 722 /* Check CRC before writing if possible */ 723 if (count == trx.len) { 724 if (crc != trx.crc32) { 725 fprintf(stderr, "%s: Bad CRC\n", path); 726 goto fail; 727 } 728 } 729 /* Do it */ 730 (void) ioctl(mtd_fd, MEMUNLOCK, &erase_info); 731 if (ioctl(mtd_fd, MEMERASE, &erase_info) != 0 || 732 write(mtd_fd, buf, count) != count) { 733 perror(mtd); 734 goto fail; 735 } 736 } 737 738#ifdef PLC 739 eval("gigle_util restart"); 740 nvram_set("plc_pconfig_state", "2"); 741 nvram_commit(); 742#endif 743 744 printf("%s: CRC OK\n", mtd); 745 ret = 0; 746 747fail: 748 if (buf) { 749 /* Dummy read to ensure chip(s) are out of lock/suspend state */ 750 (void) read(mtd_fd, buf, 2); 751 free(buf); 752 } 753 754 if (mtd_fd >= 0) 755 close(mtd_fd); 756 if (fp) 757 fclose(fp); 758 return ret; 759} 760 761#endif 762