3 Copyright 2003, CyberTAN Inc. All Rights Reserved
5 This is UNPUBLISHED PROPRIETARY SOURCE CODE of CyberTAN Inc.
6 the contents of this file may not be disclosed to third parties,
7 copied or duplicated in any form without the prior written
8 permission of CyberTAN Inc.
10 This software should be used as a reference only, and it not
11 intended for production use!
13 THIS SOFTWARE IS OFFERED "AS IS", AND CYBERTAN GRANTS NO WARRANTIES OF ANY
14 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. CYBERTAN
15 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
16 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE
21 Copyright 2005, Broadcom Corporation
24 THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
25 KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
26 SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
27 FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
32 Modified for Tomato Firmware
33 Portions, Copyright (C) 2006-2009 Jonathan Zarate
40 #include <sys/sysmacros.h>
41 #include <sys/types.h>
46 #include <sys/ioctl.h>
47 #include <sys/sysinfo.h>
48 #include <bcmendian.h>
50 #include <linux/compiler.h>
51 #include <mtd/mtd-user.h>
53 #include <linux/mtd/mtd.h>
61 // #define DEBUG_SIMULATE
72 unsigned char res3
[10];
75 // Netgear CHK Header -> contains needed checksum information (for Netgear)
76 // Information here is stored big endian (vs. later information in TRX header and linux / rootfs, which is little endian)
77 // First two entries are commented out, as they are read manually to determine the file format and header length (and rewind is not working?)
80 //uint32_t header_len;
82 uint32_t kernel_chksum
;
83 uint32_t rootfs_chksum
;
86 uint32_t image_chksum
;
87 uint32_t header_chksum
;
88 /* char board_id[] - upto MAX_BOARD_ID_LEN, not NULL terminated! */
91 // -----------------------------------------------------------------------------
93 static uint32
*crc_table
= NULL
;
95 static void crc_done(void)
101 static int crc_init(void)
106 if (crc_table
== NULL
) {
107 if ((crc_table
= malloc(sizeof(uint32
) * 256)) == NULL
) return 0;
108 for (i
= 255; i
>= 0; --i
) {
110 for (j
= 8; j
> 0; --j
) {
111 if (c
& 1) c
= (c
>> 1) ^ 0xEDB88320L
;
120 static uint32
crc_calc(uint32 crc
, char *buf
, int len
)
123 crc
= crc_table
[(crc
^ *((char *)buf
)) & 0xFF] ^ (crc
>> 8);
129 // -----------------------------------------------------------------------------
131 static int mtd_open(const char *mtdname
, mtd_info_t
*mi
)
138 if (mtd_getinfo(mtdname
, &part
, &size
)) {
139 sprintf(path
, MTD_DEV(%d
), part
);
140 if ((f
= open(path
, O_RDWR
|O_SYNC
)) >= 0) {
141 if ((mi
) && ioctl(f
, MEMGETINFO
, mi
) != 0) {
151 static int _unlock_erase(const char *mtdname
, int erase
)
162 if (!wait_action_idle(5)) return 0;
163 set_action(ACT_ERASE_NVRAM
);
164 if (erase
) led(LED_DIAG
, 1);
170 if ((mf
= mtd_open(mtdname
, &mi
)) >= 0) {
173 ei
.length
= mi
.erasesize
;
174 for (ei
.start
= 0; ei
.start
< mi
.size
; ei
.start
+= mi
.erasesize
) {
175 printf("%sing 0x%x - 0x%x\n", erase
? "Eras" : "Unlock", ei
.start
, (ei
.start
+ ei
.length
) - 1);
180 loff_t offset
= ei
.start
;
182 if ((ret
= ioctl(mf
, MEMGETBADBLOCK
, &offset
)) > 0) {
183 printf("Skipping bad block at 0x%08x\n", ei
.start
);
185 } else if (ret
< 0) {
186 if (errno
== EOPNOTSUPP
) {
187 skipbb
= 1; // Not supported by this device
189 perror("MEMGETBADBLOCK");
196 if (ioctl(mf
, MEMUNLOCK
, &ei
) != 0) {
197 // perror("MEMUNLOCK");
202 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
213 printf("%sing 0x%x - 0x%x\n", erase
? "Eras" : "Unlock", ei
.start
, ei
.length
- 1);
216 if (ioctl(mf
, MEMUNLOCK
, &ei
) != 0) {
221 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
230 read(mf
, &buf
, sizeof(buf
));
234 if (erase
) led(LED_DIAG
, 0);
235 set_action(ACT_IDLE
);
237 if (r
) printf("\"%s\" successfully %s.\n", mtdname
, erase
? "erased" : "unlocked");
238 else printf("\nError %sing MTD\n", erase
? "eras" : "unlock");
244 int mtd_unlock(const char *mtdname
)
246 return _unlock_erase(mtdname
, 0);
249 int mtd_erase(const char *mtdname
)
251 return _unlock_erase(mtdname
, 1);
254 int mtd_unlock_erase_main(int argc
, char *argv
[])
259 while ((c
= getopt(argc
, argv
, "d:")) != -1) {
268 usage_exit(argv
[0], "-d part");
271 return _unlock_erase(dev
, strstr(argv
[0], "erase") ? 1 : 0);
274 int mtd_write_main(int argc
, char *argv
[])
280 struct trx_header trx
;
281 struct code_header cth
;
282 struct chk_header netgear_hdr
;
283 uint32 netgear_chk_len
;
298 while ((c
= getopt(argc
, argv
, "i:d:w")) != -1) {
312 if ((iname
== NULL
) || (dev
== NULL
)) {
313 usage_exit(argv
[0], "-i file -d part");
316 if (!wait_action_idle(10)) {
317 printf("System is busy\n");
320 set_action(ACT_WEB_UPGRADE
);
322 if ((f
= fopen(iname
, "r")) == NULL
) {
323 error
= "Error opening input file";
327 error
= "File contains an invalid header";
329 if (safe_fread(&sig
, 1, sizeof(sig
), f
) != sizeof(sig
)) {
334 case 0x47343557: // W54G G, GL
335 case 0x53343557: // W54S GS
336 case 0x73343557: // W54s GS v4
337 case 0x55343557: // W54U SL
338 case 0x31345257: // WR41 WRH54G
339 case 0x4E303233: // 320N WRT320N
340 case 0x4E583233: // 32XN E2000
341 case 0x4E303136: // 610N WRT610N v2
342 case 0x4E583136: // 61XN E3000
343 case 0x30303845: // E800 E800
344 case 0x30303945: // E900 E900
345 case 0x30303145: // E100 E1000
346 case 0x30323145: // E120 E1200v1
347 case 0x32323145: // E122 E1200v2
348 case 0x30353145: // E150 E1500
349 case 0x30353531: // 1550 E1550
350 case 0x3031304D: // M010 M10
351 case 0x3032304D: // M020 M20
352 case 0x3036314E: // N160 WRT160N
353 case 0x42435745: // EWCB WRT300N v1
354 case 0x4E303133: // 310N WRT310N v1/v2
355 // case 0x32435745: // EWC2 WRT300N?
356 case 0x3035314E: // N150 WRT150N
357 case 0x58353245: // E25X E2500
358 case 0x30303233: // 3200 E3200
359 case 0x30303234: // 4200 E4200
360 if (safe_fread(((char *)&cth
) + 4, 1, sizeof(cth
) - 4, f
) != (sizeof(cth
) - 4)) {
363 if (memcmp(cth
.id
, "U2ND", 4) != 0) {
367 // trx should be next...
368 if (safe_fread(&sig
, 1, sizeof(sig
), f
) != sizeof(sig
)) {
372 case 0x5E24232A: // Netgear
373 // Get the Netgear header length
374 if (safe_fread(&n
, 1, sizeof(n
), f
) != sizeof(n
)) {
377 // And Byte Swap, Netgear header is big endian, machine is little endian
379 _dprintf("Read Netgear Header Length: 0x%x\n", n
);
382 // Read (formatted) Netgear CHK header (now that we know how long it is)
383 //rewind(f); ... disabled, not working for some reason? Adjust structure above to account for this.
384 if (safe_fread(&netgear_hdr
, 1, n
-sizeof(sig
)-sizeof(n
), f
) != n
-sizeof(sig
)-sizeof(n
)) {
387 _dprintf("Read Netgear Header, Magic=0x%x, Length=0x%x\n", sig
, n
);
389 // TRX (MAGIC) should be next...
390 if (safe_fread(&sig
, 1, sizeof(sig
), f
) != sizeof(sig
)) {
393 _dprintf("Read TRX Header, Magic=0x%x\n", sig
);
398 #ifndef CONFIG_BCMWL6
399 case TRX_MAGIC_F7D3301
:
400 case TRX_MAGIC_F7D3302
:
401 case TRX_MAGIC_F7D4302
:
402 case TRX_MAGIC_F5D8235V3
:
410 if (safe_fread(&sig
, 1, sizeof(sig
), f
) != sizeof(sig
)) {
414 case 0x50705710: // WR850G
416 if (safe_fread(&sig
, 1, sizeof(sig
), f
) != sizeof(sig
)) {
426 if (sig
!= TRX_MAGIC
) {
429 if ((safe_fread(((char *)&trx
) + 4, 1, sizeof(trx
) - 4, f
) != (sizeof(trx
) - 4)) || (trx
.len
<= sizeof(trx
))) {
437 #ifndef CONFIG_BCMWL6
439 trx
.magic
= TRX_MAGIC_F7D3301
;
442 trx
.magic
= TRX_MAGIC_F7D3302
;
445 trx
.magic
= TRX_MAGIC_F7D4302
;
447 case MODEL_F5D8235v3
:
448 trx
.magic
= TRX_MAGIC_F5D8235V3
;
458 error
= "Not enough memory";
461 crc
= crc_calc(0xFFFFFFFF, (char *)&trx
.flag_version
, sizeof(struct trx_header
) - OFFSETOF(struct trx_header
, flag_version
));
463 if (trx
.flag_version
& TRX_NO_HEADER
) {
464 trx
.len
-= sizeof(struct trx_header
);
465 _dprintf("don't write header\n");
468 _dprintf("trx len=%db 0x%x\n", trx
.len
, trx
.len
);
470 if ((mf
= mtd_open(dev
, &mi
)) < 0) {
471 error
= "Error opening MTD device";
475 if (mi
.erasesize
< sizeof(struct trx_header
)) {
476 error
= "Error obtaining MTD information";
480 _dprintf("mtd size=%6x, erasesize=%6x\n", mi
.size
, mi
.erasesize
);
482 total
= ROUNDUP(trx
.len
, mi
.erasesize
);
483 if (total
> mi
.size
) {
484 error
= "File is too big to fit in MTD";
489 if ((si
.freeram
* si
.mem_unit
) > (total
+ (256 * 1024))) {
493 // ei.length = ROUNDUP((si.freeram - (256 * 1024)), mi.erasesize);
494 ei
.length
= mi
.erasesize
;
496 _dprintf("freeram=%ld ei.length=%d total=%u\n", si
.freeram
, ei
.length
, total
);
498 if ((buf
= malloc(ei
.length
)) == NULL
) {
499 error
= "Not enough memory";
503 #ifdef DEBUG_SIMULATE
505 if ((of
= fopen("/mnt/out.bin", "w")) == NULL
) {
506 error
= "Error creating test file";
511 if (trx
.flag_version
& TRX_NO_HEADER
) {
515 memcpy(buf
, &trx
, sizeof(trx
));
518 _dprintf("trx.len=%ub 0x%x ofs=%ub 0x%x\n", trx
.len
, trx
.len
, ofs
, ofs
);
519 netgear_chk_len
= trx
.len
;
523 for (ei
.start
= 0; ei
.start
< total
; ei
.start
+= ei
.length
) {
524 n
= MIN(ei
.length
, trx
.len
) - ofs
;
525 if (safe_fread(buf
+ ofs
, 1, n
, f
) != n
) {
526 error
= "Error reading file";
529 trx
.len
-= (n
+ ofs
);
531 crc
= crc_calc(crc
, buf
+ ofs
, n
);
534 _dprintf("crc=%8x trx.crc=%8x\n", crc
, trx
.crc32
);
535 if (crc
!= trx
.crc32
) {
536 error
= "Image is corrupt";
542 printf("Writing %x-%x\r", ei
.start
, (ei
.start
+ ei
.length
) - 1);
545 _dprintf("ofs=%ub n=%ub 0x%x trx.len=%ub ei.start=0x%x ei.length=0x%x mi.erasesize=0x%x\n",
546 ofs
, n
, n
, trx
.len
, ei
.start
, ei
.length
, mi
.erasesize
);
550 _dprintf(" erase start=%x len=%x\n", ei
.start
, ei
.length
);
551 _dprintf(" write %x\n", n
);
553 #ifdef DEBUG_SIMULATE
554 if (fwrite(buf
, 1, n
, of
) != n
) {
556 error
= "Error writing to test file";
560 ioctl(mf
, MEMUNLOCK
, &ei
);
561 if (ioctl(mf
, MEMERASE
, &ei
) != 0 && model
!= MODEL_WNR3500LV2
) {
562 error
= "Error erasing MTD block";
565 if (write(mf
, buf
, n
) != n
) {
566 error
= "Error writing to MTD device";
573 // Netgear WNR3500L: write fake len and checksum at the end of mtd
574 // Add Netgear WNDR4000 - write real len and checksum (arrmo, Dec 21/13)
580 case MODEL_WNR2000v2
:
582 error
= "Error writing Netgear CRC";
584 // Netgear CFE has the offset of the checksum hardcoded as
585 // 0x78FFF8 on 8MB flash, and 0x38FFF8 on 4MB flash - in both
586 // cases this is 8 last bytes in the block exactly 6 blocks to the end.
587 // We rely on linux partition to be sized correctly by the kernel,
588 // so the checksum area doesn't fall outside of the linux partition,
589 // and doesn't override the rootfs.
590 // Note: For WNDR4000, the target address (offset) inside Linux is 0x6FFFF8 (displayed by CFE when programmed via tftp)
591 if (model
== MODEL_WNDR4000
) {
593 // Endian "convert" - machine is little endian, but header is big endian
594 crc
= BCMSWAP32(netgear_hdr
.kernel_chksum
);
597 ofs
= (mi
.size
> (4 *1024 * 1024) ? 0x78FFF8 : 0x38FFF8) - 0x040000;
598 n
= 0x00000004; // fake length - little endian
599 crc
= 0x02C0010E; // fake crc - little endian
601 _dprintf("Netgear CRC, Data to Write: CRC=0x%x, Len=0x%x, Offset=0x%x\n", crc
, n
, ofs
);
602 memcpy(&imageInfo
[0], (char *)&n
, 4);
603 memcpy(&imageInfo
[4], (char *)&crc
, 4);
605 ei
.start
= (ofs
/ mi
.erasesize
) * mi
.erasesize
;
606 ei
.length
= mi
.erasesize
;
607 _dprintf("Netgear CRC: Erase Start=0x%x, Length=0x%x\n", ei
.start
, ei
.length
);
609 if (lseek(mf
, ei
.start
, SEEK_SET
) < 0) {
610 _dprintf("Netgear CRC: lseek() error\n");
614 if (!(buf
= malloc(mi
.erasesize
))) {
615 _dprintf("Netgear CRC: malloc() error\n");
618 if (read(mf
, buf
, mi
.erasesize
) != mi
.erasesize
) {
619 _dprintf("Netgear CRC: read() error\n");
622 if (lseek(mf
, ei
.start
, SEEK_SET
) < 0) {
623 _dprintf("Netgear CRC: lseed() error\n");
627 tmp
= buf
+ (ofs
% mi
.erasesize
);
628 memcpy(tmp
, imageInfo
, sizeof(imageInfo
));
630 #ifdef DEBUG_SIMULATE
631 if (fseek(of
, ei
.start
, SEEK_SET
) < 0)
633 if (fwrite(buf
, 1, mi
.erasesize
, of
) != n
)
637 ioctl(mf
, MEMUNLOCK
, &ei
);
638 if (ioctl(mf
, MEMERASE
, &ei
) != 0) {
639 _dprintf("Netgear CRC: ioctl() error\n");
642 if (write(mf
, buf
, mi
.erasesize
) != mi
.erasesize
) {
643 _dprintf("Netgear CRC: write() error\n");
650 _dprintf("%s.\n", error
? : "Write Netgear Length/CRC Completed.");
651 // ignore crc write errors
656 #ifdef DEBUG_SIMULATE
663 // dummy read to ensure chip(s) are out of lock/suspend state
664 read(mf
, &n
, sizeof(n
));
671 #ifdef DEBUG_SIMULATE
672 set_action(ACT_IDLE
);
675 printf("%s\n", error
? error
: "Image successfully flashed");
676 _dprintf("%s\n", error
? error
: "Image successfully flashed");
677 return (error
? 1 : 0);