diff options
Diffstat (limited to 'tools/firmware-utils/src')
75 files changed, 14076 insertions, 1645 deletions
diff --git a/tools/firmware-utils/src/addpattern.c b/tools/firmware-utils/src/addpattern.c index da6797c9ced..9bc48653356 100644 --- a/tools/firmware-utils/src/addpattern.c +++ b/tools/firmware-utils/src/addpattern.c @@ -58,6 +58,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <errno.h> #include <time.h> #include <unistd.h> #include <sys/stat.h> @@ -77,10 +78,11 @@ /* (from 3.00.24 firmware cyutils.h) */ #define SUPPORT_4704_CHIP 0x0008 #define SUPPORT_5352E_CHIP 0x0010 +/* (from WD My Net Wi-Fi Range Extender's cyutils.s) */ +#define SUPPORT_4703_CHIP 0x0020 struct code_header { /* from cyutils.h */ - char magic[4]; - char res1[4]; /* for extra magic */ + char magic[8]; char fwdate[3]; char fwvern[3]; char id[4]; /* U2ND */ @@ -105,11 +107,25 @@ struct board_info { struct board_info boards[] = { { + .id = "E2100L", + .pattern = "NL1X", + .hw_ver = 0x00, + .sn = 0x0f, + .flags = {0x3f, 0x00}, + }, + { .id = "WRT160NL", .pattern = "NL16", .hw_ver = 0x00, .sn = 0x0f, .flags = {0x3f, 0x00}, + }, + { + .id = "mynet-rext", + .pattern = "WDHNSTFH", + .hw_ver = 0x00, + .sn = 0x00, + .flags = {0x3f, 0x00}, }, { /* Terminating entry */ .id = NULL, @@ -126,6 +142,20 @@ void usage(void) exit(EXIT_FAILURE); } +static time_t source_date_epoch = -1; +static void set_source_date_epoch() { + char *env = getenv("SOURCE_DATE_EPOCH"); + char *endptr = env; + errno = 0; + if (env && *env) { + source_date_epoch = strtoull(env, &endptr, 10); + if (errno || (endptr && *endptr != '\0')) { + fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); + exit(1); + } + } +} + struct board_info *find_board(char *id) { struct board_info *board; @@ -243,8 +273,8 @@ int main(int argc, char **argv) hdr->flags[1] = board->flags[1]; } - if (strlen(pattern) != 4) { - fprintf(stderr, "illegal pattern \"%s\": length != 4\n", pattern); + if (strlen(pattern) > 8) { + fprintf(stderr, "illegal pattern \"%s\"\n", pattern); usage(); } @@ -258,7 +288,10 @@ int main(int argc, char **argv) usage(); } - if (time(&t) == (time_t)(-1)) { + set_source_date_epoch(); + if (source_date_epoch != -1) { + t = source_date_epoch; + } else if ((time(&t) == (time_t)(-1))) { fprintf(stderr, "time call failed\n"); return EXIT_FAILURE; } @@ -270,16 +303,16 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - memcpy(&hdr->magic, pattern, 4); + memcpy(hdr->magic, pattern, strlen(pattern)); if (pbotflag) - memcpy(&hdr->res1, pbotpat, 4); + memcpy(&hdr->magic[4], pbotpat, 4); hdr->fwdate[0] = ptm->tm_year % 100; hdr->fwdate[1] = ptm->tm_mon + 1; hdr->fwdate[2] = ptm->tm_mday; hdr->fwvern[0] = v0; hdr->fwvern[1] = v1; hdr->fwvern[2] = v2; - memcpy(&hdr->id, CODE_ID, strlen(CODE_ID)); + memcpy(hdr->id, CODE_ID, strlen(CODE_ID)); off = sizeof(struct code_header); diff --git a/tools/firmware-utils/src/airlink.c b/tools/firmware-utils/src/airlink.c deleted file mode 100644 index 560a58df59e..00000000000 --- a/tools/firmware-utils/src/airlink.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Thanks to Vassily Galinsky for this tool -***************************************************************************** -AIRLINK AR525W firmware image structure - -8:-5 Extended (httpd) header checksum - sum2 - -4:-1 Extended (httpd) header magic - "ARRN" - 0x4e525241 - 0: 3 Standard (tftpd) header magic - "GMTK" - 0x4b544d47 - 4: 7 Standard (tftpd) header checksum - sum1 - 8: b 0 - c: f Size of compressed linux kernel - 10:13 Size of firmware image file with standard header - 14:17 Product code - 0x5 - 18:1b Bootloader checksum - sum0 - 1c:1f 0 - 20: Compressed linux kernel - - Squashfs or jffs2 file system - kernel dependent - either 32 bytes or sector aligned -*****************************************************************************/ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <sys/mman.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <netinet/in.h> - -typedef unsigned char uchar; - -uint32_t crctab[257] = { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - 0 -}; - -uint32_t header[] = { - 0x00000000, 0x4e525241, - 0x4b544d47, 0x00000000, 0x00000000, 0x000afd4a, - 0x00000000, 0x00000005, 0x00000000, 0x00000000 -}; - -static int JFFS2 = 0; - -int generate_image(char *kname, char *fsname, char *fname, int EHDR) -{ - int i; - uint32_t lenk, lens; - uchar *bk, *bs; - int fkd, ffd, fsd; - fkd = open(kname, O_RDONLY); - ffd = creat(fname, 0644); - if ((fkd < 0) || (ffd < 0)) - return -1; - if (fsname) { - fsd = open(fsname, O_RDONLY); - if (fsd < 0) - return -1; - } - lenk = lseek(fkd, 0, SEEK_END); - header[5] = lenk; - bk = (uchar *) mmap(NULL, lenk, PROT_READ, MAP_SHARED, fkd, 0); - if (bk == MAP_FAILED) - return -1; - if (fsname) { - lens = lseek(fsd, 0, SEEK_END); - bs = (uchar *) mmap(NULL, lens, PROT_READ, MAP_SHARED, fsd, - 0); - if (bs == MAP_FAILED) - return -1; - } - if (EHDR) - write(ffd, header, 0x28); - else - write(ffd, header + 2, 0x20); - write(ffd, bk, lenk); - lenk += 0x20; - if (!JFFS2) JFFS2 = 0x20; - printf("Padding header+kernel - 0x%x + 0x%x = 0x%x\n", - lenk, ((lenk - 1 + JFFS2) / JFFS2) * JFFS2 - lenk, - ((lenk - 1 + JFFS2) / JFFS2) * JFFS2); - for (i = 0; i < ((lenk - 1 + JFFS2) / JFFS2) * JFFS2 - lenk; i++) - write(ffd, header, 1); - if (fsname) { - write(ffd, bs, lens); - close(fsd); - } - close(ffd); - close(fkd); - return 0; -} - -uint32_t crc32(uchar * buf, uint32_t len) -{ - register int i; - uint32_t sum; - register uint32_t s0; - s0 = ~0; - for (i = 0; i < len; i++) { - s0 = (s0 >> 8) ^ crctab[(uchar) (s0 & 0xFF) ^ buf[i]]; - } - sum = ~s0; - return sum; -} - -void usage(char *prog) -{ - printf("Usage: %s [-b 0/1] image_filename \n", prog); - printf(" update checksums for firmware file\n"); - printf - ("Usage: %s [-b 0/1] [-j erasesize_in_kibytes] [-e] kernel filesystem image_filename \n", - prog); - printf(" generate firmware file and update checksums\n"); - printf("--------------------------------------------------\n"); - printf(" -e - generate header for web upload\n"); - printf(" -b 0/1 - clear/update bootloader checksum\n"); - printf(" -j erasesize_in_kibytes - generate header for jffs2 filesystem\n"); -} - -int main(int argc, char **argv) -{ - uchar b[0x400]; - char EHDR = 0; - char BHDR = 0; - int c, fd; - extern char *optarg; - extern int optind, optopt; - - while ((c = getopt(argc, argv, "b:ej:")) != -1) { - switch (c) { - case 'b': - if (optarg[0] == '1') - BHDR = 1; - break; - case 'e': - EHDR = 1; - break; - case 'j': - sscanf(optarg, "%i", &JFFS2); - JFFS2 *= 1024; - break; - case '?': - fprintf(stderr, "\nError: unknown arg %c\n\n\n", - optopt); - usage(argv[0]); - exit(-1); - } - } - if (((argc - optind) < 1) && ((argc - optind) > 3)) { - usage(argv[0]); - exit(-1); - } - if ((argc - optind) == 3) - if (generate_image - (argv[optind], argv[optind + 1], argv[optind + 2], - EHDR)) { - fprintf(stderr, - "\nError generating image file %s\n\n\n", - argv[argc - 1]); - usage(argv[0]); - exit(-1); - } - if ((argc - optind) == 2) - if (generate_image - (argv[optind], NULL, argv[optind + 1], EHDR)) { - fprintf(stderr, - "\nError generating image file %s\n\n\n", - argv[argc - 1]); - usage(argv[0]); - exit(-1); - } - - fd = open(argv[argc - 1], O_RDWR); - if (fd < 0) { - fprintf(stderr, "\nImage file not found %s\n\n\n", - argv[argc - 1]); - usage(argv[0]); - exit(-1); - } - long i, len = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - uchar *buf = malloc(len); - read(fd, buf, len); - uint32_t sum, l0; - uint32_t MagicS = 0x474d544b; - uint32_t MagicE = 0x4152524e; - if (ntohl(*((uint32_t *) buf)) == MagicS) { - fprintf(stderr, - "Image without extra 8 bytes - Standard header\n"); - buf[0x10] = len & 0xff; - buf[0x11] = (len >> 8) & 0xff; - buf[0x12] = (len >> 16) & 0xff; - buf[0x13] = (len >> 24) & 0xff; - lseek(fd, 0x10, SEEK_SET); - write(fd, buf + 0x10, 0x4); - EHDR = 0; - } else if ((ntohl(*((uint32_t *) (buf + 0x8))) == MagicS) - && ((ntohl(*((uint32_t *) (buf + 0x4))) == MagicE))) { - fprintf(stderr, - "Image with extra 8 bytes - Extended header\n"); - *((uint32_t *) (buf + 0x18)) = len - 8; - buf[0x18] = (len - 8) & 0xff; - buf[0x19] = ((len - 8) >> 8) & 0xff; - buf[0x1a] = ((len - 8) >> 16) & 0xff; - buf[0x1b] = ((len - 8) >> 24) & 0xff; - lseek(fd, 0x18, SEEK_SET); - write(fd, buf + 0x18, 0x4); - buf += 8; - EHDR = 1; - } else if (len == buf[0x10] | ((uint32_t)buf[0x11] << 8) | ((uint32_t)buf[0x12] << 16) | ((uint32_t)buf[0x13] << 24)) { - fprintf(stderr, - "Image without extra 8 bytes - Standard header\n"); - EHDR = 0; - } else if (len == (buf[0x18] | ((uint32_t)buf[0x19] << 8) | ((uint32_t)buf[0x1a] << 16) | ((uint32_t)buf[0x1b] << 24)) + 8) { - fprintf(stderr, - "Image with extra 8 bytes - Extended header\n"); - buf += 8; - EHDR = 1; - } else { - fprintf(stderr, "ERROR: Wrong image size\n"); - exit(-1); - } - l0 = buf[0x10] | ((uint32_t)buf[0x11] << 8) | ((uint32_t)buf[0x12] << 16) | ((uint32_t)buf[0x13] << 24); - if (!BHDR) - *((uint32_t *) & buf[0x18]) = 0; - unsigned long sum0 = buf[0x18] | ((uint32_t)buf[0x19] << 8) | ((uint32_t)buf[0x1a] << 16) | ((uint32_t)buf[0x1b] << 24); - unsigned long sum1 = buf[0x4] | ((uint32_t)buf[0x5] << 8) | ((uint32_t)buf[0x6] << 16) | ((uint32_t)buf[0x7] << 24); - *((uint32_t *) & buf[0x4]) = 0x0L; - memcpy(b, buf, 0x100); - memcpy(b + 0x100, buf + ((l0 >> 1) - ((l0 & 0x6) >> 1)), 0x100); - memcpy(b + 0x200, buf + (l0 - 0x200), 0x200); - *((uint32_t *) & b[0x18]) = 0x0L; - - sum = crc32(b, 0x400); - printf("CRC32 sum0 - (%x, %x, %x)\n", sum, sum0, 0x400); - if (EHDR) - lseek(fd, 0x20, SEEK_SET); - else - lseek(fd, 0x18, SEEK_SET); - buf[0x18] = (BHDR ? sum : sum0) & 0xff; - buf[0x19] = ((BHDR ? sum : sum0) >> 8) & 0xff; - buf[0x1a] = ((BHDR ? sum : sum0) >> 16) & 0xff; - buf[0x1b] = ((BHDR ? sum : sum0) >> 24) & 0xff; - write(fd, &buf[0x18], 0x4); - - sum = crc32(buf, l0); - printf("CRC32 sum1 - (%x, %x, %x)\n", sum, sum1, l0); - if (EHDR) - lseek(fd, 0xC, SEEK_SET); - else - lseek(fd, 0x4, SEEK_SET); - buf[0x4] = sum & 0xff; - buf[0x5] = (sum >> 8) & 0xff; - buf[0x6] = (sum >> 16) & 0xff; - buf[0x7] = (sum >> 24) & 0xff; - write(fd, &buf[0x4], 0x4); - if (EHDR) { - unsigned long sum2 = buf[-0x8] | ((uint32_t)buf[-0x7] << 8) | ((uint32_t)buf[-0x6] << 16) | ((uint32_t)buf[-0x5] << 24); - *((uint32_t *) & buf[-0x8]) = 0L; - sum = crc32(buf - 0x4, len - 0x4); - printf("CRC32 sum2 - (%x, %x, %x)\n", sum, sum2, - len - 0x4); - lseek(fd, 0, SEEK_SET); - *((uint32_t *) & buf[-0x8]) = htonl(sum); - write(fd, &buf[-0x8], 0x4); - buf -= 8; - } - close(fd); - free(buf); - return 0; -} diff --git a/tools/firmware-utils/src/asustrx.c b/tools/firmware-utils/src/asustrx.c new file mode 100644 index 00000000000..67f2680b31b --- /dev/null +++ b/tools/firmware-utils/src/asustrx.c @@ -0,0 +1,256 @@ +/* + * asustrx + * + * Copyright (C) 2015 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <byteswap.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le32(x) bswap_32(x) +#define le32_to_cpu(x) bswap_32(x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le32(x) (x) +#define le32_to_cpu(x) (x) +#else +#error "Unsupported endianness" +#endif + +#define TRX_MAGIC 0x30524448 +#define TRX_FLAGS_OFFSET 12 + +struct trx_header { + uint32_t magic; + uint32_t length; + uint32_t crc32; + uint16_t flags; + uint16_t version; + uint32_t offset[3]; +}; + +struct asustrx_tail { + uint8_t version[4]; + char productid[12]; + uint8_t unused[48]; +}; + +char *in_path = NULL; +char *out_path = NULL; +char *productid = NULL; +uint8_t version[4] = { }; + +static const uint32_t crc32_tbl[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +static void parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "i:o:p:v:")) != -1) { + switch (c) { + case 'i': + in_path = optarg; + break; + case 'o': + out_path = optarg; + break; + case 'p': + productid = optarg; + break; + case 'v': + if (sscanf(optarg, "%hu.%hu.%hu.%hu", &version[0], &version[1], &version[2], &version[3]) != 4) + fprintf(stderr, "Version %s doesn't match suppored 4-digits format\n", optarg); + break; + } + } +} + +static void usage() { + printf("Usage:\n"); + printf("\t-i file\t\t\t\tinput TRX file\n"); + printf("\t-o file\t\t\t\toutput Asus TRX file\n"); + printf("\t-p productid\t\t\tproduct (device) ID\n"); + printf("\t-v version\t\t\tfirmware version formatted with 4 digits like: 1.2.3.4\n"); +} + +int main(int argc, char **argv) { + struct trx_header hdr; + struct asustrx_tail tail = { }; + FILE *in, *out; + uint8_t buf[1024]; + size_t bytes; + size_t length = 0; + uint32_t crc32 = 0xffffffff; + int i; + int err = 0; + + /* Parse & validate arguments */ + parse_options(argc, argv); + if (!in_path || !out_path || !productid) { + usage(); + err = -EINVAL; + goto err; + } + + /* Fill Asus tail */ + tail.version[0] = version[0]; + tail.version[1] = version[1]; + tail.version[2] = version[2]; + tail.version[3] = version[3]; + strncpy(tail.productid, productid, sizeof(tail.productid)); + + /* Open files */ + in = fopen(in_path, "r"); + if (!in) { + fprintf(stderr, "Couldn't open %s\n", in_path); + err = -EIO; + goto err; + } + out = fopen(out_path, "w+"); + if (!out) { + fprintf(stderr, "Couldn't open %s\n", out_path); + err = -EIO; + goto err; + } + + /* Check is there is empty place for Asus tail */ + bytes = sizeof(struct asustrx_tail); + fseek(in, -bytes, SEEK_END); + if (fread(buf, 1, bytes, in) != bytes) { + fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path); + err = -EIO; + goto err; + } + for (i = 0; i < bytes; i++) { + if (buf[i]) { + fprintf(stderr, "Input TRX doesn't have last 64 B empty %s\n", out_path); + err = -ENOSPC; + goto err; + } + } + + /* Copy whole TRX */ + rewind(in); + while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { + if (fwrite(buf, 1, bytes, out) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); + err = -EIO; + goto err; + } + } + + /* Overwrite last 64 B with Asus tail */ + bytes = sizeof(tail); + fseek(out, -bytes, SEEK_CUR); + if (fwrite(&tail, 1, bytes, out) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); + err = -EIO; + goto err; + } + + /* Calculate crc32 */ + fseek(out, TRX_FLAGS_OFFSET, SEEK_SET); + length = TRX_FLAGS_OFFSET; + while ((bytes = fread(buf, 1, sizeof(buf), out )) > 0) { + length += bytes; + for (i = 0; i < bytes; i++) + crc32 = crc32_tbl[(crc32 ^ buf[i]) & 0xff] ^ (crc32 >> 8); + } + + /* Update header */ + bytes = sizeof(hdr); + rewind(out); + if (fread(&hdr, 1, sizeof(hdr), out) != bytes) { + fprintf(stderr, "Couldn't read %zu B from %s\n", bytes, in_path); + err = -EIO; + goto err; + } + hdr.crc32 = cpu_to_le32(crc32); + rewind(out); + if (fwrite(&hdr, 1, bytes, out) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); + err = -EIO; + goto err; + } + +err: + if (out) + fclose(out); + if (in) + fclose(in); + return err; +} diff --git a/tools/firmware-utils/src/bcm_tag.h b/tools/firmware-utils/src/bcm_tag.h index 2e977a2d7a6..2730edc9ad9 120000..100644 --- a/tools/firmware-utils/src/bcm_tag.h +++ b/tools/firmware-utils/src/bcm_tag.h @@ -1 +1,70 @@ -../../../target/linux/brcm63xx/files/arch/mips/include/asm/mach-bcm63xx/bcm_tag.h
\ No newline at end of file +#ifndef __BCM63XX_TAG_H +#define __BCM63XX_TAG_H + +#define TAGVER_LEN 4 /* Length of Tag Version */ +#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */ +#define SIG1_LEN 20 /* Company Signature 1 Length */ +#define SIG2_LEN 14 /* Company Signature 2 Lenght */ +#define BOARDID_LEN 16 /* Length of BoardId */ +#define ENDIANFLAG_LEN 2 /* Endian Flag Length */ +#define CHIPID_LEN 6 /* Chip Id Length */ +#define IMAGE_LEN 10 /* Length of Length Field */ +#define ADDRESS_LEN 12 /* Length of Address field */ +#define DUALFLAG_LEN 2 /* Dual Image flag Length */ +#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */ +#define RSASIG_LEN 20 /* Length of RSA Signature in tag */ +#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */ +#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */ +#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */ +#define CRC_LEN 4 /* Length of CRC in bytes */ +#define ALTTAGINFO_LEN 54 /* Alternate length for vendor information; Pirelli */ + +#define NUM_PIRELLI 2 +#define IMAGETAG_CRC_START 0xFFFFFFFF + +#define PIRELLI_BOARDS { \ + "AGPF-S0", \ + "DWV-S0", \ +} + +/* + * The broadcom firmware assumes the rootfs starts the image, + * therefore uses the rootfs start (flashImageAddress) + * to determine where to flash the image. Since we have the kernel first + * we have to give it the kernel address, but the crc uses the length + * associated with this address (rootLength), which is added to the kernel + * length (kernelLength) to determine the length of image to flash and thus + * needs to be rootfs + deadcode (jffs2 EOF marker) +*/ + +struct bcm_tag { + char tagVersion[TAGVER_LEN]; // 0-3: Version of the image tag + char sig_1[SIG1_LEN]; // 4-23: Company Line 1 + char sig_2[SIG2_LEN]; // 24-37: Company Line 2 + char chipid[CHIPID_LEN]; // 38-43: Chip this image is for + char boardid[BOARDID_LEN]; // 44-59: Board name + char big_endian[ENDIANFLAG_LEN]; // 60-61: Map endianness -- 1 BE 0 LE + char totalLength[IMAGE_LEN]; // 62-71: Total length of image + char cfeAddress[ADDRESS_LEN]; // 72-83: Address in memory of CFE + char cfeLength[IMAGE_LEN]; // 84-93: Size of CFE + char flashImageStart[ADDRESS_LEN]; // 94-105: Address in memory of image start (kernel for OpenWRT, rootfs for stock firmware) + char flashRootLength[IMAGE_LEN]; // 106-115: Size of rootfs for flashing + char kernelAddress[ADDRESS_LEN]; // 116-127: Address in memory of kernel + char kernelLength[IMAGE_LEN]; // 128-137: Size of kernel + char dualImage[DUALFLAG_LEN]; // 138-139: Unused at present + char inactiveFlag[INACTIVEFLAG_LEN]; // 140-141: Unused at present + char rsa_signature[RSASIG_LEN]; // 142-161: RSA Signature (unused at present; some vendors may use this) + char information1[TAGINFO1_LEN]; // 162-191: Compilation and related information (not generated/used by OpenWRT) + char flashLayoutVer[FLASHLAYOUTVER_LEN];// 192-195: Version flash layout + char fskernelCRC[CRC_LEN]; // 196-199: kernel+rootfs CRC32 + char information2[TAGINFO2_LEN]; // 200-215: Unused at present except Alice Gate where is is information + char imageCRC[CRC_LEN]; // 216-219: CRC32 of image less imagetag (kernel for Alice Gate) + char rootfsCRC[CRC_LEN]; // 220-223: CRC32 of rootfs partition + char kernelCRC[CRC_LEN]; // 224-227: CRC32 of kernel partition + char imageSequence[4]; // 228-231: Image sequence number + char rootLength[4]; // 232-235: steal from reserved1 to keep the real root length so we can use in the flash map even after we have change the rootLength to 0 to satisfy devices that check CRC on every boot + char headerCRC[CRC_LEN]; // 236-239: CRC32 of header excluding tagVersion + char reserved2[16]; // 240-255: Unused at present +}; + +#endif /* __BCM63XX_TAG_H */ diff --git a/tools/firmware-utils/src/bcmalgo.c b/tools/firmware-utils/src/bcmalgo.c new file mode 100644 index 00000000000..e7d3b113bec --- /dev/null +++ b/tools/firmware-utils/src/bcmalgo.c @@ -0,0 +1,248 @@ +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <sys/time.h> +#include <sys/stat.h> +#include "bcmalgo.h" + + +#define UTIL_VERSION "0.1" +#define ENDIAN_REVERSE_NEEDED + +uint32_t reverse_endian32 ( uint32_t data ) +{ +#ifdef ENDIAN_REVERSE_NEEDED + return 0 | ( data & 0x000000ff ) << 24 + | ( data & 0x0000ff00 ) << 8 + | ( data & 0x00ff0000 ) >> 8 + | ( data & 0xff000000 ) >> 24; +#else + return data; +#endif +} + +uint16_t reverse_endian16 ( uint16_t data ) +{ +#ifdef ENDIAN_REVERSE_NEEDED + return 0 | ( data & 0x00ff ) << 8 + | ( data & 0xff00 ) >> 8; +#else + return data; +#endif +} + + + +uint32_t get_buffer_crc ( char* filebuffer,size_t size ) +{ + + long crc=0xffffffffL; + long crcxor = 0xffffffffL; + long num4 = 0xffffffffL; + long num5 = size; + long num6 = 0x4c11db7L; + long num7 = 0x80000000L; + int i; + long j; + for ( i = 0; i < ( num5 ); i++ ) + { + long num2 = filebuffer[i]; + for ( j = 0x80L; j != 0L; j = j >> 1 ) + { + long num3 = crc & num7; + crc = crc << 1; + if ( ( num2 & j ) != 0L ) + { + num3 ^= num7; + } + if ( num3 != 0L ) + { + crc ^= num6; + } + } + } + crc ^= crcxor; + crc &= num4; + + uint8_t b1 = ( uint8_t ) ( ( crc & -16777216L ) >> 0x18 ); + uint8_t b2 = ( uint8_t ) ( ( crc & 0xff0000L ) >> 0x10 ); + uint8_t b3 = ( uint8_t ) ( ( crc & 0xff00L ) >> 8 ); + uint8_t b4 = ( uint8_t ) ( crc & 0xffL ); + int32_t crc_result = ( b1 | b2 << 8| b3 << 16| b4 <<24 ); + return reverse_endian32 ( crc_result ); +} + +//Thnx to Vector for the algo. +uint32_t get_file_crc ( char* filename ) +{ + struct stat buf; + stat ( filename,&buf ); + char* filebuffer = malloc ( buf.st_size+10 ); + FILE* fd = fopen ( filename,"r" ); + fread ( filebuffer, 1, buf.st_size,fd ); + fclose ( fd ); + uint32_t crc = get_buffer_crc ( filebuffer,buf.st_size ); + free ( filebuffer ); + return crc; +} + + + +uint16_t get_hcs ( ldr_header_t* hd ) +{ + uint8_t* head = ( uint8_t* ) hd; + uint8_t hcs_minor; + uint8_t hcs_major; + uint16_t n = 0xffff; + uint16_t m = 0; + int state = 0; + int i,j; + for ( i = 0; i < 0x54; i++ ) + { + uint16_t m = head[i]; + m = m << 8; + for ( j = 0; j < 8; j++ ) + { + if ( ( ( n ^ m ) & 0x8000 ) == 0 ) + { + state = 0; + } + else + { + state = 1; + } + n = n << 1; + if ( state ) + { + n ^= 0x1021; + } + m = m << 1; + } + n &= 0xffff; + } + n ^= 0xffff; + hcs_major = ( uint8_t ) ( ( n & 0xff00 ) >> 8 ); + hcs_minor = ( uint8_t ) ( n & 0xff ); + uint16_t hcs = hcs_major <<8 | hcs_minor; + return hcs; +} + +ldr_header_t* construct_header ( uint32_t magic, uint16_t rev_maj,uint16_t rev_min, uint32_t build_date, uint32_t filelen, uint32_t ldaddress, const char* filename, uint32_t crc_data ) +{ + ldr_header_t* hd = malloc ( sizeof ( ldr_header_t ) ); + hd->magic=reverse_endian16 ( magic ); + hd->control=0; //FixMe: Make use of it once compression is around + hd->rev_min = reverse_endian16 ( rev_min ); + hd->rev_maj = reverse_endian16 ( rev_maj ); + hd->build_date = reverse_endian32 ( build_date ); + hd->filelen = reverse_endian32 ( filelen ); + hd->ldaddress = reverse_endian32 ( ldaddress ); + printf ( "Creating header for %s...\n", filename ); + if ( strlen ( filename ) >63 ) + { + printf ( "[!] Filename too long - stripping it to 63 bytes.\n" ); + strncpy ( ( char* ) &hd->filename, filename, 63 ); + hd->filename[63]=0x00; + } + else + { + strcpy ( ( char* ) &hd->filename, filename ); + } + hd->crc=reverse_endian32 ( crc_data ); + hd->hcs = reverse_endian16 ( get_hcs ( hd ) ); + return hd; +} + +static char control_unc[] = "Uncompressed"; +static char control_lz[] = "LZRW1/KH"; +static char control_mlzo[] = "mini-LZO"; +static char control_nrv[] = "NRV2D99 [Bootloader?]"; +static char control_nstdlzma[] = "(non-standard) LZMA"; +static char control_unk[] = "Unknown"; +char* get_control_info ( uint16_t control ) +{ + control = reverse_endian16 ( control ); + switch ( control ) + { + case 0: + return control_unc; + break; + case 1: + return control_lz; + break; + case 2: + return control_mlzo; + break; + case 3: + return control_unc; + break; + case 4: + return control_nrv; + break; + case 5: + return control_nstdlzma; + break; + case 6: + return control_unc; + break; + case 7: + return control_unc; + break; + default: + return control_unk; + break; + } + +} + +int dump_header ( ldr_header_t* hd ) +{ + printf ( "=== Header Information ===\n" ); + printf ( "Header magic:\t0x%04X\n",reverse_endian16 ( hd->magic ) ); + printf ( "Control:\t0x%04X (%s)\n",reverse_endian16 ( hd->control ), get_control_info ( hd->control ) ); + printf ( "Major rev. :\t0x%04X\n",reverse_endian16 ( hd->rev_maj ) ); + printf ( "Minor rev. :\t0x%04X\n",reverse_endian16 ( hd->rev_min ) ); + printf ( "File name :\t%s\n", ( char* ) &hd->filename ); + printf ( "File length:\t%d bytes\n", reverse_endian32 ( hd->filelen ) ); + printf ( "Build time:\t0x%08X //FixMe: print in human-readable form\n", reverse_endian32 ( hd->build_date ) ); //FixMe: + printf ( "HCS:\t\t0x%04X ",reverse_endian16 ( hd->hcs ) ); + uint16_t hcs = get_hcs ( hd ); + int ret=0; + if ( hcs ==reverse_endian16 ( hd->hcs ) ) + { + printf ( "(OK!)\n" ); + } + else + { + printf ( "(ERROR! expected 0x%04X)\n",hcs ); + ret=1; + } +//printf("HCS:\t0x%02X",reverse_endian32(hd->hcs)); + printf ( "Load address:\t0x%08X\n", reverse_endian32 ( hd->ldaddress ) ); //FixMe: + printf ( "HNW:\t\t0x%04X\n",reverse_endian16 ( hd->her_znaet_chto ) ); //Hell knows what + printf ( "CRC:\t\t0x%08X\n",reverse_endian32 ( hd->crc ) ); + printf ( "=== Binary Header Dump===\n" ); + int i,j; + uint8_t* head = ( uint8_t* ) hd; + for ( i=0;i<=sizeof ( ldr_header_t );i++ ) + { + if ( i % 8==0 ) + printf ( "\n" ); + printf ( "0x%02x ",head[i] ); + } + printf ( "\n\n== End Of Header dump ==\n" ); + return ret; +} + + +void print_copyright() +{ + printf ( "Part of bcm-utils package ver. " UTIL_VERSION " \n" ); + printf ( "Copyright (C) 2009 Andrew 'Necromant' Andrianov\n" + "This is free software, and you are welcome to redistribute it\n" + "under certain conditions. See COPYING for details\n" ); +} diff --git a/tools/firmware-utils/src/bcmalgo.h b/tools/firmware-utils/src/bcmalgo.h new file mode 100644 index 00000000000..46647cf6d3d --- /dev/null +++ b/tools/firmware-utils/src/bcmalgo.h @@ -0,0 +1,83 @@ +#ifndef bcmutils_H +#define bcmutils_H + +typedef struct +{ + uint16_t magic; + uint16_t control; + uint16_t rev_maj; + uint16_t rev_min; + uint32_t build_date; + uint32_t filelen; + uint32_t ldaddress; + char filename[64]; + uint16_t hcs; + uint16_t her_znaet_chto; //v dushe ne ebu + uint32_t crc; +} ldr_header_t; + + +/** + * Reverses endianess of a 32bit int, if the ENDIAN_REVERSE_NEEDED defined at compile-time + * @param data + * @return + */ +uint32_t reverse_endian32 ( uint32_t data ); + +/** + * Reverses endianess of a 16bit int, if the ENDIAN_REVERSE_NEEDED defined at compile-time + * @param data + * @return + */ +uint16_t reverse_endian16 ( uint16_t data ); +/** + * Calculates the strange crc (used by bcm modems) of the file. Thnx fly out to Vector for the algorithm. + * @param filename + * @return + */ +uint32_t get_file_crc ( char* filename ); + +/** + * Calculates HCS of the header. + * @param hd + * @return + */ +uint16_t get_hcs ( ldr_header_t* hd ); + +/** + * Constructs the header of the image with the information given It also automagically calculates HCS and writes it there. + * @param magic - magic device bytes + * @param rev_maj - major revision + * @param rev_min - minor revision + * @param build_date - build date (seconds from EPOCH UTC) + * @param filelen - file length in bytes + * @param ldaddress - Load adress + * @param filename - filename + * @param crc_data - the crc of the data + * @return + */ +ldr_header_t* construct_header ( uint32_t magic, uint16_t rev_maj,uint16_t rev_min, uint32_t build_date, uint32_t filelen, uint32_t ldaddress, const char* filename, uint32_t crc_data ); + +/** + * Dumps header information to stdout. + * @param hd + */ +int dump_header ( ldr_header_t* hd ); + + +/** + * Returns a null terminated string describing what the control number meens + * DO NOT FREE IT!!! + * @param control + * @return + */ +char* get_control_info ( uint16_t control ); +#endif + +/** + * Calculates bcmCRC of a data buffer. + * @param filebuffer - pointer to buffer + * @param size - buffer size + * @return + */ +uint32_t get_buffer_crc ( char* filebuffer, size_t size ); diff --git a/tools/firmware-utils/src/buffalo-enc.c b/tools/firmware-utils/src/buffalo-enc.c index 13d270b3f73..08fad4ee61b 100644 --- a/tools/firmware-utils/src/buffalo-enc.c +++ b/tools/firmware-utils/src/buffalo-enc.c @@ -34,6 +34,8 @@ static unsigned char seed = 'O'; static char *product; static char *version; static int do_decrypt; +static int offset; +static int size; void usage(int status) { @@ -52,6 +54,8 @@ void usage(int status) " -p <product> set product name to <product>\n" " -v <version> set version to <version>\n" " -h show this screen\n" +" -O Offset of encrypted data in file (decryption)\n" +" -S Size of unencrypted data in file (encryption)\n" ); exit(status); @@ -85,8 +89,9 @@ static int decrypt_file(void) memset(&ep, '\0', sizeof(ep)); ep.key = (unsigned char *) crypt_key; + ep.longstate = longstate; - err = decrypt_buf(&ep, buf, src_len); + err = decrypt_buf(&ep, buf + offset, src_len - offset); if (err) { ERR("unable to decrypt '%s'", ifname); goto out; @@ -99,7 +104,7 @@ static int decrypt_file(void) printf("Data len\t: %u\n", ep.datalen); printf("Checksum\t: 0x%08x\n", ep.csum); - err = write_buf_to_file(ofname, buf, ep.datalen); + err = write_buf_to_file(ofname, buf + offset, ep.datalen); if (err) { ERR("unable to write to file '%s'", ofname); goto out; @@ -115,7 +120,7 @@ out: static int encrypt_file(void) { struct enc_param ep; - ssize_t src_len; + ssize_t src_len, tail_dst, tail_len, tail_src; unsigned char *buf; uint32_t hdrlen; ssize_t totlen = 0; @@ -128,8 +133,12 @@ static int encrypt_file(void) goto out; } - totlen = enc_compute_buf_len(product, version, src_len); - hdrlen = enc_compute_header_len(product, version); + if (size) { + tail_dst = enc_compute_buf_len(product, version, size); + tail_len = src_len - size; + totlen = tail_dst + tail_len; + } else + totlen = enc_compute_buf_len(product, version, src_len); buf = malloc(totlen); if (buf == NULL) { @@ -137,12 +146,21 @@ static int encrypt_file(void) goto out; } + hdrlen = enc_compute_header_len(product, version); + err = read_file_to_buf(ifname, &buf[hdrlen], src_len); if (err) { ERR("unable to read from file '%s'", ofname); goto free_buf; } + if (size) { + tail_src = hdrlen + size; + memmove(&buf[tail_dst], &buf[tail_src], tail_len); + memset(&buf[tail_src], 0, tail_dst - tail_src); + src_len = size; + } + memset(&ep, '\0', sizeof(ep)); ep.key = (unsigned char *) crypt_key; ep.seed = seed; @@ -238,7 +256,7 @@ int main(int argc, char *argv[]) while ( 1 ) { int c; - c = getopt(argc, argv, "adi:m:o:hp:v:k:r:s:"); + c = getopt(argc, argv, "adi:m:o:hlp:v:k:O:r:s:S:"); if (c == -1) break; @@ -270,6 +288,12 @@ int main(int argc, char *argv[]) case 's': seed = strtoul(optarg, NULL, 16); break; + case 'O': + offset = strtoul(optarg, NULL, 0); + break; + case 'S': + size = strtoul(optarg, NULL, 0); + break; case 'h': usage(EXIT_SUCCESS); break; diff --git a/tools/firmware-utils/src/buffalo-lib.c b/tools/firmware-utils/src/buffalo-lib.c index 29aee9f88dd..b1d5ede0a28 100644 --- a/tools/firmware-utils/src/buffalo-lib.c +++ b/tools/firmware-utils/src/buffalo-lib.c @@ -179,7 +179,7 @@ int bcrypt_buf(unsigned char seed, unsigned char *key, unsigned char *src, uint32_t buffalo_csum(uint32_t csum, void *buf, unsigned long len) { - char *p = buf; + signed char *p = buf; while (len--) { int i; @@ -249,10 +249,10 @@ static uint32_t get_be32(void *data) static int check_magic(void *magic) { - if (!memcmp("start", magic, ENC_MAGIC_LEN)); + if (!memcmp("start", magic, ENC_MAGIC_LEN)) return 0; - if (!memcmp("asar1", magic, ENC_MAGIC_LEN)); + if (!memcmp("asar1", magic, ENC_MAGIC_LEN)) return 0; return -1; diff --git a/tools/firmware-utils/src/buffalo-lib.h b/tools/firmware-utils/src/buffalo-lib.h index ba8a5081294..7eb9bf53987 100644 --- a/tools/firmware-utils/src/buffalo-lib.h +++ b/tools/firmware-utils/src/buffalo-lib.h @@ -69,6 +69,26 @@ struct buffalo_tag2 { uint8_t unknown2[3]; } __attribute ((packed)); +struct buffalo_tag3 { + unsigned char product[TAG_PRODUCT_LEN]; + unsigned char brand[TAG_BRAND_LEN]; + unsigned char ver_major[TAG_VERSION_LEN]; + unsigned char ver_minor[TAG_VERSION_LEN]; + unsigned char region_code[2]; + uint32_t region_mask; + unsigned char unknown0[2]; + unsigned char language[TAG_LANGUAGE_LEN]; + unsigned char platform[TAG_PLATFORM_LEN]; + unsigned char hwv[TAG_HWVER_LEN]; + unsigned char hwv_val[TAG_HWVER_VAL_LEN]; + uint8_t unknown1[24]; + + uint32_t total_len; + uint32_t crc; + uint32_t len1; + uint32_t base2; +} __attribute ((packed)); + #define ENC_PRODUCT_LEN 32 #define ENC_VERSION_LEN 8 #define ENC_MAGIC_LEN 6 diff --git a/tools/firmware-utils/src/buffalo-tag.c b/tools/firmware-utils/src/buffalo-tag.c index b5db72ef968..6d479f7fba9 100644 --- a/tools/firmware-utils/src/buffalo-tag.c +++ b/tools/firmware-utils/src/buffalo-tag.c @@ -48,6 +48,7 @@ static uint32_t base2; static char *region_code; static uint32_t region_mask; static int num_regions; +static int dhp; void usage(int status) { @@ -63,6 +64,7 @@ void usage(int status) " -d <base2>\n" " -f <flag> set flag to <flag>\n" " -i <file> read input from the file <file>\n" +" -I <file> read input from the file <file> for DHP series\n" " -l <language> set language to <language>\n" " -m <version> set minor version to <version>\n" " -o <file> write output to the file <file>\n" @@ -227,6 +229,37 @@ static void fixup_tag2(unsigned char *buf, ssize_t buflen) tag->crc = htonl(buffalo_crc(buf, buflen)); } +static void fixup_tag3(unsigned char *buf, ssize_t totlen) +{ + struct buffalo_tag3 *tag = (struct buffalo_tag3 *) buf; + + memset(tag, '\0', sizeof(*tag)); + + memcpy(tag->brand, brand, strlen(brand)); + memcpy(tag->product, product, strlen(product)); + memcpy(tag->platform, platform, strlen(platform)); + memcpy(tag->ver_major, major, strlen(major)); + memcpy(tag->ver_minor, minor, strlen(minor)); + memcpy(tag->language, language, strlen(language)); + + if (num_regions > 1) { + tag->region_code[0] = 'M'; + tag->region_code[1] = '_'; + tag->region_mask = htonl(region_mask); + } else { + memcpy(tag->region_code, region_code, 2); + } + + tag->total_len = htonl(totlen); + tag->len1 = htonl(fsize[0]); + tag->base2 = htonl(base2); + + if (hwver) { + memcpy(tag->hwv, "hwv", 3); + memcpy(tag->hwv_val, hwver, strlen(hwver)); + } +} + static int tag_file(void) { unsigned char *buf; @@ -237,7 +270,9 @@ static int tag_file(void) int ret = -1; int i; - if (num_files == 1) + if (dhp) + hdrlen = sizeof(struct buffalo_tag3); + else if (num_files == 1) hdrlen = sizeof(struct buffalo_tag); else hdrlen = sizeof(struct buffalo_tag2); @@ -270,7 +305,9 @@ static int tag_file(void) offset += fsize[i]; } - if (num_files == 1) + if (dhp) + fixup_tag3(buf, fsize[0] + 200); + else if (num_files == 1) fixup_tag(buf, buflen); else fixup_tag2(buf, buflen); @@ -299,7 +336,7 @@ int main(int argc, char *argv[]) while ( 1 ) { int c; - c = getopt(argc, argv, "a:b:c:d:f:hi:l:m:o:p:r:sv:w:"); + c = getopt(argc, argv, "a:b:c:d:f:hi:l:m:o:p:r:sv:w:I:"); if (c == -1) break; @@ -319,6 +356,9 @@ int main(int argc, char *argv[]) case 'f': flag = strtoul(optarg, NULL, 2); break; + case 'I': + dhp = 1; + /* FALLTHROUGH */ case 'i': err = process_ifname(optarg); if (err) diff --git a/tools/firmware-utils/src/buffalo-tftp.c b/tools/firmware-utils/src/buffalo-tftp.c index 1a2551a41c7..087f9955b64 100644 --- a/tools/firmware-utils/src/buffalo-tftp.c +++ b/tools/firmware-utils/src/buffalo-tftp.c @@ -70,7 +70,6 @@ static int crypt_file(void) { unsigned char *buf = NULL; ssize_t src_len; - ssize_t crypt_len; int err; int ret = -1; @@ -92,7 +91,6 @@ static int crypt_file(void) goto out; } - crypt_len = (src_len > 512) ? 512 : src_len; if (do_decrypt) crypt_header(buf, 512, crypt_key2, crypt_key1); else diff --git a/tools/firmware-utils/src/csysimg.h b/tools/firmware-utils/src/csysimg.h index 19dfbd4334c..65ab062f31e 100644 --- a/tools/firmware-utils/src/csysimg.h +++ b/tools/firmware-utils/src/csysimg.h @@ -49,6 +49,7 @@ #define SIG_BR6114WG SIG_BR6104IPC #define SIG_BR6524K "2-K-" #define SIG_BR6524KP "2-KP" /* FIXME: valid? */ +#define SIG_BR6524N "WNRA" #define SIG_BR6524WG "2-WG" /* FIXME: valid? */ #define SIG_BR6524WP "2-WP" /* FIXME: valid? */ #define SIG_BR6541K "4--K" diff --git a/tools/firmware-utils/src/dgfirmware.c b/tools/firmware-utils/src/dgfirmware.c index 5ff3b69646d..e3257f10779 100644 --- a/tools/firmware-utils/src/dgfirmware.c +++ b/tools/firmware-utils/src/dgfirmware.c @@ -1,5 +1,6 @@ #include <stdlib.h> #include <stdio.h> +#include <string.h> #define IMG_SIZE 0x3e0000 diff --git a/tools/firmware-utils/src/dgn3500sum.c b/tools/firmware-utils/src/dgn3500sum.c new file mode 100644 index 00000000000..eb80e6c01b0 --- /dev/null +++ b/tools/firmware-utils/src/dgn3500sum.c @@ -0,0 +1,166 @@ +/* ************************************************************************** + + This program creates a modified 16bit checksum used for the Netgear + DGN3500 series routers. The difference between this and a standard + checksum is that every 0x100 bytes added 0x100 have to be subtracted + from the sum. + + (C) 2013 Marco Antonio Mauro <marcus90 at gmail.com> + + Based on previous unattributed work. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ************************************************************************* */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +unsigned char PidDataWW[70] = +{ + 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x37, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, + 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, +} ; + +unsigned char PidDataDE[70] = +{ + 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x37, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, + 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, +} ; + +unsigned char PidDataNA[70] = +{ + 0x73, 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x59, 0x50, 0x35, 0x37, 0x32, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x37, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, + 0x45, 0x72, 0x43, 0x6F, 0x4D, 0x6D, +} ; + +/* ******************************************************************* + Reads the file into memory and returns pointer to the buffer. */ +static char *readfile(char *filename, int *size) +{ + FILE *fp; + char *buffer; + struct stat info; + + if (stat(filename,&info)!=0) + return NULL; + + if ((fp=fopen(filename,"r"))==NULL) + return NULL; + + buffer=NULL; + for (;;) + { + if ((buffer=(char *)malloc(info.st_size+1))==NULL) + break; + + if (fread(buffer,1,info.st_size,fp)!=info.st_size) + { + free(buffer); + buffer=NULL; + break; + } + + buffer[info.st_size]='\0'; + if(size) *size = info.st_size; + + break; + } + + (void)fclose(fp); + + return buffer; +} + + +/* ******************************************************************* */ +int main(int argc, char** argv) +{ + unsigned long start, i; + char *endptr, *buffer, *p; + int count; // size of file in bytes + unsigned short sum = 0, sum1 = 0; + char sumbuf[9]; + + if(argc < 3) { + printf("ERROR: Argument missing!\n\nUsage %s filename starting offset in hex [PID code]\n\n", argv[0]); + return 1; + } + + + FILE *fp = fopen(argv[1], "a"); + if(!fp) { + printf("ERROR: File not writeable!\n"); + return 1; + } + if(argc == 4) + { + printf("%s: PID type: %s\n", argv[0], argv[3]); + if(strcmp(argv[3], "DE")==0) + fwrite(PidDataDE, sizeof(PidDataDE), sizeof(char), fp); /* write DE pid */ + else if(strcmp(argv[3], "NA")==0) + fwrite(PidDataNA, sizeof(PidDataNA), sizeof(char), fp); /* write NA pid */ + else /* if(strcmp(argv[3], "WW")) */ + fwrite(PidDataWW, sizeof(PidDataWW), sizeof(char), fp); /* write WW pid */ + } + else + fwrite(PidDataWW, sizeof(PidDataWW), sizeof(char), fp); /* write WW pid if unspecified */ + + fclose(fp); + + /* Read the file to calculate the checksums */ + buffer = readfile(argv[1], &count); + if(!buffer) { + printf("ERROR: File %s not found!\n", argv[1]); + return 1; + } + + p = buffer; + for(i = 0; i < count; i++) + { + sum += p[i]; + } + + start = strtol(argv[2], &endptr, 16); + p = buffer+start; + for(i = 0; i < count - start; i++) + { + sum1 += p[i]; + } + + sprintf(sumbuf,"%04X%04X",sum1,sum); + /* Append the 2 checksums to end of file */ + fp = fopen(argv[1], "a"); + if(!fp) { + printf("ERROR: File not writeable!\n"); + return 1; + } + fwrite(sumbuf, 8, sizeof(char), fp); + fclose(fp); + free(buffer); + return 0; +} diff --git a/tools/firmware-utils/src/dns313-header.c b/tools/firmware-utils/src/dns313-header.c new file mode 100644 index 00000000000..e69e57e7baa --- /dev/null +++ b/tools/firmware-utils/src/dns313-header.c @@ -0,0 +1,239 @@ +/* + * dns313-header.c + * + * Program to add the modified U-Boot header to a binary used with + * the D-Link DNS-313 boot loader when booting directly from an + * EXT2 formatted hard drive. + * + * The DNS313 use the same header on zImage, ramdisk, rootfs. + * + * Written by Linus Walleij <linus.walleij@linaro.org> + * License terms: GPLv2 + */ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <stdint.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +/* + * This is the U-Boot magic number, so the U-Boot header was used + * (obviously) as a template for this custom header. + */ +#define IH_MAGIC 0x27051956 +#define HEADER_SIZE 0x68 + +#define OFFSET_MAGIC 0x00 +#define OFFSET_HCRC 0x04 +#define OFFSET_TIME 0x08 +#define OFFSET_SIZE 0x0c +#define OFFSET_LOAD 0x10 +#define OFFSET_EP 0x14 +#define OFFSET_DCRC 0x18 +#define OFFSET_OS 0x1c +#define OFFSET_ARCH 0x1d +#define OFFSET_TYPE 0x1e +#define OFFSET_COMP 0x1f +#define OFFSET_NAME 0x20 +#define NAME_LEN 0x20 +#define OFFSET_MODEL 0x40 +#define MODEL_LEN 0x10 +#define OFFSET_VERSION 0x50 +#define VERSION_LEN 0x10 +#define OFFSET_MAC 0x60 +#define MAC_LEN 6 + +static const uint32_t crc32_table[256] = { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +}; + +static uint32_t crc32(uint32_t crc, + const unsigned char *buf, + unsigned int len) +{ + crc = crc ^ 0xffffffffUL; + do { + crc = crc32_table[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8); + } while (--len); + return crc ^ 0xffffffffUL; +} + +static void be_wr(unsigned char *buf, uint32_t val) +{ + buf[0] = (val >> 24) & 0xFFU; + buf[1] = (val >> 16) & 0xFFU; + buf[2] = (val >> 8) & 0xFFU; + buf[3] = val & 0xFFU; +} + +int main(int argc, char **argv) +{ + int fdin; + int fdout; + struct stat sb; + uint32_t filesize; + uint32_t padding; + int ret = 0; + const char *pathin; + const char *pathout; + unsigned char *buffer; + unsigned char *infop; + uint32_t sum; + size_t bufsize; + size_t bytes; + int i; + + if (argc < 3) { + printf("Too few arguments.\n"); + printf("%s <infile> <outfile>\n", argv[0]); + } + + pathin = argv[1]; + pathout = argv[2]; + + ret = stat(pathin, &sb); + if (ret < 0) + return ret; + + filesize = sb.st_size; + padding = filesize % 4; + printf("INFILE: %s, size: %08x bytes\n", pathin, filesize); + /* File + extended header size */ + bufsize = filesize + HEADER_SIZE; + + printf("Allocate %08x bytes\n", bufsize); + buffer = malloc(bufsize); + if (!buffer) { + printf("OOM: could not allocate buffer\n"); + return 0; + } + + memset(buffer, 0x00, bufsize); + + /* Read file to buffer */ + fdin = open(pathin, O_RDONLY); + if (!fdin) { + printf("ERROR: could not open input file\n"); + return 0; + } + bytes = read(fdin, buffer + HEADER_SIZE, filesize); + if (bytes < filesize) { + printf("ERROR: could not read entire file\n"); + return 0; + } + close(fdin); + + /* PREP HEADER AND FOOTER */ + infop = buffer; + + be_wr(buffer + OFFSET_MAGIC, IH_MAGIC); + + /* FIXME: use actual time */ + be_wr(buffer + OFFSET_TIME, 0x4c06738c); + be_wr(buffer + OFFSET_SIZE, filesize); + + /* Load address & entry point */ + be_wr(buffer + OFFSET_LOAD, 0x00008000); + be_wr(buffer + OFFSET_EP, 0x00008000); + + buffer[OFFSET_OS] = 0x05; /* Linux */ + buffer[OFFSET_ARCH] = 0x02; /* ARM */ + buffer[OFFSET_TYPE] = 0x02; /* OS kernel image */ + buffer[OFFSET_COMP] = 0x01; /* gzip */ + + /* The vendor firmware just hardcodes this */ + strncpy(buffer + OFFSET_NAME, "kernel.img", NAME_LEN); + buffer[OFFSET_NAME + NAME_LEN - 1] = '\0'; + strncpy(buffer + OFFSET_MODEL, "dns-313v3", MODEL_LEN); + buffer[OFFSET_MODEL + MODEL_LEN - 1] = '\0'; + strncpy(buffer + OFFSET_VERSION, "2.01b04", VERSION_LEN); + buffer[OFFSET_VERSION + VERSION_LEN - 1] = '\0'; + /* Just some MAC address from the example */ + buffer[OFFSET_MAC] = 0x00; + buffer[OFFSET_MAC + 1] = 0x80; + buffer[OFFSET_MAC + 2] = 0xc8; + buffer[OFFSET_MAC + 3] = 0x16; + buffer[OFFSET_MAC + 4] = 0x81; + buffer[OFFSET_MAC + 5] = 0x68; + + /* Checksum payload */ + sum = crc32(0, buffer + HEADER_SIZE, filesize); + be_wr(buffer + OFFSET_DCRC, sum); + printf("data checksum: 0x%08x\n", sum); + + /* Checksum header, then write that into the header checksum */ + sum = crc32(0, buffer, HEADER_SIZE); + be_wr(buffer + OFFSET_HCRC, sum); + printf("header checksum: 0x%08x\n", sum); + + printf("OUTFILE: %s, size: %08x bytes\n", pathout, bufsize); + fdout = open(pathout, O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRGRP); + if (!fdout) { + printf("ERROR: could not open output file\n"); + return 0; + } + bytes = write(fdout, buffer, bufsize); + if (bytes < bufsize) { + printf("ERROR: could not write complete output file\n"); + return 0; + } + close(fdout); + + free(buffer); + + return 0; +} diff --git a/tools/firmware-utils/src/edimax_fw_header.c b/tools/firmware-utils/src/edimax_fw_header.c new file mode 100644 index 00000000000..b85e3a1781c --- /dev/null +++ b/tools/firmware-utils/src/edimax_fw_header.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2014 Gabor Juhos <juhosg@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#define MAX_MAGIC_LEN 16 +#define MAX_MODEL_LEN 32 +#define MAX_VERSION_LEN 14 +#define MAX_MTD_NAME_LEN 16 + +#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + +struct edimax_header { + char magic[MAX_MAGIC_LEN]; + char model[MAX_MODEL_LEN]; + unsigned char force; + unsigned char header_csum; + unsigned char data_csum; + uint32_t data_size; + uint32_t start_addr; + uint32_t end_addr; + char fw_version[MAX_VERSION_LEN]; + unsigned char type; + char mtd_name[MAX_MTD_NAME_LEN]; +} __attribute__ ((packed)); + +/* + * Globals + */ +static char *ofname; +static char *ifname; +static char *progname; + +static char *model; +static char *magic = "eDiMaX"; +static char *fw_version = ""; +static char *mtd_name; +static int force; +static uint32_t start_addr; +static uint32_t end_addr; +static uint8_t image_type; +static int data_size; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -e <addr> set end addr to <addr>\n" +" -f set force flag\n" +" -h show this screen\n" +" -i <file> read input data from the file <file>\n" +" -o <file> write output to the file <file>\n" +" -m <model> set model to <model>\n" +" -M <magic> set image magic to <magic>\n" +" -n <name> set MTD device name to <name>\n" +" -s <addr> set start address to <addr>\n" +" -t <type> set image type to <type>\n" +" -v <version> set firmware version to <version>\n" + ); + + exit(status); +} + +int +str2u32(char *arg, uint32_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err)) { + return -1; + } + + *val = t; + return 0; +} + +int +str2u8(char *arg, uint8_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x100)) { + return -1; + } + + *val = t & 0xFF; + return 0; +} + +static int get_file_size(char *name) +{ + struct stat st; + int res; + + res = stat(name, &st); + if (res){ + ERRS("stat failed on %s", name); + return -1; + } + + return st.st_size; +} + +static int read_to_buf(char *name, char *buf, int buflen) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", name); + goto out; + } + + errno = 0; + fread(buf, buflen, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", name); + goto out_close; + } + + ret = EXIT_SUCCESS; + +out_close: + fclose(f); +out: + return ret; +} + +static int check_options(void) +{ +#define CHKSTR(_name, _msg) \ + do { \ + if (_name == NULL) { \ + ERR("no %s specified", _msg); \ + return -1; \ + } \ + } while (0) + +#define CHKSTRLEN(_name, _msg) \ + do { \ + int field_len; \ + CHKSTR(_name, _msg); \ + field_len = FIELD_SIZEOF(struct edimax_header, _name) - 1; \ + if (strlen(_name) > field_len) { \ + ERR("'%s' is too long, max %s length is %d", \ + _name, _msg, field_len); \ + return -1; \ + } \ + } while (0) + + CHKSTR(ofname, "output file"); + CHKSTR(ifname, "input file"); + + CHKSTRLEN(magic, "magic"); + CHKSTRLEN(model, "model"); + CHKSTRLEN(mtd_name, "MTD device name"); + CHKSTRLEN(fw_version, "firware version"); + + data_size = get_file_size(ifname); + if (data_size < 0) + return -1; + + return 0; +} + +static int write_fw(char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + +out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return ret; +} + +static unsigned char checksum(unsigned char *p, unsigned len) +{ + unsigned char csum = 0; + + while (len--) + csum += *p++; + + csum ^= 0xb9; + + return csum; +} + +static int build_fw(void) +{ + int buflen; + char *buf; + char *data; + struct edimax_header *hdr; + int ret = EXIT_FAILURE; + + buflen = sizeof(struct edimax_header) + data_size; + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + data = buf + sizeof(struct edimax_header); + + /* read input file */ + ret = read_to_buf(ifname, data, data_size); + if (ret) + goto out_free_buf; + + /* fill firmware header */ + hdr = (struct edimax_header *)buf; + memset(hdr, 0, sizeof(struct edimax_header)); + + strncpy(hdr->model, model, sizeof(hdr->model)); + strncpy(hdr->magic, magic, sizeof(hdr->magic)); + strncpy(hdr->fw_version, fw_version, sizeof(hdr->fw_version)); + strncpy(hdr->mtd_name, mtd_name, sizeof(hdr->mtd_name)); + + hdr->force = force; + hdr->start_addr = htonl(start_addr); + hdr->end_addr = htonl(end_addr); + hdr->data_size = htonl(data_size); + hdr->type = image_type; + + hdr->data_csum = checksum((unsigned char *)data, data_size); + hdr->header_csum = checksum((unsigned char *)hdr, + sizeof(struct edimax_header)); + + ret = write_fw(buf, buflen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + +out_free_buf: + free(buf); +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "e:fhi:o:m:M:n:s:t:v:"); + if (c == -1) + break; + + switch (c) { + case 'e': + if (str2u32(optarg, &end_addr)) { + ERR("%s is invalid '%s'", + "end address", optarg); + goto out; + } + break; + case 'f': + force = 1; + break; + case 'i': + ifname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case 'o': + ofname = optarg; + break; + case 'm': + model = optarg; + break; + case 'M': + magic = optarg; + break; + case 'n': + mtd_name = optarg; + break; + case 's': + if (str2u32(optarg, &start_addr)) { + ERR("%s is invalid '%s'", + "start address", optarg); + goto out; + } + break; + case 't': + if (str2u8(optarg, &image_type)) { + ERR("%s is invalid '%s'", + "image type", optarg); + goto out; + } + break; + case 'v': + fw_version = optarg; + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + ret = build_fw(); + +out: + return ret; +} diff --git a/tools/firmware-utils/src/fix-u-media-header.c b/tools/firmware-utils/src/fix-u-media-header.c new file mode 100644 index 00000000000..21f184e66da --- /dev/null +++ b/tools/firmware-utils/src/fix-u-media-header.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> + +#include "cyg_crc.h" + +#include <arpa/inet.h> +#include <netinet/in.h> + +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 32 /* Image Name Length */ + +#define UM_MAGIC 0x55525F46 +#define UM_HEADER_LEN 12 + +/* + * all data in network byte order (aka natural aka bigendian) + */ +struct u_media_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN - UM_HEADER_LEN]; /* Image Name */ + + uint32_t ih_UMedia_magic; /* U-Media magic number */ + uint32_t ih_UMedia_boardID; /* U-Media board ID */ + uint8_t ih_UMedia_imageType; /* U-Media image type */ + uint8_t ih_UMedia_LoadDefault; /* U-Media load to factory default setting */ + uint8_t ih_UMedia_temp1; /* U-Media didn't use this tag */ + uint8_t ih_UMedia_temp2; /* U-Media didn't use this tag */ +} __attribute__ ((packed)); + +struct if_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +static char *progname; +static char *ofname; +static struct if_info if_info; +static int factory_defaults; +static uint32_t board_id; +static uint8_t image_type; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board_id> set board ID to <board_id>\n" +" -i <file> read input from the file <file>\n" +" -F load factory defaults\n" +" -o <file> write output to the file <file>\n" +" -T <type> set image type to <type>\n" +" -h show this screen\n" + ); + + exit(status); +} + +static int str2u32(char *arg, uint32_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err)) { + return -1; + } + + *val = t; + return 0; +} + +static int str2u8(char *arg, uint8_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err)) { + return -1; + } + + if (t > 255) + return -1; + + *val = t; + return 0; +} + +static int get_file_stat(struct if_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) + return 0; + + res = stat(fdata->file_name, &st); + if (res){ + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + return 0; +} + +static int read_to_buf(struct if_info *fdata, char *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + +out_close: + fclose(f); +out: + return ret; +} + +static int check_options(void) +{ + int ret; + + if (ofname == NULL) { + ERR("no %s specified", "output file"); + return -1; + } + + if (if_info.file_name == NULL) { + ERR("no %s specified", "input file"); + return -1; + } + + ret = get_file_stat(&if_info); + if (ret) + return ret; + + return 0; +} + +static int write_fw(char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + ret = EXIT_SUCCESS; + +out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return ret; +} + +static int fix_header(void) +{ + int buflen; + char *buf; + uint32_t crc, crc_orig; + struct u_media_header *hdr; + int ret = EXIT_FAILURE; + + buflen = if_info.file_size; + if (buflen < sizeof(*hdr)) { + ERR("invalid input file\n"); + return ret; + } + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + ret = read_to_buf(&if_info, buf); + if (ret) + goto out_free_buf; + + hdr = (struct u_media_header *) buf; + if (ntohl(hdr->ih_magic) != IH_MAGIC) { + ERR("invalid input file, bad magic\n"); + goto out_free_buf; + } + + /* verify header CRC */ + crc_orig = ntohl(hdr->ih_hcrc); + hdr->ih_hcrc = 0; + crc = cyg_ether_crc32((unsigned char *)hdr, sizeof(*hdr)); + if (crc != crc_orig) { + ERR("invalid input file, bad header CRC\n"); + goto out_free_buf; + } + + hdr->ih_name[IH_NMLEN - UM_HEADER_LEN - 1] = '\0'; + + /* set U-Media specific fields */ + hdr->ih_UMedia_magic = htonl(UM_MAGIC); + hdr->ih_UMedia_boardID = htonl(board_id); + hdr->ih_UMedia_imageType = image_type; + hdr->ih_UMedia_LoadDefault = (factory_defaults) ? 1 : 0; + + /* update header CRC */ + crc = cyg_ether_crc32((unsigned char *)hdr, sizeof(*hdr)); + hdr->ih_hcrc = htonl(crc); + + ret = write_fw(buf, buflen); + if (ret) + goto out_free_buf; + + DBG("U-Media header fixed in \"%s\"", ofname); + + ret = EXIT_SUCCESS; + +out_free_buf: + free(buf); +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "B:Fi:o:T:h"); + if (c == -1) + break; + + switch (c) { + case 'B': + if (str2u32(optarg, &board_id)) { + ERR("%s is invalid '%s'", + "board ID", optarg); + goto out; + } + break; + case 'T': + if (str2u8(optarg, &image_type)) { + ERR("%s is invalid '%s'", + "image type", optarg); + goto out; + } + break; + case 'F': + factory_defaults = 1; + break; + case 'i': + if_info.file_name = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + ret = fix_header(); + +out: + return ret; +} diff --git a/tools/firmware-utils/src/hcsmakeimage.c b/tools/firmware-utils/src/hcsmakeimage.c new file mode 100644 index 00000000000..7baa7b5845e --- /dev/null +++ b/tools/firmware-utils/src/hcsmakeimage.c @@ -0,0 +1,203 @@ +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <getopt.h> +#include <unistd.h> +#include <errno.h> +#include <time.h> +#include <sys/stat.h> +#include <libgen.h> +#include "bcmalgo.h" + + +int flag_print_version; +int flag_print_help; +int flag_compress; + +uint16_t sa2100_magic = 0x2100; +uint16_t sa3349_magic = 0x3349; +uint32_t default_date = 0x00000000; //A long time ago in a galaxy far far away.... +uint32_t default_load_address = 0x80010000; //The default load_address for the firmware image + +static void print_help ( const char* ename ) +{ + printf ( "Firmware image packer and calculator for broadcom-based modems.\n" ); + printf ( "Part of bcm-utils package.\n" ); + printf ( "(c) 2009 Necromant (http://necromant.ath.cx). Thanks to Luke-jr for his initial work.\n" ); + printf ( "usage: %s [options]\n", ename ); + printf ( "Valid options are:\n" ); + printf ( "--magic_bytes=value \t- specify magic bytes at the beginning of the image. default - 3349\n" ); + printf ( "\t\t\t these can be sa2100 (for DPC2100 modem),\n\t\t\t sa3349 (haxorware guys use this one for some reason),\n\t\t\t or a custom hex value e.g. 0xFFFF\n" ); + printf ( "--compress \t\t - Make use of LZMA (weird!) compression (Doesn't work yet).\n" ); + printf ( "--rev_maj=value\t\t - major revision number. default 0\n" ); + printf ( "--rev_min=value\t\t - minor revision number default 0\n" ); + printf ( "--filename=value\t - use this filename in header instead of default (input filename)\n" ); + printf ( "--ldaddress=value\t - hex value of the target load address. defaults to 0x80010000\n" ); + printf ( "--input_file=value\t - What file are we packing?\n" ); + printf ( "--output_file=value\t - What file shall we write? (default: image.bin)\n" ); +#ifdef _HAX0RSTYLE + printf ( "--credz\t - Give some credz!\n" ); +#endif + printf ( "\n" ); +} + +static time_t source_date_epoch = -1; +static void set_source_date_epoch() { + char *env = getenv("SOURCE_DATE_EPOCH"); + char *endptr = env; + errno = 0; + if (env && *env) { + source_date_epoch = strtoull(env, &endptr, 10); + if (errno || (endptr && *endptr != '\0')) { + fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); + exit(1); + } + } +} + +int main ( int argc, char** argv ) +{ + if ( argc<2 ) + { + print_help ( argv[0] ); + } + + static struct option long_options[] = + { + {"magic_bytes", required_argument, 0, 'm'}, + {"rev_maj", required_argument, 0, 'j'}, + {"rev_min", required_argument, 0, 'n'}, + {"ldaddress", required_argument, 0, 'l'}, + {"filename", required_argument, 0, 'f'}, + {"input_file", required_argument, 0, 'i'}, + {"output_file", required_argument, 0, 'o'}, + {"compress", no_argument, &flag_compress, 'c'}, + {"version", no_argument, &flag_print_version, 'v'}, + {"help", no_argument, &flag_print_help, 'h'}, + {0, 0, 0, 0} + }; + int option_index = 0; + int opt_result=0; + char* filename=NULL; + char* input=NULL; + char* magic=NULL; + char* major=NULL; + char* minor=NULL; + char* ldaddr=NULL; + char* output=NULL; + + while ( opt_result>=0 ) + { + opt_result = getopt_long ( argc, argv, "m:j:n:f:i:o:vh", long_options, &option_index ); + switch ( opt_result ) + { + case 0: + printf ( "o!\n" ); + break; + case 'h': + print_help ( argv[0] ); + break; + case 'l': + ldaddr=optarg; + break; + case 'f': + filename=optarg; + break; + case 'i': + input=optarg; + break; + case 'o': + output=optarg; + break; + case 'm': + magic=optarg; + break; + case 'j': + major=optarg; + break; + case 'n': + minor=optarg; + break; + } + } + if ( input==NULL ) + { + printf ( "Telepaths are still on holidays. I guess you should tell me what file should I process.\n\n" ); + exit ( 1 ); + } + if ( access ( input,R_OK ) !=0 ) + { + printf ( "I cannot access the file %s. Is it there? Am I allowed?\n\n", input ); + exit ( 1 ); + } + uint32_t magicnum=sa2100_magic; + + if ( magic ) + { + if ( strcmp ( magic,"sa2100" ) ==0 ) magicnum=sa2100_magic; else + if ( strcmp ( magic,"sa3349" ) ==0 ) magicnum=sa3349_magic; else + { + sscanf ( magic, "0x%04X", &magicnum ); + } + } + unsigned int majrev=0; + if ( major ) + { + sscanf ( major, "%d", &majrev ); + } + unsigned int minrev=0; + if ( minor ) + { + sscanf ( minor, "%d", &minrev ); + } + uint32_t ldaddress = default_load_address; + if ( ldaddr ) + { + sscanf ( ldaddr, "0x%08X", &ldaddress ); + } + char* dupe = strdup(input); + char* fname = basename ( dupe ); + if ( filename ) + { + fname = filename; + } + + time_t t = -1; + set_source_date_epoch(); + if (source_date_epoch != -1) { + t = source_date_epoch; + } else if ((time(&t) == (time_t)(-1))) { + fprintf(stderr, "time call failed\n"); + return EXIT_FAILURE; + } + + struct stat buf; + stat ( input,&buf ); + ldr_header_t* head = construct_header ( magicnum, (uint16_t) majrev, (uint16_t) minrev, ( uint32_t ) t, ( uint32_t ) buf.st_size, ldaddress, fname, get_file_crc ( input ) ); + free(dupe); + //uint32_t magic, uint16_t rev_maj,uint16_t rev_min, uint32_t build_date, uint32_t filelen, uint32_t ldaddress, const char* filename, uint32_t crc + //FILE* fd = fopen ("/tftpboot/haxorware11rev32.bin","r"); + //fread(head,sizeof(ldr_header_t),1,fd); + char* filebuffer = malloc ( buf.st_size+10 ); + FILE* fd = fopen ( input,"r" ); + fread ( filebuffer, 1, buf.st_size,fd ); + if (!output) + { + output = malloc(strlen(input+5)); + strcpy(output,input); + strcat(output,".bin"); + } + dump_header ( head ); + FILE* fd_out = fopen ( output,"w+" ); + if (!fd_out) + { + fprintf(stderr, "Failed to open output file: %s\n", output); + exit(1); + } + fwrite ( head,1,sizeof ( ldr_header_t ),fd_out ); + fwrite ( filebuffer,1,buf.st_size,fd_out ); + printf("Firmware image %s is ready\n", output); + return 0; +} diff --git a/tools/firmware-utils/src/imagetag.c b/tools/firmware-utils/src/imagetag.c index bebaba2f29d..bc70399ccaa 100644 --- a/tools/firmware-utils/src/imagetag.c +++ b/tools/firmware-utils/src/imagetag.c @@ -11,13 +11,14 @@ #include <stdlib.h> #include <string.h> #include <stdint.h> -#include <time.h> #include <unistd.h> #include <sys/stat.h> #include <netinet/in.h> +#include <inttypes.h> #include "bcm_tag.h" #include "imagetag_cmdline.h" +#include "cyg_crc.h" #define DEADCODE 0xDEADC0DE @@ -30,54 +31,11 @@ struct kernelhdr { static char pirellitab[NUM_PIRELLI][BOARDID_LEN] = PIRELLI_BOARDS; -static uint32_t crc32tab[256] = { - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D -}; - void int2tag(char *tag, uint32_t value) { uint32_t network = htonl(value); memcpy(tag, (char *)(&network), 4); } -uint32_t crc32(uint32_t crc, uint8_t *data, size_t len) -{ - while (len--) - crc = (crc >> 8) ^ crc32tab[(crc ^ *data++) & 0xFF]; - - return crc; -} - uint32_t compute_crc32(uint32_t crc, FILE *binfile, size_t compute_start, size_t compute_len) { uint8_t readbuf[1024]; @@ -88,14 +46,14 @@ uint32_t compute_crc32(uint32_t crc, FILE *binfile, size_t compute_start, size_t /* read block of 1024 bytes */ while (binfile && !feof(binfile) && !ferror(binfile) && (compute_len >= sizeof(readbuf))) { read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), binfile); - crc = crc32(crc, readbuf, read); + crc = cyg_crc32_accumulate(crc, readbuf, read); compute_len = compute_len - read; } /* Less than 1024 bytes remains, read compute_len bytes */ if (binfile && !feof(binfile) && !ferror(binfile) && (compute_len > 0)) { read = fread(readbuf, sizeof(uint8_t), compute_len, binfile); - crc = crc32(crc, readbuf, read); + crc = cyg_crc32_accumulate(crc, readbuf, read); } return crc; @@ -125,7 +83,8 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ struct kernelhdr khdr; FILE *kernelfile = NULL, *rootfsfile = NULL, *binfile = NULL, *cfefile = NULL; size_t cfeoff, cfelen, kerneloff, kernellen, rootfsoff, rootfslen, \ - read, imagelen, rootfsoffpadlen = 0, kernelfslen, kerneloffpadlen = 0, oldrootfslen; + read, imagelen, rootfsoffpadlen = 0, kernelfslen, kerneloffpadlen = 0, oldrootfslen, \ + rootfsend; uint8_t readbuf[1024]; uint32_t imagecrc = IMAGETAG_CRC_START; uint32_t kernelcrc = IMAGETAG_CRC_START; @@ -199,11 +158,19 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ kernellen += sizeof(khdr); } - /* Build the rootfs address and length (start and end do need to be aligned on flash erase block boundaries */ + /* Build the rootfs address and length */ rootfsoff = kerneloff + kernellen; - rootfsoff = (rootfsoff % block_size) > 0 ? (((rootfsoff / block_size) + 1) * block_size) : rootfsoff; - rootfslen = getlen(rootfsfile); - rootfslen = ( (rootfslen % block_size) > 0 ? (((rootfslen / block_size) + 1) * block_size) : rootfslen ); + /* align the start if requested */ + if (args->align_rootfs_flag) + rootfsoff = (rootfsoff % block_size) > 0 ? (((rootfsoff / block_size) + 1) * block_size) : rootfsoff; + else + rootfsoff = (rootfsoff % 4) > 0 ? (((rootfsoff / 4) + 1) * 4) : rootfsoff; + + /* align the end */ + rootfsend = rootfsoff + getlen(rootfsfile); + if ((rootfsend % block_size) > 0) + rootfsend = (((rootfsend / block_size) + 1) * block_size); + rootfslen = rootfsend - rootfsoff; imagelen = rootfsoff + rootfslen - kerneloff + sizeof(deadcode); rootfsoffpadlen = rootfsoff - (kerneloff + kernellen); @@ -231,6 +198,19 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ fseek(binfile, rootfsoff + rootfslen - fwaddr + cfelen, SEEK_SET); fwrite(&deadcode, sizeof(uint32_t), 1, binfile); + oldrootfslen = rootfslen; + if (args->pad_given) { + uint32_t allfs = 0xffffffff; + uint32_t pad_size = args->pad_arg * 1024 * 1024; + + printf("Padding image to %d bytes ...\n", pad_size); + while (imagelen < pad_size) { + fwrite(&allfs, sizeof(uint32_t), 1, binfile); + imagelen += 4; + rootfslen += 4; + } + } + /* Flush the binfile buffer so that when we read from file, it contains * everything in the buffer */ @@ -260,6 +240,7 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ rootfslen = oldrootfslen; rootfslen = ( (rootfslen % block_size) > 0 ? (((rootfslen / block_size) + 1) * block_size) : rootfslen ); kerneloffpadlen = rootfslen - oldrootfslen; + oldrootfslen = rootfslen; kerneloff = rootfsoff + rootfslen; kernellen = getlen(kernelfile); @@ -325,7 +306,7 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ sprintf(tag.totalLength, "%lu", imagelen); if (args->cfe_given) { - sprintf(tag.cfeAddress, "%lu", flash_start); + sprintf(tag.cfeAddress, "%" PRIu32, flash_start); sprintf(tag.cfeLength, "%lu", cfelen); } else { /* We don't include CFE */ @@ -343,7 +324,7 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ sprintf(tag.flashImageStart, "%lu", kerneloff); sprintf(tag.flashRootLength, "%lu", rootfslen + sizeof(deadcode)); } - int2tag(tag.rootLength, rootfslen + sizeof(deadcode)); + int2tag(tag.rootLength, oldrootfslen + sizeof(deadcode)); if (args->rsa_signature_given) { strncpy(tag.rsa_signature, args->rsa_signature_arg, RSASIG_LEN); @@ -366,7 +347,7 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ } if (args->altinfo_given) { - strncpy(&tag.information1[0], args->altinfo_arg, ALTTAGINFO_LEN); + strncpy(tag.information1, args->altinfo_arg, TAGINFO1_LEN); } if (args->second_image_flag_given) { @@ -397,7 +378,7 @@ int tagfile(const char *kernel, const char *rootfs, const char *bin, \ int2tag(&(tag.rootfsCRC[0]), rootfscrc); int2tag(tag.kernelCRC, kernelcrc); int2tag(tag.fskernelCRC, kernelfscrc); - int2tag(tag.headerCRC, crc32(IMAGETAG_CRC_START, (uint8_t*)&tag, sizeof(tag) - 20)); + int2tag(tag.headerCRC, cyg_crc32_accumulate(IMAGETAG_CRC_START, (uint8_t*)&tag, sizeof(tag) - 20)); fseek(binfile, 0L, SEEK_SET); fwrite(&tag, sizeof(uint8_t), sizeof(tag), binfile); @@ -418,7 +399,7 @@ int main(int argc, char **argv) kernel = rootfs = bin = NULL; - if (cmdline_parser(argc, argv, &parsed_args)) { + if (imagetag_cmdline(argc, argv, &parsed_args)) { exit(1); } @@ -483,6 +464,14 @@ int main(int argc, char **argv) exit(1); } } + + if (parsed_args.pad_given) { + if (parsed_args.pad_arg < 0) { + fprintf(stderr, "Error: pad size must be positive.\r"); + exit(1); + } + } + flash_start = strtoul(parsed_args.flash_start_arg, NULL, 16); image_offset = strtoul(parsed_args.image_offset_arg, NULL, 16); block_size = strtoul(parsed_args.block_size_arg, NULL, 16); diff --git a/tools/firmware-utils/src/imagetag.ggo b/tools/firmware-utils/src/imagetag.ggo index 7e8e7d90e32..73184852935 100644 --- a/tools/firmware-utils/src/imagetag.ggo +++ b/tools/firmware-utils/src/imagetag.ggo @@ -42,3 +42,5 @@ option "second-image-flag" - "Dual Image Flag (2=not-specified)." values="0", "1 option "inactive" - "Inactive Flag (2=not-specified)." values="0", "1", "2" default="2" typestr="flag-value" optional option "reserved2" - "String for second reserved section." string optional option "kernel-file-has-header" - "Indicates that the kernel file includes the kernel header with correct load address and entry point, so no changes are needed" flag off +option "pad" p "Pad the image to this size if smaller (in MiB)" int typestr="size (in MiB)" optional +option "align-rootfs" - "Align the rootfs start to erase block size" flag off diff --git a/tools/firmware-utils/src/imagetag_cmdline.c b/tools/firmware-utils/src/imagetag_cmdline.c index 91ac90b09d0..86c90bbb678 100644 --- a/tools/firmware-utils/src/imagetag_cmdline.c +++ b/tools/firmware-utils/src/imagetag_cmdline.c @@ -1,7 +1,7 @@ /* - File autogenerated by gengetopt version 2.22.4 + File autogenerated by gengetopt version 2.22.5 generated with the following command: - gengetopt --file-name=imagetag_cmdline --file-name=imagetag_cmdline + gengetopt -i imagetag.ggo -f imagetag_cmdline --file-name=imagetag_cmdline The developers of gengetopt consider the fixed text that goes in all gengetopt output files to be in the public domain: @@ -58,13 +58,16 @@ const char *gengetopt_args_info_help[] = { " --inactive=flag-value Inactive Flag (2=not-specified). (possible \n values=\"0\", \"1\", \"2\" default=`2')", " --reserved2=STRING String for second reserved section.", " --kernel-file-has-header Indicates that the kernel file includes the \n kernel header with correct load address and \n entry point, so no changes are needed \n (default=off)", + " -p, --pad=size (in MiB) Pad the image to this size if smaller (in MiB)", + " --align-rootfs Align the rootfs start to erase block size \n (default=off)", 0 }; typedef enum {ARG_NO , ARG_FLAG , ARG_STRING -} cmdline_parser_arg_type; + , ARG_INT +} imagetag_cmdline_arg_type; static void clear_given (struct gengetopt_args_info *args_info); @@ -72,14 +75,14 @@ static void clear_args (struct gengetopt_args_info *args_info); static int -cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error); +imagetag_cmdline_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params, const char *additional_error); static int -cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); +imagetag_cmdline_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); -const char *cmdline_parser_second_image_flag_values[] = {"0", "1", "2", 0}; /*< Possible values for second-image-flag. */ -const char *cmdline_parser_inactive_values[] = {"0", "1", "2", 0}; /*< Possible values for inactive. */ +const char *imagetag_cmdline_second_image_flag_values[] = {"0", "1", "2", 0}; /*< Possible values for second-image-flag. */ +const char *imagetag_cmdline_inactive_values[] = {"0", "1", "2", 0}; /*< Possible values for inactive. */ static char * gengetopt_strdup (const char *s); @@ -113,6 +116,8 @@ void clear_given (struct gengetopt_args_info *args_info) args_info->inactive_given = 0 ; args_info->reserved2_given = 0 ; args_info->kernel_file_has_header_given = 0 ; + args_info->pad_given = 0 ; + args_info->align_rootfs_given = 0 ; } static @@ -165,6 +170,8 @@ void clear_args (struct gengetopt_args_info *args_info) args_info->reserved2_arg = NULL; args_info->reserved2_orig = NULL; args_info->kernel_file_has_header_flag = 0; + args_info->pad_orig = NULL; + args_info->align_rootfs_flag = 0; } @@ -199,19 +206,21 @@ void init_args_info(struct gengetopt_args_info *args_info) args_info->inactive_help = gengetopt_args_info_help[23] ; args_info->reserved2_help = gengetopt_args_info_help[24] ; args_info->kernel_file_has_header_help = gengetopt_args_info_help[25] ; + args_info->pad_help = gengetopt_args_info_help[26] ; + args_info->align_rootfs_help = gengetopt_args_info_help[27] ; } void -cmdline_parser_print_version (void) +imagetag_cmdline_print_version (void) { printf ("%s %s\n", - (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), - CMDLINE_PARSER_VERSION); + (strlen(IMAGETAG_CMDLINE_PACKAGE_NAME) ? IMAGETAG_CMDLINE_PACKAGE_NAME : IMAGETAG_CMDLINE_PACKAGE), + IMAGETAG_CMDLINE_VERSION); } static void print_help_common(void) { - cmdline_parser_print_version (); + imagetag_cmdline_print_version (); if (strlen(gengetopt_args_info_purpose) > 0) printf("\n%s\n", gengetopt_args_info_purpose); @@ -226,7 +235,7 @@ static void print_help_common(void) { } void -cmdline_parser_print_help (void) +imagetag_cmdline_print_help (void) { int i = 0; print_help_common(); @@ -235,7 +244,7 @@ cmdline_parser_print_help (void) } void -cmdline_parser_init (struct gengetopt_args_info *args_info) +imagetag_cmdline_init (struct gengetopt_args_info *args_info) { clear_given (args_info); clear_args (args_info); @@ -243,7 +252,7 @@ cmdline_parser_init (struct gengetopt_args_info *args_info) } void -cmdline_parser_params_init(struct cmdline_parser_params *params) +imagetag_cmdline_params_init(struct imagetag_cmdline_params *params) { if (params) { @@ -255,12 +264,12 @@ cmdline_parser_params_init(struct cmdline_parser_params *params) } } -struct cmdline_parser_params * -cmdline_parser_params_create(void) +struct imagetag_cmdline_params * +imagetag_cmdline_params_create(void) { - struct cmdline_parser_params *params = - (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); - cmdline_parser_params_init(params); + struct imagetag_cmdline_params *params = + (struct imagetag_cmdline_params *)malloc(sizeof(struct imagetag_cmdline_params)); + imagetag_cmdline_params_init(params); return params; } @@ -276,7 +285,7 @@ free_string_field (char **s) static void -cmdline_parser_release (struct gengetopt_args_info *args_info) +imagetag_cmdline_release (struct gengetopt_args_info *args_info) { free_string_field (&(args_info->kernel_arg)); @@ -323,6 +332,7 @@ cmdline_parser_release (struct gengetopt_args_info *args_info) free_string_field (&(args_info->inactive_orig)); free_string_field (&(args_info->reserved2_arg)); free_string_field (&(args_info->reserved2_orig)); + free_string_field (&(args_info->pad_orig)); @@ -384,13 +394,13 @@ write_into_file(FILE *outfile, const char *opt, const char *arg, const char *val int -cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +imagetag_cmdline_dump(FILE *outfile, struct gengetopt_args_info *args_info) { int i = 0; if (!outfile) { - fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + fprintf (stderr, "%s: cannot dump options to stream\n", IMAGETAG_CMDLINE_PACKAGE); return EXIT_FAILURE; } @@ -439,13 +449,17 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) if (args_info->rsa_signature_given) write_into_file(outfile, "rsa-signature", args_info->rsa_signature_orig, 0); if (args_info->second_image_flag_given) - write_into_file(outfile, "second-image-flag", args_info->second_image_flag_orig, cmdline_parser_second_image_flag_values); + write_into_file(outfile, "second-image-flag", args_info->second_image_flag_orig, imagetag_cmdline_second_image_flag_values); if (args_info->inactive_given) - write_into_file(outfile, "inactive", args_info->inactive_orig, cmdline_parser_inactive_values); + write_into_file(outfile, "inactive", args_info->inactive_orig, imagetag_cmdline_inactive_values); if (args_info->reserved2_given) write_into_file(outfile, "reserved2", args_info->reserved2_orig, 0); if (args_info->kernel_file_has_header_given) write_into_file(outfile, "kernel-file-has-header", 0, 0 ); + if (args_info->pad_given) + write_into_file(outfile, "pad", args_info->pad_orig, 0); + if (args_info->align_rootfs_given) + write_into_file(outfile, "align-rootfs", 0, 0 ); i = EXIT_SUCCESS; @@ -453,7 +467,7 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) } int -cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +imagetag_cmdline_file_save(const char *filename, struct gengetopt_args_info *args_info) { FILE *outfile; int i = 0; @@ -462,20 +476,20 @@ cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_ if (!outfile) { - fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + fprintf (stderr, "%s: cannot open file for writing: %s\n", IMAGETAG_CMDLINE_PACKAGE, filename); return EXIT_FAILURE; } - i = cmdline_parser_dump(outfile, args_info); + i = imagetag_cmdline_dump(outfile, args_info); fclose (outfile); return i; } void -cmdline_parser_free (struct gengetopt_args_info *args_info) +imagetag_cmdline_free (struct gengetopt_args_info *args_info) { - cmdline_parser_release (args_info); + imagetag_cmdline_release (args_info); } /** @brief replacement of strdup, which is not standard */ @@ -494,21 +508,21 @@ gengetopt_strdup (const char *s) } int -cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +imagetag_cmdline (int argc, char **argv, struct gengetopt_args_info *args_info) { - return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); + return imagetag_cmdline2 (argc, argv, args_info, 0, 1, 1); } int -cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params) +imagetag_cmdline_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params) { int result; - result = cmdline_parser_internal (argc, argv, args_info, params, 0); + result = imagetag_cmdline_internal (argc, argv, args_info, params, 0); if (result == EXIT_FAILURE) { - cmdline_parser_free (args_info); + imagetag_cmdline_free (args_info); exit (EXIT_FAILURE); } @@ -516,10 +530,10 @@ cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info } int -cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +imagetag_cmdline2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) { int result; - struct cmdline_parser_params params; + struct imagetag_cmdline_params params; params.override = override; params.initialize = initialize; @@ -527,11 +541,11 @@ cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, i params.check_ambiguity = 0; params.print_errors = 1; - result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + result = imagetag_cmdline_internal (argc, argv, args_info, ¶ms, 0); if (result == EXIT_FAILURE) { - cmdline_parser_free (args_info); + imagetag_cmdline_free (args_info); exit (EXIT_FAILURE); } @@ -539,16 +553,16 @@ cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, i } int -cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +imagetag_cmdline_required (struct gengetopt_args_info *args_info, const char *prog_name) { int result = EXIT_SUCCESS; - if (cmdline_parser_required2(args_info, prog_name, 0) > 0) + if (imagetag_cmdline_required2(args_info, prog_name, 0) > 0) result = EXIT_FAILURE; if (result == EXIT_FAILURE) { - cmdline_parser_free (args_info); + imagetag_cmdline_free (args_info); exit (EXIT_FAILURE); } @@ -556,7 +570,7 @@ cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog } int -cmdline_parser_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error) +imagetag_cmdline_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error) { int error = 0; FIX_UNUSED (additional_error); @@ -623,8 +637,8 @@ static char *package_name = 0; * @param possible_values the possible values for this option (if specified) * @param default_value the default value (in case the option only accepts fixed values) * @param arg_type the type of this option - * @param check_ambiguity @see cmdline_parser_params.check_ambiguity - * @param override @see cmdline_parser_params.override + * @param check_ambiguity @see imagetag_cmdline_params.check_ambiguity + * @param override @see imagetag_cmdline_params.override * @param no_free whether to free a possible previous value * @param multiple_option whether this is a multiple option * @param long_opt the corresponding long option @@ -636,7 +650,7 @@ int update_arg(void *field, char **orig_field, unsigned int *field_given, unsigned int *prev_given, char *value, const char *possible_values[], const char *default_value, - cmdline_parser_arg_type arg_type, + imagetag_cmdline_arg_type arg_type, int check_ambiguity, int override, int no_free, int multiple_option, const char *long_opt, char short_opt, @@ -690,6 +704,9 @@ int update_arg(void *field, char **orig_field, case ARG_FLAG: *((int *)field) = !*((int *)field); break; + case ARG_INT: + if (val) *((int *)field) = strtol (val, &stop_char, 0); + break; case ARG_STRING: if (val) { string_field = (char **)field; @@ -702,6 +719,17 @@ int update_arg(void *field, char **orig_field, break; }; + /* check numeric conversion */ + switch(arg_type) { + case ARG_INT: + if (val && !(stop_char && *stop_char == '\0')) { + fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val); + return 1; /* failure */ + } + break; + default: + ; + }; /* store the original value */ switch(arg_type) { @@ -725,9 +753,9 @@ int update_arg(void *field, char **orig_field, int -cmdline_parser_internal ( +imagetag_cmdline_internal ( int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params, const char *additional_error) + struct imagetag_cmdline_params *params, const char *additional_error) { int c; /* Character of the parsed option. */ @@ -747,9 +775,9 @@ cmdline_parser_internal ( check_ambiguity = params->check_ambiguity; if (initialize) - cmdline_parser_init (args_info); + imagetag_cmdline_init (args_info); - cmdline_parser_init (&local_args_info); + imagetag_cmdline_init (&local_args_info); optarg = 0; optind = 0; @@ -787,23 +815,25 @@ cmdline_parser_internal ( { "inactive", 1, NULL, 0 }, { "reserved2", 1, NULL, 0 }, { "kernel-file-has-header", 0, NULL, 0 }, + { "pad", 1, NULL, 'p' }, + { "align-rootfs", 0, NULL, 0 }, { 0, 0, 0, 0 } }; - c = getopt_long (argc, argv, "hVi:f:o:b:c:s:n:v:a:m:k:l:e:y:1:2:r:", long_options, &option_index); + c = getopt_long (argc, argv, "hVi:f:o:b:c:s:n:v:a:m:k:l:e:y:1:2:r:p:", long_options, &option_index); if (c == -1) break; /* Exit from `while (1)' loop. */ switch (c) { case 'h': /* Print help and exit. */ - cmdline_parser_print_help (); - cmdline_parser_free (&local_args_info); + imagetag_cmdline_print_help (); + imagetag_cmdline_free (&local_args_info); exit (EXIT_SUCCESS); case 'V': /* Print version and exit. */ - cmdline_parser_print_version (); - cmdline_parser_free (&local_args_info); + imagetag_cmdline_print_version (); + imagetag_cmdline_free (&local_args_info); exit (EXIT_SUCCESS); case 'i': /* File with LZMA compressed kernel to include in the image.. */ @@ -1010,6 +1040,18 @@ cmdline_parser_internal ( goto failure; break; + case 'p': /* Pad the image to this size if smaller (in MiB). */ + + + if (update_arg( (void *)&(args_info->pad_arg), + &(args_info->pad_orig), &(args_info->pad_given), + &(local_args_info.pad_given), optarg, 0, 0, ARG_INT, + check_ambiguity, override, 0, 0, + "pad", 'p', + additional_error)) + goto failure; + + break; case 0: /* Long option with no short option */ /* File with CFE to include in the image.. */ @@ -1059,7 +1101,7 @@ cmdline_parser_internal ( if (update_arg( (void *)&(args_info->second_image_flag_arg), &(args_info->second_image_flag_orig), &(args_info->second_image_flag_given), - &(local_args_info.second_image_flag_given), optarg, cmdline_parser_second_image_flag_values, "2", ARG_STRING, + &(local_args_info.second_image_flag_given), optarg, imagetag_cmdline_second_image_flag_values, "2", ARG_STRING, check_ambiguity, override, 0, 0, "second-image-flag", '-', additional_error)) @@ -1073,7 +1115,7 @@ cmdline_parser_internal ( if (update_arg( (void *)&(args_info->inactive_arg), &(args_info->inactive_orig), &(args_info->inactive_given), - &(local_args_info.inactive_given), optarg, cmdline_parser_inactive_values, "2", ARG_STRING, + &(local_args_info.inactive_given), optarg, imagetag_cmdline_inactive_values, "2", ARG_STRING, check_ambiguity, override, 0, 0, "inactive", '-', additional_error)) @@ -1106,6 +1148,18 @@ cmdline_parser_internal ( goto failure; } + /* Align the rootfs start to erase block size. */ + else if (strcmp (long_options[option_index].name, "align-rootfs") == 0) + { + + + if (update_arg((void *)&(args_info->align_rootfs_flag), 0, &(args_info->align_rootfs_given), + &(local_args_info.align_rootfs_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "align-rootfs", '-', + additional_error)) + goto failure; + + } break; case '?': /* Invalid option. */ @@ -1113,7 +1167,7 @@ cmdline_parser_internal ( goto failure; default: /* bug: option not considered. */ - fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + fprintf (stderr, "%s: option unknown: %c%s\n", IMAGETAG_CMDLINE_PACKAGE, c, (additional_error ? additional_error : "")); abort (); } /* switch */ } /* while */ @@ -1122,10 +1176,10 @@ cmdline_parser_internal ( if (check_required) { - error += cmdline_parser_required2 (args_info, argv[0], additional_error); + error += imagetag_cmdline_required2 (args_info, argv[0], additional_error); } - cmdline_parser_release (&local_args_info); + imagetag_cmdline_release (&local_args_info); if ( error ) return (EXIT_FAILURE); @@ -1134,6 +1188,6 @@ cmdline_parser_internal ( failure: - cmdline_parser_release (&local_args_info); + imagetag_cmdline_release (&local_args_info); return (EXIT_FAILURE); } diff --git a/tools/firmware-utils/src/imagetag_cmdline.h b/tools/firmware-utils/src/imagetag_cmdline.h index c566a9f2932..3f55c509bb3 100644 --- a/tools/firmware-utils/src/imagetag_cmdline.h +++ b/tools/firmware-utils/src/imagetag_cmdline.h @@ -1,6 +1,6 @@ /** @file imagetag_cmdline.h * @brief The header file for the command line option parser - * generated by GNU Gengetopt version 2.22.4 + * generated by GNU Gengetopt version 2.22.5 * http://www.gnu.org/software/gengetopt. * DO NOT modify this file, since it can be overwritten * @author GNU Gengetopt by Lorenzo Bettini */ @@ -19,19 +19,19 @@ extern "C" { #endif /* __cplusplus */ -#ifndef CMDLINE_PARSER_PACKAGE +#ifndef IMAGETAG_CMDLINE_PACKAGE /** @brief the program name (used for printing errors) */ -#define CMDLINE_PARSER_PACKAGE "imagetag" +#define IMAGETAG_CMDLINE_PACKAGE "imagetag" #endif -#ifndef CMDLINE_PARSER_PACKAGE_NAME +#ifndef IMAGETAG_CMDLINE_PACKAGE_NAME /** @brief the complete program name (used for help and version) */ -#define CMDLINE_PARSER_PACKAGE_NAME "imagetag" +#define IMAGETAG_CMDLINE_PACKAGE_NAME "imagetag" #endif -#ifndef CMDLINE_PARSER_VERSION +#ifndef IMAGETAG_CMDLINE_VERSION /** @brief the program version */ -#define CMDLINE_PARSER_VERSION "2.0.0" +#define IMAGETAG_CMDLINE_VERSION "2.0.0" #endif /** @brief Where the command line options are stored */ @@ -109,6 +109,11 @@ struct gengetopt_args_info const char *reserved2_help; /**< @brief String for second reserved section. help description. */ int kernel_file_has_header_flag; /**< @brief Indicates that the kernel file includes the kernel header with correct load address and entry point, so no changes are needed (default=off). */ const char *kernel_file_has_header_help; /**< @brief Indicates that the kernel file includes the kernel header with correct load address and entry point, so no changes are needed help description. */ + int pad_arg; /**< @brief Pad the image to this size if smaller (in MiB). */ + char * pad_orig; /**< @brief Pad the image to this size if smaller (in MiB) original value given at command line. */ + const char *pad_help; /**< @brief Pad the image to this size if smaller (in MiB) help description. */ + int align_rootfs_flag; /**< @brief Align the rootfs start to erase block size (default=off). */ + const char *align_rootfs_help; /**< @brief Align the rootfs start to erase block size help description. */ unsigned int help_given ; /**< @brief Whether help was given. */ unsigned int version_given ; /**< @brief Whether version was given. */ @@ -136,11 +141,13 @@ struct gengetopt_args_info unsigned int inactive_given ; /**< @brief Whether inactive was given. */ unsigned int reserved2_given ; /**< @brief Whether reserved2 was given. */ unsigned int kernel_file_has_header_given ; /**< @brief Whether kernel-file-has-header was given. */ + unsigned int pad_given ; /**< @brief Whether pad was given. */ + unsigned int align_rootfs_given ; /**< @brief Whether align-rootfs was given. */ } ; /** @brief The additional parameters to pass to parser functions */ -struct cmdline_parser_params +struct imagetag_cmdline_params { int override; /**< @brief whether to override possibly already present options (default 0) */ int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ @@ -163,7 +170,7 @@ extern const char *gengetopt_args_info_help[]; * @param args_info the structure where option information will be stored * @return 0 if everything went fine, NON 0 if an error took place */ -int cmdline_parser (int argc, char **argv, +int imagetag_cmdline (int argc, char **argv, struct gengetopt_args_info *args_info); /** @@ -175,9 +182,9 @@ int cmdline_parser (int argc, char **argv, * @param initialize whether to initialize the option structure my_args_info * @param check_required whether to check that all required options were provided * @return 0 if everything went fine, NON 0 if an error took place - * @deprecated use cmdline_parser_ext() instead + * @deprecated use imagetag_cmdline_ext() instead */ -int cmdline_parser2 (int argc, char **argv, +int imagetag_cmdline2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required); @@ -189,9 +196,9 @@ int cmdline_parser2 (int argc, char **argv, * @param params additional parameters for the parser * @return 0 if everything went fine, NON 0 if an error took place */ -int cmdline_parser_ext (int argc, char **argv, +int imagetag_cmdline_ext (int argc, char **argv, struct gengetopt_args_info *args_info, - struct cmdline_parser_params *params); + struct imagetag_cmdline_params *params); /** * Save the contents of the option struct into an already open FILE stream. @@ -199,7 +206,7 @@ int cmdline_parser_ext (int argc, char **argv, * @param args_info the option struct to dump * @return 0 if everything went fine, NON 0 if an error took place */ -int cmdline_parser_dump(FILE *outfile, +int imagetag_cmdline_dump(FILE *outfile, struct gengetopt_args_info *args_info); /** @@ -209,44 +216,44 @@ int cmdline_parser_dump(FILE *outfile, * @param args_info the option struct to save * @return 0 if everything went fine, NON 0 if an error took place */ -int cmdline_parser_file_save(const char *filename, +int imagetag_cmdline_file_save(const char *filename, struct gengetopt_args_info *args_info); /** * Print the help */ -void cmdline_parser_print_help(void); +void imagetag_cmdline_print_help(void); /** * Print the version */ -void cmdline_parser_print_version(void); +void imagetag_cmdline_print_version(void); /** - * Initializes all the fields a cmdline_parser_params structure + * Initializes all the fields a imagetag_cmdline_params structure * to their default values * @param params the structure to initialize */ -void cmdline_parser_params_init(struct cmdline_parser_params *params); +void imagetag_cmdline_params_init(struct imagetag_cmdline_params *params); /** - * Allocates dynamically a cmdline_parser_params structure and initializes + * Allocates dynamically a imagetag_cmdline_params structure and initializes * all its fields to their default values - * @return the created and initialized cmdline_parser_params structure + * @return the created and initialized imagetag_cmdline_params structure */ -struct cmdline_parser_params *cmdline_parser_params_create(void); +struct imagetag_cmdline_params *imagetag_cmdline_params_create(void); /** * Initializes the passed gengetopt_args_info structure's fields * (also set default values for options that have a default) * @param args_info the structure to initialize */ -void cmdline_parser_init (struct gengetopt_args_info *args_info); +void imagetag_cmdline_init (struct gengetopt_args_info *args_info); /** * Deallocates the string fields of the gengetopt_args_info structure * (but does not deallocate the structure itself) * @param args_info the structure to deallocate */ -void cmdline_parser_free (struct gengetopt_args_info *args_info); +void imagetag_cmdline_free (struct gengetopt_args_info *args_info); /** * Checks that all the required options were specified @@ -255,11 +262,11 @@ void cmdline_parser_free (struct gengetopt_args_info *args_info); * possible errors * @return */ -int cmdline_parser_required (struct gengetopt_args_info *args_info, +int imagetag_cmdline_required (struct gengetopt_args_info *args_info, const char *prog_name); -extern const char *cmdline_parser_second_image_flag_values[]; /**< @brief Possible values for second-image-flag. */ -extern const char *cmdline_parser_inactive_values[]; /**< @brief Possible values for inactive. */ +extern const char *imagetag_cmdline_second_image_flag_values[]; /**< @brief Possible values for second-image-flag. */ +extern const char *imagetag_cmdline_inactive_values[]; /**< @brief Possible values for inactive. */ #ifdef __cplusplus diff --git a/tools/firmware-utils/src/jcgimage.c b/tools/firmware-utils/src/jcgimage.c new file mode 100644 index 00000000000..354c26be19b --- /dev/null +++ b/tools/firmware-utils/src/jcgimage.c @@ -0,0 +1,425 @@ +/* + * jcgimage - Create a JCG firmware image + * + * Copyright (C) 2015 Reinhard Max <reinhard@m4x.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +/* + * JCG firmware update images consist of a 512 byte header and a + * modified uImage (details below) as the payload. + * + * The payload is obfuscated by XORing it with a key that is generated + * from parts of the header. Fortunately only non-essential parts of + * the header are used for this and zeroing them results in a zero + * key, effectively disabling the obfuscation and allowing us to use + * clear text payloads. + * + * The mandatory parts of the header are: + * + * - A magic string of "YSZJ" at offset 0. + * - A value of 1 at offset 39 (header format version?) + * - A CRC32 checksum of the payload at offset 504. + * - A CRC32 checksum of the header at offset 508. + * + * An image constructed by these rules will be accepted by JCG's + * U-Boot in resuce mode via TFTP and the payload will be written to + * the flash starting at offset 0x00050000. + * + * JCG's U-Boot does check the content or size of the payload + * image. If it is too large, it wraps around and overwrites U-Boot, + * requiring JTAG to revive the board. To prevent such bricking from + * happening, this tool refuses to build such overlong images. + * + * Two more conditions have to be met for a JCG image to be accepted + * as a valid update by the web interface of the stock firware: + * + * - The bytes at offsets 109 and 111 in the header must be a binary + * representation of the first two components of the firmware + * version as displayed in the update web form, or it will be + * rejected as "incorrect product". + * + * - The payload must start with a valid uImage header whose data + * CRC checksum matches the whole rest of the update file rather + * than just the number of bytes specified in the size field of the + * header. + * + * This last condition is met by JCG's original firmware images, + * because they have both, kernel and rootfs inside the uImage and + * abuse the last four bytes of the name field to record the offset of + * the file system from the start of the uImage header. This tool + * produces such images when called with -k and -r, which are meant to + * repack the original firmware after modifying the file systen, + * e.g. to add debugging tools and enable shell access. + * + * In contrast, OpenWrt sysupgrade images consist of a uImage that + * only contains the kernel and has the rootfs appended to it. Hence, + * the CRC over kernel and file system does not match the one in the + * uImage header. Fixing this by adjusting the uImage header is not + * possible, because it makes the uImage unusable for booting. Instead + * we append four "patch" bytes to the end of the file system, that + * are calculated to force the checksum of kernel+fs to be the same as + * for the kernel alone. + * + */ + +#include <zlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <libgen.h> +#include <stdlib.h> +#include <errno.h> +#include <err.h> +#include <time.h> +#include <sys/mman.h> +#include <arpa/inet.h> +#include <assert.h> + +/* + * JCG Firmware image header + */ +#define JH_MAGIC 0x59535a4a /* "YSZJ" */ +struct jcg_header { + uint32_t jh_magic; + uint8_t jh_version[32]; /* Firmware version string. + Fill with zeros to avoid encryption */ + uint32_t jh_type; /* must be 1 */ + uint8_t jh_info[64]; /* Firmware info string. Fill with + zeros to avoid encryption */ + uint32_t jh_time; /* Image creation time in seconds since + * the Epoch. Does not seem to be used + * by the stock firmware. */ + uint16_t jh_major; /* Major fimware version */ + uint16_t jh_minor; /* Minor fimrmware version */ + uint8_t jh_unknown[392]; /* Apparently unused and all zeros */ + uint32_t jh_dcrc; /* CRC checksum of the payload */ + uint32_t jh_hcrc; /* CRC checksum of the header */ +}; + +/* + * JCG uses a modified uImage header that replaces the last four bytes + * of the image name with the length of the kernel in the image. + */ +#define IH_MAGIC 0x27051956 /* Image Magic Number */ +#define IH_NMLEN 28 /* Image Name Length */ + +struct uimage_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN];/* Image Name */ + uint32_t ih_fsoff; /* Offset of the file system + partition from the start of + the header */ +}; + +/* + * Open the named file and return its size and file descriptor. + * Exit in case of errors. + */ +int +opensize(char *name, size_t *size) +{ + struct stat s; + int fd = open(name, O_RDONLY); + if (fd < 0) { + err(1, "cannot open \"%s\"", name); + } + if (fstat(fd, &s) == -1) { + err(1, "cannot stat \"%s\"", name); + } + *size = s.st_size; + return fd; +} + +static time_t source_date_epoch = -1; +static void set_source_date_epoch() { + char *env = getenv("SOURCE_DATE_EPOCH"); + char *endptr = env; + errno = 0; + if (env && *env) { + source_date_epoch = strtoull(env, &endptr, 10); + if (errno || (endptr && *endptr != '\0')) { + fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); + exit(1); + } + } +} + +/* + * Write the JCG header + */ +void +mkjcgheader(struct jcg_header *h, size_t psize, char *version) +{ + uLong crc; + uint16_t major = 0, minor = 0; + void *payload = (void *)h + sizeof(*h); + time_t t; + + if (source_date_epoch != -1) { + t = source_date_epoch; + } else if ((time(&t) == (time_t)(-1))) { + err(1, "time call failed"); + } + + if (version != NULL) { + if (sscanf(version, "%hu.%hu", &major, &minor) != 2) { + err(1, "cannot parse version \"%s\"", version); + } + } + + memset(h, 0, sizeof(*h)); + h->jh_magic = htonl(JH_MAGIC); + h->jh_type = htonl(1); + h->jh_time = htonl(t); + h->jh_major = htons(major); + h->jh_minor = htons(minor); + + /* CRC over JCG payload (uImage) */ + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, payload, psize); + h->jh_dcrc = htonl(crc); + + /* CRC over JCG header */ + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (void *)h, sizeof(*h)); + h->jh_hcrc = htonl(crc); +} + +/* + * Write the uImage header + */ +void +mkuheader(struct uimage_header *h, size_t ksize, size_t fsize) +{ + uLong crc; + void *payload = (void *)h + sizeof(*h); + + // printf("mkuheader: %p, %zd, %zd\n", h, ksize, fsize); + memset(h, 0, sizeof(*h)); + h->ih_magic = htonl(IH_MAGIC); + h->ih_time = htonl(time(NULL)); + h->ih_size = htonl(ksize + fsize); + h->ih_load = htonl(0x80000000); + h->ih_ep = htonl(0x80292000); + h->ih_os = 0x05; + h->ih_arch = 0x05; + h->ih_type = 0x02; + h->ih_comp = 0x03; + h->ih_fsoff = htonl(sizeof(*h) + ksize); + strcpy((char *)h->ih_name, "Linux Kernel Image"); + + /* CRC over uImage payload (kernel and file system) */ + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, payload, ntohl(h->ih_size)); + h->ih_dcrc = htonl(crc); + printf("CRC1: %08lx\n", crc); + + /* CRC over uImage header */ + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, (void *)h, sizeof(*h)); + h->ih_hcrc = htonl(crc); + printf("CRC2: %08lx\n", crc); +} + +/* + * Calculate a "patch" value and write it into the last four bytes of + * buf, so that the CRC32 checksum of the whole buffer is dcrc. + * + * Based on: SAR-PR-2006-05: Reversing CRC – Theory and Practice. + * Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich. + * http://sar.informatik.hu-berlin.de/research/publications/#SAR-PR-2006-05 + */ +void +craftcrc(uint32_t dcrc, uint8_t *buf, size_t len) +{ + int i; + uint32_t a; + uint32_t patch = 0; + uint32_t crc = crc32(0L, Z_NULL, 0); + + a = ~dcrc; + for (i = 0; i < 32; i++) { + if (patch & 1) { + patch = (patch >> 1) ^ 0xedb88320L; + } else { + patch >>= 1; + } + if (a & 1) { + patch ^= 0x5b358fd3L; + } + a >>= 1; + } + patch ^= ~crc32(crc, buf, len - 4); + for (i = 0; i < 4; i++) { + buf[len - 4 + i] = patch & 0xff; + patch >>= 8; + } + /* Verify that we actually get the desired result */ + crc = crc32(0L, Z_NULL, 0); + crc = crc32(crc, buf, len); + if (crc != dcrc) { + errx(1, "CRC patching is broken: wanted %08x, but got %08x.", + dcrc, crc); + } +} + +void +usage() { + fprintf(stderr, "Usage:\n" + "jcgimage -o outfile -u uImage [-v version]\n" + "jcgimage -o outfile -k kernel -f rootfs [-v version]\n"); + exit(1); +} + +#define MODE_UNKNOWN 0 +#define MODE_UIMAGE 1 +#define MODE_KR 2 + +/* The output image must not be larger than 4MiB - 5*64kiB */ +#define MAXSIZE (size_t)(4 * 1024 * 1024 - 5 * 64 * 1024) + +int +main(int argc, char **argv) +{ + struct jcg_header *jh; + struct uimage_header *uh; + int c; + char *imagefile = NULL; + char *file1 = NULL; + char *file2 = NULL; + char *version = NULL; + int mode = MODE_UNKNOWN; + int fdo, fd1, fd2; + size_t size1, size2, sizeu, sizeo, off1, off2; + void *map; + + /* Make sure the headers have the right size */ + assert(sizeof(struct jcg_header) == 512); + assert(sizeof(struct uimage_header) == 64); + set_source_date_epoch(); + + while ((c = getopt(argc, argv, "o:k:f:u:v:h")) != -1) { + switch (c) { + case 'o': + imagefile = optarg; + break; + case 'k': + if (mode == MODE_UIMAGE) { + errx(1,"-k cannot be combined with -u"); + } + mode = MODE_KR; + file1 = optarg; + break; + case 'f': + if (mode == MODE_UIMAGE) { + errx(1,"-f cannot be combined with -u"); + } + mode = MODE_KR; + file2 = optarg; + break; + case 'u': + if (mode == MODE_KR) { + errx(1,"-u cannot be combined with -k and -r"); + } + mode = MODE_UIMAGE; + file1 = optarg; + break; + case 'v': + version = optarg; + break; + case 'h': + default: + usage(); + } + } + if (optind != argc) { + errx(1, "illegal arg \"%s\"", argv[optind]); + } + if (imagefile == NULL) { + errx(1, "no output file specified"); + } + if (mode == MODE_UNKNOWN) { + errx(1, "specify either -u or -k and -r"); + } + if (mode == MODE_KR) { + if (file1 == NULL || file2 == NULL) { + errx(1,"need -k and -r"); + } + fd2 = opensize(file2, &size2); + } + fd1 = opensize(file1, &size1); + if (mode == MODE_UIMAGE) { + off1 = sizeof(*jh); + sizeu = size1 + 4; + sizeo = sizeof(*jh) + sizeu; + } else { + off1 = sizeof(*jh) + sizeof(*uh); + off2 = sizeof(*jh) + sizeof(*uh) + size1; + sizeu = sizeof(*uh) + size1 + size2; + sizeo = sizeof(*jh) + sizeu; + } + + if (sizeo > MAXSIZE) { + errx(1,"payload too large: %zd > %zd\n", sizeo, MAXSIZE); + } + + fdo = open(imagefile, O_RDWR | O_CREAT | O_TRUNC, 00644); + if (fdo < 0) { + err(1, "cannot open \"%s\"", imagefile); + } + + if (ftruncate(fdo, sizeo) == -1) { + err(1, "cannot grow \"%s\" to %zd bytes", imagefile, sizeo); + } + map = mmap(NULL, sizeo, PROT_READ|PROT_WRITE, MAP_SHARED, fdo, 0); + uh = map + sizeof(*jh); + if (map == MAP_FAILED) { + err(1, "cannot mmap \"%s\"", imagefile); + } + + if (read(fd1, map + off1, size1) != size1) { + err(1, "cannot copy %s", file1); + } + + if (mode == MODE_KR) { + if (read(fd2, map+off2, size2) != size2) { + err(1, "cannot copy %s", file2); + } + mkuheader(uh, size1, size2); + } else if (mode == MODE_UIMAGE) { + craftcrc(ntohl(uh->ih_dcrc), (void*)uh + sizeof(*uh), + sizeu - sizeof(*uh)); + } + mkjcgheader(map, sizeu, version); + munmap(map, sizeo); + close(fdo); + return 0; +} diff --git a/tools/firmware-utils/src/lzma2eva.c b/tools/firmware-utils/src/lzma2eva.c index 0bc13fa4f34..1d7e3648899 100644 --- a/tools/firmware-utils/src/lzma2eva.c +++ b/tools/firmware-utils/src/lzma2eva.c @@ -48,7 +48,7 @@ main(int argc, char *argv[]) const char *infile, *outfile; FILE *in, *out; - static const uint8_t buf[4096]; + static uint8_t buf[4096]; size_t elems; uint8_t properties; diff --git a/tools/firmware-utils/src/md5.c b/tools/firmware-utils/src/md5.c index 20397603834..52d96accd30 100644 --- a/tools/firmware-utils/src/md5.c +++ b/tools/firmware-utils/src/md5.c @@ -1,307 +1,296 @@ - - /* - *********************************************************************** - ** md5.c -- the source code for MD5 routines ** - ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** - ** Created: 2/17/90 RLR ** - ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. ** - *********************************************************************** + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * (This is a heavily cut-down "BSD license".) + * + * This differs from Colin Plumb's older public domain implementation in that + * no exactly 32-bit integer data type is required (any 32-bit or wider + * unsigned integer data type will do), there's no compile-time endianness + * configuration, and the function prototypes match OpenSSL's. No code from + * Colin Plumb's implementation has been reused; this comment merely compares + * the properties of the two independent implementations. + * + * The primary goals of this implementation are portability and ease of use. + * It is meant to be fast, but not as fast as possible. Some known + * optimizations are not included to reduce source code size and avoid + * compile-time configuration. */ -/* - *********************************************************************** - ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** - ** ** - ** License to copy and use this software is granted provided that ** - ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** - ** Digest Algorithm" in all material mentioning or referencing this ** - ** software or this function. ** - ** ** - ** License is also granted to make and use derivative works ** - ** provided that such works are identified as "derived from the RSA ** - ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** - ** material mentioning or referencing the derived work. ** - ** ** - ** RSA Data Security, Inc. makes no representations concerning ** - ** either the merchantability of this software or the suitability ** - ** of this software for any particular purpose. It is provided "as ** - ** is" without express or implied warranty of any kind. ** - ** ** - ** These notices must be retained in any copies of any part of this ** - ** documentation and/or software. ** - *********************************************************************** - */ +#ifndef HAVE_OPENSSL #include <string.h> + #include "md5.h" /* - *********************************************************************** - ** Message-digest routines: ** - ** To form the message digest for a message M ** - ** (1) Initialize a context buffer mdContext using MD5_Init ** - ** (2) Call MD5_Update on mdContext and M ** - ** (3) Call MD5_Final on mdContext ** - ** The message digest is now in mdContext->digest[0...15] ** - *********************************************************************** + * The basic MD5 functions. + * + * F and G are optimized compared to their RFC 1321 definitions for + * architectures that lack an AND-NOT instruction, just like in Colin Plumb's + * implementation. + */ +#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) +#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) +#define H(x, y, z) (((x) ^ (y)) ^ (z)) +#define H2(x, y, z) ((x) ^ ((y) ^ (z))) +#define I(x, y, z) ((y) ^ ((x) | ~(z))) + +/* + * The MD5 transformation for all four rounds. */ +#define STEP(f, a, b, c, d, x, t, s) \ + (a) += f((b), (c), (d)) + (x) + (t); \ + (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ + (a) += (b); -/* forward declaration */ -static void Transform (); - -static unsigned char PADDING[64] = { - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* F, G, H and I are basic MD5 functions */ -#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) -#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) -#define H(x, y, z) ((x) ^ (y) ^ (z)) -#define I(x, y, z) ((y) ^ ((x) | (~z))) - -/* ROTATE_LEFT rotates x left n bits */ -#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) - -/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ -/* Rotation is separate from addition to prevent recomputation */ -#define FF(a, b, c, d, x, s, ac) \ - {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define GG(a, b, c, d, x, s, ac) \ - {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define HH(a, b, c, d, x, s, ac) \ - {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } -#define II(a, b, c, d, x, s, ac) \ - {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ - (a) = ROTATE_LEFT ((a), (s)); \ - (a) += (b); \ - } - -#ifdef __STDC__ -#define UL(x) x##U +/* + * SET reads 4 input bytes in little-endian byte order and stores them + * in a properly aligned word in host byte order. + * + * The check for little-endian architectures that tolerate unaligned + * memory accesses is just an optimization. Nothing will break if it + * doesn't work. + */ +#if defined(__i386__) || defined(__x86_64__) || defined(__vax__) +#define SET(n) \ + (*(MD5_u32plus *)&ptr[(n) * 4]) +#define GET(n) \ + SET(n) #else -#define UL(x) x +#define SET(n) \ + (ctx->block[(n)] = \ + (MD5_u32plus)ptr[(n) * 4] | \ + ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ + ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ + ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) +#define GET(n) \ + (ctx->block[(n)]) #endif -/* The routine MD5_Init initializes the message-digest context - mdContext. All fields are set to zero. +/* + * This processes one or more 64-byte data blocks, but does NOT update + * the bit counters. There are no alignment requirements. */ -void MD5_Init (mdContext) -MD5_CTX *mdContext; +static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) { - mdContext->i[0] = mdContext->i[1] = (UINT4)0; - - /* Load magic initialization constants. - */ - mdContext->buf[0] = (UINT4)0x67452301; - mdContext->buf[1] = (UINT4)0xefcdab89; - mdContext->buf[2] = (UINT4)0x98badcfe; - mdContext->buf[3] = (UINT4)0x10325476; + const unsigned char *ptr; + MD5_u32plus a, b, c, d; + MD5_u32plus saved_a, saved_b, saved_c, saved_d; + + ptr = (const unsigned char *)data; + + a = ctx->a; + b = ctx->b; + c = ctx->c; + d = ctx->d; + + do { + saved_a = a; + saved_b = b; + saved_c = c; + saved_d = d; + +/* Round 1 */ + STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) + STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) + STEP(F, c, d, a, b, SET(2), 0x242070db, 17) + STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) + STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) + STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) + STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) + STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) + STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) + STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) + STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) + STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) + STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) + STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) + STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) + STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) + +/* Round 2 */ + STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) + STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) + STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) + STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) + STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) + STEP(G, d, a, b, c, GET(10), 0x02441453, 9) + STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) + STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) + STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) + STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) + STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) + STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) + STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) + STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) + STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) + STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) + +/* Round 3 */ + STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) + STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) + STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) + STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) + STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) + STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) + STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) + STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) + STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) + STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) + STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) + STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) + STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) + STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) + STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) + STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) + +/* Round 4 */ + STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) + STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) + STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) + STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) + STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) + STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) + STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) + STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) + STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) + STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) + STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) + STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) + STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) + STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) + STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) + STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) + + a += saved_a; + b += saved_b; + c += saved_c; + d += saved_d; + + ptr += 64; + } while (size -= 64); + + ctx->a = a; + ctx->b = b; + ctx->c = c; + ctx->d = d; + + return ptr; } -/* The routine MD5Update updates the message-digest context to - account for the presence of each of the characters inBuf[0..inLen-1] - in the message whose digest is being computed. - */ -void MD5_Update (mdContext, inBuf, inLen) -MD5_CTX *mdContext; -unsigned char *inBuf; -unsigned int inLen; +void MD5_Init(MD5_CTX *ctx) { - UINT4 in[16]; - int mdi; - unsigned int i, ii; - - /* compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); - - /* update number of bits */ - if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) - mdContext->i[1]++; - mdContext->i[0] += ((UINT4)inLen << 3); - mdContext->i[1] += ((UINT4)inLen >> 29); - - while (inLen--) { - /* add new character to buffer, increment mdi */ - mdContext->in[mdi++] = *inBuf++; - - /* transform if necessary */ - if (mdi == 0x40) { - for (i = 0, ii = 0; i < 16; i++, ii += 4) - in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | - (((UINT4)mdContext->in[ii+2]) << 16) | - (((UINT4)mdContext->in[ii+1]) << 8) | - ((UINT4)mdContext->in[ii]); - Transform (mdContext->buf, in); - mdi = 0; - } - } + ctx->a = 0x67452301; + ctx->b = 0xefcdab89; + ctx->c = 0x98badcfe; + ctx->d = 0x10325476; + + ctx->lo = 0; + ctx->hi = 0; } -/* The routine MD5Final terminates the message-digest computation and - ends with the desired message digest in mdContext->digest[0...15]. - */ -void MD5_Final (hash, mdContext) -unsigned char hash[]; -MD5_CTX *mdContext; +void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) { - UINT4 in[16]; - int mdi; - unsigned int i, ii; - unsigned int padLen; - - /* save number of bits */ - in[14] = mdContext->i[0]; - in[15] = mdContext->i[1]; - - /* compute number of bytes mod 64 */ - mdi = (int)((mdContext->i[0] >> 3) & 0x3F); - - /* pad out to 56 mod 64 */ - padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); - MD5_Update (mdContext, PADDING, padLen); - - /* append length in bits and transform */ - for (i = 0, ii = 0; i < 14; i++, ii += 4) - in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | - (((UINT4)mdContext->in[ii+2]) << 16) | - (((UINT4)mdContext->in[ii+1]) << 8) | - ((UINT4)mdContext->in[ii]); - Transform (mdContext->buf, in); - - /* store buffer in digest */ - for (i = 0, ii = 0; i < 4; i++, ii += 4) { - mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); - mdContext->digest[ii+1] = - (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); - mdContext->digest[ii+2] = - (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); - mdContext->digest[ii+3] = - (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); - } - memcpy(hash, mdContext->digest, 16); + MD5_u32plus saved_lo; + unsigned long used, available; + + saved_lo = ctx->lo; + if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) + ctx->hi++; + ctx->hi += size >> 29; + + used = saved_lo & 0x3f; + + if (used) { + available = 64 - used; + + if (size < available) { + memcpy(&ctx->buffer[used], data, size); + return; + } + + memcpy(&ctx->buffer[used], data, available); + data = (const unsigned char *)data + available; + size -= available; + body(ctx, ctx->buffer, 64); + } + + if (size >= 64) { + data = body(ctx, data, size & ~(unsigned long)0x3f); + size &= 0x3f; + } + + memcpy(ctx->buffer, data, size); } -/* Basic MD5 step. Transforms buf based on in. - */ -static void Transform (buf, in) -UINT4 *buf; -UINT4 *in; +void MD5_Final(unsigned char *result, MD5_CTX *ctx) { - UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; - - /* Round 1 */ -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 - FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */ - FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */ - FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */ - FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */ - FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */ - FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */ - FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */ - FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */ - FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */ - FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */ - FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */ - FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */ - FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */ - FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */ - FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */ - FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */ - - /* Round 2 */ -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 - GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */ - GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */ - GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */ - GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */ - GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */ - GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */ - GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */ - GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */ - GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */ - GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */ - GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */ - GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */ - GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */ - GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */ - GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */ - GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */ - - /* Round 3 */ -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 - HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */ - HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */ - HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */ - HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */ - HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */ - HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */ - HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */ - HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */ - HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */ - HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */ - HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */ - HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */ - HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */ - HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */ - HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */ - HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */ - - /* Round 4 */ -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */ - II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */ - II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */ - II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */ - II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */ - II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */ - II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */ - II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */ - II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */ - II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */ - II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */ - II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */ - II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */ - II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */ - II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */ - II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */ - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; + unsigned long used, available; + + used = ctx->lo & 0x3f; + + ctx->buffer[used++] = 0x80; + + available = 64 - used; + + if (available < 8) { + memset(&ctx->buffer[used], 0, available); + body(ctx, ctx->buffer, 64); + used = 0; + available = 64; + } + + memset(&ctx->buffer[used], 0, available - 8); + + ctx->lo <<= 3; + ctx->buffer[56] = ctx->lo; + ctx->buffer[57] = ctx->lo >> 8; + ctx->buffer[58] = ctx->lo >> 16; + ctx->buffer[59] = ctx->lo >> 24; + ctx->buffer[60] = ctx->hi; + ctx->buffer[61] = ctx->hi >> 8; + ctx->buffer[62] = ctx->hi >> 16; + ctx->buffer[63] = ctx->hi >> 24; + + body(ctx, ctx->buffer, 64); + + result[0] = ctx->a; + result[1] = ctx->a >> 8; + result[2] = ctx->a >> 16; + result[3] = ctx->a >> 24; + result[4] = ctx->b; + result[5] = ctx->b >> 8; + result[6] = ctx->b >> 16; + result[7] = ctx->b >> 24; + result[8] = ctx->c; + result[9] = ctx->c >> 8; + result[10] = ctx->c >> 16; + result[11] = ctx->c >> 24; + result[12] = ctx->d; + result[13] = ctx->d >> 8; + result[14] = ctx->d >> 16; + result[15] = ctx->d >> 24; + + memset(ctx, 0, sizeof(*ctx)); } -/* - *********************************************************************** - ** End of md5.c ** - ******************************** (cut) ******************************** - */ +#endif diff --git a/tools/firmware-utils/src/md5.h b/tools/firmware-utils/src/md5.h index f7a0c964183..2da44bf355a 100644 --- a/tools/firmware-utils/src/md5.h +++ b/tools/firmware-utils/src/md5.h @@ -1,65 +1,45 @@ /* - *********************************************************************** - ** md5.h -- header file for implementation of MD5 ** - ** RSA Data Security, Inc. MD5 Message-Digest Algorithm ** - ** Created: 2/17/90 RLR ** - ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** - ** Revised (for MD5): RLR 4/27/91 ** - ** -- G modified to have y&~z instead of y&z ** - ** -- FF, GG, HH modified to add in last register done ** - ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** - ** -- distinct additive constant for each step ** - ** -- round 4 added, working mod 7 ** - *********************************************************************** + * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. + * MD5 Message-Digest Algorithm (RFC 1321). + * + * Homepage: + * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 + * + * Author: + * Alexander Peslyak, better known as Solar Designer <solar at openwall.com> + * + * This software was written by Alexander Peslyak in 2001. No copyright is + * claimed, and the software is hereby placed in the public domain. + * In case this attempt to disclaim copyright and place the software in the + * public domain is deemed null and void, then the software is + * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See md5.c for more information. */ -/* - *********************************************************************** - ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** - ** ** - ** License to copy and use this software is granted provided that ** - ** it is identified as the "RSA Data Security, Inc. MD5 Message- ** - ** Digest Algorithm" in all material mentioning or referencing this ** - ** software or this function. ** - ** ** - ** License is also granted to make and use derivative works ** - ** provided that such works are identified as "derived from the RSA ** - ** Data Security, Inc. MD5 Message-Digest Algorithm" in all ** - ** material mentioning or referencing the derived work. ** - ** ** - ** RSA Data Security, Inc. makes no representations concerning ** - ** either the merchantability of this software or the suitability ** - ** of this software for any particular purpose. It is provided "as ** - ** is" without express or implied warranty of any kind. ** - ** ** - ** These notices must be retained in any copies of any part of this ** - ** documentation and/or software. ** - *********************************************************************** - */ +#ifdef HAVE_OPENSSL +#include <openssl/md5.h> +#elif !defined(_MD5_H) +#define _MD5_H -#ifndef __MD5_INCLUDE__ +/* Any 32-bit or wider unsigned integer data type will do */ +typedef unsigned int MD5_u32plus; -/* typedef a 32-bit type */ -#ifdef _LP64 -typedef unsigned int UINT4; -typedef int INT4; -#else -typedef unsigned long UINT4; -typedef long INT4; -#endif -#define _UINT4_T - -/* Data structure for MD5 (Message-Digest) computation */ typedef struct { - UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ - UINT4 buf[4]; /* scratch buffer */ - unsigned char in[64]; /* input buffer */ - unsigned char digest[16]; /* actual digest after MD5Final call */ + MD5_u32plus lo, hi; + MD5_u32plus a, b, c, d; + unsigned char buffer[64]; + MD5_u32plus block[16]; } MD5_CTX; -void MD5_Init (); -void MD5_Update (); -void MD5_Final (); +extern void MD5_Init(MD5_CTX *ctx); +extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); +extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); -#define __MD5_INCLUDE__ -#endif /* __MD5_INCLUDE__ */ +#endif diff --git a/tools/firmware-utils/src/mkbrnimg.c b/tools/firmware-utils/src/mkbrnimg.c index fff92c4ef66..b7a73ff59d2 100644 --- a/tools/firmware-utils/src/mkbrnimg.c +++ b/tools/firmware-utils/src/mkbrnimg.c @@ -32,9 +32,14 @@ static uint32_t crc32[1<<BPB]; +static char *output_file = "default-brnImage"; +static uint32_t magic = 0x12345678; +static char *signature = "BRNDTW502"; +static uint32_t crc32_poly = 0x2083b8ed; + static void init_crc32() { - const uint32_t poly = ntohl(0x2083b8ed); + const uint32_t poly = ntohl(crc32_poly); int n; for (n = 0; n < 1<<BPB; n++) { @@ -61,21 +66,17 @@ static void usage(const char *) __attribute__ (( __noreturn__ )); static void usage(const char *mess) { fprintf(stderr, "Error: %s\n", mess); - fprintf(stderr, "Usage: mkbrnimg [-o output_file] [-m magic] [-s signature] kernel_file [additional files]\n"); + fprintf(stderr, "Usage: mkbrnimg [-o output_file] [-m magic] [-s signature] [-p crc32 poly] kernel_file [additional files]\n"); fprintf(stderr, "\n"); exit(1); } -static char *output_file = "default-brnImage"; -static uint32_t magic = 0x12345678; -static char *signature = "BRNDTW502"; - static void parseopts(int *argc, char ***argv) { char *endptr; int res; - while ((res = getopt(*argc, *argv, "o:m:s:")) != -1) { + while ((res = getopt(*argc, *argv, "o:m:s:p:")) != -1) { switch (res) { default: usage("Unknown option"); @@ -91,6 +92,11 @@ static void parseopts(int *argc, char ***argv) case 's': signature = optarg; break; + case 'p': + crc32_poly = strtoul(optarg, &endptr, 0); + if (endptr == optarg || *endptr != 0) + usage("'crc32 poly' must be a decimal or hexadecimal 32-bit value"); + break; } } *argc -= optind; @@ -136,7 +142,7 @@ static void appendfile(int outfd, char *path, int kernel) { // write padding padded_len = ((len + sizeof(footer) + sizeof(padding) - 1) & ~(sizeof(padding) - 1)) - sizeof(footer); - fprintf(stderr, "len=%08x padded_len=%08x\n", len, padded_len); + fprintf(stderr, "len=%08zx padded_len=%08zx\n", len, padded_len); write(outfd, padding, padded_len - len); // write footer diff --git a/tools/firmware-utils/src/mkbuffaloimg.c b/tools/firmware-utils/src/mkbuffaloimg.c new file mode 100644 index 00000000000..364dda005b0 --- /dev/null +++ b/tools/firmware-utils/src/mkbuffaloimg.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2016 FUKAUMI Naoki <naobsd@gmail.com> + * + * Based on mkdniimg.c + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> + +#define DNI_HDR_LEN 128 + +/* + * Globals + */ +static char *ifname; +static char *progname; +static char *ofname; +static char *version = "0.00_0.00"; +static char *region = "JP"; +static char *rootfs_size; +static char *kernel_size; + +static char *board_id; +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board> create image for the board specified with <board>\n" +" -i <file> read input from the file <file>\n" +" -o <file> write output to the file <file>\n" +" -v <version> set image version to <version>\n" +" -r <region> set image region to <region>\n" +" -R <rootfs_size> set RootfsSize to <rootfs_size>\n" +" -K <kernel_size> set KernelSize to <kernel_size>\n" +" -h show this screen\n" + ); + + exit(status); +} + +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + int buflen; + int err; + struct stat st; + char *buf; + int i; + uint8_t csum; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "B:i:o:v:r:R:K:h"); + if (c == -1) + break; + + switch (c) { + case 'B': + board_id = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'v': + version = optarg; + break; + case 'r': + region = optarg; + break; + case 'R': + rootfs_size = optarg; + break; + case 'K': + kernel_size = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (board_id == NULL) { + ERR("no board specified"); + goto err; + } + + if (rootfs_size == NULL) { + ERR("no rootfs_size specified"); + goto err; + } + + if (kernel_size == NULL) { + ERR("no kernel_size specified"); + goto err; + } + + if (ifname == NULL) { + ERR("no input file specified"); + goto err; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto err; + } + + err = stat(ifname, &st); + if (err){ + ERRS("stat failed on %s", ifname); + goto err; + } + + buflen = st.st_size + DNI_HDR_LEN + 1; + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto err; + } + + memset(buf, 0, DNI_HDR_LEN); + snprintf(buf, DNI_HDR_LEN, "device:%s\nversion:%s\nregion:%s\n" + "RootfsSize:%s\nKernelSize:%s\nInfoHeadSize:128\n", + board_id, version, region, rootfs_size, kernel_size); + buf[DNI_HDR_LEN - 2] = 0x12; + buf[DNI_HDR_LEN - 1] = 0x32; + + infile = fopen(ifname, "r"); + if (infile == NULL) { + ERRS("could not open \"%s\" for reading", ifname); + goto err_free; + } + + errno = 0; + fread(buf + DNI_HDR_LEN, st.st_size, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto err_close_in; + } + + csum = 0; + for (i = 0; i < (st.st_size + DNI_HDR_LEN); i++) + csum += buf[i]; + + csum = 0xff - csum; + buf[st.st_size + DNI_HDR_LEN] = csum; + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto err_close_in; + } + + errno = 0; + fwrite(buf, buflen, 1, outfile); + if (errno) { + ERRS("unable to write to file %s", ofname); + goto err_close_out; + } + + res = EXIT_SUCCESS; + + fflush(outfile); + + err_close_out: + fclose(outfile); + if (res != EXIT_SUCCESS) { + unlink(ofname); + } + + err_close_in: + fclose(infile); + + err_free: + free(buf); + + err: + return res; +} diff --git a/tools/firmware-utils/src/mkcameofw.c b/tools/firmware-utils/src/mkcameofw.c new file mode 100644 index 00000000000..e0da4baf330 --- /dev/null +++ b/tools/firmware-utils/src/mkcameofw.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#define MAX_MODEL_LEN 20 +#define MAX_SIGNATURE_LEN 30 +#define MAX_REGION_LEN 4 +#define MAX_VERSION_LEN 12 + +#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ + uint32_t write_size; +}; + +struct img_header { + uint32_t checksum; + uint32_t image_size; + uint32_t kernel_size; + char model[MAX_MODEL_LEN]; + char signature[MAX_SIGNATURE_LEN]; + char region[MAX_REGION_LEN]; + char version[MAX_VERSION_LEN]; + unsigned char header_len; + unsigned char is_tgz; + unsigned char pad[4]; +} __attribute__ ((packed)); + +/* + * Globals + */ +static char *ofname; +static char *progname; + +static char *model; +static char *signature; +static char *region = "DEF"; +static char *version; +static struct file_info kernel_info; +static struct file_info rootfs_info; +static uint32_t kernel_size; +static uint32_t image_size; +static int combined; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt " (%s)\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -k <file> read kernel image from the file <file>\n" +" -c use the kernel image as a combined image\n" +" -M <model> set model to <model>\n" +" -o <file> write output to the file <file>\n" +" -r <file> read rootfs image from the file <file>\n" +" -S <signature> set image signature to <signature>\n" +" -R <region> set image region to <region>\n" +" -V <version> set image version to <version>\n" +" -I <size> set image size to <size>\n" +" -K <size> set kernel size to <size>\n" +" -h show this screen\n" + ); + + exit(status); +} + +int +str2u32(char *arg, uint32_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err)) { + return -1; + } + + *val = t; + return 0; +} + +static int get_file_stat(struct file_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) + return 0; + + res = stat(fdata->file_name, &st); + if (res){ + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + fdata->write_size = fdata->file_size; + return 0; +} + +static int read_to_buf(struct file_info *fdata, char *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + +out_close: + fclose(f); +out: + return ret; +} + +static int check_options(void) +{ + int ret; + +#define CHKSTR(_name, _msg) \ + do { \ + if (_name == NULL) { \ + ERR("no %s specified", _msg); \ + return -1; \ + } \ + } while (0) + +#define CHKSTRLEN(_name, _msg) \ + do { \ + int field_len; \ + CHKSTR(_name, _msg); \ + field_len = FIELD_SIZEOF(struct img_header, _name) - 1; \ + if (strlen(_name) > field_len) { \ + ERR("%s is too long, max length is %d", \ + _msg, field_len); \ + return -1; \ + } \ + } while (0) + + CHKSTRLEN(model, "model"); + CHKSTRLEN(signature, "signature"); + CHKSTRLEN(region, "region"); + CHKSTRLEN(version, "version"); + CHKSTR(ofname, "output file"); + CHKSTR(kernel_info.file_name, "kernel image"); + + ret = get_file_stat(&kernel_info); + if (ret) + return ret; + + if (combined) { + if (!kernel_size) { + ERR("kernel size must be specified for combined images"); + return -1; \ + } + + if (!image_size) + image_size = kernel_info.file_size; + + if (kernel_info.file_size > image_size) { + ERR("kernel image is too big"); + return -1; + } + + kernel_info.write_size = image_size; + } else { + CHKSTR(rootfs_info.file_name, "rootfs image"); + + ret = get_file_stat(&rootfs_info); + if (ret) + return ret; + + if (kernel_size) { + /* override kernel size */ + kernel_info.write_size = kernel_size; + } + + if (image_size) { + if (image_size < kernel_info.write_size) + kernel_info.write_size = image_size; + + /* override rootfs size */ + rootfs_info.write_size = image_size - kernel_info.write_size; + } + + if (kernel_info.file_size > kernel_info.write_size) { + ERR("kernel image is too big"); + return -1; + } + + if (rootfs_info.file_size > rootfs_info.write_size) { + ERR("rootfs image is too big"); + return -1; + } + } + + return 0; +} + +static int write_fw(char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + +out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return ret; +} + +static uint32_t get_csum(unsigned char *p, uint32_t len) +{ + uint32_t csum = 0; + + while (len--) + csum += *p++; + + return csum; +} + +static int build_fw(void) +{ + int buflen; + char *buf; + char *p; + uint32_t csum; + struct img_header *hdr; + int ret = EXIT_FAILURE; + + buflen = sizeof(struct img_header) + + kernel_info.write_size + rootfs_info.write_size; + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + memset(buf, 0, buflen); + + p = buf + sizeof(struct img_header); + + /* read kernel data */ + ret = read_to_buf(&kernel_info, p); + if (ret) + goto out_free_buf; + + if (!combined) { + p += kernel_info.write_size; + + /* read rootfs data */ + ret = read_to_buf(&rootfs_info, p); + if (ret) + goto out_free_buf; + } + + csum = get_csum((unsigned char *)(buf + sizeof(struct img_header)), + buflen - sizeof(struct img_header)); + + /* fill firmware header */ + hdr = (struct img_header *) buf; + + hdr->checksum = htonl(csum); + hdr->image_size = htonl(buflen - sizeof(struct img_header)); + if (!combined) + hdr->kernel_size = htonl(kernel_info.write_size); + else + hdr->kernel_size = htonl(kernel_size); + hdr->header_len = sizeof(struct img_header); + strncpy(hdr->model, model, sizeof(hdr->model)); + strncpy(hdr->signature, signature, sizeof(hdr->signature)); + strncpy(hdr->version, version, sizeof(hdr->version)); + strncpy(hdr->region, region, sizeof(hdr->region)); + + ret = write_fw(buf, buflen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + +out_free_buf: + free(buf); +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "M:S:V:R:k:K:I:r:o:hc"); + if (c == -1) + break; + + switch (c) { + case 'M': + model = optarg; + break; + case 'S': + signature = optarg; + break; + case 'V': + version = optarg; + break; + case 'R': + region = optarg; + break; + case 'k': + kernel_info.file_name = optarg; + break; + case 'K': + if (str2u32(optarg, &kernel_size)) { + ERR("%s is invalid '%s'", + "kernel size", optarg); + goto out; + } + break; + case 'I': + if (str2u32(optarg, &image_size)) { + ERR("%s is invalid '%s'", + "image size", optarg); + goto out; + } + break; + case 'r': + rootfs_info.file_name = optarg; + break; + case 'c': + combined = 1; + break; + case 'o': + ofname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + ret = build_fw(); + +out: + return ret; +} + diff --git a/tools/firmware-utils/src/mkcasfw.c b/tools/firmware-utils/src/mkcasfw.c index 626e77d4c49..5655bf1894c 100644 --- a/tools/firmware-utils/src/mkcasfw.c +++ b/tools/firmware-utils/src/mkcasfw.c @@ -258,7 +258,7 @@ static struct board_info boards[] = { #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) @@ -708,7 +708,7 @@ image_writeout(FILE *outfile, struct image_desc *desc) /* write padding data if neccesary */ padlen = desc->out_size - desc->file_size; - DBG(1,"padding desc, length=%d", padlen); + DBG(1,"padding desc, length=%zu", padlen); res = write_out_padding(outfile, padlen, desc->padc, &css); desc->csum = csum_get(&css); @@ -985,11 +985,11 @@ main(int argc, char *argv[]) res = ERR_FATAL; if (keep_invalid_images == 0) { - WARN("generation of invalid images disabled", ofname); + WARN("generation of invalid images \"%s\" disabled", ofname); goto out; } - WARN("generating invalid image", ofname); + WARN("generating invalid image: \"%s\"", ofname); } outfile = fopen(ofname, "w"); diff --git a/tools/firmware-utils/src/mkchkimg.c b/tools/firmware-utils/src/mkchkimg.c index e152f7d4685..0fe01f07079 100644 --- a/tools/firmware-utils/src/mkchkimg.c +++ b/tools/firmware-utils/src/mkchkimg.c @@ -31,6 +31,20 @@ #define MAX_BOARD_ID_LEN (64) +/* + * Note on the reserved field of the chk_header: + * OFW naming scheme is typically: DEVICENAME-VA.B.C.D_E.F.G.chk, with A-G + * between 0 and 255. For instance: EX3700_EX3800-V1.0.0.58_1.0.38.chk + * The reserved field works like this: + * reserved[0]: region code. 1 for WW (WorldWide) and 2 for NA (North America) + * reserved[1]: A + * reserved[2]: B + * reserved[3]: C + * reserved[4]: D + * reserved[5]: E + * reserved[6]: F + * reserved[7]: G + */ struct chk_header { uint32_t magic; uint32_t header_len; @@ -248,10 +262,10 @@ main (int argc, char * argv[]) hdr->reserved[1] = 1; /* Major */ hdr->reserved[2] = 1; /* Minor */ hdr->reserved[3] = 99; /* Build */ - hdr->reserved[4] = 0; /* Unknown t1 ? was 1 */ - hdr->reserved[5] = 0; /* Unknonw t2 ? was 0 */ - hdr->reserved[6] = 0; /* Unknonw t3 ? was 1 */ - hdr->reserved[7] = 0; /* Unused ? */ + hdr->reserved[4] = 0; + hdr->reserved[5] = 0; + hdr->reserved[6] = 0; + hdr->reserved[7] = 0; message (" Board Id: %s", board_id); message (" Region: %s", region == 1 ? "World Wide (WW)" : (region == 2 ? "North America (NA)" : "Unknown")); diff --git a/tools/firmware-utils/src/mkcsysimg.c b/tools/firmware-utils/src/mkcsysimg.c index 4f2352a60e7..77fbbaa57fa 100644 --- a/tools/firmware-utils/src/mkcsysimg.c +++ b/tools/firmware-utils/src/mkcsysimg.c @@ -160,6 +160,7 @@ static struct board_info boards[] = { BOARD_ADM("BR-6114WG", "Edimax BR-6114WG", 2, SIG_BR6114WG), BOARD_ADM("BR-6524K", "Edimax BR-6524K", 2, SIG_BR6524K), BOARD_ADM("BR-6524KP", "Edimax BR-6524KP", 2, SIG_BR6524KP), + BOARD_ADM("BR-6524N", "Edimax BR-6524N", 2, SIG_BR6524N), BOARD_ADM("BR-6524WG", "Edimax BR-6524WG", 4, SIG_BR6524WG), BOARD_ADM("BR-6524WP", "Edimax BR-6524WP", 4, SIG_BR6524WP), BOARD_ADM("BR-6541K", "Edimax BR-6541K", 2, SIG_BR6541K), @@ -198,7 +199,7 @@ static struct board_info boards[] = { #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", progname, ## __VA_ARGS__ \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", progname, ## __VA_ARGS__ \ , strerror(save)); \ } while (0) @@ -646,7 +647,7 @@ block_writeout_data(FILE *outfile, struct csys_block *block) /* write padding data if neccesary */ padlen = block->size_avail - block->file_size; - DBG(1,"padding block, length=%d", padlen); + DBG(1,"padding block, length=%zu", padlen); res = write_out_padding(outfile, padlen, block->padc, block->css); return res; @@ -1121,11 +1122,11 @@ main(int argc, char *argv[]) res = ERR_FATAL; if (keep_invalid_images == 0) { - WARN("generation of invalid images disabled", ofname); + WARN("generation of invalid images \"%s\" disabled", ofname); goto out; } - WARN("generating invalid image", ofname); + WARN("generating invalid image: \"%s\"", ofname); } outfile = fopen(ofname, "w"); diff --git a/tools/firmware-utils/src/mkdapimg.c b/tools/firmware-utils/src/mkdapimg.c new file mode 100644 index 00000000000..ed662d8ecc0 --- /dev/null +++ b/tools/firmware-utils/src/mkdapimg.c @@ -0,0 +1,226 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include <stdarg.h> +#include <getopt.h> +#include <string.h> +#include <errno.h> + +#include <netinet/in.h> // htonl + +// Usage: mkdapimg [-p] [-m <model>] -s <sig> -i <input> -o <output> +// +// e.g.: mkdapimg -s RT3052-AP-DAP1350-3 -i sysupgarde.bin -o factory.bin +// +// If the model string <model> is not given, we will assume that +// the leading characters upto the first "-" is the model. +// +// The "-p" (patch) option is used to patch the exisiting image with the +// specified model and signature. +// The "-x" (fix) option will recalculate the payload size and checksum +// during the patch mode operation. + +// The img_hdr_struct was taken from the D-Link SDK: +// DAP-1350_A1_FW1.11NA_GPL/GPL_Source_Code/Uboot/DAP-1350/httpd/header.h + +#define MAX_MODEL_NAME_LEN 20 +#define MAX_SIG_LEN 30 +#define MAX_REGION_LEN 4 +#define MAX_VERSION_LEN 12 + +struct img_hdr_struct { + uint32_t checksum; + char model[MAX_MODEL_NAME_LEN]; + char sig[MAX_SIG_LEN]; + uint8_t partition; + uint8_t hdr_len; + uint8_t rsv1; + uint8_t rsv2; + uint32_t flash_byte_cnt; +} imghdr ; + +char *progname; + +void +perrexit(int code, char *msg) +{ + fprintf(stderr, "%s: %s: %s\n", progname, msg, strerror(errno)); + exit(code); +} + +void +usage() +{ + fprintf(stderr, "usage: %s [-p] [-m model] [-r region] [-v version] -s signature -i input -o output\n", progname); + exit(1); +} + +int +main(int ac, char *av[]) +{ + char model[MAX_MODEL_NAME_LEN+1]; + char signature[MAX_SIG_LEN+1]; + char region[MAX_REGION_LEN+1]; + char version[MAX_VERSION_LEN+1]; + int patchmode = 0; + int fixmode = 0; + int have_regionversion = 0; + + FILE *ifile, *ofile; + int c; + uint32_t cksum; + uint32_t bcnt; + + progname = basename(av[0]); + memset(model, 0, sizeof(model)); + memset(signature, 0, sizeof(signature)); + memset(region, 0, sizeof(region)); + memset(version, 0, sizeof(version)); + + while ( 1 ) { + int c; + + c = getopt(ac, av, "pxm:r:v:s:i:o:"); + if (c == -1) + break; + + switch (c) { + case 'p': + patchmode = 1; + break; + case 'x': + fixmode = 1; + break; + case 'm': + if (strlen(optarg) > MAX_MODEL_NAME_LEN) { + fprintf(stderr, "%s: model name exceeds %d chars\n", + progname, MAX_MODEL_NAME_LEN); + exit(1); + } + strcpy(model, optarg); + break; + case 'r': + if (strlen(optarg) > MAX_REGION_LEN) { + fprintf(stderr, "%s: region exceeds %d chars\n", + progname, MAX_REGION_LEN); + exit(1); + } + have_regionversion = 1; + strcpy(region, optarg); + break; + case 'v': + if (strlen(optarg) > MAX_VERSION_LEN) { + fprintf(stderr, "%s: version exceeds %d chars\n", + progname, MAX_VERSION_LEN); + exit(1); + } + have_regionversion = 1; + strcpy(version, optarg); + break; + case 's': + if (strlen(optarg) > MAX_SIG_LEN) { + fprintf(stderr, "%s: signature exceeds %d chars\n", + progname, MAX_SIG_LEN); + exit(1); + } + strcpy(signature, optarg); + break; + case 'i': + if ((ifile = fopen(optarg, "r")) == NULL) + perrexit(1, optarg); + break; + case 'o': + if ((ofile = fopen(optarg, "w")) == NULL) + perrexit(1, optarg); + break; + default: + usage(); + } + } + + if (signature[0] == 0 || ifile == NULL || ofile == NULL) { + usage(); + } + + if (model[0] == 0) { + char *p = strchr(signature, '-'); + if (p == NULL) { + fprintf(stderr, "%s: model name unknown\n", progname); + exit(1); + } + if (p - signature > MAX_MODEL_NAME_LEN) { + *p = 0; + fprintf(stderr, "%s: auto model name failed, string %s too long\n", progname, signature); + exit(1); + } + strncpy(model, signature, p - signature); + } + + if (patchmode) { + if (fread(&imghdr, sizeof(imghdr), 1, ifile) < 0) + perrexit(2, "fread on input"); + } + + for (bcnt = 0, cksum = 0 ; (c = fgetc(ifile)) != EOF ; bcnt++) + cksum += c & 0xff; + + if (fseek(ifile, patchmode ? sizeof(imghdr) : 0, SEEK_SET) < 0) + perrexit(2, "fseek on input"); + + if (patchmode == 0) { + // Fill in the header + memset(&imghdr, 0, sizeof(imghdr)); + imghdr.checksum = htonl(cksum); + imghdr.partition = 0 ; // don't care? + imghdr.hdr_len = sizeof(imghdr); + if (have_regionversion) { + imghdr.hdr_len += MAX_REGION_LEN; + imghdr.hdr_len += MAX_VERSION_LEN; + } + imghdr.flash_byte_cnt = htonl(bcnt); + } else { + if (ntohl(imghdr.checksum) != cksum) { + fprintf(stderr, "%s: patch mode, checksum mismatch\n", + progname); + if (fixmode) { + fprintf(stderr, "%s: fixing\n", progname); + imghdr.checksum = htonl(cksum); + } else + exit(3); + } else if (ntohl(imghdr.flash_byte_cnt) != bcnt) { + fprintf(stderr, "%s: patch mode, size mismatch\n", + progname); + if (fixmode) { + fprintf(stderr, "%s: fixing\n", progname); + imghdr.flash_byte_cnt = htonl(bcnt); + } else + exit(3); + } + } + + strncpy(imghdr.model, model, MAX_MODEL_NAME_LEN); + strncpy(imghdr.sig, signature, MAX_SIG_LEN); + + if (fwrite(&imghdr, sizeof(imghdr), 1, ofile) < 0) + perrexit(2, "fwrite header on output"); + if (have_regionversion) { + if (fwrite(®ion, MAX_REGION_LEN, 1, ofile) < 0) + perrexit(2, "fwrite header on output"); + if (fwrite(&version, MAX_VERSION_LEN, 1, ofile) < 0) + perrexit(2, "fwrite header on output"); + } + + while ((c = fgetc(ifile)) != EOF) { + if (fputc(c, ofile) == EOF) + perrexit(2, "fputc on output"); + } + + if (ferror(ifile)) + perrexit(2, "fgetc on input"); + + + fclose(ofile); + fclose(ifile); +} diff --git a/tools/firmware-utils/src/mkdapimg2.c b/tools/firmware-utils/src/mkdapimg2.c new file mode 100644 index 00000000000..aef003c280c --- /dev/null +++ b/tools/firmware-utils/src/mkdapimg2.c @@ -0,0 +1,204 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2 as published by the Free Software Foundation. + * + * (C) Nicolò Veronese <nicveronese@gmail.com> + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> +#include <libgen.h> +#include <stdarg.h> +#include <getopt.h> +#include <string.h> +#include <errno.h> + +#include <netinet/in.h> // htonl + +// Usage: mkdapimg2 -s signature [-v version] [-r region] +// [-k uImage block size] -i <input> -o <output> +// +// NOTE: The kernel block size is used to know the offset of the rootfs +// in the image file. +// +// The system writes in the uImage partition until the end of uImage +// is reached, after that, the system jumps to the offset specified with the -k +// parameter and begin writing at the beginning of the rootfs MTD partition. +// +// If the -k parameter is the size of the original uImage partition, the system +// continue writing in the rootfs partition starting from the last block +// that has been wrote. (This is useful if the new kernel size is +// different from the original one) +// +// Example: +// ------------------------------------------ +// Creating 7 MTD partitions on "ath-nor0": +// 0x000000000000-0x000000010000 : "u-boot" +// 0x000000010000-0x000000020000 : "ART" +// 0x000000020000-0x000000030000 : "MP" +// 0x000000030000-0x000000040000 : "config" +// 0x000000040000-0x000000120000 : "uImage" +// 0x000000120000-0x000000800000 : "rootfs" +// 0x000000040000-0x000000800000 : "firmware" +// ------------------------------------------ +// +// 0x000000120000-0x000000040000 = 0xE0000 -> 917504 +// +// e.g.: mkdapimg2 -s HONEYBEE-FIRMWARE-DAP-1330 -v 1.00.21 -r Default +// -k 917504 -i sysupgarde.bin -o factory.bin +// +// +// The img_hdr_struct was taken from the D-Link SDK: +// DAP-1330_OSS-firmware_1.00b21/DAP-1330_OSS-firmware_1.00b21/uboot/uboot.patch + +#define MAX_SIGN_LEN 32 +#define MAX_FW_VER_LEN 16 +#define MAX_REG_LEN 8 + +struct img_hdr_struct { + uint32_t hdr_len; + uint32_t checksum; + uint32_t total_size; + uint32_t kernel_size; + char signature[MAX_SIGN_LEN]; + char fw_ver[MAX_FW_VER_LEN]; + char fw_reg[MAX_REG_LEN]; +} imghdr ; + +char *progname; + +void +perrexit(int code, char *msg) +{ + fprintf(stderr, "%s: %s: %s\n", progname, msg, strerror(errno)); + exit(code); +} + +void +usage() +{ + fprintf(stderr, "usage: %s -s signature [-v version] [-r region] [-k uImage part size] -i <input> -o <output>\n", progname); + exit(1); +} + +int +main(int ac, char *av[]) +{ + char signature[MAX_SIGN_LEN]; + char version[MAX_FW_VER_LEN]; + char region[MAX_REG_LEN]; + int kernel = 0; + + FILE *ifile, *ofile; + int c; + + uint32_t cksum; + uint32_t bcnt; + + progname = basename(av[0]); + + memset(signature, 0, sizeof(signature)); + memset(version, 0, sizeof(version)); + memset(region, 0, sizeof(region)); + + while ( 1 ) { + char *ptr; + int c; + + c = getopt(ac, av, "s:v:r:k:i:o:"); + if (c == -1) + break; + + switch (c) { + case 's': + if (strlen(optarg) > MAX_SIGN_LEN + 1) { + fprintf(stderr, "%s: signature exceeds %d chars\n", + progname, MAX_SIGN_LEN); + exit(1); + } + strcpy(signature, optarg); + break; + case 'v': + if (strlen(optarg) > MAX_FW_VER_LEN + 1) { + fprintf(stderr, "%s: version exceeds %d chars\n", + progname, MAX_FW_VER_LEN); + exit(1); + } + strcpy(version, optarg); + break; + case 'r': + if (strlen(optarg) > MAX_REG_LEN + 1) { + fprintf(stderr, "%s: region exceeds %d chars\n", + progname, MAX_REG_LEN); + exit(1); + } + strcpy(region, optarg); + break; + case 'k': + kernel = strtoul(optarg, &ptr, 0); + if(ptr[0] == 'k'){ + kernel *= 1000; + } + break; + case 'i': + if ((ifile = fopen(optarg, "r")) == NULL) + perrexit(1, optarg); + break; + case 'o': + if ((ofile = fopen(optarg, "w")) == NULL) + perrexit(1, optarg); + break; + default: + usage(); + } + } + + if (signature[0] == 0 || ifile == NULL || ofile == NULL) { + usage(); + exit(1); + } + + for (bcnt = 0, cksum = 0 ; (c = fgetc(ifile)) != EOF ; bcnt++) + cksum += c & 0xff; + + if (fseek(ifile, 0, SEEK_SET) < 0) + perrexit(2, "fseek on input"); + + // Fill in the header + memset(&imghdr, 0, sizeof(imghdr)); + imghdr.hdr_len = sizeof(imghdr); + imghdr.checksum = htonl(cksum); + imghdr.total_size = htonl(bcnt); + imghdr.kernel_size = htonl(kernel); + + strncpy(imghdr.signature, signature, MAX_SIGN_LEN); + strncpy(imghdr.fw_ver, version, MAX_FW_VER_LEN); + strncpy(imghdr.fw_reg, region, MAX_REG_LEN); + + if (fwrite(&imghdr, sizeof(imghdr), 1, ofile) < 0) + perrexit(2, "fwrite header on output"); + + while ((c = fgetc(ifile)) != EOF) { + if (fputc(c, ofile) == EOF) + perrexit(2, "fputc on output"); + } + + if (ferror(ifile)) + perrexit(2, "fgetc on input"); + + fclose(ofile); + fclose(ifile); + + fprintf(stderr, "imgHdr.hdr_len = %lu\n", sizeof(imghdr)); + fprintf(stderr, "imgHdr.checksum = 0x%08x\n", cksum); + fprintf(stderr, "imgHdr.total_size = 0x%08x\n", bcnt); + fprintf(stderr, "imgHdr.kernel_size = 0x%08x\n", kernel); + fprintf(stderr, "imgHdr.header = %s\n", signature); + fprintf(stderr, "imgHdr.fw_ver = %s\n", version); + fprintf(stderr, "imgHdr.fw_reg = %s\n", region); + + return 0; +} diff --git a/tools/firmware-utils/src/mkdcs932.c b/tools/firmware-utils/src/mkdcs932.c new file mode 100644 index 00000000000..28c67aa3b30 --- /dev/null +++ b/tools/firmware-utils/src/mkdcs932.c @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License, + * version 2 as published by the Free Software Foundation. + * + * (C) John Crispin <blogic@openwrt.org> + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include <errno.h> + +int main(int argc, char **argv) +{ + uint32_t t = 0, sum = 0x55aa55aa; + int fd; + + if ((argc != 2) || ((fd = open(argv[1], O_RDWR)) == -1)) { + fprintf(stderr, "Usage: %s input_file\n", *argv); + return -EINVAL; + } + + lseek(fd, -4, SEEK_END); + write(fd, &t, 4); + lseek(fd, 0, SEEK_SET); + + while (read(fd, &t, 4) > 0) + sum -= t; + + lseek(fd, -4, SEEK_END); + write(fd, &sum, 4); + close(fd); + + return 0; +} diff --git a/tools/firmware-utils/src/mkdhpimg.c b/tools/firmware-utils/src/mkdhpimg.c new file mode 100644 index 00000000000..e61d0425043 --- /dev/null +++ b/tools/firmware-utils/src/mkdhpimg.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 FUKAUMI Naoki <naobsd@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <sys/stat.h> +#include <err.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "buffalo-lib.h" + +#define DHP_HEADER_SIZE 20 + +static char *progname; + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s <in> <out>\n", progname); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + struct stat in_st; + size_t size; + uint32_t crc; + int in, out; + uint8_t *buf; + + progname = argv[0]; + + if (argc != 3) + usage(); + + if ((in = open(argv[1], O_RDONLY)) == -1) + err(EXIT_FAILURE, "%s", argv[1]); + + if (fstat(in, &in_st) == -1) + err(EXIT_FAILURE, "%s", argv[1]); + + size = DHP_HEADER_SIZE + in_st.st_size; + + if ((buf = malloc(size)) == NULL) + err(EXIT_FAILURE, "malloc"); + + memset(buf, 0, DHP_HEADER_SIZE); + buf[0x0] = 0x62; + buf[0x1] = 0x67; + buf[0x2] = 0x6e; + buf[0xb] = 0xb1; + buf[0xc] = (size >> 24) & 0xff; + buf[0xd] = (size >> 16) & 0xff; + buf[0xe] = (size >> 8) & 0xff; + buf[0xf] = size & 0xff; + + read(in, &buf[DHP_HEADER_SIZE], in_st.st_size); + close(in); + + crc = buffalo_crc(buf, size); + buf[0x10] = (crc >> 24) & 0xff; + buf[0x11] = (crc >> 16) & 0xff; + buf[0x12] = (crc >> 8) & 0xff; + buf[0x13] = crc & 0xff; + + if ((out = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644)) == -1) + err(EXIT_FAILURE, "%s", argv[2]); + write(out, buf, size); + close(out); + + free(buf); + + return EXIT_SUCCESS; +} diff --git a/tools/firmware-utils/src/mkdlinkfw-lib.c b/tools/firmware-utils/src/mkdlinkfw-lib.c new file mode 100644 index 00000000000..fcab8562319 --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw-lib.c @@ -0,0 +1,172 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com> + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <stdbool.h> +#include <endian.h> +#include <errno.h> +#include <time.h> +#include <sys/stat.h> +#include <zlib.h> /*for crc32 */ + +#include "mkdlinkfw-lib.h" + +extern char *progname; + +static unsigned char jffs2_eof_mark[4] = { 0xde, 0xad, 0xc0, 0xde }; + +uint32_t jboot_timestamp(void) +{ + time_t rawtime; + time(&rawtime); + return (((uint32_t) rawtime) - TIMESTAMP_MAGIC) >> 2; +} + +uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size) +{ + uint32_t counter = start_val; + uint16_t *ptr = data; + + while (size > 1) { + counter += *ptr; + ++ptr; + while (counter >> 16) + counter = (uint16_t) counter + (counter >> 16); + size -= 2; + } + if (size > 0) { + counter += *(uint8_t *) ptr; + counter -= 0xFF; + } + while (counter >> 16) + counter = (uint16_t) counter + (counter >> 16); + return counter; +} + +int get_file_stat(struct file_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) + return 0; + + res = stat(fdata->file_name, &st); + if (res) { + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + return 0; +} + +int read_to_buf(const struct file_info *fdata, char *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + + out_close: + fclose(f); + out: + return ret; +} + +int pad_jffs2(char *buf, int currlen, int maxlen) +{ + int len; + uint32_t pad_mask; + + len = currlen; + pad_mask = (4 * 1024) | (64 * 1024); /* EOF at 4KB and at 64KB */ + while ((len < maxlen) && (pad_mask != 0)) { + uint32_t mask; + int i; + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if (pad_mask & mask) + break; + } + + len = ALIGN(len, mask); + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if ((len & (mask - 1)) == 0) + pad_mask &= ~mask; + } + + for (i = 0; i < sizeof(jffs2_eof_mark); i++) + buf[len + i] = jffs2_eof_mark[i]; + + len += sizeof(jffs2_eof_mark); + } + + return len; +} + +int write_fw(const char *ofname, const char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + + out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) + unlink(ofname); + out: + return ret; +} diff --git a/tools/firmware-utils/src/mkdlinkfw-lib.h b/tools/firmware-utils/src/mkdlinkfw-lib.h new file mode 100644 index 00000000000..d61124cb636 --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw-lib.h @@ -0,0 +1,83 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com> + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#ifndef mkdlinkfw_lib_h +#define mkdlinkfw_lib_h + +#define AUH_MAGIC "DLK" +#define AUH_SIZE 80 +#define AUH_LVPS 0x01 +#define AUH_HDR_ID 0x4842 +#define AUH_HDR_VER 0x02 +#define AUH_SEC_ID 0x04 +#define AUH_INFO_TYPE 0x04 + +#define STAG_SIZE 16 +#define STAG_ID 0x04 +#define STAG_MAGIC 0x2B24 +#define STAG_CMARK_FACTORY 0xFF + +#define SCH2_SIZE 40 +#define SCH2_MAGIC 0x2124 +#define SCH2_VER 0x02 + +#define FLAT 0 +#define JZ 1 +#define GZIP 2 +#define LZMA 3 + +#define RAM_ENTRY_ADDR 0x80000000 +#define RAM_LOAD_ADDR 0x80000000 +#define JBOOT_SIZE 0x10000 + +#define ALL_HEADERS_SIZE (AUH_SIZE + STAG_SIZE + SCH2_SIZE) +#define MAX_HEADER_COUNTER 10 +#define TIMESTAMP_MAGIC 0x35016f00L + +#define FACTORY 0 +#define SYSUPGRADE 1 + +#define ALIGN(x, a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__); \ +} while (0) + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +uint32_t jboot_timestamp(void); +uint16_t jboot_checksum(uint16_t start_val, uint16_t *data, int size); +int get_file_stat(struct file_info *fdata); +int read_to_buf(const struct file_info *fdata, char *buf); +int pad_jffs2(char *buf, int currlen, int maxlen); +int write_fw(const char *ofname, const char *data, int len); + +#endif /* mkdlinkfw_lib_h */ diff --git a/tools/firmware-utils/src/mkdlinkfw.c b/tools/firmware-utils/src/mkdlinkfw.c new file mode 100644 index 00000000000..87605004fe9 --- /dev/null +++ b/tools/firmware-utils/src/mkdlinkfw.c @@ -0,0 +1,665 @@ +/* + * mkdlinkfw + * + * Copyright (C) 2018 Paweł Dembicki <paweldembicki@gmail.com> + * + * This tool is based on mktplinkfw. + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <stdbool.h> +#include <endian.h> +#include <errno.h> +#include <sys/stat.h> +#include <zlib.h> /*for crc32 */ + +#include "mkdlinkfw-lib.h" + +/* ARM update header 2.0 + * used only in factory images to erase and flash selected area + */ +struct auh_header { + uint8_t rom_id[12]; /* 12-bit rom-id unique per router type */ + uint16_t derange; /* used for scramble header */ + uint16_t image_checksum; /* jboot_checksum of flashed data */ + + uint32_t space1; /* zeros */ + uint32_t space2; /* zeros */ + uint16_t space3; /* zerosu */ + uint8_t lpvs; /* must be 0x01 */ + uint8_t mbz; /* bust be 0 */ + uint32_t time_stamp; /* timestamp calculated in jboot way */ + + uint32_t erase_start; /* erase start address */ + uint32_t erase_length; /* erase length address */ + uint32_t data_offset; /* data start address */ + uint32_t data_length; /* data length address */ + + uint32_t space4; /* zeros */ + uint32_t space5; /* zeros */ + uint32_t space6; /* zeros */ + uint32_t space7; /* zeros */ + + uint16_t header_id; /* magic 0x4842 */ + uint16_t header_version; /* 0x02 for 2.0 */ + uint16_t space8; /* zeros */ + uint8_t section_id; /* section id */ + uint8_t image_info_type; /* (?) 0x04 in factory images */ + uint32_t image_info_offset; /* (?) zeros in factory images */ + uint16_t family_member; /* unique per router type */ + uint16_t header_checksum; /* negated jboot_checksum of header data */ +}; + +struct stag_header { /* used only of sch2 wrapped kernel data */ + uint8_t cmark; /* in factory 0xFF ,in sysuograde must be the same as id */ + uint8_t id; /* 0x04 */ + uint16_t magic; /* magic 0x2B24 */ + uint32_t time_stamp; /* timestamp calculated in jboot way */ + uint32_t image_length; /* lentgh of kernel + sch2 header */ + uint16_t image_checksum; /* negated jboot_checksum of sch2 + kernel */ + uint16_t tag_checksum; /* negated jboot_checksum of stag header data */ +}; + +struct sch2_header { /* used only in kernel partitions */ + uint16_t magic; /* magic 0x2124 */ + uint8_t cp_type; /* 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma */ + uint8_t version; /* 0x02 for sch2 */ + uint32_t ram_addr; /* ram entry address */ + uint32_t image_len; /* kernel image length */ + uint32_t image_crc32; /* kernel image crc */ + uint32_t start_addr; /* ram start address */ + uint32_t rootfs_addr; /* rootfs flash address */ + uint32_t rootfs_len; /* rootfls length */ + uint32_t rootfs_crc32; /* rootfs crc32 */ + uint32_t header_crc32; /* sch2 header crc32, durring calculation this area is replaced by zero */ + uint16_t header_length; /* sch2 header length: 0x28 */ + uint16_t cmd_line_length; /* cmd line length, known zeros */ +}; + +/* globals */ +static struct file_info inspect_info; +struct file_info kernel_info; +struct file_info rootfs_info; +struct file_info image_info; + +char *ofname; +char *progname; +uint32_t firmware_size; +uint16_t family_member; +char *rom_id[12] = { 0 }; + +char image_type; +int add_jffs2_eof; + +static void usage(int status) +{ + fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stderr, + "\n" + "Options:\n" + " -i <file> inspect given firmware file <file>\n" + " -f set family member id (hexval prefixed with 0x)\n" + " -F <file> read image and convert it to FACTORY\n" + " -k <file> read kernel image from the file <file>\n" + " -r <file> read rootfs image from the file <file>\n" + " -o <file> write output to the file <file>\n" + " -s <size> set firmware partition size\n" + " -m <version> set rom id to <version> (12-bit string val: \"DLK*********\")\n" + " -h show this screen\n"); + + exit(status); +} + +void print_auh_header(struct auh_header *printed_header) +{ + printf("\trom_id: %s\n" + "\tderange: 0x%04X\n" + "\timage_checksum: 0x%04X\n" + "\tspace1: 0x%08X\n" + "\tspace2: 0x%08X\n" + "\tspace3: 0x%04X\n" + "\tlpvs: 0x%02X\n" + "\tmbz: 0x%02X\n" + "\ttime_stamp: 0x%08X\n" + "\terase_start: 0x%08X\n" + "\terase_length: 0x%08X\n" + "\tdata_offset: 0x%08X\n" + "\tdata_length: 0x%08X\n" + "\tspace4: 0x%08X\n" + "\tspace5: 0x%08X\n" + "\tspace6: 0x%08X\n" + "\tspace7: 0x%08X\n" + "\theader_id: 0x%04X\n" + "\theader_version: 0x%02X\n" + "\tspace8: 0x%04X\n" + "\tsection_id: 0x%02X\n" + "\timage_info_type: 0x%02X\n" + "\timage_info_offset 0x%08X\n" + "\tfamily_member: 0x%04X\n" + "\theader_checksum: 0x%04X\n", + printed_header->rom_id, + printed_header->derange, + printed_header->image_checksum, + printed_header->space1, + printed_header->space2, + printed_header->space3, + printed_header->lpvs, + printed_header->mbz, + printed_header->time_stamp, + printed_header->erase_start, + printed_header->erase_length, + printed_header->data_offset, + printed_header->data_length, + printed_header->space4, + printed_header->space5, + printed_header->space6, + printed_header->space7, + printed_header->header_id, + printed_header->header_version, + printed_header->space8, + printed_header->section_id, + printed_header->image_info_type, + printed_header->image_info_offset, + printed_header->family_member, printed_header->header_checksum); +} + +void print_stag_header(struct stag_header *printed_header) +{ + printf("\tcmark: 0x%02X\n" + "\tid: 0x%02X\n" + "\tmagic: 0x%04X\n" + "\ttime_stamp: 0x%08X\n" + "\timage_length: 0x%04X\n" + "\timage_checksum: 0x%04X\n" + "\ttag_checksum: 0x%04X\n", + printed_header->cmark, + printed_header->id, + printed_header->magic, + printed_header->time_stamp, + printed_header->image_length, + printed_header->image_checksum, printed_header->tag_checksum); +} + +void print_sch2_header(struct sch2_header *printed_header) +{ + printf("\tmagic: 0x%04X\n" + "\tcp_type: 0x%02X\n" + "\tversion: 0x%02X\n" + "\tram_addr: 0x%08X\n" + "\timage_len: 0x%08X\n" + "\timage_crc32: 0x%08X\n" + "\tstart_addr: 0x%08X\n" + "\trootfs_addr: 0x%08X\n" + "\trootfs_len: 0x%08X\n" + "\trootfs_crc32: 0x%08X\n" + "\theader_crc32: 0x%08X\n" + "\theader_length: 0x%04X\n" + "\tcmd_line_length: 0x%04X\n", + printed_header->magic, + printed_header->cp_type, + printed_header->version, + printed_header->ram_addr, + printed_header->image_len, + printed_header->image_crc32, + printed_header->start_addr, + printed_header->rootfs_addr, + printed_header->rootfs_len, + printed_header->rootfs_crc32, + printed_header->header_crc32, + printed_header->header_length, printed_header->cmd_line_length); +} + +static int find_auh_headers(char *buf) +{ + char *tmp_buf = buf; + struct auh_header *tmp_header[MAX_HEADER_COUNTER]; + int header_counter = 0; + + int ret = EXIT_FAILURE; + + while (tmp_buf - buf <= inspect_info.file_size - AUH_SIZE) { + if (!memcmp(tmp_buf, AUH_MAGIC, 3)) { + if (((struct auh_header *)tmp_buf)->header_checksum == + (uint16_t) ~jboot_checksum(0, (uint16_t *) tmp_buf, + AUH_SIZE - 2)) { + uint16_t checksum = 0; + printf("Find proper AUH header at: 0x%lX!\n", + tmp_buf - buf); + tmp_header[header_counter] = + (struct auh_header *)tmp_buf; + checksum = + jboot_checksum(0, (uint16_t *) ((char *) + tmp_header + [header_counter] + + AUH_SIZE), + tmp_header + [header_counter]->data_length); + if (tmp_header[header_counter]->image_checksum + == checksum) + printf("Image checksum ok.\n"); + else + ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", tmp_header[header_counter]->image_checksum, checksum); + header_counter++; + if (header_counter > MAX_HEADER_COUNTER) + break; + } + } + tmp_buf++; + } + + if (header_counter == 0) + ERR("Can't find proper AUH header!\n"); + else if (header_counter > MAX_HEADER_COUNTER) + ERR("To many AUH headers!\n"); + else { + for (int i = 0; i < header_counter; i++) { + printf("AUH %d:\n", i); + print_auh_header(tmp_header[i]); + } + + ret = EXIT_SUCCESS; + } + + return ret; +} + +static int check_stag_header(char *buf, struct stag_header *header) +{ + + int ret = EXIT_FAILURE; + + uint8_t cmark_tmp = header->cmark; + header->cmark = header->id; + + if (header->tag_checksum == + (uint16_t) ~jboot_checksum(0, (uint16_t *) header, + STAG_SIZE - 2)) { + uint16_t checksum = 0; + printf("Find proper STAG header at: 0x%lX!\n", + (char *)header - buf); + checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE), + header->image_length); + if (header->image_checksum == checksum) { + printf("Image checksum ok.\n"); + header->cmark = cmark_tmp; + print_stag_header(header); + ret = EXIT_SUCCESS; + } else + ERR("Image checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_checksum, checksum); + } else + ERR("STAG header checksum incorrect!"); + + header->cmark = cmark_tmp; + return ret; +} + +static int check_sch2_header(char *buf, struct sch2_header *header) +{ + + int ret = EXIT_FAILURE; + + uint32_t crc32_tmp = header->header_crc32; + header->header_crc32 = 0; + + if (crc32_tmp == crc32(0, (uint8_t *) header, header->header_length)) { + uint32_t crc32_val; + printf("Find proper SCH2 header at: 0x%lX!\n", + (char *)header - buf); + + crc32_val = + crc32(0, (uint8_t *) header + header->header_length, + header->image_len); + if (header->image_crc32 == crc32_val) { + printf("Kernel checksum ok.\n"); + + header->header_crc32 = crc32_tmp; + print_sch2_header(header); + ret = EXIT_SUCCESS; + } else + ERR("Kernel checksum incorrect! Stored: 0x%X Calculated: 0x%X\n", header->image_crc32, crc32_val); + + } else + ERR("SCH2 header checksum incorrect!"); + + header->header_crc32 = crc32_tmp; + return ret; +} + +static int inspect_fw(void) +{ + char *buf; + struct stag_header *stag_header_kernel; + struct sch2_header *sch2_header_kernel; + int ret = EXIT_FAILURE; + + buf = malloc(inspect_info.file_size); + if (!buf) { + ERR("no memory for buffer!\n"); + goto out; + } + + ret = read_to_buf(&inspect_info, buf); + if (ret) + goto out_free_buf; + + ret = find_auh_headers(buf); + if (ret) + goto out_free_buf; + + stag_header_kernel = (struct stag_header *)(buf + AUH_SIZE); + + ret = check_stag_header(buf, stag_header_kernel); + if (ret) + goto out_free_buf; + + sch2_header_kernel = (struct sch2_header *)(buf + AUH_SIZE + STAG_SIZE); + + ret = check_sch2_header(buf, sch2_header_kernel); + if (ret) + goto out_free_buf; + + out_free_buf: + free(buf); + out: + return ret; +} + +static int check_options(void) +{ + int ret; + + if (inspect_info.file_name) { + ret = get_file_stat(&inspect_info); + if (ret) + return ret; + + return 0; + } + + return 0; +} + +int fill_sch2(struct sch2_header *header, char *kernel_ptr, char *rootfs_ptr) +{ + + header->magic = SCH2_MAGIC; + header->cp_type = LZMA; + header->version = SCH2_VER; + header->ram_addr = RAM_LOAD_ADDR; + header->image_len = kernel_info.file_size; + header->image_crc32 = crc32(0, (uint8_t *) kernel_ptr, kernel_info.file_size); + header->start_addr = RAM_ENTRY_ADDR; + header->rootfs_addr = + JBOOT_SIZE + STAG_SIZE + SCH2_SIZE + kernel_info.file_size; + header->rootfs_len = rootfs_info.file_size; + header->rootfs_crc32 = crc32(0, (uint8_t *) rootfs_ptr, rootfs_info.file_size); + header->header_crc32 = 0; + header->header_length = SCH2_SIZE; + header->cmd_line_length = 0; + + header->header_crc32 = crc32(0, (uint8_t *) header, header->header_length); + + return EXIT_SUCCESS; +} + +int fill_stag(struct stag_header *header, uint32_t length) +{ + header->cmark = STAG_ID; + header->id = STAG_ID; + header->magic = STAG_MAGIC; + header->time_stamp = jboot_timestamp(); + header->image_length = length + SCH2_SIZE; + header->image_checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + STAG_SIZE), + header->image_length); + header->tag_checksum = + ~jboot_checksum(0, (uint16_t *) header, STAG_SIZE - 2); + + if (image_type == FACTORY) + header->cmark = STAG_CMARK_FACTORY; + + return EXIT_SUCCESS; +}; + +int fill_auh(struct auh_header *header, uint32_t length) +{ + memcpy(header->rom_id, rom_id, 12); + header->derange = 0; + header->image_checksum = + jboot_checksum(0, (uint16_t *) ((char *)header + AUH_SIZE), length); + header->space1 = 0; + header->space2 = 0; + header->space3 = 0; + header->lpvs = AUH_LVPS; + header->mbz = 0; + header->time_stamp = jboot_timestamp(); + header->erase_start = JBOOT_SIZE; + header->erase_length = firmware_size; + header->data_offset = JBOOT_SIZE; + header->data_length = length; + header->space4 = 0; + header->space5 = 0; + header->space6 = 0; + header->space7 = 0; + header->header_id = AUH_HDR_ID; + header->header_version = AUH_HDR_VER; + header->space8 = 0; + header->section_id = AUH_SEC_ID; + header->image_info_type = AUH_INFO_TYPE; + header->image_info_offset = 0; + header->family_member = family_member; + header->header_checksum = + ~jboot_checksum(0, (uint16_t *) header, AUH_SIZE - 2); + + return EXIT_SUCCESS; +} + +int build_fw(void) +{ + char *buf; + char *kernel_ptr; + char *rootfs_ptr; + int ret = EXIT_FAILURE; + int writelen; + + struct stag_header *stag_header_kernel; + struct sch2_header *sch2_header_kernel; + + if (!kernel_info.file_name | !rootfs_info.file_name) + goto out; + + ret = get_file_stat(&kernel_info); + if (ret) + goto out; + ret = get_file_stat(&rootfs_info); + if (ret) + goto out; + + buf = malloc(firmware_size); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + if (rootfs_info.file_size + kernel_info.file_size + ALL_HEADERS_SIZE > + firmware_size) { + ERR("data is bigger than firmware_size!\n"); + goto out; + } + + memset(buf, 0xff, firmware_size); + + stag_header_kernel = (struct stag_header *)buf; + + sch2_header_kernel = + (struct sch2_header *)((char *)stag_header_kernel + STAG_SIZE); + kernel_ptr = (char *)sch2_header_kernel + SCH2_SIZE; + + ret = read_to_buf(&kernel_info, kernel_ptr); + if (ret) + goto out_free_buf; + + rootfs_ptr = kernel_ptr + kernel_info.file_size; + + ret = read_to_buf(&rootfs_info, rootfs_ptr); + if (ret) + goto out_free_buf; + + writelen = rootfs_ptr + rootfs_info.file_size - buf; + + fill_sch2(sch2_header_kernel, kernel_ptr, rootfs_ptr); + fill_stag(stag_header_kernel, kernel_info.file_size); + + ret = write_fw(ofname, buf, writelen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + + out_free_buf: + free(buf); + out: + return ret; +} + +int wrap_fw(void) +{ + char *buf; + char *image_ptr; + int ret = EXIT_FAILURE; + int writelen; + + struct auh_header *auh_header_kernel; + + if (!image_info.file_name) + goto out; + + ret = get_file_stat(&image_info); + if (ret) + goto out; + + buf = malloc(firmware_size); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + if (image_info.file_size + AUH_SIZE > + firmware_size) { + ERR("data is bigger than firmware_size!\n"); + goto out; + } + if (!family_member) { + ERR("No family_member!\n"); + goto out; + } + if (!(rom_id[0])) { + ERR("No rom_id!\n"); + goto out; + } + memset(buf, 0xff, firmware_size); + + image_ptr = (char *)(buf + AUH_SIZE); + + ret = read_to_buf(&image_info, image_ptr); + if (ret) + goto out_free_buf; + + writelen = image_ptr + image_info.file_size - buf; + + auh_header_kernel = (struct auh_header *)buf; + fill_auh(auh_header_kernel, writelen - AUH_SIZE); + + ret = write_fw(ofname, buf, writelen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + + out_free_buf: + free(buf); + out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + image_type = SYSUPGRADE; + family_member = 0; + firmware_size = 0; + + while (1) { + int c; + + c = getopt(argc, argv, "f:F:i:hk:m:o:r:s:"); + if (c == -1) + break; + + switch (c) { + case 'f': + sscanf(optarg, "0x%hx", &family_member); + break; + case 'F': + image_info.file_name = optarg; + image_type = FACTORY; + break; + case 'i': + inspect_info.file_name = optarg; + break; + case 'k': + kernel_info.file_name = optarg; + break; + case 'm': + if (strlen(optarg) == 12) + memcpy(rom_id, optarg, 12); + break; + case 'r': + rootfs_info.file_name = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + sscanf(optarg, "0x%x", &firmware_size); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + if (!inspect_info.file_name) { + if (image_type == FACTORY) + ret = wrap_fw(); + else + ret = build_fw(); + } + else + ret = inspect_fw(); + + out: + return ret; + +} diff --git a/tools/firmware-utils/src/mkdniimg.c b/tools/firmware-utils/src/mkdniimg.c index 7230f09d492..852b07dd9f6 100644 --- a/tools/firmware-utils/src/mkdniimg.c +++ b/tools/firmware-utils/src/mkdniimg.c @@ -43,7 +43,7 @@ static char *board_id; #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) diff --git a/tools/firmware-utils/src/mkfwimage.c b/tools/firmware-utils/src/mkfwimage.c index a527014b551..d8d5239cc55 100644 --- a/tools/firmware-utils/src/mkfwimage.c +++ b/tools/firmware-utils/src/mkfwimage.c @@ -61,7 +61,7 @@ fw_layout_t fw_layout_data[] = { .name = "RSPRO", .kern_start = 0xbf030000, .kern_entry = 0x80060000, - .firmware_max_length= 0x00B00000, + .firmware_max_length= 0x00F00000, }, { .name = "LS-SR71", @@ -79,6 +79,12 @@ fw_layout_t fw_layout_data[] = { .name = "XM", .kern_start = 0x9f050000, .kern_entry = 0x80002000, + .firmware_max_length= 0x00760000, + }, + { + .name = "UBDEV01", + .kern_start = 0x9f050000, + .kern_entry = 0x80002000, .firmware_max_length= 0x006A0000, }, { .name = "", @@ -104,8 +110,6 @@ typedef struct part_data { #define OPTIONS "B:hv:m:o:r:k:" -static int debug = 0; - typedef struct image_info { char magic[16]; char version[256]; @@ -236,9 +240,9 @@ static int create_image_layout(const char* kernelfile, const char* rootfsfile, c fw_layout_t* p; p = &fw_layout_data[0]; - while ((strlen(p->name) != 0) && (strncmp(p->name, board_name, sizeof(board_name)) != 0)) + while (*p->name && (strcmp(p->name, board_name) != 0)) p++; - if (p->name == NULL) { + if (!*p->name) { printf("BUG! Unable to find default fw layout!\n"); exit(-1); } @@ -247,7 +251,7 @@ static int create_image_layout(const char* kernelfile, const char* rootfsfile, c strcpy(kernel->partition_name, "kernel"); kernel->partition_index = 1; kernel->partition_baseaddr = p->kern_start; - if ( (kernel->partition_length = filelength(kernelfile)) < 0) return (-1); + if ( (kernel->partition_length = filelength(kernelfile)) == (u_int32_t)-1) return (-1); kernel->partition_memaddr = p->kern_entry; kernel->partition_entryaddr = p->kern_entry; strncpy(kernel->filename, kernelfile, sizeof(kernel->filename)); @@ -263,8 +267,8 @@ static int create_image_layout(const char* kernelfile, const char* rootfsfile, c rootfs->partition_entryaddr = 0x00000000; strncpy(rootfs->filename, rootfsfile, sizeof(rootfs->filename)); -printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr); -printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr); + printf("kernel: %d 0x%08x\n", kernel->partition_length, kernel->partition_baseaddr); + printf("root: %d 0x%08x\n", rootfs->partition_length, rootfs->partition_baseaddr); im->part_count = 2; return 0; diff --git a/tools/firmware-utils/src/mkfwimage2.c b/tools/firmware-utils/src/mkfwimage2.c index ee09abba1ae..89a98051b43 100644 --- a/tools/firmware-utils/src/mkfwimage2.c +++ b/tools/firmware-utils/src/mkfwimage2.c @@ -197,6 +197,10 @@ int str2u32(char *arg, u_int32_t *val) return 0; } +#ifndef STRINGIFY +#define STRINGIFY2(X) #X +#define STRINGIFY(X) STRINGIFY2(X) +#endif static int image_layout_add_partition(const char *part_desc) { part_data_t *d; @@ -212,7 +216,7 @@ static int image_layout_add_partition(const char *part_desc) } d = &im.parts[im.part_count]; - t = sscanf(part_desc, "%15[a-zA-Z]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%256s", + t = sscanf(part_desc, "%15[-0-9a-zA-Z]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%15[0-9a-fA-Fx]:%"STRINGIFY(PATH_MAX)"s", d->partition_name, offset, length, diff --git a/tools/firmware-utils/src/mkheader_gemtek.c b/tools/firmware-utils/src/mkheader_gemtek.c new file mode 100644 index 00000000000..9e618efbadd --- /dev/null +++ b/tools/firmware-utils/src/mkheader_gemtek.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 Claudio Leite <leitec@staticky.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Builds a proper flash image for routers using some Gemtek + * OEM boards. These include the Airlink101 AR725W, the + * Asante SmartHub 600 (AWRT-600N), and Linksys WRT100/110. + * + * The resulting image is compatible with the factory firmware + * web upgrade and TFTP interface. + * + * To build: + * gcc -O2 -o mkheader_gemtek mkheader_gemtek.c -lz + * + * Claudio Leite <leitec@staticky.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include <zlib.h> /* for crc32() */ + +/* + * The header is in little-endian format. In case + * we are on a BE host, we need to swap binary + * values. + */ +#ifdef __APPLE__ +# include <libkern/OSByteOrder.h> +# define le32 OSSwapHostToLittleInt32 +#else +# if defined(__linux__) +# include <endian.h> +# if __BYTE_ORDER == __BIG_ENDIAN +# define CPU_BIG_ENDIAN +# endif +# else +# include <sys/endian.h> /* BSD's should have this */ +# if _BYTE_ORDER == _BIG_ENDIAN +# define CPU_BIG_ENDIAN +# endif +# endif +# ifdef CPU_BIG_ENDIAN +# define le32(x) (((x & 0xff000000) >> 24) | \ + ((x & 0x00ff0000) >> 8) | \ + ((x & 0x0000ff00) << 8) | \ + ((x & 0x000000ff) << 24)) +# else +# define le32(x) (x) +# endif +#endif + +struct gemtek_header { + uint8_t magic[4]; + uint8_t version[4]; + uint32_t product_id; + uint32_t imagesz; + uint32_t checksum; + uint32_t fast_checksum; + uint8_t build[4]; + uint8_t lang[4]; +}; + +#define HDRLEN sizeof(struct gemtek_header) + +struct machines { + char *desc; + char *id; + uint32_t maxsize; + struct gemtek_header header; +}; + +struct machines mach_def[] = { + {"Airlink101 AR725W", "ar725w", 0x340000, + {"GMTK", "1003", le32(0x03000001), 0, 0, + 0, "01\0\0", "EN\0\0"}}, + {"Asante AWRT-600N", "awrt600n", 0x340000, + {"A600", "1005", le32(0x03000001), 0, 0, + 0, "01\0\0", "EN\0\0"}}, + {"Linksys WRT100", "wrt100", 0x320000, + {"GMTK", "1007", le32(0x03040001), 0, 0, + 0, "2\0\0\0", "EN\0\0"}}, + {"Linksys WRT110", "wrt110", 0x320000, + {"GMTK", "1007", le32(0x03040001), 0, 0, + 0, "2\0\0\0", "EN\0\0"}}, + {0} +}; + +int +main(int argc, char *argv[]) +{ + unsigned long res, flen; + struct gemtek_header my_hdr; + FILE *f, *f_out; + int image_type = -1, index; + uint8_t *buf; + uint32_t crc; + + if (argc < 3) { + fprintf(stderr, "mkheader_gemtek <uImage> <webflash image> [machine ID]\n"); + fprintf(stderr, " where [machine ID] is one of:\n"); + for (index = 0; mach_def[index].desc != 0; index++) { + fprintf(stderr, " %-10s %s", mach_def[index].id, mach_def[index].desc); + if (index == 0) + fprintf(stderr, " (default)\n"); + else + fprintf(stderr, "\n"); + } + + exit(-1); + } + + if (argc == 4) { + for(index = 0; mach_def[index].id != 0; index++) { + if(strcmp(mach_def[index].id, argv[3]) == 0) { + image_type = index; + break; + } + } + + if(image_type == -1) { + fprintf(stderr, "\nERROR: invalid machine type\n"); + exit(-1); + } + } else + image_type = 0; + + printf("Opening %s...\n", argv[1]); + + f = fopen(argv[1], "r"); + if(!f) { + fprintf(stderr, "\nERROR: couldn't open input image\n"); + exit(-1); + } + + fseek(f, 0, SEEK_END); + flen = (unsigned long) ftell(f); + + printf(" %lu (0x%lX) bytes long\n", flen, flen); + + if (flen > mach_def[image_type].maxsize) { + fprintf(stderr, "\nERROR: image exceeds maximum compatible size\n"); + goto f_error; + } + + buf = malloc(flen + HDRLEN); + if (!buf) { + fprintf(stderr, "\nERROR: couldn't allocate buffer\n"); + goto f_error; + } + rewind(f); + res = fread(buf + HDRLEN, 1, flen, f); + if (res != flen) { + perror("Couldn't read entire file: fread()"); + goto f_error; + } + fclose(f); + + printf("\nCreating %s...\n", argv[2]); + + memcpy(&my_hdr, &mach_def[image_type].header, HDRLEN); + + printf(" Using %s magic\n", mach_def[image_type].desc); + + my_hdr.imagesz = le32(flen + HDRLEN); + memcpy(my_hdr.lang, "EN", 2); + + memcpy(buf, &my_hdr, HDRLEN); + + crc = crc32(0, buf, flen + HDRLEN); + printf(" CRC32: %08X\n", crc); + + my_hdr.checksum = le32(crc); + memcpy(buf, &my_hdr, HDRLEN); + + printf(" Writing...\n"); + + f_out = fopen(argv[2], "w"); + if(!f_out) { + fprintf(stderr, "\nERROR: couldn't open output image\n"); + exit(-1); + } + + fwrite(buf, 1, flen + HDRLEN, f_out); + + fclose(f_out); + + free(buf); + return 0; + +f_error: + fclose(f); + exit(-1); +} diff --git a/tools/firmware-utils/src/mkhilinkfw.c b/tools/firmware-utils/src/mkhilinkfw.c new file mode 100644 index 00000000000..55908e5caa7 --- /dev/null +++ b/tools/firmware-utils/src/mkhilinkfw.c @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2013 Jeff Kent <jeff@jkent.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This tool encrypts and decrypts uImage formatted firmware for Hilink + * HLK-RM04 wireless modules. It will also truncate a dump of mtd6 and make + * it an image suitable for flashing via the stock firmware upgrade page. + * + * Build instructions: + * gcc -lcrypto hlkcrypt.c -o hlkcrypt + */ + +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <openssl/des.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#define DES_KEY "H@L9K*(3" + +#ifndef min +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; }) +#endif + +#define IH_MAGIC 0x27051956 +#define IH_NMLEN 32 +typedef struct image_header { + uint32_t ih_magic; /* Image Header Magic Number */ + uint32_t ih_hcrc; /* Image Header CRC Checksum */ + uint32_t ih_time; /* Image Creation Timestamp */ + uint32_t ih_size; /* Image Data Size */ + uint32_t ih_load; /* Data Load Address */ + uint32_t ih_ep; /* Entry Point Address */ + uint32_t ih_dcrc; /* Image Data CRC Checksum */ + uint8_t ih_os; /* Operating System */ + uint8_t ih_arch; /* CPU architecture */ + uint8_t ih_type; /* Image Type */ + uint8_t ih_comp; /* Compression Type */ + uint8_t ih_name[IH_NMLEN]; /* Image Name */ +} image_header_t; + +static int temp_fd = -1; +static DES_key_schedule schedule; + +static void show_usage(const char *arg0); +static void exit_cleanup(void); +static void copy_file(int src, int dst); +static void do_encrypt(void *p, off_t len); +static void do_decrypt(void *p, off_t len); + + +int main(int argc, char **argv) +{ + int encrypt_opt = 0; + int decrypt_opt = 0; + int input_opt = 0; + int output_opt = 0; + char *input_filename = NULL; + char *output_filename = NULL; + + int input_fd; + int output_fd; + off_t file_len; + char *p; + char buf[sizeof(image_header_t) + 3]; + image_header_t *header; + + while (1) { + static struct option long_options[] = { + {"encrypt", no_argument, 0, 'e'}, + {"decrypt", no_argument, 0, 'd'}, + {"input", required_argument, 0, 'i'}, + {"output", required_argument, 0, 'o'}, + {0, 0, 0, 0 } + }; + int option_index = 0; + int c = getopt_long(argc, argv, "dei:o:", + long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'd': + decrypt_opt++; + if (decrypt_opt > 1) { + fprintf(stderr, "%s: decrypt may only be specified once\n", + argv[0]); + show_usage(argv[0]); + } + break; + + case 'e': + encrypt_opt++; + if (encrypt_opt > 1) { + fprintf(stderr, "%s: encrypt may only be specified once\n", + argv[0]); + show_usage(argv[0]); + } + break; + + case 'i': + input_opt++; + if (input_opt > 1) { + fprintf(stderr, "%s: only one input file may be specified\n", + argv[0]); + show_usage(argv[0]); + } + if (strcmp("-", optarg) != 0) { + input_filename = optarg; + } + break; + + case 'o': + output_opt++; + if (output_opt > 1) { + fprintf(stderr, "%s: only one output file may be specified\n", + argv[0]); + show_usage(argv[0]); + } + if (strcmp("-", optarg) != 0) { + output_filename = optarg; + } + break; + + case '?': + exit(-1); + + default: + abort(); + } + } + + if (decrypt_opt && encrypt_opt) { + fprintf(stderr, "%s: decrypt and encrypt may not be used together\n", + argv[0]); + show_usage(argv[0]); + } + + if (!decrypt_opt && !encrypt_opt) { + fprintf(stderr, "%s: neither decrypt or encrypt were specified\n", + argv[0]); + show_usage(argv[0]); + } + + temp_fd = fileno(tmpfile()); + if (temp_fd < 0) { + fprintf(stderr, "Can't create temporary file\n"); + exit(EXIT_FAILURE); + } + + atexit(exit_cleanup); + DES_set_key_unchecked((const_DES_cblock *)DES_KEY, &schedule); + + if (input_filename) { + input_fd = open(input_filename, O_RDONLY); + if (input_fd < 0) { + fprintf(stderr, "Can't open %s for reading: %s\n", input_filename, + strerror(errno)); + exit(EXIT_FAILURE); + } + copy_file(input_fd, temp_fd); + close(input_fd); + } + else { + copy_file(STDIN_FILENO, temp_fd); + } + + file_len = lseek(temp_fd, 0, SEEK_CUR); + if (file_len < 64) { + fprintf(stderr, "Not enough data\n"); + exit(EXIT_FAILURE); + } + + p = mmap(0, file_len, PROT_READ|PROT_WRITE, MAP_SHARED, temp_fd, 0); + if (p == MAP_FAILED) { + fprintf(stderr, "mmap failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + + if (encrypt_opt) { + header = (image_header_t *)p; + off_t len = min(file_len, + ntohl(header->ih_size) + sizeof(image_header_t)); + if (ntohl(header->ih_magic) != IH_MAGIC) { + fprintf(stderr, "Header magic incorrect: " + "expected 0x%08X, got 0x%08X\n", + IH_MAGIC, ntohl(header->ih_magic)); + munmap(p, file_len); + exit(EXIT_FAILURE); + } + do_encrypt(p, len); + munmap(p, file_len); + if (len != file_len) { + if (ftruncate(temp_fd, len) < 0) { + fprintf(stderr, "ftruncate failed: %s\n", strerror(errno)); + exit(EXIT_FAILURE); + } + } + } + + if (decrypt_opt) { + off_t header_len = min(file_len, sizeof(image_header_t) + 3); + memcpy(buf, p, header_len); + do_decrypt(buf, header_len); + header = (image_header_t *)buf; + if (ntohl(header->ih_magic) != IH_MAGIC) { + fprintf(stderr, "Header magic incorrect: " + "expected 0x%08X, got 0x%08X\n", + IH_MAGIC, ntohl(header->ih_magic)); + exit(EXIT_FAILURE); + } + do_decrypt(p, file_len); + munmap(p, file_len); + } + + lseek(temp_fd, 0, SEEK_SET); + if (output_filename) { + output_fd = creat(output_filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if (output_fd < 0) { + fprintf(stderr, "Can't open %s for writing: %s\n", + output_filename, strerror(errno)); + exit(EXIT_FAILURE); + } + copy_file(temp_fd, output_fd); + close(output_fd); + } + else { + copy_file(temp_fd, STDOUT_FILENO); + } + + exit(EXIT_SUCCESS); + return 0; +} + +static void show_usage(const char *arg0) +{ + fprintf(stderr, "usage: %s -d|-e [-i FILE] [-o FILE]\n\n", arg0); + fprintf(stderr, "%-15s %s\n", "-d, --decrypt", "decrypt data"); + fprintf(stderr, "%-15s %s\n", "-e, --encrypt", "encrypt data"); + fprintf(stderr, "%-15s %s\n", "-i, --input", "intput file (defaults to stdin)"); + fprintf(stderr, "%-15s %s\n", "-o, --output", "output file (defaults to stdout)"); + exit(-1); +} + +static void exit_cleanup(void) +{ + if (temp_fd >= 0) { + close(temp_fd); + } +} + +static void copy_file(int src, int dst) +{ + char buf[4096]; + ssize_t size; + + while ((size = read(src, buf, 4096)) > 0) { + write(dst, buf, size); + } +} + +static void do_encrypt(void *p, off_t len) +{ + DES_cblock *pblock; + int num_blocks; + + num_blocks = len / 8; + pblock = (DES_cblock *) p; + while (num_blocks--) { + DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT); + pblock++; + } + + num_blocks = (len - 3) / 8; + pblock = (DES_cblock *) (p + 3); + while (num_blocks--) { + DES_ecb_encrypt(pblock, pblock, &schedule, DES_ENCRYPT); + pblock++; + } +} + +static void do_decrypt(void *p, off_t len) +{ + DES_cblock *pblock; + int num_blocks; + + num_blocks = (len - 3) / 8; + pblock = (DES_cblock *) (p + 3); + while (num_blocks--) { + DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT); + pblock++; + } + + num_blocks = len / 8; + pblock = (DES_cblock *) p; + while (num_blocks--) { + DES_ecb_encrypt(pblock, pblock, &schedule, DES_DECRYPT); + pblock++; + } +} diff --git a/tools/firmware-utils/src/mkmerakifw-old.c b/tools/firmware-utils/src/mkmerakifw-old.c new file mode 100644 index 00000000000..05317c2a5b4 --- /dev/null +++ b/tools/firmware-utils/src/mkmerakifw-old.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com> + * Copyright (C) 2016 Christian Lamparter <chunkeey@googlemail.com> + * + * The format of the header this tool generates was first documented by + * Chris Blake <chrisrblake93 (at) gmail.com> in a shell script of the + * same purpose. I have created this reimplementation at his request. The + * original script can be found at: + * <https://github.com/riptidewave93/meraki-partbuilder> + * + * Support for the old header format, which is used by the Cisco Z1 AP + * has been reverse engineered from the nandloader's nand_load_bk function. + * The original code is part of Cisco's GPL code and can be found at: + * <https://github.com/riptidewave93/meraki-linux> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <libgen.h> +#include <endian.h> +#include <getopt.h> +#include <errno.h> +#include <arpa/inet.h> + +#define PADDING_BYTE 0xff + +#define HDR_LENGTH 0x00000020 +#define HDR_OFF_MAGIC1 0 +#define HDR_OFF_LOAD_ADDR 4 +#define HDR_OFF_IMAGELEN 8 +#define HDR_OFF_ENTRY 12 +#define HDR_OFF_CHECKSUM 16 +#define HDR_OFF_FILLER0 20 +#define HDR_OFF_FILLER1 24 +#define HDR_OFF_FILLER2 28 + +struct board_info { + char *id; + char *description; + uint32_t magic; + uint32_t imagelen; + uint32_t load_addr; + uint32_t entry; +}; + +/* + * Globals + */ +static char *progname; +static bool strip_padding; + +static char *board_id; +static const struct board_info *board; + +static const struct board_info boards[] = { + { + .id = "z1", + .description = "Meraki Z1 Access Point", + .magic = 0x4d495053, + .imagelen = 0x007e0000, + .load_addr = 0x80060000, + .entry = 0x80060000 + }, { + /* terminating entry */ + } +}; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +static const struct board_info *find_board(const char *id) +{ + const struct board_info *ret; + const struct board_info *board; + + ret = NULL; + for (board = boards; board->id != NULL; board++) { + if (strcasecmp(id, board->id) == 0) { + ret = board; + break; + } + } + + return ret; +} + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + const struct board_info *board; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board> create image for the board specified with <board>\n" +" -i <file> read kernel image from the file <file>\n" +" -o <file> write output to the file <file>\n" +" -s strip padding from the end of the image\n" +" -h show this screen\n" + ); + + fprintf(stream, "\nBoards:\n"); + for (board = boards; board->id != NULL; board++) + fprintf(stream, " %-16s%s\n", board->id, board->description); + + exit(status); +} + +static void writel(unsigned char *buf, size_t offset, uint32_t value) +{ + value = htonl(value); + memcpy(buf + offset, &value, sizeof(uint32_t)); +} + +static const uint32_t crc32_table[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +static inline uint32_t crc32_accumulate_8(const uint32_t crc, const uint8_t ch) +{ + return crc32_table[(crc ^ ch) & 0xff] ^ (crc >> 8); +} + +static void crc32_csum(uint8_t *buf, const size_t len) +{ + uint32_t crc; + size_t i; + + crc = ~0; + for (i = 0; i < len; i += 4) { + crc = crc32_accumulate_8(crc, buf[i + 3]); + crc = crc32_accumulate_8(crc, buf[i + 2]); + crc = crc32_accumulate_8(crc, buf[i + 1]); + crc = crc32_accumulate_8(crc, buf[i]); + } + crc = ~crc; + + writel(buf, HDR_OFF_CHECKSUM, crc); +} + + +static int meraki_build_hdr(const struct board_info *board, const size_t klen, + FILE *out, FILE *in) +{ + unsigned char *kernel; + unsigned char *buf; + size_t buflen; + size_t kspace; + + size_t rc; + buflen = board->imagelen; + kspace = buflen - HDR_LENGTH; + + if (klen > kspace) { + ERR("kernel file is too big - max size: 0x%08lX\n", kspace); + return EXIT_FAILURE; + } + + /* If requested, resize buffer to remove padding */ + if (strip_padding) + buflen = klen + HDR_LENGTH; + + /* Allocate and initialize buffer for final image */ + buf = malloc(buflen); + if (buf == NULL) { + ERRS("no memory for buffer: %s\n"); + return EXIT_FAILURE; + } + memset(buf, PADDING_BYTE, buflen); + + /* Load kernel */ + kernel = buf + HDR_LENGTH; + fread(kernel, klen, 1, in); + + /* Write magic values and filler */ + writel(buf, HDR_OFF_MAGIC1, board->magic); + writel(buf, HDR_OFF_FILLER0, 0); + writel(buf, HDR_OFF_FILLER1, 0); + writel(buf, HDR_OFF_FILLER2, 0); + + /* Write load and kernel entry point address */ + writel(buf, HDR_OFF_LOAD_ADDR, board->load_addr); + writel(buf, HDR_OFF_ENTRY, board->entry); + + /* Write header and image length */ + writel(buf, HDR_OFF_IMAGELEN, klen); + + /* this gets replaced later, after the checksum has been calculated */ + writel(buf, HDR_OFF_CHECKSUM, 0); + + /* Write checksum */ + crc32_csum(buf, klen + HDR_LENGTH); + + rc = fwrite(buf, buflen, 1, out); + + free(buf); + + return rc == 1 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + char *ofname = NULL, *ifname = NULL; + FILE *out, *in; + size_t klen; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "B:i:o:sh"); + if (c == -1) + break; + + switch (c) { + case 'B': + board_id = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + strip_padding = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (board_id == NULL) { + ERR("no board specified"); + goto err; + } + + board = find_board(board_id); + if (board == NULL) { + ERR("unknown board \"%s\"", board_id); + goto err; + } + + if (ifname == NULL) { + ERR("no input file specified"); + goto err; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto err; + } + + in = fopen(ifname, "r"); + if (in == NULL) { + ERRS("could not open \"%s\" for reading: %s", ifname); + goto err; + } + + /* Get kernel length */ + fseek(in, 0, SEEK_END); + klen = ftell(in); + rewind(in); + + out = fopen(ofname, "w"); + if (out == NULL) { + ERRS("could not open \"%s\" for writing: %s", ofname); + goto err_close_in; + } + + ret = meraki_build_hdr(board, klen, out, in); + fclose(out); + +err_close_in: + fclose(in); + +err: + return ret; +} diff --git a/tools/firmware-utils/src/mkmerakifw.c b/tools/firmware-utils/src/mkmerakifw.c new file mode 100644 index 00000000000..1a50f1658f7 --- /dev/null +++ b/tools/firmware-utils/src/mkmerakifw.c @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2015 Thomas Hebb <tommyhebb@gmail.com> + * + * The format of the header this tool generates was first documented by + * Chris Blake <chrisrblake93 (at) gmail.com> in a shell script of the + * same purpose. I have created this reimplementation at his request. The + * original script can be found at: + * <https://github.com/riptidewave93/meraki-partbuilder> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <libgen.h> +#include <getopt.h> +#include <errno.h> +#include <arpa/inet.h> + +#include "sha1.h" + +#define PADDING_BYTE 0xff + +#define HDR_LENGTH 0x00000400 +#define HDR_OFF_MAGIC1 0 +#define HDR_OFF_HDRLEN 4 +#define HDR_OFF_IMAGELEN 8 +#define HDR_OFF_CHECKSUM 12 +#define HDR_OFF_MAGIC2 32 +#define HDR_OFF_MAGIC3 36 +#define HDR_OFF_STATICHASH 40 +#define HDR_OFF_KERNEL_OFFSET 40 +#define HDR_OFF_RAMDISK_OFFSET 44 +#define HDR_OFF_FDT_OFFSET 48 +#define HDR_OFF_UNKNOWN_OFFSET 52 + +struct board_info { + uint32_t magic1; + uint32_t magic2; + uint32_t magic3; + uint32_t imagelen; + union { + unsigned char statichash[20]; + struct { + uint32_t kernel_offset; + uint32_t ramdisk_offset; + uint32_t fdt_offset; + uint32_t unknown_offset; + } mx60; + } u; + char *id; + char *description; +}; + +/* + * Globals + */ +static char *progname; + +static char *board_id; +static const struct board_info *board; + +static const struct board_info boards[] = { + { + .id = "mr18", + .description = "Meraki MR18 Access Point", + .magic1 = 0x8e73ed8a, + .magic2 = 0x8e73ed8a, + .imagelen = 0x00800000, + .u.statichash = {0xda, 0x39, 0xa3, 0xee, 0x5e, + 0x6b, 0x4b, 0x0d, 0x32, 0x55, + 0xbf, 0xef, 0x95, 0x60, 0x18, + 0x90, 0xaf, 0xd8, 0x07, 0x09}, + }, { + .id = "mr24", + .description = "Meraki MR24 Access Point", + .magic1 = 0x8e73ed8a, + .magic2 = 0x8e73ed8a, + .imagelen = 0x00800000, + .u.statichash = {0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff}, + }, { + .id = "mx60", + .description = "Meraki MX60/MX60W Security Appliance", + .magic1 = 0x8e73ed8a, + .magic2 = 0xa1f0beef, /* Enables use of load addr in statichash */ + .magic3 = 0x00060001, /* This goes along with magic2 */ + .imagelen = 0x3fd00000, + /* The static hash below does the following: + * 1st Row: Kernel Offset + * 2nd Row: Ramdisk Offset + * 3rd Row: FDT Offset + * 4th Row: ? Unused/Unknown ? + * 5th Row: ? Unused/Unknown ? + */ + .u.mx60 = { + .kernel_offset = 0x10000, + .ramdisk_offset = 0x3FFC00, + .fdt_offset = 0x0400, + .unknown_offset = 0x0400, + }, + }, { + /* terminating entry */ + } +}; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +static const struct board_info *find_board(const char *id) +{ + const struct board_info *ret; + const struct board_info *board; + + ret = NULL; + for (board = boards; board->id != NULL; board++) { + if (strcasecmp(id, board->id) == 0) { + ret = board; + break; + } + } + + return ret; +} + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + const struct board_info *board; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board> create image for the board specified with <board>\n" +" -i <file> read kernel image from the file <file>\n" +" -o <file> write output to the file <file>\n" +" -s strip padding from the end of the image\n" +" -h show this screen\n" + ); + + fprintf(stream, "\nBoards:\n"); + for (board = boards; board->id != NULL; board++) + fprintf(stream, " %-16s%s\n", board->id, board->description); + + exit(status); +} + +void writel(unsigned char *buf, size_t offset, uint32_t value) +{ + value = htonl(value); + memcpy(buf + offset, &value, sizeof(uint32_t)); +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + long klen; + size_t kspace; + unsigned char *kernel; + size_t buflen; + unsigned char *buf; + bool strip_padding = false; + char *ofname = NULL, *ifname = NULL; + FILE *out, *in; + + progname = basename(argv[0]); + + while (1) { + int c; + + c = getopt(argc, argv, "B:i:o:sh"); + if (c == -1) + break; + + switch (c) { + case 'B': + board_id = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + strip_padding = true; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (board_id == NULL) { + ERR("no board specified"); + goto err; + } + + board = find_board(board_id); + if (board == NULL) { + ERR("unknown board \"%s\"", board_id); + goto err; + } + + if (ifname == NULL) { + ERR("no input file specified"); + goto err; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto err; + } + + in = fopen(ifname, "r"); + if (in == NULL) { + ERRS("could not open \"%s\" for reading: %s", ifname); + goto err; + } + + buflen = board->imagelen; + kspace = buflen - HDR_LENGTH; + + /* Get kernel length */ + fseek(in, 0, SEEK_END); + klen = ftell(in); + rewind(in); + + if (klen > kspace) { + ERR("file \"%s\" is too big - max size: 0x%08lX\n", + ifname, kspace); + goto err_close_in; + } + + /* If requested, resize buffer to remove padding */ + if (strip_padding) + buflen = klen + HDR_LENGTH; + + /* Allocate and initialize buffer for final image */ + buf = malloc(buflen); + if (buf == NULL) { + ERRS("no memory for buffer: %s\n"); + goto err_close_in; + } + memset(buf, PADDING_BYTE, buflen); + + /* Load kernel */ + kernel = buf + HDR_LENGTH; + fread(kernel, klen, 1, in); + + /* Write magic values */ + writel(buf, HDR_OFF_MAGIC1, board->magic1); + writel(buf, HDR_OFF_MAGIC2, board->magic2); + writel(buf, HDR_OFF_MAGIC3, board->magic3); + + /* Write header and image length */ + writel(buf, HDR_OFF_HDRLEN, HDR_LENGTH); + writel(buf, HDR_OFF_IMAGELEN, klen); + + /* Write checksum and static hash */ + sha1_csum(kernel, klen, buf + HDR_OFF_CHECKSUM); + + switch (board->magic2) { + case 0xa1f0beef: + writel(buf, HDR_OFF_KERNEL_OFFSET, board->u.mx60.kernel_offset); + writel(buf, HDR_OFF_RAMDISK_OFFSET, board->u.mx60.ramdisk_offset); + writel(buf, HDR_OFF_FDT_OFFSET, board->u.mx60.fdt_offset), + writel(buf, HDR_OFF_UNKNOWN_OFFSET, board->u.mx60.unknown_offset); + break; + + case 0x8e73ed8a: + memcpy(buf + HDR_OFF_STATICHASH, board->u.statichash, 20); + break; + } + + /* Save finished image */ + out = fopen(ofname, "w"); + if (out == NULL) { + ERRS("could not open \"%s\" for writing: %s", ofname); + goto err_free; + } + fwrite(buf, buflen, 1, out); + + ret = EXIT_SUCCESS; + + fclose(out); + +err_free: + free(buf); + +err_close_in: + fclose(in); + +err: + return ret; +} diff --git a/tools/firmware-utils/src/mkplanexfw.c b/tools/firmware-utils/src/mkplanexfw.c index 1bdccb7c026..0b71f80438f 100644 --- a/tools/firmware-utils/src/mkplanexfw.c +++ b/tools/firmware-utils/src/mkplanexfw.c @@ -82,7 +82,7 @@ static struct board_info boards[] = { #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) diff --git a/tools/firmware-utils/src/mkporayfw.c b/tools/firmware-utils/src/mkporayfw.c new file mode 100644 index 00000000000..6ec4f320d93 --- /dev/null +++ b/tools/firmware-utils/src/mkporayfw.c @@ -0,0 +1,791 @@ +/* + * Builder/viewer/extractor utility for Poray firmware image files + * + * Copyright (C) 2013 Michel Stempin <michel.stempin@wanadoo.fr> + * Copyright (C) 2013 Felix Kaechele <felix@fetzig.org> + * Copyright (C) 2013 <admin@openschemes.com> + * + * This tool is based on: + * TP-Link firmware upgrade tool. + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * + * Itself based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <libgen.h> +#include <getopt.h> +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#if (__BYTE_ORDER == __BIG_ENDIAN) +# define HOST_TO_BE32(x) (x) +# define BE32_TO_HOST(x) (x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE32_TO_HOST(x) bswap_32(x) +#else +# define HOST_TO_BE32(x) bswap_32(x) +# define BE32_TO_HOST(x) bswap_32(x) +# define HOST_TO_LE32(x) (x) +# define LE32_TO_HOST(x) (x) +#endif + +/* Fixed header flags */ +#define HEADER_FLAGS 0x020e0000 + +/* Recognized Hardware ID magic */ +#define HWID_HAME_MPR_A1_L8 0x32473352 +#define HWID_PORAY_R50B 0x31353033 +#define HWID_PORAY_R50D 0x33353033 +#define HWID_PORAY_R50E 0x34353033 +#define HWID_PORAY_M3 0x31353335 +#define HWID_PORAY_M4 0x32353335 +#define HWID_PORAY_Q3 0x33353335 +#define HWID_PORAY_X5_X6 0x35353335 +#define HWID_PORAY_X8 0x36353335 +#define HWID_PORAY_X1 0x38353335 +#define HWID_NEXX_WT1520 0x30353332 +#define HWID_NEXX_WT3020 0x30323033 +#define HWID_A5_V11 0x32473352 + +/* Recognized XOR obfuscation keys */ +#define KEY_HAME 0 +#define KEY_PORAY_1 1 +#define KEY_PORAY_2 2 +#define KEY_PORAY_3 3 +#define KEY_PORAY_4 4 +#define KEY_NEXX_1 5 +#define KEY_NEXX_2 6 +#define KEY_A5_V11 7 + +/* XOR key length */ +#define KEY_LEN 15 + +struct file_info { + char *file_name; /* Name of the file */ + uint32_t file_size; /* Length of the file */ +}; + +struct fw_header { + uint32_t hw_id; /* Hardware id */ + uint32_t firmware_len; /* Firmware data length */ + uint32_t flags; /* Header flags */ + uint8_t pad[16]; +} __attribute__ ((packed)); + +struct flash_layout { + char *id; + uint32_t fw_max_len; +}; + +struct board_info { + char *id; + uint32_t hw_id; + char *layout_id; + uint32_t key; +}; + +/* + * Globals + */ +static char *ofname; +static char *progname; + +static char *board_id; +static struct board_info *board; +static char *layout_id; +static struct flash_layout *layout; +static char *opt_hw_id; +static uint32_t hw_id; +static struct file_info firmware_info; +static uint32_t firmware_len = 0; + +static int inspect = 0; +static int extract = 0; + +static uint8_t key[][KEY_LEN] = { + {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x12, 0x03, 0x08}, + {0x89, 0x6B, 0x5A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, + {0xC9, 0x1C, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, + {0x19, 0x1B, 0x3A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, + {0x79, 0x7B, 0x7A, 0x93, 0x92, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0xE6, 0xC7}, + {0x19, 0x1C, 0x4A, 0x93, 0x96, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0x16, 0xC6}, + {0x39, 0x1C, 0x4A, 0x93, 0x96, 0x95, 0xC3, 0x63, 0xD0, 0xA3, 0x9C, 0x92, 0x2E, 0x16, 0xC6}, + {0xC8, 0x3C, 0x3A, 0x93, 0xA2, 0x95, 0xC3, 0x63, 0x48, 0x45, 0x58, 0x09, 0x20, 0x11, 0x08}, +}; + +static struct flash_layout layouts[] = { + { + .id = "4M", + .fw_max_len = 0x3c0000, + }, { + .id = "8M", + .fw_max_len = 0x7c0000, + }, { + /* terminating entry */ + } +}; + +static struct board_info boards[] = { + { + .id = "A5-V11", + .hw_id = HWID_A5_V11, + .layout_id = "4M", + .key = KEY_A5_V11, + }, { + .id = "MPR-A1", + .hw_id = HWID_HAME_MPR_A1_L8, + .layout_id = "4M", + .key = KEY_HAME, + }, { + .id = "MPR-L8", + .hw_id = HWID_HAME_MPR_A1_L8, + .layout_id = "4M", + .key = KEY_HAME, + }, { + .id = "R50B", + .hw_id = HWID_PORAY_R50B, + .layout_id = "4M", + .key = KEY_PORAY_2, + }, { + .id = "R50D", + .hw_id = HWID_PORAY_R50D, + .layout_id = "4M", + .key = KEY_PORAY_3, + }, { + .id = "R50E", + .hw_id = HWID_PORAY_R50E, + .layout_id = "4M", + .key = KEY_PORAY_4, + }, { + .id = "M3", + .hw_id = HWID_PORAY_M3, + .layout_id = "4M", + .key = KEY_PORAY_1, + }, { + .id = "M4", + .hw_id = HWID_PORAY_M4, + .layout_id = "4M", + .key = KEY_PORAY_1, + }, { + .id = "Q3", + .hw_id = HWID_PORAY_Q3, + .layout_id = "4M", + .key = KEY_PORAY_1, + }, { + .id = "X5 or X6", + .hw_id = HWID_PORAY_X5_X6, + .layout_id = "8M", + .key = KEY_PORAY_1, + }, { + .id = "X5", + .hw_id = HWID_PORAY_X5_X6, + .layout_id = "8M", + .key = KEY_PORAY_1, + }, { + .id = "X6", + .hw_id = HWID_PORAY_X5_X6, + .layout_id = "8M", + .key = KEY_PORAY_1, + }, { + .id = "X8", + .hw_id = HWID_PORAY_X8, + .layout_id = "8M", + .key = KEY_PORAY_1, + }, { + .id = "X1", + .hw_id = HWID_PORAY_X1, + .layout_id = "8M", + .key = KEY_PORAY_1, + }, { + .id = "WT1520", + .hw_id = HWID_NEXX_WT1520, + .layout_id = "4M", + .key = KEY_NEXX_1, + }, { + .id = "WT1520", + .hw_id = HWID_NEXX_WT1520, + .layout_id = "8M", + .key = KEY_NEXX_1, + }, { + .id = "WT3020", + .hw_id = HWID_NEXX_WT3020, + .layout_id = "4M", + .key = KEY_NEXX_2, + }, { + .id = "WT3020", + .hw_id = HWID_NEXX_WT3020, + .layout_id = "8M", + .key = KEY_NEXX_2, + }, { + + + + + /* terminating entry */ + } +}; + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ":%s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +/* + * Find a board by its name + */ +static struct board_info *find_board(char *id) +{ + struct board_info *ret; + struct board_info *board; + + ret = NULL; + for (board = boards; board->id != NULL; board++){ + if (strcasecmp(id, board->id) == 0) { + ret = board; + break; + } + }; + + return ret; +} + +/* + * Find a board by its hardware ID + */ +static struct board_info *find_board_by_hwid(uint32_t hw_id) +{ + struct board_info *board; + + for (board = boards; board->id != NULL; board++) { + if (hw_id == board->hw_id) + return board; + }; + + return NULL; +} + +/* + * Find a Flash memory layout by its name + */ +static struct flash_layout *find_layout(char *id) +{ + struct flash_layout *ret; + struct flash_layout *l; + + ret = NULL; + for (l = layouts; l->id != NULL; l++){ + if (strcasecmp(id, l->id) == 0) { + ret = l; + break; + } + }; + + return ret; +} + +/* + * Display usage + */ +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -B <board> create image for the board specified with <board>\n" +" -H <hwid> use hardware id specified with <hwid>\n" +" -F <id> use flash layout specified with <id>\n" +" -f <file> read firmware image from the file <file>\n" +" -o <file> write output to the file <file>\n" +" -i inspect given firmware file (requires -f)\n" +" -x extract combined kernel and rootfs while inspecting (implies -i)\n" +" -h show this screen\n" + ); + + exit(status); +} + +/* + * Get file statistics + */ +static int get_file_stat(struct file_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) { + return 0; + } + res = stat(fdata->file_name, &st); + if (res){ + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + return 0; +} + +/* + * Read file into buffer + */ +static int read_to_buf(struct file_info *fdata, uint8_t *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "rb"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + + out_close: + fclose(f); + out: + return ret; +} + +/* + * Check command line options + */ +static int check_options(void) +{ + int ret; + + if (firmware_info.file_name == NULL) { + ERR("no firmware image specified"); + return -1; + } + + ret = get_file_stat(&firmware_info); + if (ret) + return ret; + + if (inspect) + return 0; + + if (board_id == NULL && opt_hw_id == NULL) { + ERR("either board or hardware id must be specified"); + return -1; + } + + if (board_id) { + board = find_board(board_id); + if (board == NULL) { + ERR("unknown/unsupported board id \"%s\"", board_id); + return -1; + } + if (layout_id == NULL) { + layout_id = board->layout_id; + } + hw_id = board->hw_id; + } else { + hw_id = strtoul(opt_hw_id, NULL, 0); + board = find_board_by_hwid(hw_id); + if (layout_id == NULL) { + layout_id = board->layout_id; + } + } + + layout = find_layout(layout_id); + if (layout == NULL) { + ERR("unknown flash layout \"%s\"", layout_id); + return -1; + } + + firmware_len = firmware_info.file_size; + + if (firmware_info.file_size > + layout->fw_max_len - sizeof (struct fw_header)) { + ERR("firmware image is too big"); + return -1; + } + + if (ofname == NULL) { + ERR("no output file specified"); + return -1; + } + return 0; +} + +/* + * Fill in firmware header + */ +static void fill_header(uint8_t *buf) +{ + struct fw_header *hdr = (struct fw_header *) buf; + + memset(hdr, 0, sizeof (struct fw_header)); + hdr->hw_id = HOST_TO_LE32(hw_id); + hdr->firmware_len = HOST_TO_LE32(firmware_len); + hdr->flags = HOST_TO_LE32(HEADER_FLAGS); +} + +/* + * Compute firmware checksum + */ +static uint16_t checksum_fw(uint8_t *data, int len) +{ + int i; + int32_t checksum = 0; + + for (i = 0; i < len - 1; i += 2) { + checksum += (data[i + 1] << 8) | data[i]; + } + if (i < len) { + checksum += data[i]; + } + checksum = checksum + (checksum >> 16) + 0xffff; + checksum = ~(checksum + (checksum >> 16)) & 0xffff; + return (uint16_t) checksum; +} + +/* + * (De)obfuscate firmware using an XOR operation with a fixed length key + */ +static void xor_fw(uint8_t *data, int len) +{ + int i; + + for (i = 0; i <= len; i++) { + data[i] ^= key[board->key][i % KEY_LEN]; + } +} + +/* + * Write firmware to file + */ +static int write_fw(uint8_t *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "wb"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + + out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) { + unlink(ofname); + } + out: + return ret; +} + +/* + * Build firmware file + */ +static int build_fw(void) +{ + int buflen; + uint8_t *buf, *p; + int ret = EXIT_FAILURE; + int writelen = 0; + uint16_t checksum; + + buflen = layout->fw_max_len; + + buf = (uint8_t *) malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + memset(buf, 0xff, buflen); + p = buf + sizeof (struct fw_header); + ret = read_to_buf(&firmware_info, p); + if (ret) { + goto out_free_buf; + } + writelen = sizeof (struct fw_header) + firmware_len + 2; + + /* Fill in header */ + fill_header(buf); + + /* Compute firmware checksum */ + checksum = checksum_fw(buf + sizeof (struct fw_header), firmware_len); + + /* Cannot use network order function because checksum is not word-aligned */ + buf[writelen - 1] = checksum >> 8; + buf[writelen - 2] = checksum & 0xff; + + /* XOR obfuscate firmware */ + xor_fw(buf + sizeof (struct fw_header), firmware_len + 2); + + /* Write firmware file */ + ret = write_fw(buf, writelen); + if (ret) { + goto out_free_buf; + } + ret = EXIT_SUCCESS; + + out_free_buf: + free(buf); + out: + return ret; +} + +/* Helper functions to inspect_fw() representing different output formats */ +static inline void inspect_fw_pstr(char *label, char *str) +{ + printf("%-23s: %s\n", label, str); +} + +static inline void inspect_fw_phex(char *label, uint32_t val) +{ + printf("%-23s: 0x%08x\n", label, val); +} + +static inline void inspect_fw_phexpost(char *label, + uint32_t val, char *post) +{ + printf("%-23s: 0x%08x (%s)\n", label, val, post); +} + +static inline void inspect_fw_phexdef(char *label, + uint32_t val, uint32_t defval) +{ + printf("%-23s: 0x%08x ", label, val); + + if (val == defval) { + printf("(== OpenWrt default)\n"); + } else { + printf("(OpenWrt default: 0x%08x)\n", defval); + } +} + +static inline void inspect_fw_phexexp(char *label, + uint32_t val, uint32_t expval) +{ + printf("%-23s: 0x%08x ", label, val); + + if (val == expval) { + printf("(ok)\n"); + } else { + printf("(expected: 0x%08x)\n", expval); + } +} + +static inline void inspect_fw_phexdec(char *label, uint32_t val) +{ + printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); +} + +static inline void inspect_fw_pchecksum(char *label, + uint16_t val, uint16_t expval) +{ + printf("%-23s: 0x%04x ", label, val); + if (val == expval) { + printf("(ok)\n"); + } else { + printf("(expected: 0x%04x)\n", expval); + } +} + +static int inspect_fw(void) +{ + uint8_t *buf; + struct fw_header *hdr; + int ret = EXIT_FAILURE; + uint16_t computed_checksum, file_checksum; + + buf = (uint8_t *) malloc(firmware_info.file_size); + if (!buf) { + ERR("no memory for buffer!\n"); + goto out; + } + + ret = read_to_buf(&firmware_info, buf); + if (ret) { + goto out_free_buf; + } + hdr = (struct fw_header *)buf; + + inspect_fw_pstr("File name", firmware_info.file_name); + inspect_fw_phexdec("File size", firmware_info.file_size); + + printf("\n"); + + inspect_fw_phexdec("Header size", sizeof (struct fw_header)); + board = find_board_by_hwid(LE32_TO_HOST(hdr->hw_id)); + if (board) { + layout = find_layout(board->layout_id); + inspect_fw_phexpost("Hardware ID", + LE32_TO_HOST( hdr->hw_id), board->id); + } else { + inspect_fw_phexpost("Hardware ID", + LE32_TO_HOST(hdr->hw_id), "unknown"); + } + inspect_fw_phexdec("Firmware data length", + LE32_TO_HOST(hdr->firmware_len)); + + inspect_fw_phexexp("Flags", + LE32_TO_HOST(hdr->flags), HEADER_FLAGS); + printf("\n"); + + /* XOR unobfuscate firmware */ + xor_fw(buf + sizeof (struct fw_header), LE32_TO_HOST(hdr->firmware_len) + 2); + + /* Compute firmware checksum */ + computed_checksum = checksum_fw(buf + sizeof (struct fw_header), LE32_TO_HOST(hdr->firmware_len)); + + /* Cannot use network order function because checksum is not word-aligned */ + file_checksum = (buf[firmware_info.file_size - 1] << 8) | buf[firmware_info.file_size - 2]; + inspect_fw_pchecksum("Firmware checksum", computed_checksum, file_checksum); + + /* Verify checksum */ + if (computed_checksum != file_checksum) { + ret = -1; + ERR("checksums do not match"); + goto out_free_buf; + } + + printf("\n"); + + if (extract) { + FILE *fp; + char *filename; + + if (ofname == NULL) { + filename = malloc(strlen(firmware_info.file_name) + 10); + sprintf(filename, "%s-firmware", firmware_info.file_name); + } else { + filename = ofname; + } + printf("Extracting firmware to \"%s\"...\n", filename); + fp = fopen(filename, "wb"); + if (fp) { + if (!fwrite(buf + sizeof (struct fw_header), + LE32_TO_HOST(hdr->firmware_len), 1, fp)) { + ERRS("error in fwrite(): %s", strerror(errno)); + } + fclose(fp); + } else { + ERRS("error in fopen(): %s", strerror(errno)); + } + if (ofname == NULL) { + free(filename); + } + printf("\n"); + } + + out_free_buf: + free(buf); + out: + return ret; +} + +/* + * Main entry point + */ +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + + int c; + + while ((c = getopt(argc, argv, "B:H:F:f:o:ixh")) != -1) { + switch (c) { + case 'B': + board_id = optarg; + break; + case 'H': + opt_hw_id = optarg; + break; + case 'F': + layout_id = optarg; + break; + case 'f': + firmware_info.file_name = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'i': + inspect = 1; + break; + case 'x': + inspect = 1; + extract = 1; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) { + goto out; + } + if (!inspect) { + ret = build_fw(); + } else { + ret = inspect_fw(); + } + + out: + return ret; +} diff --git a/tools/firmware-utils/src/mkrtn56uimg.c b/tools/firmware-utils/src/mkrtn56uimg.c new file mode 100644 index 00000000000..92aaf314a17 --- /dev/null +++ b/tools/firmware-utils/src/mkrtn56uimg.c @@ -0,0 +1,293 @@ +/* + * + * Copyright (C) 2014 OpenWrt.org + * Copyright (C) 2014 Mikko Hissa <mikko.hissa@werzek.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <unistd.h> +#include <zlib.h> + +#define IH_MAGIC 0x27051956 +#define IH_NMLEN 32 +#define IH_PRODLEN 23 + +#define IH_TYPE_INVALID 0 +#define IH_TYPE_STANDALONE 1 +#define IH_TYPE_KERNEL 2 +#define IH_TYPE_RAMDISK 3 +#define IH_TYPE_MULTI 4 +#define IH_TYPE_FIRMWARE 5 +#define IH_TYPE_SCRIPT 6 +#define IH_TYPE_FILESYSTEM 7 + +/* + * Compression Types + */ +#define IH_COMP_NONE 0 +#define IH_COMP_GZIP 1 +#define IH_COMP_BZIP2 2 +#define IH_COMP_LZMA 3 + +typedef struct { + uint8_t major; + uint8_t minor; +} version_t; + +typedef struct { + version_t kernel; + version_t fs; + uint8_t productid[IH_PRODLEN]; + uint8_t sub_fs; + uint32_t ih_ksz; +} asus_t; + +typedef struct image_header { + uint32_t ih_magic; + uint32_t ih_hcrc; + uint32_t ih_time; + uint32_t ih_size; + uint32_t ih_load; + uint32_t ih_ep; + uint32_t ih_dcrc; + uint8_t ih_os; + uint8_t ih_arch; + uint8_t ih_type; + uint8_t ih_comp; + union { + uint8_t ih_name[IH_NMLEN]; + asus_t asus; + } tail; +} image_header_t; + +typedef struct squashfs_sb { + uint32_t s_magic; + uint32_t pad0[9]; + uint64_t bytes_used; +} squashfs_sb_t; + +typedef enum { + NONE, FACTORY, SYSUPGRADE, +} op_mode_t; + +void +calc_crc(image_header_t *hdr, void *data, uint32_t len) +{ + /* + * Calculate payload checksum + */ + hdr->ih_dcrc = htonl(crc32(0, (Bytef *)data, len)); + hdr->ih_size = htonl(len); + /* + * Calculate header checksum + */ + hdr->ih_hcrc = 0; + hdr->ih_hcrc = htonl(crc32(0, (Bytef *)hdr, sizeof(image_header_t))); +} + + +static void +usage(const char *progname, int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + int i; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, "\n" + "Options:\n" + " -f <file> generate a factory flash image <file>\n" + " -s <file> generate a sysupgrade flash image <file>\n" + " -h show this screen\n"); + exit(status); +} + +int +process_image(char *progname, char *filename, op_mode_t opmode) +{ + int fd, len; + void *data, *ptr; + char namebuf[IH_NMLEN]; + struct stat sbuf; + uint32_t checksum, offset_kernel, offset_sqfs, offset_end, + offset_sec_header, offset_eb, offset_image_end; + squashfs_sb_t *sqs; + image_header_t *hdr; + + if ((fd = open(filename, O_RDWR, 0666)) < 0) { + fprintf (stderr, "%s: Can't open %s: %s\n", + progname, filename, strerror(errno)); + return (EXIT_FAILURE); + } + + if (fstat(fd, &sbuf) < 0) { + fprintf (stderr, "%s: Can't stat %s: %s\n", + progname, filename, strerror(errno)); + return (EXIT_FAILURE); + } + + if ((unsigned)sbuf.st_size < sizeof(image_header_t)) { + fprintf (stderr, + "%s: Bad size: \"%s\" is no valid image\n", + progname, filename); + return (EXIT_FAILURE); + } + + ptr = (void *)mmap(0, sbuf.st_size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, 0); + + if ((caddr_t)ptr == (caddr_t)-1) { + fprintf (stderr, "%s: Can't read %s: %s\n", + progname, filename, strerror(errno)); + return (EXIT_FAILURE); + } + + hdr = ptr; + + if (ntohl(hdr->ih_magic) != IH_MAGIC) { + fprintf (stderr, + "%s: Bad Magic Number: \"%s\" is no valid image\n", + progname, filename); + return (EXIT_FAILURE); + } + + if (opmode == FACTORY) { + strncpy(namebuf, hdr->tail.ih_name, IH_NMLEN); + hdr->tail.asus.kernel.major = 0; + hdr->tail.asus.kernel.minor = 0; + hdr->tail.asus.fs.major = 0; + hdr->tail.asus.fs.minor = 0; + strncpy((char *)&hdr->tail.asus.productid, "RT-N56U", IH_PRODLEN); + } + + if (hdr->tail.asus.ih_ksz == 0) + hdr->tail.asus.ih_ksz = htonl(ntohl(hdr->ih_size) + sizeof(image_header_t)); + + offset_kernel = sizeof(image_header_t); + offset_sqfs = ntohl(hdr->tail.asus.ih_ksz); + sqs = ptr + offset_sqfs; + offset_sec_header = offset_sqfs + sqs->bytes_used; + + /* + * Reserve space for the second header. + */ + offset_end = offset_sec_header + sizeof(image_header_t); + offset_eb = ((offset_end>>16)+1)<<16; + + if (opmode == FACTORY) + offset_image_end = offset_eb + 4; + else + offset_image_end = sbuf.st_size; + /* + * Move the second header at the end of the image. + */ + offset_end = offset_sec_header; + offset_sec_header = offset_eb - sizeof(image_header_t); + + /* + * Remove jffs2 markers between squashfs and eb boundary. + */ + if (opmode == FACTORY) + memset(ptr+offset_end, 0xff ,offset_eb - offset_end); + + /* + * Grow the image if needed. + */ + if (offset_image_end > sbuf.st_size) { + (void) munmap((void *)ptr, sbuf.st_size); + ftruncate(fd, offset_image_end); + ptr = (void *)mmap(0, offset_image_end, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, 0); + /* + * jffs2 marker + */ + if (opmode == FACTORY) { + *(uint8_t *)(ptr+offset_image_end-4) = 0xde; + *(uint8_t *)(ptr+offset_image_end-3) = 0xad; + *(uint8_t *)(ptr+offset_image_end-2) = 0xc0; + *(uint8_t *)(ptr+offset_image_end-1) = 0xde; + } + } + + /* + * Calculate checksums for the second header to be used after flashing. + */ + if (opmode == FACTORY) { + hdr = ptr+offset_sec_header; + memcpy(hdr, ptr, sizeof(image_header_t)); + strncpy(hdr->tail.ih_name, namebuf, IH_NMLEN); + calc_crc(hdr, ptr+offset_kernel, offset_sqfs - offset_kernel); + calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_image_end - offset_kernel); + } else { + calc_crc((image_header_t *)ptr, ptr+offset_kernel, offset_sqfs - offset_kernel); + } + + if (sbuf.st_size > offset_image_end) + (void) munmap((void *)ptr, sbuf.st_size); + else + (void) munmap((void *)ptr, offset_image_end); + + ftruncate(fd, offset_image_end); + (void) close (fd); + + return EXIT_SUCCESS; +} + +int +main(int argc, char **argv) +{ + int opt; + char *filename, *progname; + op_mode_t opmode = NONE; + + progname = argv[0]; + + while ((opt = getopt(argc, argv,":s:f:h?")) != -1) { + switch (opt) { + case 's': + opmode = SYSUPGRADE; + filename = optarg; + break; + case 'f': + opmode = FACTORY; + filename = optarg; + break; + case 'h': + opmode = NONE; + default: + usage(progname, EXIT_FAILURE); + opmode = NONE; + } + } + + if(filename == NULL) + opmode = NONE; + + switch (opmode) { + case NONE: + usage(progname, EXIT_FAILURE); + break; + case FACTORY: + case SYSUPGRADE: + return process_image(progname, filename, opmode); + break; + } + + return EXIT_SUCCESS; +} + diff --git a/tools/firmware-utils/src/mksenaofw.c b/tools/firmware-utils/src/mksenaofw.c new file mode 100644 index 00000000000..0f10ebdfbeb --- /dev/null +++ b/tools/firmware-utils/src/mksenaofw.c @@ -0,0 +1,420 @@ +/* + * + * Copyright (C) 2012 OpenWrt.org + * Copyright (C) 2012 Mikko Hissa <mikko.hissa@uta.fi> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <libgen.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include "md5.h" + +#define HDR_LEN 0x60 +#define BUF_SIZE 0x200 +#define VERSION_SIZE 0x10 +#define MD5_SIZE 0x10 +#define PAD_SIZE 0x20 + +#define DEFAULT_BLOCK_SIZE 65535 + +#define DEFAULT_HEAD_VALUE 0x0 +#define DEFAULT_VERSION "123" +#define DEFAULT_MAGIC 0x12345678 + +typedef struct { + uint32_t head; + uint32_t vendor_id; + uint32_t product_id; + uint8_t version[VERSION_SIZE]; + uint32_t firmware_type; + uint32_t filesize; + uint32_t zero; + uint8_t md5sum[MD5_SIZE]; + uint8_t pad[PAD_SIZE]; + uint32_t chksum; + uint32_t magic; +} img_header; + +typedef struct { + uint8_t id; + char * name; +} firmware_type; + +typedef enum { + NONE, ENCODE, DECODE +} op_mode; + +static firmware_type FIRMWARE_TYPES[] = { + { 0x01, "bootloader" }, + { 0x02, "kernel" }, + { 0x03, "kernelapp" }, + { 0x04, "apps" }, + /* The types below this line vary by manufacturer */ + { 0x05, "littleapps (D-Link)/factoryapps (EnGenius)" }, + { 0x06, "sounds (D-Link)/littleapps (EnGenius)" }, + { 0x07, "userconfig (D-Link)/appdata (EnGenius)" }, + { 0x08, "userconfig (EnGenius)"}, + { 0x09, "odmapps (EnGenius)"}, + { 0x0a, "factoryapps (D-Link)" }, + { 0x0b, "odmapps (D-Link)" }, + { 0x0c, "langpack (D-Link)" } +}; + +static long get_file_size(const char *filename) +{ + FILE *fp_file; + long result; + + fp_file = fopen(filename, "r"); + if (!fp_file) + return -1; + fseek(fp_file, 0, SEEK_END); + result = ftell(fp_file); + fclose(fp_file); + return result; +} + +static int header_checksum(void *data, int len) +{ + int i; + int sum; + + sum = 0; + if (data != NULL && len >= 0) { + for (i = 0; i < len; ++i) + sum += *(unsigned char *) (data + i); + return sum; + } + + return -1; +} + +static int md5_file(const char *filename, uint8_t *dst) +{ + FILE *fp_src; + MD5_CTX ctx; + char buf[BUF_SIZE]; + size_t bytes_read; + + MD5_Init(&ctx); + + fp_src = fopen(filename, "r+b"); + if (!fp_src) { + return -1; + } + while (!feof(fp_src)) { + bytes_read = fread(&buf, 1, BUF_SIZE, fp_src); + MD5_Update(&ctx, &buf, bytes_read); + } + fclose(fp_src); + + MD5_Final(dst, &ctx); + + return 0; +} + +static int encode_image(const char *input_file_name, + const char *output_file_name, img_header *header, int block_size) +{ + char buf[BUF_SIZE]; + size_t bytes_read; + size_t pad_len = 0; + size_t bytes_avail; + + FILE *fp_input; + FILE *fp_output; + + int i; + long magic; + + fp_input = fopen(input_file_name, "r+b"); + if (!fp_input) { + fprintf(stderr, "Cannot open %s !!\n", input_file_name); + return -1; + } + + fp_output = fopen(output_file_name, "w+b"); + if (!fp_output) { + fprintf(stderr, "Cannot open %s !!\n", output_file_name); + fclose(fp_input); + return -1; + } + + header->filesize = get_file_size(input_file_name); + if (!header->filesize) { + fprintf(stderr, "File %s open/size error!\n", input_file_name); + fclose(fp_input); + fclose(fp_output); + return -1; + } + /* + * Zero padding + */ + if (block_size > 0) { + pad_len = block_size - (header->filesize % block_size); + } + + if (md5_file(input_file_name, (uint8_t *) &header->md5sum) < 0) { + fprintf(stderr, "MD5 failed on file %s\n", input_file_name); + fclose(fp_input); + fclose(fp_output); + return -1; + } + header->zero = 0; + header->chksum = header_checksum(header, HDR_LEN); + header->head = htonl(header->head); + header->vendor_id = htonl(header->vendor_id); + header->product_id = htonl(header->product_id); + header->firmware_type = htonl(header->firmware_type); + header->filesize = htonl(header->filesize); + header->chksum = htonl(header->chksum); + magic = header->magic; + header->magic = htonl(header->magic); + + fwrite(header, HDR_LEN, 1, fp_output); + + while (!feof(fp_input) || pad_len > 0) { + + if (!feof(fp_input)) + bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); + else + bytes_read = 0; + + /* + * No more bytes read, start padding + */ + if (bytes_read < BUF_SIZE && pad_len > 0) { + bytes_avail = BUF_SIZE - bytes_read; + memset( &buf[bytes_read], 0, bytes_avail); + bytes_read += bytes_avail < pad_len ? bytes_avail : pad_len; + pad_len -= bytes_avail < pad_len ? bytes_avail : pad_len; + } + + for (i = 0; i < bytes_read; i++) + buf[i] ^= magic >> (i % 8) & 0xff; + fwrite(&buf, bytes_read, 1, fp_output); + } + + fclose(fp_input); + fclose(fp_output); + return 1; +} + +int decode_image(const char *input_file_name, const char *output_file_name) +{ + img_header header; + char buf[BUF_SIZE]; + + FILE *fp_input; + FILE *fp_output; + unsigned int i; + + size_t bytes_read; + size_t bytes_written; + + fp_input = fopen(input_file_name, "r+b"); + if (!fp_input) { + fprintf(stderr, "Cannot open %s !!\n", input_file_name); + fclose(fp_input); + return -1; + } + + fp_output = fopen(output_file_name, "w+b"); + if (!fp_output) { + fprintf(stderr, "Cannot open %s !!\n", output_file_name); + fclose(fp_output); + return -1; + } + + if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) { + fprintf(stderr, "Incorrect header size!!"); + fclose(fp_input); + fclose(fp_output); + return -1; + } + + header.head = ntohl(header.head); + header.vendor_id = ntohl(header.vendor_id); + header.product_id = ntohl(header.product_id); + header.firmware_type = ntohl(header.firmware_type); + header.filesize = ntohl(header.filesize); + header.chksum = ntohl(header.chksum); + header.magic = ntohl(header.magic); + + bytes_written = 0; + while (!feof(fp_input)) { + + bytes_read = fread(&buf, 1, BUF_SIZE, fp_input); + for (i = 0; i < bytes_read; i++) + buf[i] ^= header.magic >> (i % 8) & 0xff; + + /* + * Handle padded source file + */ + if (bytes_written + bytes_read > header.filesize) { + bytes_read = header.filesize - bytes_written; + if (bytes_read > 0) + fwrite(&buf, bytes_read, 1, fp_output); + break; + } + + fwrite(&buf, bytes_read, 1, fp_output); + bytes_written += bytes_read; + } + + fclose(fp_input); + fclose(fp_output); + + return 1; +} + +static void usage(const char *progname, int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + int i; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, "\n" + "Options:\n" + " -e <file> encode image file <file>\n" + " -d <file> decode image file <file>\n" + " -o <file> write output to the file <file>\n" + " -t <type> set image type to <type>\n" + " valid image <type> values:\n"); + for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); i++) { + fprintf(stream, " %-5i= %s\n", FIRMWARE_TYPES[i].id, + FIRMWARE_TYPES[i].name); + } + fprintf(stream, " -v <version> set image version to <version>\n" + " -r <vendor> set image vendor id to <vendor>\n" + " -p <product> set image product id to <product>\n" + " -m <magic> set encoding magic <magic>\n" + " -z enable image padding to <blocksize>\n" + " -b <blocksize> set image <blocksize>, defaults to %u\n" + " -h show this screen\n", DEFAULT_BLOCK_SIZE); + exit(status); +} + +int main(int argc, char *argv[]) +{ + int opt; + char *input_file, *output_file, *progname = NULL; + op_mode mode = NONE; + int tmp, i, pad = 0; + int block_size; + img_header header; + + block_size = DEFAULT_BLOCK_SIZE; + progname = basename(argv[0]); + + memset(&header, 0, sizeof( img_header )); + header.magic = DEFAULT_MAGIC; + header.head = DEFAULT_HEAD_VALUE; + strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1); + + while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:h?z")) != -1) { + switch (opt) { + case 'e': + input_file = optarg; + mode = ENCODE; + break; + case 'd': + input_file = optarg; + mode = DECODE; + break; + case 'o': + output_file = optarg; + break; + case 't': + tmp = strtol(optarg, 0, 10); + for (i = 0; i < sizeof(FIRMWARE_TYPES) / sizeof(firmware_type); + i++) { + if (FIRMWARE_TYPES[i].id == tmp) { + header.firmware_type = FIRMWARE_TYPES[i].id; + break; + } + } + if (header.firmware_type == 0) { + fprintf(stderr, "Invalid firmware type \"0\"!\n"); + usage(progname, EXIT_FAILURE); + } + break; + case 'v': + strncpy( (char*)&header.version, optarg, + VERSION_SIZE - 1); + break; + case 'r': + header.vendor_id = strtol(optarg, 0, 0); + break; + case 'p': + header.product_id = strtol(optarg, 0, 0); + break; + case 'm': + header.magic = strtoul(optarg, 0, 16); + break; + case 'z': + pad = 1; + break; + case 'b': + block_size = strtol(optarg, 0, 10); + break; + case 'h': + usage(progname, EXIT_SUCCESS); + break; + case ':': + fprintf(stderr, "Option -%c requires an operand\n", optopt); + usage(progname, EXIT_FAILURE); + break; + case '?': + fprintf(stderr, "Unrecognized option: -%c\n", optopt); + usage(progname, EXIT_FAILURE); + break; + default: + usage(progname, EXIT_FAILURE); + break; + } + } + + /* Check required arguments */ + if (mode == NONE) { + fprintf(stderr, "A mode must be defined\n"); + usage(progname, EXIT_FAILURE); + } + + if (input_file == NULL || output_file == NULL) { + fprintf(stderr, "Input and output files must be defined\n"); + usage(progname, EXIT_FAILURE); + } + + if (mode == DECODE) { + if (decode_image(input_file, output_file) < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; + } + + if (header.firmware_type == 0) { + fprintf(stderr, "Firmware type must be defined\n"); + usage(progname, EXIT_FAILURE); + } + + if (header.vendor_id == 0 || header.product_id == 0) { + fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n"); + usage(progname, EXIT_FAILURE); + } + + if (encode_image(input_file, output_file, &header, pad ? block_size : 0) < 0) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/tools/firmware-utils/src/mksercommfw.c b/tools/firmware-utils/src/mksercommfw.c new file mode 100644 index 00000000000..7f31d4f4c79 --- /dev/null +++ b/tools/firmware-utils/src/mksercommfw.c @@ -0,0 +1,255 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +/* #define DEBUG 1 */ + +/* + * Fw Header Layout for Netgear / Sercomm devices + * */ +static const char *magic = "sErCoMm"; /* 7 */ +/* 7-11: version control/download control ? */ +unsigned char version[4] = {0x00, 0x01, 0x00, 0x00}; +char *hwID = ""; /* 11-43 , ASCII/HEX */ +char *hwVer = ""; /* 44-57 , ASCII/HEX */ +char *swVer = ""; /* 58-62 , ASCII/HEX */ +/* magic again. */ + +#define HEADER_SIZE 71 + +/* null bytes until 511 */ +u_int32_t checksum = 0xFF; /* checksum */ +/* 512 onwards -> ZIP containing rootfs with the same Header */ + + +/* appended on rootfs for the Header. */ +const int footer_size = 128; + +struct file_info { + char *file_name; /* name of the file */ + char *file_data; /* data of the file in memory */ + u_int32_t file_size; /* length of the file */ +}; + +u_int8_t getCheckSum(char *data, int len) +{ + + int32_t previous = 0; + u_int32_t new = 0; + + for (u_int32_t i = 0; i < len; i++) { + new = (data[i] + previous) % 256; + previous = new | previous & -256; + } + return (u_int8_t) new; +} + +void *bufferFile(struct file_info *finfo, int dontload) +{ + int fs = 0; + FILE *f = NULL; + +#ifdef DEBUG + printf("Opening file: %s\n", finfo->file_name); +#endif + f = fopen(finfo->file_name, "rb"); + if (f == NULL) { + perror("Error"); + exit(1); + } + + fseek(f, 0L, SEEK_END); + fs = ftell(f); + rewind(f); + +#ifdef DEBUG + printf("Filesize: %i .\n", fs); +#endif + + finfo->file_size = fs; + + if (dontload) { + return 0; + } + + char *data = malloc(fs); + finfo->file_data = data; + + int read = fread(data, fs, 1, f); + + if (read != 1) { + printf("Error reading file %s.", finfo->file_name); + exit(1); + } + +#ifdef DEBUG + printf("File: read successfully %i bytes.\n", read*fs); +#endif + fclose(f); +} + +void *writeFile(struct file_info *finfo) +{ + +#ifdef DEBUG + printf("Writing file: %s.\n", finfo->file_name); +#endif + + FILE *fout = fopen(finfo->file_name, "w"); + + if (!fwrite(finfo->file_data, finfo->file_size, 1, fout)) { + printf("Wanted to write, but something went wrong.\n"); + fclose(fout); + exit(1); + } + fclose(fout); +} + +void *rmFile(struct file_info *finfo) +{ + remove(finfo->file_name); + free(finfo->file_data); + finfo->file_size = 0; +} + +void *usage(char *argv[]) +{ + printf("Usage: %s <sysupgradefile> <kernel_offset> <HWID> <HWVER> <SWID>\n" + "All are positional arguments ... \n" + " sysupgradefile: File with the kernel uimage at 0\n" + " kernel_offset: Offset in Hex where the kernel is located\n" + " HWID: Hardware ID, ASCII\n" + " HWVER: Hardware Version, ASCII\n" + " SWID: Software Version, Hex\n" + " \n" + " ", argv[0]); +} + +int main(int argc, char *argv[]) +{ + printf("Building fw image for sercomm devices.\n"); + + if (argc == 2) { + struct file_info myfile = {argv[1], 0, 0}; + bufferFile(&myfile, 0); + char chksum = getCheckSum(myfile.file_data, myfile.file_size); + printf("Checksum for File: %X.\n", chksum); + return 0; + } + + if (argc != 6) { + usage(argv); + return 1; + } + + /* Args */ + + struct file_info sysupgrade = {argv[1], 0, 0}; + bufferFile(&sysupgrade, 0); + + int kernel_offset = 0x90000; /* offset for the kernel inside the rootfs, default val */ + sscanf(argv[2], "%X", &kernel_offset); +#ifdef DEBUG + printf("Kernel_offset: at %X/%i bytes.\n", kernel_offset, kernel_offset); +#endif + char *hwID = argv[3]; + char *hwVer = argv[4]; + u_int32_t swVer = 0; + sscanf(argv[5],"%4X",&swVer); + swVer = bswap_32(swVer); + + char *rootfsname = malloc(2*strlen(sysupgrade.file_name) + 8); + sprintf(rootfsname, "%s.rootfs", sysupgrade.file_name); + + char *zipfsname = malloc(2*strlen(rootfsname) + 5); + sprintf(zipfsname, "%s.zip", rootfsname); + /* / Args */ + +#ifdef DEBUG + printf("Building header: %s %s %2X %s.\n", hwID , hwVer, swVer, magic); +#endif + /* Construct the firmware header/magic */ + struct file_info header = {0, 0, 0}; + header.file_size = HEADER_SIZE; + header.file_data = malloc(HEADER_SIZE); + bzero(header.file_data, header.file_size); + + char *tg = header.file_data; + strcpy(tg, magic); + memcpy(tg+7, version, 4*sizeof(char)); + strcpy(tg+11, hwID); + strcpy(tg+45, hwVer); + memcpy(tg+55, &swVer,sizeof(u_int32_t)); + strcpy(tg+63, magic); + +#ifdef DEBUG + printf("Header done, now creating rootfs."); +#endif + /* Construct a rootfs */ + struct file_info rootfs = {0, 0, 0}; + rootfs.file_size = sysupgrade.file_size + kernel_offset + footer_size; + rootfs.file_data = malloc(rootfs.file_size); + bzero(rootfs.file_data, rootfs.file_size); + rootfs.file_name = rootfsname; + + /* copy Owrt image to Kernel location */ + memcpy(rootfs.file_data+kernel_offset, sysupgrade.file_data, sysupgrade.file_size); + + /* 22 added to get away from sysup image, no other reason. + * updater searches for magic anyway */ + tg = rootfs.file_data + kernel_offset + sysupgrade.file_size+22; + + memcpy(tg, header.file_data, header.file_size); + writeFile(&rootfs); + +#ifdef DEBUG + printf("Preparing to zip.\n"); +#endif + /* now that we got the rootfs, repeat the whole thing again(sorta): + * 1. zip the rootfs */ + char *zipper = malloc(5 + 2*strlen(rootfs.file_name) + 4); + sprintf(zipper, "%s %s %s", "zip ", zipfsname, rootfs.file_name); + int ret = system(zipper); + + /* clear rootfs file */ + rmFile(&rootfs); + + /* and load zipped fs */ + struct file_info zippedfs = {zipfsname, 0, 0}; + bufferFile(&zippedfs, 0); + +#ifdef DEBUG + printf("Creating Image.\n"); +#endif + + /* 2. create new file 512+rootfs size */ + struct file_info image = {argv[1], 0, 0}; + image.file_data = malloc(zippedfs.file_size + 512); + image.file_size = zippedfs.file_size + 512; + + /* 3. copy zipfile at loc 512 */ + memcpy(image.file_data+512, zippedfs.file_data, zippedfs.file_size); + rmFile(&zippedfs); + + /* 4. add header to file */ + memcpy(image.file_data, header.file_data, header.file_size); + + /* 5. do a checksum run, and compute checksum */ + char chksum = getCheckSum(image.file_data, image.file_size); +#ifdef DEBUG + printf("Checksum for Image: %X.\n", chksum); +#endif + + /* 6. write the checksum invert into byte 511 to bring it to 0 */ + chksum = (chksum ^ 0xFF) + 1; + memcpy(image.file_data+511, &chksum, 1); + + chksum = getCheckSum(image.file_data, image.file_size); +#ifdef DEBUG + printf("Checksum for after fix: %X.\n", chksum); +#endif + /* 7. pray that the updater will accept the file */ + writeFile(&image); + return 0; +} diff --git a/tools/firmware-utils/src/mktplinkfw-lib.c b/tools/firmware-utils/src/mktplinkfw-lib.c new file mode 100644 index 00000000000..e3213274c19 --- /dev/null +++ b/tools/firmware-utils/src/mktplinkfw-lib.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * + * This tool was based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <stdbool.h> +#include <endian.h> +#include <errno.h> +#include <sys/stat.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include "mktplinkfw-lib.h" +#include "md5.h" + +extern char *ofname; +extern char *progname; +extern uint32_t kernel_len; +extern struct file_info kernel_info; +extern struct file_info rootfs_info; +extern struct flash_layout *layout; +extern uint32_t rootfs_ofs; +extern uint32_t rootfs_align; +extern int combined; +extern int strip_padding; +extern int add_jffs2_eof; + +static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; + +void fill_header(char *buf, int len); + +struct flash_layout *find_layout(struct flash_layout *layouts, const char *id) +{ + struct flash_layout *ret; + struct flash_layout *l; + + ret = NULL; + for (l = layouts; l->id != NULL; l++){ + if (strcasecmp(id, l->id) == 0) { + ret = l; + break; + } + }; + + return ret; +} + +void get_md5(const char *data, int size, uint8_t *md5) +{ + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, data, size); + MD5_Final(md5, &ctx); +} + +int get_file_stat(struct file_info *fdata) +{ + struct stat st; + int res; + + if (fdata->file_name == NULL) + return 0; + + res = stat(fdata->file_name, &st); + if (res){ + ERRS("stat failed on %s", fdata->file_name); + return res; + } + + fdata->file_size = st.st_size; + return 0; +} + +int read_to_buf(const struct file_info *fdata, char *buf) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(fdata->file_name, "r"); + if (f == NULL) { + ERRS("could not open \"%s\" for reading", fdata->file_name); + goto out; + } + + errno = 0; + fread(buf, fdata->file_size, 1, f); + if (errno != 0) { + ERRS("unable to read from file \"%s\"", fdata->file_name); + goto out_close; + } + + ret = EXIT_SUCCESS; + +out_close: + fclose(f); +out: + return ret; +} + +static int pad_jffs2(char *buf, int currlen, int maxlen) +{ + int len; + uint32_t pad_mask; + + len = currlen; + pad_mask = (4 * 1024) | (64 * 1024); /* EOF at 4KB and at 64KB */ + while ((len < maxlen) && (pad_mask != 0)) { + uint32_t mask; + int i; + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if (pad_mask & mask) + break; + } + + len = ALIGN(len, mask); + + for (i = 10; i < 32; i++) { + mask = 1 << i; + if ((len & (mask - 1)) == 0) + pad_mask &= ~mask; + } + + for (i = 0; i < sizeof(jffs2_eof_mark); i++) + buf[len + i] = jffs2_eof_mark[i]; + + len += sizeof(jffs2_eof_mark); + } + + return len; +} + +int write_fw(const char *ofname, const char *data, int len) +{ + FILE *f; + int ret = EXIT_FAILURE; + + f = fopen(ofname, "w"); + if (f == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + errno = 0; + fwrite(data, len, 1, f); + if (errno) { + ERRS("unable to write output file"); + goto out_flush; + } + + DBG("firmware file \"%s\" completed", ofname); + + ret = EXIT_SUCCESS; + +out_flush: + fflush(f); + fclose(f); + if (ret != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return ret; +} + +/* Helper functions to inspect_fw() representing different output formats */ +inline void inspect_fw_pstr(const char *label, const char *str) +{ + printf("%-23s: %s\n", label, str); +} + +inline void inspect_fw_phex(const char *label, uint32_t val) +{ + printf("%-23s: 0x%08x\n", label, val); +} + +inline void inspect_fw_phexdec(const char *label, uint32_t val) +{ + printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); +} + +inline void inspect_fw_pmd5sum(const char *label, const uint8_t *val, const char *text) +{ + int i; + + printf("%-23s:", label); + for (i=0; i<MD5SUM_LEN; i++) + printf(" %02x", val[i]); + printf(" %s\n", text); +} + +// header_size = sizeof(struct fw_header) +int build_fw(size_t header_size) +{ + int buflen; + char *buf; + char *p; + int ret = EXIT_FAILURE; + int writelen = 0; + + writelen = header_size + kernel_len; + + if (combined) + buflen = writelen; + else + buflen = layout->fw_max_len; + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + memset(buf, 0xff, buflen); + p = buf + header_size; + ret = read_to_buf(&kernel_info, p); + if (ret) + goto out_free_buf; + + if (!combined) { + p = buf + rootfs_ofs; + + ret = read_to_buf(&rootfs_info, p); + if (ret) + goto out_free_buf; + + writelen = rootfs_ofs + rootfs_info.file_size; + + if (add_jffs2_eof) + writelen = pad_jffs2(buf, writelen, layout->fw_max_len); + } + + if (!strip_padding) + writelen = buflen; + + fill_header(buf, writelen); + ret = write_fw(ofname, buf, writelen); + if (ret) + goto out_free_buf; + + ret = EXIT_SUCCESS; + +out_free_buf: + free(buf); +out: + return ret; +} diff --git a/tools/firmware-utils/src/mktplinkfw-lib.h b/tools/firmware-utils/src/mktplinkfw-lib.h new file mode 100644 index 00000000000..31e6d0b1e6b --- /dev/null +++ b/tools/firmware-utils/src/mktplinkfw-lib.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * + * This tool was based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + + +#ifndef mktplinkfw_lib_h +#define mktplinkfw_lib_h + +#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +#define MD5SUM_LEN 16 + +/* + * Message macros + */ +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define DBG(fmt, ...) do { \ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +struct flash_layout { + char *id; + uint32_t fw_max_len; + uint32_t kernel_la; + uint32_t kernel_ep; + uint32_t rootfs_ofs; +}; + +struct flash_layout *find_layout(struct flash_layout *layouts, const char *id); +void get_md5(const char *data, int size, uint8_t *md5); +int get_file_stat(struct file_info *fdata); +int read_to_buf(const struct file_info *fdata, char *buf); +int write_fw(const char *ofname, const char *data, int len); +inline void inspect_fw_pstr(const char *label, const char *str); +inline void inspect_fw_phex(const char *label, uint32_t val); +inline void inspect_fw_phexdec(const char *label, uint32_t val); +inline void inspect_fw_pmd5sum(const char *label, const uint8_t *val, const char *text); +int build_fw(size_t header_size); + +#endif /* mktplinkfw_lib_h */ diff --git a/tools/firmware-utils/src/mktplinkfw.c b/tools/firmware-utils/src/mktplinkfw.c index f8fa2f14b40..ce2acc20c93 100644 --- a/tools/firmware-utils/src/mktplinkfw.c +++ b/tools/firmware-utils/src/mktplinkfw.c @@ -19,6 +19,8 @@ #include <libgen.h> #include <getopt.h> /* for getopt() */ #include <stdarg.h> +#include <stdbool.h> +#include <endian.h> #include <errno.h> #include <sys/stat.h> @@ -26,37 +28,10 @@ #include <netinet/in.h> #include "md5.h" - -#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) +#include "mktplinkfw-lib.h" #define HEADER_VERSION_V1 0x01000000 -#define HWID_TL_MR3020_V1 0x30200001 -#define HWID_TL_MR3220_V1 0x32200001 -#define HWID_TL_MR3420_V1 0x34200001 -#define HWID_TL_WA701N_V1 0x07010001 -#define HWID_TL_WA901ND_V1 0x09010001 -#define HWID_TL_WA901ND_V2 0x09010002 -#define HWID_TL_WR703N_V1 0x07030101 -#define HWID_TL_WR741ND_V1 0x07410001 -#define HWID_TL_WR741ND_V4 0x07410004 -#define HWID_TL_WR740N_V1 0x07400001 -#define HWID_TL_WR740N_V3 0x07400003 -#define HWID_TL_WR743ND_V1 0x07430001 -#define HWID_TL_WR841N_V1_5 0x08410002 -#define HWID_TL_WR841ND_V3 0x08410003 -#define HWID_TL_WR841ND_V5 0x08410005 -#define HWID_TL_WR841ND_V7 0x08410007 -#define HWID_TL_WR941ND_V2 0x09410002 -#define HWID_TL_WR941ND_V4 0x09410004 -#define HWID_TL_WR1043ND_V1 0x10430001 -#define HWID_TL_WR2543N_V1 0x25430001 - -#define MD5SUM_LEN 16 - -struct file_info { - char *file_name; /* name of the file */ - uint32_t file_size; /* length of the file */ -}; +#define HEADER_VERSION_V2 0x02000000 struct fw_header { uint32_t version; /* header version */ @@ -64,7 +39,7 @@ struct fw_header { char fw_version[36]; uint32_t hw_id; /* hardware id */ uint32_t hw_rev; /* hardware revision */ - uint32_t unk1; + uint32_t region_code; /* region code */ uint8_t md5sum1[MD5SUM_LEN]; uint32_t unk2; uint8_t md5sum2[MD5SUM_LEN]; @@ -78,62 +53,68 @@ struct fw_header { uint32_t rootfs_len; /* rootfs data length */ uint32_t boot_ofs; /* bootloader data offset */ uint32_t boot_len; /* bootloader data length */ - uint8_t pad[360]; + uint16_t ver_hi; + uint16_t ver_mid; + uint16_t ver_lo; + uint8_t pad[130]; + char region_str1[32]; + char region_str2[32]; + uint8_t pad2[160]; } __attribute__ ((packed)); -struct flash_layout { - char *id; - uint32_t fw_max_len; - uint32_t kernel_la; - uint32_t kernel_ep; - uint32_t rootfs_ofs; +struct fw_region { + char name[4]; + uint32_t code; }; -struct board_info { - char *id; - uint32_t hw_id; - uint32_t hw_rev; - char *layout_id; -}; /* * Globals */ -static char *ofname; -static char *progname; +char *ofname; +char *progname; static char *vendor = "TP-LINK Technologies"; static char *version = "ver. 1.0"; +static char *fw_ver = "0.0.0"; +static uint32_t hdr_ver = HEADER_VERSION_V1; -static char *board_id; -static struct board_info *board; static char *layout_id; -static struct flash_layout *layout; +struct flash_layout *layout; static char *opt_hw_id; static uint32_t hw_id; static char *opt_hw_rev; static uint32_t hw_rev; -static struct file_info kernel_info; +static uint32_t opt_hdr_ver = 1; +static char *country; +static const struct fw_region *region; +static int fw_ver_lo; +static int fw_ver_mid; +static int fw_ver_hi; +struct file_info kernel_info; static uint32_t kernel_la = 0; static uint32_t kernel_ep = 0; -static uint32_t kernel_len = 0; -static struct file_info rootfs_info; -static uint32_t rootfs_ofs = 0; -static uint32_t rootfs_align; +uint32_t kernel_len = 0; +struct file_info rootfs_info; +uint32_t rootfs_ofs = 0; +uint32_t rootfs_align; static struct file_info boot_info; -static int combined; -static int strip_padding; -static int add_jffs2_eof; -static unsigned char jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; +int combined; +int strip_padding; +int add_jffs2_eof; +static uint32_t fw_max_len; +static uint32_t reserved_space; static struct file_info inspect_info; static int extract = 0; +static bool endian_swap = false; +static bool rootfs_ofs_calc = false; -char md5salt_normal[MD5SUM_LEN] = { +static const char md5salt_normal[MD5SUM_LEN] = { 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb, 0xdd, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x38, }; -char md5salt_boot[MD5SUM_LEN] = { +static const char md5salt_boot[MD5SUM_LEN] = { 0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa, 0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, }; @@ -164,268 +145,85 @@ static struct flash_layout layouts[] = { .kernel_ep = 0x80060000, .rootfs_ofs = 0x100000, }, { - /* terminating entry */ - } -}; - -static struct board_info boards[] = { - { - .id = "TL-MR3020v1", - .hw_id = HWID_TL_MR3020_V1, - .hw_rev = 1, - .layout_id = "4Mlzma", - }, { - .id = "TL-MR3220v1", - .hw_id = HWID_TL_MR3220_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-MR3420v1", - .hw_id = HWID_TL_MR3420_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WA701Nv1", - .hw_id = HWID_TL_WA701N_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WA901NDv1", - .hw_id = HWID_TL_WA901ND_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WA901NDv2", - .hw_id = HWID_TL_WA901ND_V2, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR741NDv1", - .hw_id = HWID_TL_WR741ND_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR741NDv4", - .hw_id = HWID_TL_WR741ND_V4, - .hw_rev = 1, - .layout_id = "4Mlzma", - }, { - .id = "TL-WR740Nv1", - .hw_id = HWID_TL_WR740N_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR740Nv3", - .hw_id = HWID_TL_WR740N_V3, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR743NDv1", - .hw_id = HWID_TL_WR743ND_V1, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR841Nv1.5", - .hw_id = HWID_TL_WR841N_V1_5, - .hw_rev = 2, - .layout_id = "4M", - }, { - .id = "TL-WR841NDv3", - .hw_id = HWID_TL_WR841ND_V3, - .hw_rev = 3, - .layout_id = "4M", - }, { - .id = "TL-WR841NDv5", - .hw_id = HWID_TL_WR841ND_V5, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR841NDv7", - .hw_id = HWID_TL_WR841ND_V7, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR941NDv2", - .hw_id = HWID_TL_WR941ND_V2, - .hw_rev = 2, - .layout_id = "4M", - }, { - .id = "TL-WR941NDv4", - .hw_id = HWID_TL_WR941ND_V4, - .hw_rev = 1, - .layout_id = "4M", - }, { - .id = "TL-WR1043NDv1", - .hw_id = HWID_TL_WR1043ND_V1, - .hw_rev = 1, - .layout_id = "8M", + .id = "16M", + .fw_max_len = 0xf80000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x140000, }, { - .id = "TL-WR2543Nv1", - .hw_id = HWID_TL_WR2543N_V1, - .hw_rev = 1, - .layout_id = "8Mlzma", + .id = "16Mlzma", + .fw_max_len = 0xf80000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x100000, }, { - .id = "TL-WR703Nv1", - .hw_id = HWID_TL_WR703N_V1, - .hw_rev = 1, - .layout_id = "4Mlzma", + .id = "16Mppc", + .fw_max_len = 0xf80000, + .kernel_la = 0x00000000 , + .kernel_ep = 0xc0000000, + .rootfs_ofs = 0x2a0000, }, { /* terminating entry */ } }; -/* - * Message macros - */ -#define ERR(fmt, ...) do { \ - fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ - progname, ## __VA_ARGS__ ); \ -} while (0) - -#define ERRS(fmt, ...) do { \ - int save = errno; \ - fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ - progname, ## __VA_ARGS__, strerror(save)); \ -} while (0) - -#define DBG(fmt, ...) do { \ - fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ -} while (0) - -static struct board_info *find_board(char *id) -{ - struct board_info *ret; - struct board_info *board; - - ret = NULL; - for (board = boards; board->id != NULL; board++){ - if (strcasecmp(id, board->id) == 0) { - ret = board; - break; - } - }; - - return ret; -} +static const struct fw_region regions[] = { + /* Default region (universal) uses code 0 as well */ + {"US", 1}, + {"EU", 0}, + {"BR", 0}, +}; -static struct board_info *find_board_by_hwid(uint32_t hw_id) -{ - struct board_info *board; +static const struct fw_region * find_region(const char *country) { + size_t i; - for (board = boards; board->id != NULL; board++) { - if (hw_id == board->hw_id) - return board; - }; + for (i = 0; i < ARRAY_SIZE(regions); i++) { + if (strcasecmp(regions[i].name, country) == 0) + return ®ions[i]; + } return NULL; } -static struct flash_layout *find_layout(char *id) -{ - struct flash_layout *ret; - struct flash_layout *l; - - ret = NULL; - for (l = layouts; l->id != NULL; l++){ - if (strcasecmp(id, l->id) == 0) { - ret = l; - break; - } - }; - - return ret; -} - static void usage(int status) { - FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; - struct board_info *board; - - fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); - fprintf(stream, + fprintf(stderr, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stderr, "\n" "Options:\n" -" -B <board> create image for the board specified with <board>\n" " -c use combined kernel image\n" +" -e swap endianness in kernel load address and entry point\n" " -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n" " -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n" " -H <hwid> use hardware id specified with <hwid>\n" +" -W <hwrev> use hardware revision specified with <hwrev>\n" +" -C <country> set region code to <country>\n" " -F <id> use flash layout specified with <id>\n" " -k <file> read kernel image from the file <file>\n" " -r <file> read rootfs image from the file <file>\n" " -a <align> align the rootfs start on an <align> bytes boundary\n" " -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n" +" -O calculate rootfs offset for combined images\n" " -o <file> write output to the file <file>\n" " -s strip padding from the end of the image\n" " -j add jffs2 end-of-filesystem markers\n" " -N <vendor> set image vendor to <vendor>\n" " -V <version> set image version to <version>\n" +" -v <version> set firmware version to <version>\n" +" -m <version> set header version to <version>\n" " -i <file> inspect given firmware file <file>\n" " -x extract kernel and rootfs while inspecting (requires -i)\n" +" -X <size> reserve <size> bytes in the firmware image (hexval prefixed with 0x)\n" " -h show this screen\n" ); exit(status); } -static int get_md5(char *data, int size, char *md5) -{ - MD5_CTX ctx; - - MD5_Init(&ctx); - MD5_Update(&ctx, data, size); - MD5_Final(md5, &ctx); -} - -static int get_file_stat(struct file_info *fdata) -{ - struct stat st; - int res; - - if (fdata->file_name == NULL) - return 0; - - res = stat(fdata->file_name, &st); - if (res){ - ERRS("stat failed on %s", fdata->file_name); - return res; - } - - fdata->file_size = st.st_size; - return 0; -} - -static int read_to_buf(struct file_info *fdata, char *buf) -{ - FILE *f; - int ret = EXIT_FAILURE; - - f = fopen(fdata->file_name, "r"); - if (f == NULL) { - ERRS("could not open \"%s\" for reading", fdata->file_name); - goto out; - } - - errno = 0; - fread(buf, fdata->file_size, 1, f); - if (errno != 0) { - ERRS("unable to read from file \"%s\"", fdata->file_name); - goto out_close; - } - - ret = EXIT_SUCCESS; - - out_close: - fclose(f); - out: - return ret; -} - static int check_options(void) { int ret; + int exceed_bytes; if (inspect_info.file_name) { ret = get_file_stat(&inspect_info); @@ -438,48 +236,55 @@ static int check_options(void) return -1; } - if (board_id == NULL && opt_hw_id == NULL) { - ERR("either board or hardware id must be specified"); + if (opt_hw_id == NULL) { + ERR("hardware id not specified"); + return -1; + } + hw_id = strtoul(opt_hw_id, NULL, 0); + + if (!combined && layout_id == NULL) { + ERR("flash layout is not specified"); return -1; } - if (board_id) { - board = find_board(board_id); - if (board == NULL) { - ERR("unknown/unsupported board id \"%s\"", board_id); + if (opt_hw_rev) + hw_rev = strtoul(opt_hw_rev, NULL, 0); + else + hw_rev = 1; + + if (country) { + region = find_region(country); + if (!region) { + ERR("unknown region code \"%s\"", country); return -1; } - if (layout_id == NULL) - layout_id = board->layout_id; + } - hw_id = board->hw_id; - hw_rev = board->hw_rev; + if (combined) { + if (!kernel_la || !kernel_ep) { + ERR("kernel loading address and entry point must be specified for combined image"); + return -1; + } } else { - if (layout_id == NULL) { - ERR("flash layout is not specified"); + layout = find_layout(layouts, layout_id); + if (layout == NULL) { + ERR("unknown flash layout \"%s\"", layout_id); return -1; } - hw_id = strtoul(opt_hw_id, NULL, 0); - if (opt_hw_rev) - hw_rev = strtoul(opt_hw_rev, NULL, 0); - else - hw_rev = 1; - } + if (!kernel_la) + kernel_la = layout->kernel_la; + if (!kernel_ep) + kernel_ep = layout->kernel_ep; + if (!rootfs_ofs) + rootfs_ofs = layout->rootfs_ofs; - layout = find_layout(layout_id); - if (layout == NULL) { - ERR("unknown flash layout \"%s\"", layout_id); - return -1; + if (reserved_space > layout->fw_max_len) { + ERR("reserved space is not valid"); + return -1; + } } - if (!kernel_la) - kernel_la = layout->kernel_la; - if (!kernel_ep) - kernel_ep = layout->kernel_ep; - if (!rootfs_ofs) - rootfs_ofs = layout->rootfs_ofs; - if (kernel_info.file_name == NULL) { ERR("no kernel image specified"); return -1; @@ -491,13 +296,9 @@ static int check_options(void) kernel_len = kernel_info.file_size; - if (combined) { - if (kernel_info.file_size > - layout->fw_max_len - sizeof(struct fw_header)) { - ERR("kernel image is too big"); - return -1; - } - } else { + if (!combined) { + fw_max_len = layout->fw_max_len - reserved_space; + if (rootfs_info.file_name == NULL) { ERR("no rootfs image specified"); return -1; @@ -509,26 +310,26 @@ static int check_options(void) if (rootfs_align) { kernel_len += sizeof(struct fw_header); - kernel_len = ALIGN(kernel_len, rootfs_align); + rootfs_ofs = ALIGN(kernel_len, rootfs_align); kernel_len -= sizeof(struct fw_header); - DBG("kernel length aligned to %u", kernel_len); + DBG("rootfs offset aligned to 0x%u", rootfs_ofs); - if (kernel_len + rootfs_info.file_size > - layout->fw_max_len - sizeof(struct fw_header)) { - ERR("images are too big"); + exceed_bytes = kernel_len + rootfs_info.file_size - (fw_max_len - sizeof(struct fw_header)); + if (exceed_bytes > 0) { + ERR("images are too big by %i bytes", exceed_bytes); return -1; } } else { - if (kernel_info.file_size > - rootfs_ofs - sizeof(struct fw_header)) { - ERR("kernel image is too big"); + exceed_bytes = kernel_info.file_size - (rootfs_ofs - sizeof(struct fw_header)); + if (exceed_bytes > 0) { + ERR("kernel image is too big by %i bytes", exceed_bytes); return -1; } - if (rootfs_info.file_size > - (layout->fw_max_len - rootfs_ofs)) { - ERR("rootfs image is too big"); + exceed_bytes = rootfs_info.file_size - (fw_max_len - rootfs_ofs); + if (exceed_bytes > 0) { + ERR("rootfs image is too big by %i bytes", exceed_bytes); return -1; } } @@ -539,227 +340,79 @@ static int check_options(void) return -1; } + ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo); + if (ret != 3) { + ERR("invalid firmware version '%s'", fw_ver); + return -1; + } + + if (opt_hdr_ver == 1) { + hdr_ver = HEADER_VERSION_V1; + } else if (opt_hdr_ver == 2) { + hdr_ver = HEADER_VERSION_V2; + } else { + ERR("invalid header version '%u'", opt_hdr_ver); + return -1; + } + return 0; } -static void fill_header(char *buf, int len) +void fill_header(char *buf, int len) { struct fw_header *hdr = (struct fw_header *)buf; memset(hdr, 0, sizeof(struct fw_header)); - hdr->version = htonl(HEADER_VERSION_V1); + hdr->version = htonl(hdr_ver); strncpy(hdr->vendor_name, vendor, sizeof(hdr->vendor_name)); strncpy(hdr->fw_version, version, sizeof(hdr->fw_version)); hdr->hw_id = htonl(hw_id); hdr->hw_rev = htonl(hw_rev); - if (boot_info.file_size == 0) - memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); - else - memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); - hdr->kernel_la = htonl(kernel_la); hdr->kernel_ep = htonl(kernel_ep); - hdr->fw_length = htonl(layout->fw_max_len); hdr->kernel_ofs = htonl(sizeof(struct fw_header)); hdr->kernel_len = htonl(kernel_len); + if (!combined) { + if (boot_info.file_size == 0) + memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); + else + memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); + + hdr->fw_length = htonl(layout->fw_max_len); hdr->rootfs_ofs = htonl(rootfs_ofs); hdr->rootfs_len = htonl(rootfs_info.file_size); } - get_md5(buf, len, hdr->md5sum1); -} - -static int pad_jffs2(char *buf, int currlen) -{ - int len; - uint32_t pad_mask; - - len = currlen; - pad_mask = (64 * 1024); - while ((len < layout->fw_max_len) && (pad_mask != 0)) { - uint32_t mask; - int i; - - for (i = 10; i < 32; i++) { - mask = 1 << i; - if (pad_mask & mask) - break; - } - - len = ALIGN(len, mask); - - for (i = 10; i < 32; i++) { - mask = 1 << i; - if ((len & (mask - 1)) == 0) - pad_mask &= ~mask; - } - - for (i = 0; i < sizeof(jffs2_eof_mark); i++) - buf[len + i] = jffs2_eof_mark[i]; - - len += sizeof(jffs2_eof_mark); - } - - return len; -} - -static int write_fw(char *data, int len) -{ - FILE *f; - int ret = EXIT_FAILURE; - - f = fopen(ofname, "w"); - if (f == NULL) { - ERRS("could not open \"%s\" for writing", ofname); - goto out; - } - - errno = 0; - fwrite(data, len, 1, f); - if (errno) { - ERRS("unable to write output file"); - goto out_flush; - } - - DBG("firmware file \"%s\" completed", ofname); - - ret = EXIT_SUCCESS; - - out_flush: - fflush(f); - fclose(f); - if (ret != EXIT_SUCCESS) { - unlink(ofname); + if (combined && rootfs_ofs_calc) { + hdr->rootfs_ofs = htonl(sizeof(struct fw_header) + kernel_len); } - out: - return ret; -} -static int build_fw(void) -{ - int buflen; - char *buf; - char *p; - int ret = EXIT_FAILURE; - int writelen = 0; - - buflen = layout->fw_max_len; - - buf = malloc(buflen); - if (!buf) { - ERR("no memory for buffer\n"); - goto out; + hdr->ver_hi = htons(fw_ver_hi); + hdr->ver_mid = htons(fw_ver_mid); + hdr->ver_lo = htons(fw_ver_lo); + + if (region) { + hdr->region_code = htonl(region->code); + snprintf( + hdr->region_str1, sizeof(hdr->region_str1), "00000000;%02X%02X%02X%02X;", + region->name[0], region->name[1], region->name[2], region->name[3] + ); + snprintf( + hdr->region_str2, sizeof(hdr->region_str2), "%02X%02X%02X%02X", + region->name[0], region->name[1], region->name[2], region->name[3] + ); } - memset(buf, 0xff, buflen); - p = buf + sizeof(struct fw_header); - ret = read_to_buf(&kernel_info, p); - if (ret) - goto out_free_buf; - - writelen = sizeof(struct fw_header) + kernel_len; - - if (!combined) { - if (rootfs_align) - p = buf + writelen; - else - p = buf + rootfs_ofs; - - ret = read_to_buf(&rootfs_info, p); - if (ret) - goto out_free_buf; - - if (rootfs_align) - writelen += rootfs_info.file_size; - else - writelen = rootfs_ofs + rootfs_info.file_size; - - if (add_jffs2_eof) - writelen = pad_jffs2(buf, writelen); + if (endian_swap) { + hdr->kernel_la = bswap_32(hdr->kernel_la); + hdr->kernel_ep = bswap_32(hdr->kernel_ep); } - if (!strip_padding) - writelen = buflen; - - fill_header(buf, writelen); - ret = write_fw(buf, writelen); - if (ret) - goto out_free_buf; - - ret = EXIT_SUCCESS; - - out_free_buf: - free(buf); - out: - return ret; -} - -/* Helper functions to inspect_fw() representing different output formats */ -static inline void inspect_fw_pstr(char *label, char *str) -{ - printf("%-23s: %s\n", label, str); -} - -static inline void inspect_fw_phex(char *label, uint32_t val) -{ - printf("%-23s: 0x%08x\n", label, val); -} - -static inline void inspect_fw_phexpost(char *label, - uint32_t val, char *post) -{ - printf("%-23s: 0x%08x (%s)\n", label, val, post); -} - -static inline void inspect_fw_phexdef(char *label, - uint32_t val, uint32_t defval) -{ - printf("%-23s: 0x%08x ", label, val); - - if (val == defval) - printf("(== OpenWrt default)\n"); - else - printf("(OpenWrt default: 0x%08x)\n", defval); -} - -static inline void inspect_fw_phexexp(char *label, - uint32_t val, uint32_t expval) -{ - printf("%-23s: 0x%08x ", label, val); - - if (val == expval) - printf("(ok)\n"); - else - printf("(expected: 0x%08x)\n", expval); -} - -static inline void inspect_fw_phexdec(char *label, uint32_t val) -{ - printf("%-23s: 0x%08x / %8u bytes\n", label, val, val); -} - -static inline void inspect_fw_phexdecdef(char *label, - uint32_t val, uint32_t defval) -{ - printf("%-23s: 0x%08x / %8u bytes ", label, val, val); - - if (val == defval) - printf("(== OpenWrt default)\n"); - else - printf("(OpenWrt default: 0x%08x)\n", defval); -} - -static inline void inspect_fw_pmd5sum(char *label, uint8_t *val, char *text) -{ - int i; - - printf("%-23s:", label); - for (i=0; i<MD5SUM_LEN; i++) - printf(" %02x", val[i]); - printf(" %s\n", text); + if (!combined) + get_md5(buf, len, hdr->md5sum1); } static int inspect_fw(void) @@ -767,7 +420,6 @@ static int inspect_fw(void) char *buf; struct fw_header *hdr; uint8_t md5sum[MD5SUM_LEN]; - struct board_info *board; int ret = EXIT_FAILURE; buf = malloc(inspect_info.file_size); @@ -784,16 +436,14 @@ static int inspect_fw(void) inspect_fw_pstr("File name", inspect_info.file_name); inspect_fw_phexdec("File size", inspect_info.file_size); - if (ntohl(hdr->version) != HEADER_VERSION_V1) { - ERR("file does not seem to have V1 header!\n"); + if ((ntohl(hdr->version) != HEADER_VERSION_V1) && + (ntohl(hdr->version) != HEADER_VERSION_V2)) { + ERR("file does not seem to have V1/V2 header!\n"); goto out_free_buf; } inspect_fw_phexdec("Version 1 Header size", sizeof(struct fw_header)); - if (ntohl(hdr->unk1) != 0) - inspect_fw_phexdec("Unknown value 1", hdr->unk1); - memcpy(md5sum, hdr->md5sum1, sizeof(md5sum)); if (ntohl(hdr->boot_len) == 0) memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum)); @@ -818,19 +468,9 @@ static int inspect_fw(void) inspect_fw_pstr("Vendor name", hdr->vendor_name); inspect_fw_pstr("Firmware version", hdr->fw_version); - board = find_board_by_hwid(ntohl(hdr->hw_id)); - if (board) { - layout = find_layout(board->layout_id); - inspect_fw_phexpost("Hardware ID", - ntohl(hdr->hw_id), board->id); - inspect_fw_phexexp("Hardware Revision", - ntohl(hdr->hw_rev), board->hw_rev); - } else { - inspect_fw_phexpost("Hardware ID", - ntohl(hdr->hw_id), "unknown"); - inspect_fw_phex("Hardware Revision", - ntohl(hdr->hw_rev)); - } + inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id)); + inspect_fw_phex("Hardware Revision", ntohl(hdr->hw_rev)); + inspect_fw_phex("Region code", ntohl(hdr->region_code)); printf("\n"); @@ -838,24 +478,12 @@ static int inspect_fw(void) ntohl(hdr->kernel_ofs)); inspect_fw_phexdec("Kernel data length", ntohl(hdr->kernel_len)); - if (board) { - inspect_fw_phexdef("Kernel load address", - ntohl(hdr->kernel_la), - layout ? layout->kernel_la : 0xffffffff); - inspect_fw_phexdef("Kernel entry point", - ntohl(hdr->kernel_ep), - layout ? layout->kernel_ep : 0xffffffff); - inspect_fw_phexdecdef("Rootfs data offset", - ntohl(hdr->rootfs_ofs), - layout ? layout->rootfs_ofs : 0xffffffff); - } else { - inspect_fw_phex("Kernel load address", - ntohl(hdr->kernel_la)); - inspect_fw_phex("Kernel entry point", - ntohl(hdr->kernel_ep)); - inspect_fw_phexdec("Rootfs data offset", - ntohl(hdr->rootfs_ofs)); - } + inspect_fw_phex("Kernel load address", + ntohl(hdr->kernel_la)); + inspect_fw_phex("Kernel entry point", + ntohl(hdr->kernel_ep)); + inspect_fw_phexdec("Rootfs data offset", + ntohl(hdr->rootfs_ofs)); inspect_fw_phexdec("Rootfs data length", ntohl(hdr->rootfs_len)); inspect_fw_phexdec("Boot loader data offset", @@ -911,16 +539,13 @@ static int inspect_fw(void) int main(int argc, char *argv[]) { int ret = EXIT_FAILURE; - int err; - - FILE *outfile; progname = basename(argv[0]); while ( 1 ) { int c; - c = getopt(argc, argv, "a:B:H:E:F:L:V:N:W:ci:k:r:R:o:xhsj"); + c = getopt(argc, argv, "a:H:E:F:L:m:V:N:W:C:ci:k:r:R:o:OxX:ehsjv:"); if (c == -1) break; @@ -928,9 +553,6 @@ int main(int argc, char *argv[]) case 'a': sscanf(optarg, "0x%x", &rootfs_align); break; - case 'B': - board_id = optarg; - break; case 'H': opt_hw_id = optarg; break; @@ -943,12 +565,21 @@ int main(int argc, char *argv[]) case 'W': opt_hw_rev = optarg; break; + case 'C': + country = optarg; + break; case 'L': sscanf(optarg, "0x%x", &kernel_la); break; + case 'm': + sscanf(optarg, "%u", &opt_hdr_ver); + break; case 'V': version = optarg; break; + case 'v': + fw_ver = optarg; + break; case 'N': vendor = optarg; break; @@ -967,6 +598,9 @@ int main(int argc, char *argv[]) case 'o': ofname = optarg; break; + case 'O': + rootfs_ofs_calc = 1; + break; case 's': strip_padding = 1; break; @@ -979,9 +613,15 @@ int main(int argc, char *argv[]) case 'x': extract = 1; break; + case 'e': + endian_swap = true; + break; case 'h': usage(EXIT_SUCCESS); break; + case 'X': + sscanf(optarg, "0x%x", &reserved_space); + break; default: usage(EXIT_FAILURE); break; @@ -993,11 +633,10 @@ int main(int argc, char *argv[]) goto out; if (!inspect_info.file_name) - ret = build_fw(); + ret = build_fw(sizeof(struct fw_header)); else ret = inspect_fw(); out: return ret; } - diff --git a/tools/firmware-utils/src/mktplinkfw2.c b/tools/firmware-utils/src/mktplinkfw2.c new file mode 100644 index 00000000000..dead49e7af8 --- /dev/null +++ b/tools/firmware-utils/src/mktplinkfw2.c @@ -0,0 +1,628 @@ +/* + * Copyright (C) 2009 Gabor Juhos <juhosg@openwrt.org> + * + * This tool was based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian <lark@linux.net.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> /* for unlink() */ +#include <libgen.h> +#include <getopt.h> /* for getopt() */ +#include <stdarg.h> +#include <errno.h> +#include <stdbool.h> +#include <endian.h> +#include <sys/stat.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include "md5.h" +#include "mktplinkfw-lib.h" + +struct fw_header { + uint32_t version; /* 0x00: header version */ + char fw_version[48]; /* 0x04: fw version string */ + uint32_t hw_id; /* 0x34: hardware id */ + uint32_t hw_rev; /* 0x38: FIXME: hardware revision? */ + uint32_t hw_ver_add; /* 0x3c: additional hardware version */ + uint8_t md5sum1[MD5SUM_LEN]; /* 0x40 */ + uint32_t unk2; /* 0x50: 0x00000000 */ + uint8_t md5sum2[MD5SUM_LEN]; /* 0x54 */ + uint32_t unk3; /* 0x64: 0xffffffff */ + + uint32_t kernel_la; /* 0x68: kernel load address */ + uint32_t kernel_ep; /* 0x6c: kernel entry point */ + uint32_t fw_length; /* 0x70: total length of the image */ + uint32_t kernel_ofs; /* 0x74: kernel data offset */ + uint32_t kernel_len; /* 0x78: kernel data length */ + uint32_t rootfs_ofs; /* 0x7c: rootfs data offset */ + uint32_t rootfs_len; /* 0x80: rootfs data length */ + uint32_t boot_ofs; /* 0x84: bootloader offset */ + uint32_t boot_len; /* 0x88: bootloader length */ + uint16_t unk4; /* 0x8c: 0x55aa */ + uint8_t sver_hi; /* 0x8e */ + uint8_t sver_lo; /* 0x8f */ + uint8_t unk5; /* 0x90: magic: 0xa5 */ + uint8_t ver_hi; /* 0x91 */ + uint8_t ver_mid; /* 0x92 */ + uint8_t ver_lo; /* 0x93 */ + uint8_t pad[364]; +} __attribute__ ((packed)); + +#define FLAG_LE_KERNEL_LA_EP 0x00000001 /* Little-endian used for kernel load address & entry point */ + +struct board_info { + char *id; + uint32_t hw_id; + uint32_t hw_rev; + uint32_t hw_ver_add; + char *layout_id; + uint32_t hdr_ver; + uint32_t flags; +}; + +/* + * Globals + */ +char *ofname; +char *progname; +static char *vendor = "TP-LINK Technologies"; +static char *version = "ver. 1.0"; +static char *fw_ver = "0.0.0"; +static char *sver = "1.0"; +static uint32_t hdr_ver = 2; + +static struct board_info custom_board; + +static struct board_info *board; +static char *layout_id; +struct flash_layout *layout; +static char *opt_hw_id; +static char *opt_hw_rev; +static char *opt_hw_ver_add; +static int fw_ver_lo; +static int fw_ver_mid; +static int fw_ver_hi; +static int sver_lo; +static int sver_hi; +struct file_info kernel_info; +static uint32_t kernel_la = 0; +static uint32_t kernel_ep = 0; +uint32_t kernel_len = 0; +struct file_info rootfs_info; +uint32_t rootfs_ofs = 0; +uint32_t rootfs_align; +static struct file_info boot_info; +int combined; +int strip_padding; +int add_jffs2_eof; + +static struct file_info inspect_info; +static int extract = 0; + +char md5salt_normal[MD5SUM_LEN] = { + 0xdc, 0xd7, 0x3a, 0xa5, 0xc3, 0x95, 0x98, 0xfb, + 0xdc, 0xf9, 0xe7, 0xf4, 0x0e, 0xae, 0x47, 0x37, +}; + +char md5salt_boot[MD5SUM_LEN] = { + 0x8c, 0xef, 0x33, 0x5f, 0xd5, 0xc5, 0xce, 0xfa, + 0xac, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, +}; + +static struct flash_layout layouts[] = { + { + .id = "4Mmtk", + .fw_max_len = 0x3d0000, + .kernel_la = 0x80000000, + .kernel_ep = 0x80000000, + .rootfs_ofs = 0x140000, + }, { + .id = "8Mltq", + .fw_max_len = 0x7a0000, + .kernel_la = 0x80002000, + .kernel_ep = 0x80002000, + .rootfs_ofs = 0x140000, + }, { + .id = "16Mltq", + .fw_max_len = 0xf90000, + .kernel_la = 0x80002000, + .kernel_ep = 0x800061b0, + .rootfs_ofs = 0x140000, + }, { + .id = "8Mmtk", + .fw_max_len = 0x7a0000, + .kernel_la = 0x80000000, + .kernel_ep = 0x80000000, + .rootfs_ofs = 0x140000, + }, { + .id = "8MLmtk", + .fw_max_len = 0x7b0000, + .kernel_la = 0x80000000, + .kernel_ep = 0x80000000, + .rootfs_ofs = 0x140000, + }, { + /* terminating entry */ + } +}; + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + struct board_info *board; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -c use combined kernel image\n" +" -e swap endianness in kernel load address and entry point\n" +" -E <ep> overwrite kernel entry point with <ep> (hexval prefixed with 0x)\n" +" -L <la> overwrite kernel load address with <la> (hexval prefixed with 0x)\n" +" -H <hwid> use hardware id specified with <hwid>\n" +" -W <hwrev> use hardware revision specified with <hwrev>\n" +" -w <hwveradd> use additional hardware version specified with <hwveradd>\n" +" -F <id> use flash layout specified with <id>\n" +" -k <file> read kernel image from the file <file>\n" +" -r <file> read rootfs image from the file <file>\n" +" -a <align> align the rootfs start on an <align> bytes boundary\n" +" -R <offset> overwrite rootfs offset with <offset> (hexval prefixed with 0x)\n" +" -o <file> write output to the file <file>\n" +" -s strip padding from the end of the image\n" +" -j add jffs2 end-of-filesystem markers\n" +" -N <vendor> set image vendor to <vendor>\n" +" -T <version> set header version to <version>\n" +" -V <version> set image version to <version>\n" +" -v <version> set firmware version to <version>\n" +" -y <version> set secondary version to <version>\n" +" -i <file> inspect given firmware file <file>\n" +" -x extract kernel and rootfs while inspecting (requires -i)\n" +" -h show this screen\n" + ); + + exit(status); +} + +static int check_options(void) +{ + int ret; + + if (inspect_info.file_name) { + ret = get_file_stat(&inspect_info); + if (ret) + return ret; + + return 0; + } else if (extract) { + ERR("no firmware for inspection specified"); + return -1; + } + + if (opt_hw_id == NULL) { + ERR("hardware id must be specified"); + return -1; + } + + board = &custom_board; + + if (layout_id == NULL) { + ERR("flash layout is not specified"); + return -1; + } + + board->hw_id = strtoul(opt_hw_id, NULL, 0); + + board->hw_rev = 1; + board->hw_ver_add = 0; + + if (opt_hw_rev) + board->hw_rev = strtoul(opt_hw_rev, NULL, 0); + if (opt_hw_ver_add) + board->hw_ver_add = strtoul(opt_hw_ver_add, NULL, 0); + + layout = find_layout(layouts, layout_id); + if (layout == NULL) { + ERR("unknown flash layout \"%s\"", layout_id); + return -1; + } + + if (!kernel_la) + kernel_la = layout->kernel_la; + if (!kernel_ep) + kernel_ep = layout->kernel_ep; + if (!rootfs_ofs) + rootfs_ofs = layout->rootfs_ofs; + + if (kernel_info.file_name == NULL) { + ERR("no kernel image specified"); + return -1; + } + + ret = get_file_stat(&kernel_info); + if (ret) + return ret; + + kernel_len = kernel_info.file_size; + + if (combined) { + if (kernel_info.file_size > + layout->fw_max_len - sizeof(struct fw_header)) { + ERR("kernel image is too big"); + return -1; + } + } else { + if (rootfs_info.file_name == NULL) { + ERR("no rootfs image specified"); + return -1; + } + + ret = get_file_stat(&rootfs_info); + if (ret) + return ret; + + if (rootfs_align) { + kernel_len += sizeof(struct fw_header); + rootfs_ofs = ALIGN(kernel_len, rootfs_align); + kernel_len -= sizeof(struct fw_header); + + DBG("rootfs offset aligned to 0x%u", rootfs_ofs); + + if (kernel_len + rootfs_info.file_size > + layout->fw_max_len - sizeof(struct fw_header)) { + ERR("images are too big"); + return -1; + } + } else { + if (kernel_info.file_size > + rootfs_ofs - sizeof(struct fw_header)) { + ERR("kernel image is too big"); + return -1; + } + + if (rootfs_info.file_size > + (layout->fw_max_len - rootfs_ofs)) { + ERR("rootfs image is too big"); + return -1; + } + } + } + + if (ofname == NULL) { + ERR("no output file specified"); + return -1; + } + + ret = sscanf(fw_ver, "%d.%d.%d", &fw_ver_hi, &fw_ver_mid, &fw_ver_lo); + if (ret != 3) { + ERR("invalid firmware version '%s'", fw_ver); + return -1; + } + + ret = sscanf(sver, "%d.%d", &sver_hi, &sver_lo); + if (ret != 2) { + ERR("invalid secondary version '%s'", sver); + return -1; + } + + return 0; +} + +void fill_header(char *buf, int len) +{ + struct fw_header *hdr = (struct fw_header *)buf; + unsigned ver_len; + + memset(hdr, '\xff', sizeof(struct fw_header)); + + hdr->version = htonl(bswap_32(hdr_ver)); + ver_len = strlen(version); + if (ver_len > (sizeof(hdr->fw_version) - 1)) + ver_len = sizeof(hdr->fw_version) - 1; + + memcpy(hdr->fw_version, version, ver_len); + hdr->fw_version[ver_len] = 0; + + hdr->hw_id = htonl(board->hw_id); + hdr->hw_rev = htonl(board->hw_rev); + hdr->hw_ver_add = htonl(board->hw_ver_add); + + if (boot_info.file_size == 0) { + memcpy(hdr->md5sum1, md5salt_normal, sizeof(hdr->md5sum1)); + hdr->boot_ofs = htonl(0); + hdr->boot_len = htonl(0); + } else { + memcpy(hdr->md5sum1, md5salt_boot, sizeof(hdr->md5sum1)); + hdr->boot_ofs = htonl(rootfs_ofs + rootfs_info.file_size); + hdr->boot_len = htonl(rootfs_info.file_size); + } + + hdr->kernel_la = htonl(kernel_la); + hdr->kernel_ep = htonl(kernel_ep); + hdr->fw_length = htonl(layout->fw_max_len); + hdr->kernel_ofs = htonl(sizeof(struct fw_header)); + hdr->kernel_len = htonl(kernel_len); + if (!combined) { + hdr->rootfs_ofs = htonl(rootfs_ofs); + hdr->rootfs_len = htonl(rootfs_info.file_size); + } + + hdr->boot_ofs = htonl(0); + hdr->boot_len = htonl(boot_info.file_size); + + hdr->unk2 = htonl(0); + hdr->unk3 = htonl(0xffffffff); + hdr->unk4 = htons(0x55aa); + hdr->unk5 = 0xa5; + + hdr->sver_hi = sver_hi; + hdr->sver_lo = sver_lo; + + hdr->ver_hi = fw_ver_hi; + hdr->ver_mid = fw_ver_mid; + hdr->ver_lo = fw_ver_lo; + + if (board->flags & FLAG_LE_KERNEL_LA_EP) { + hdr->kernel_la = bswap_32(hdr->kernel_la); + hdr->kernel_ep = bswap_32(hdr->kernel_ep); + } + + get_md5(buf, len, hdr->md5sum1); +} + +static int inspect_fw(void) +{ + char *buf; + struct fw_header *hdr; + uint8_t md5sum[MD5SUM_LEN]; + struct board_info *board; + int ret = EXIT_FAILURE; + + buf = malloc(inspect_info.file_size); + if (!buf) { + ERR("no memory for buffer!\n"); + goto out; + } + + ret = read_to_buf(&inspect_info, buf); + if (ret) + goto out_free_buf; + hdr = (struct fw_header *)buf; + + board = &custom_board; + + if (board->flags & FLAG_LE_KERNEL_LA_EP) { + hdr->kernel_la = bswap_32(hdr->kernel_la); + hdr->kernel_ep = bswap_32(hdr->kernel_ep); + } + + inspect_fw_pstr("File name", inspect_info.file_name); + inspect_fw_phexdec("File size", inspect_info.file_size); + + switch(bswap_32(ntohl(hdr->version))) { + case 2: + case 3: + break; + default: + ERR("file does not seem to have V2/V3 header!\n"); + goto out_free_buf; + } + + inspect_fw_phexdec("Version 2 Header size", sizeof(struct fw_header)); + + memcpy(md5sum, hdr->md5sum1, sizeof(md5sum)); + if (ntohl(hdr->boot_len) == 0) + memcpy(hdr->md5sum1, md5salt_normal, sizeof(md5sum)); + else + memcpy(hdr->md5sum1, md5salt_boot, sizeof(md5sum)); + get_md5(buf, inspect_info.file_size, hdr->md5sum1); + + if (memcmp(md5sum, hdr->md5sum1, sizeof(md5sum))) { + inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(*ERROR*)"); + inspect_fw_pmd5sum(" --> expected", hdr->md5sum1, ""); + } else { + inspect_fw_pmd5sum("Header MD5Sum1", md5sum, "(ok)"); + } + if (ntohl(hdr->unk2) != 0) + inspect_fw_phexdec("Unknown value 2", hdr->unk2); + inspect_fw_pmd5sum("Header MD5Sum2", hdr->md5sum2, + "(purpose yet unknown, unchecked here)"); + + if (ntohl(hdr->unk3) != 0xffffffff) + inspect_fw_phexdec("Unknown value 3", hdr->unk3); + + if (ntohs(hdr->unk4) != 0x55aa) + inspect_fw_phexdec("Unknown value 4", hdr->unk4); + + if (hdr->unk5 != 0xa5) + inspect_fw_phexdec("Unknown value 5", hdr->unk5); + + printf("\n"); + + inspect_fw_pstr("Firmware version", hdr->fw_version); + inspect_fw_phex("Hardware ID", ntohl(hdr->hw_id)); + inspect_fw_phex("Hardware Revision", + ntohl(hdr->hw_rev)); + inspect_fw_phex("Additional HW Version", + ntohl(hdr->hw_ver_add)); + + printf("%-23s: %d.%d.%d-%d.%d\n", "Software version", + hdr->ver_hi, hdr->ver_mid, hdr->ver_lo, + hdr->sver_hi, hdr->sver_lo); + + printf("\n"); + + inspect_fw_phexdec("Kernel data offset", + ntohl(hdr->kernel_ofs)); + inspect_fw_phexdec("Kernel data length", + ntohl(hdr->kernel_len)); + inspect_fw_phex("Kernel load address", + ntohl(hdr->kernel_la)); + inspect_fw_phex("Kernel entry point", + ntohl(hdr->kernel_ep)); + inspect_fw_phexdec("Rootfs data offset", + ntohl(hdr->rootfs_ofs)); + inspect_fw_phexdec("Rootfs data length", + ntohl(hdr->rootfs_len)); + inspect_fw_phexdec("Boot loader data offset", + ntohl(hdr->boot_ofs)); + inspect_fw_phexdec("Boot loader data length", + ntohl(hdr->boot_len)); + inspect_fw_phexdec("Total firmware length", + ntohl(hdr->fw_length)); + + if (extract) { + FILE *fp; + char *filename; + + printf("\n"); + + filename = malloc(strlen(inspect_info.file_name) + 8); + sprintf(filename, "%s-kernel", inspect_info.file_name); + printf("Extracting kernel to \"%s\"...\n", filename); + fp = fopen(filename, "w"); + if (fp) { + if (!fwrite(buf + ntohl(hdr->kernel_ofs), + ntohl(hdr->kernel_len), 1, fp)) { + ERR("error in fwrite(): %s", strerror(errno)); + } + fclose(fp); + } else { + ERR("error in fopen(): %s", strerror(errno)); + } + free(filename); + + filename = malloc(strlen(inspect_info.file_name) + 8); + sprintf(filename, "%s-rootfs", inspect_info.file_name); + printf("Extracting rootfs to \"%s\"...\n", filename); + fp = fopen(filename, "w"); + if (fp) { + if (!fwrite(buf + ntohl(hdr->rootfs_ofs), + ntohl(hdr->rootfs_len), 1, fp)) { + ERR("error in fwrite(): %s", strerror(errno)); + } + fclose(fp); + } else { + ERR("error in fopen(): %s", strerror(errno)); + } + free(filename); + } + + out_free_buf: + free(buf); + out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret = EXIT_FAILURE; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "a:H:E:F:L:V:N:W:w:ci:k:r:R:o:xhsjv:y:T:e"); + if (c == -1) + break; + + switch (c) { + case 'a': + sscanf(optarg, "0x%x", &rootfs_align); + break; + case 'H': + opt_hw_id = optarg; + break; + case 'E': + sscanf(optarg, "0x%x", &kernel_ep); + break; + case 'F': + layout_id = optarg; + break; + case 'W': + opt_hw_rev = optarg; + break; + case 'w': + opt_hw_ver_add = optarg; + break; + case 'L': + sscanf(optarg, "0x%x", &kernel_la); + break; + case 'V': + version = optarg; + break; + case 'v': + fw_ver = optarg; + break; + case 'y': + sver = optarg; + break; + case 'N': + vendor = optarg; + break; + case 'c': + combined++; + break; + case 'k': + kernel_info.file_name = optarg; + break; + case 'r': + rootfs_info.file_name = optarg; + break; + case 'R': + sscanf(optarg, "0x%x", &rootfs_ofs); + break; + case 'o': + ofname = optarg; + break; + case 's': + strip_padding = 1; + break; + case 'i': + inspect_info.file_name = optarg; + break; + case 'j': + add_jffs2_eof = 1; + break; + case 'x': + extract = 1; + break; + case 'T': + hdr_ver = atoi(optarg); + break; + case 'e': + custom_board.flags = FLAG_LE_KERNEL_LA_EP; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + if (!inspect_info.file_name) + ret = build_fw(sizeof(struct fw_header)); + else + ret = inspect_fw(); + + out: + return ret; +} + diff --git a/tools/firmware-utils/src/mkwrggimg.c b/tools/firmware-utils/src/mkwrggimg.c new file mode 100644 index 00000000000..9995b9a13d8 --- /dev/null +++ b/tools/firmware-utils/src/mkwrggimg.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> + * Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#define _ANSI_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <libgen.h> +#include <getopt.h> +#include <stdarg.h> +#include <errno.h> +#include <sys/stat.h> + +#include "md5.h" + +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERRS(fmt, ...) do { \ + int save = errno; \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt ", %s\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +#define WRGG03_MAGIC 0x20080321 + +struct wrgg03_header { + char signature[32]; + uint32_t magic1; + uint32_t magic2; + char version[16]; + char model[16]; + uint32_t flag[2]; + uint32_t reserve[2]; + char buildno[16]; + uint32_t size; + uint32_t offset; + char devname[32]; + char digest[16]; +} __attribute__ ((packed)); + +static char *progname; +static char *ifname; +static char *ofname; +static char *signature; +static char *version; +static char *model; +static uint32_t flag = 0; +static uint32_t reserve = 0; +static char *buildno; +static uint32_t offset; +static char *devname; +static int big_endian; + +void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -b create image in big endian format\n" +" -B <buildno> build number\n" +" -i <file> read input from the file <file>\n" +" -d <name> set device name to <name>\n" +" -m <model> model name\n" +" -o <file> write output to the file <file>\n" +" -O <offset> set offset to <offset>\n" +" -s <sig> set image signature to <sig>\n" +" -h show this screen\n" + ); + + exit(status); +} + +static void put_u32(void *data, uint32_t val, int swap) +{ + unsigned char *p = data; + + if (swap) { + p[0] = (val >> 24) & 0xff; + p[1] = (val >> 16) & 0xff; + p[2] = (val >> 8) & 0xff; + p[3] = val & 0xff; + } else { + p[3] = (val >> 24) & 0xff; + p[2] = (val >> 16) & 0xff; + p[1] = (val >> 8) & 0xff; + p[0] = val & 0xff; + } +} + +static void get_digest(struct wrgg03_header *header, char *data, int size) +{ + MD5_CTX ctx; + + MD5_Init(&ctx); + + MD5_Update(&ctx, (char *)&header->offset, sizeof(header->offset)); + MD5_Update(&ctx, (char *)&header->devname, sizeof(header->devname)); + MD5_Update(&ctx, data, size); + + MD5_Final(header->digest, &ctx); +} + +int main(int argc, char *argv[]) +{ + struct wrgg03_header *header; + char *buf; + struct stat st; + int buflen; + int err; + int res = EXIT_FAILURE; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "bd:i:m:o:s:v:B:O:h"); + if (c == -1) + break; + + switch (c) { + case 'b': + big_endian = 1; + break; + case 'B': + buildno = optarg; + break; + case 'd': + devname = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'm': + model = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + signature = optarg; + break; + case 'v': + version = optarg; + break; + case 'O': + offset = strtoul(optarg, NULL, 0); + break; + case 'h': + usage(EXIT_SUCCESS); + break; + + default: + usage(EXIT_FAILURE); + break; + } + } + + if (signature == NULL) { + ERR("no signature specified"); + goto err; + } + + if (ifname == NULL) { + ERR("no input file specified"); + goto err; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto err; + } + + if (devname == NULL) { + ERR("no device name specified"); + goto err; + } + + if (model == NULL) { + ERR("no model name specified"); + goto err; + } + + if (buildno == NULL) { + ERR("no build number specified"); + goto err; + } + + if (version == NULL) { + ERR("no version specified"); + goto err; + } + + err = stat(ifname, &st); + if (err){ + ERRS("stat failed on %s", ifname); + goto err; + } + + buflen = st.st_size + sizeof(struct wrgg03_header); + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto err; + } + + infile = fopen(ifname, "r"); + if (infile == NULL) { + ERRS("could not open \"%s\" for reading", ifname); + goto err_free; + } + + errno = 0; + fread(buf + sizeof(struct wrgg03_header), st.st_size, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto close_in; + } + + header = (struct wrgg03_header *) buf; + memset(header, '\0', sizeof(struct wrgg03_header)); + + strncpy(header->signature, signature, sizeof(header->signature)); + put_u32(&header->magic1, WRGG03_MAGIC, 0); + put_u32(&header->magic2, WRGG03_MAGIC, 0); + strncpy(header->version, version, sizeof(header->version)); + strncpy(header->model, model, sizeof(header->model)); + put_u32(&header->flag, flag, 0); + put_u32(&header->reserve, reserve, 0); + strncpy(header->buildno, buildno, sizeof(header->buildno)); + put_u32(&header->size, st.st_size, big_endian); + put_u32(&header->offset, offset, big_endian); + strncpy(header->devname, devname, sizeof(header->devname)); + + get_digest(header, buf + sizeof(struct wrgg03_header), st.st_size); + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto close_in; + } + + errno = 0; + fwrite(buf, buflen, 1, outfile); + if (errno) { + ERRS("unable to write to file %s", ofname); + goto close_out; + } + + fflush(outfile); + + res = EXIT_SUCCESS; + +close_out: + fclose(outfile); + if (res != EXIT_SUCCESS) + unlink(ofname); +close_in: + fclose(infile); +err_free: + free(buf); +err: + return res; +} diff --git a/tools/firmware-utils/src/mkzcfw.c b/tools/firmware-utils/src/mkzcfw.c index 7674e706c99..2326f1ff5c5 100644 --- a/tools/firmware-utils/src/mkzcfw.c +++ b/tools/firmware-utils/src/mkzcfw.c @@ -100,7 +100,7 @@ static struct board_info boards[] = { #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) diff --git a/tools/firmware-utils/src/mkzynfw.c b/tools/firmware-utils/src/mkzynfw.c index 36c176f4325..ccbabfe7f20 100644 --- a/tools/firmware-utils/src/mkzynfw.c +++ b/tools/firmware-utils/src/mkzynfw.c @@ -27,6 +27,7 @@ #if defined(__CYGWIN__) # include <byteswap.h> #endif +#include <inttypes.h> #include "zynos.h" @@ -687,7 +688,7 @@ write_out_file(FILE *outfile, char *name, size_t len, struct csum_state *css) FILE *f; int res; - DBG(2, "writing out file, name=%s, len=%d", + DBG(2, "writing out file, name=%s, len=%zu", name, len); errno = 0; @@ -988,7 +989,7 @@ calc_block_offsets(int type, uint32_t *offset) uint32_t avail; int i, res; - DBG(1,"calculating block offsets, starting with %lu", + DBG(1,"calculating block offsets, starting with %" PRIu32, *offset); res = 0; diff --git a/tools/firmware-utils/src/nand_ecc.c b/tools/firmware-utils/src/nand_ecc.c index b1e9305158c..58fb6ce0db4 100644 --- a/tools/firmware-utils/src/nand_ecc.c +++ b/tools/firmware-utils/src/nand_ecc.c @@ -2,7 +2,7 @@ * calculate ecc code for nand flash * * Copyright (C) 2008 yajin <yajin@vm-kernel.org> - * Copyright (C) 2009 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as diff --git a/tools/firmware-utils/src/osbridge-crc.c b/tools/firmware-utils/src/osbridge-crc.c index f2e3920a48c..5fd236a0746 100644 --- a/tools/firmware-utils/src/osbridge-crc.c +++ b/tools/firmware-utils/src/osbridge-crc.c @@ -51,7 +51,7 @@ static char *ofname; #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) diff --git a/tools/firmware-utils/src/oseama.c b/tools/firmware-utils/src/oseama.c new file mode 100644 index 00000000000..4434b11162e --- /dev/null +++ b/tools/firmware-utils/src/oseama.c @@ -0,0 +1,556 @@ +/* + * oseama + * + * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <byteswap.h> +#include <endian.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "md5.h" + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_be32(x) (x) +#define be32_to_cpu(x) (x) +#define cpu_to_be16(x) (x) +#define be16_to_cpu(x) (x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_be32(x) bswap_32(x) +#define be32_to_cpu(x) bswap_32(x) +#define cpu_to_be16(x) bswap_16(x) +#define be16_to_cpu(x) bswap_16(x) +#else +#error "Unsupported endianness" +#endif + +#define SEAMA_MAGIC 0x5ea3a417 + +struct seama_seal_header { + uint32_t magic; + uint16_t reserved; + uint16_t metasize; + uint32_t imagesize; +} __attribute__ ((packed)); + +struct seama_entity_header { + uint32_t magic; + uint16_t reserved; + uint16_t metasize; + uint32_t imagesize; + uint8_t md5[16]; +} __attribute__ ((packed)); + +char *seama_path; +int entity_idx = -1; +char *out_path; + +static inline size_t oseama_min(size_t x, size_t y) { + return x < y ? x : y; +} + +/************************************************** + * Info + **************************************************/ + +static void oseama_info_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "e:")) != -1) { + switch (c) { + case 'e': + entity_idx = atoi(optarg); + break; + } + } +} + +static int oseama_info_entities(FILE *seama) { + struct seama_entity_header hdr; + size_t bytes, metasize, imagesize; + uint8_t buf[1024]; + char *end, *tmp; + int i = 0; + int err = 0; + + while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) { + if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { + fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_out; + } + metasize = be16_to_cpu(hdr.metasize); + imagesize = be32_to_cpu(hdr.imagesize); + + if (entity_idx >= 0 && i != entity_idx) { + fseek(seama, metasize + imagesize, SEEK_CUR); + i++; + continue; + } + + if (metasize >= sizeof(buf)) { + fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%zd B)\n", sizeof(buf), metasize); + err = -EINVAL; + goto err_out; + } + + if (entity_idx < 0) + printf("\n"); + printf("Entity offset:\t%ld\n", ftell(seama) - sizeof(hdr)); + printf("Entity size:\t%zd\n", sizeof(hdr) + metasize + imagesize); + printf("Meta size:\t%zd\n", metasize); + printf("Image size:\t%zd\n", imagesize); + + bytes = fread(buf, 1, metasize, seama); + if (bytes != metasize) { + fprintf(stderr, "Couldn't read %zd B of meta\n", metasize); + err = -EIO; + goto err_out; + } + + end = (char *)&buf[metasize - 1]; + *end = '\0'; + for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { + printf("Meta entry:\t%s\n", tmp); + } + + fseek(seama, imagesize, SEEK_CUR); + i++; + } + +err_out: + return err; +} + +static int oseama_info(int argc, char **argv) { + FILE *seama; + struct seama_seal_header hdr; + size_t bytes; + uint16_t metasize; + uint32_t imagesize; + uint8_t buf[1024]; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No Seama file passed\n"); + err = -EINVAL; + goto out; + } + seama_path = argv[2]; + + optind = 3; + oseama_info_parse_options(argc, argv); + + seama = fopen(seama_path, "r"); + if (!seama) { + fprintf(stderr, "Couldn't open %s\n", seama_path); + err = -EACCES; + goto out; + } + + bytes = fread(&hdr, 1, sizeof(hdr), seama); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", seama_path); + err = -EIO; + goto err_close; + } + metasize = be16_to_cpu(hdr.metasize); + imagesize = be32_to_cpu(hdr.imagesize); + + if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { + fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_close; + } + + if (metasize >= sizeof(buf)) { + fprintf(stderr, "Too small buffer (%zu B) to read all meta info (%d B)\n", sizeof(buf), metasize); + err = -EINVAL; + goto err_close; + } + + if (imagesize) { + fprintf(stderr, "Invalid Seama image size: 0x%08x (should be 0)\n", imagesize); + err = -EINVAL; + goto err_close; + } + + bytes = fread(buf, 1, metasize, seama); + if (bytes != metasize) { + fprintf(stderr, "Couldn't read %d B of meta\n", metasize); + err = -EIO; + goto err_close; + } + + if (entity_idx < 0) { + char *end, *tmp; + + printf("Meta size:\t%d\n", metasize); + printf("Image size:\t%d\n", imagesize); + + end = (char *)&buf[metasize - 1]; + *end = '\0'; + for (tmp = (char *)buf; tmp < end && strlen(tmp); tmp += strlen(tmp) + 1) { + printf("Meta entry:\t%s\n", tmp); + } + } + + oseama_info_entities(seama); + +err_close: + fclose(seama); +out: + return err; +} + +/************************************************** + * Create + **************************************************/ + +static ssize_t oseama_entity_append_file(FILE *seama, const char *in_path) { + FILE *in; + size_t bytes; + ssize_t length = 0; + uint8_t buf[128]; + + in = fopen(in_path, "r"); + if (!in) { + fprintf(stderr, "Couldn't open %s\n", in_path); + return -EACCES; + } + + while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { + if (fwrite(buf, 1, bytes, seama) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, seama_path); + length = -EIO; + break; + } + length += bytes; + } + + fclose(in); + + return length; +} + +static ssize_t oseama_entity_append_zeros(FILE *seama, size_t length) { + uint8_t *buf; + + buf = malloc(length); + if (!buf) + return -ENOMEM; + memset(buf, 0, length); + + if (fwrite(buf, 1, length, seama) != length) { + fprintf(stderr, "Couldn't write %zu B to %s\n", length, seama_path); + return -EIO; + } + + return length; +} + +static ssize_t oseama_entity_align(FILE *seama, size_t curr_offset, size_t alignment) { + if (curr_offset & (alignment - 1)) { + size_t length = alignment - (curr_offset % alignment); + + return oseama_entity_append_zeros(seama, length); + } + + return 0; +} + +static int oseama_entity_write_hdr(FILE *seama, size_t metasize, size_t imagesize) { + struct seama_entity_header hdr = {}; + uint8_t buf[128]; + size_t length = imagesize; + size_t bytes; + MD5_CTX ctx; + + fseek(seama, sizeof(hdr) + metasize, SEEK_SET); + MD5_Init(&ctx); + while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) { + MD5_Update(&ctx, buf, bytes); + length -= bytes; + } + MD5_Final(hdr.md5, &ctx); + + hdr.magic = cpu_to_be32(SEAMA_MAGIC); + hdr.metasize = cpu_to_be16(metasize); + hdr.imagesize = cpu_to_be32(imagesize); + + fseek(seama, 0, SEEK_SET); + bytes = fwrite(&hdr, 1, sizeof(hdr), seama); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't write Seama entity header to %s\n", seama_path); + return -EIO; + } + + return 0; +} + +static int oseama_entity(int argc, char **argv) { + FILE *seama; + ssize_t sbytes; + size_t curr_offset = sizeof(struct seama_entity_header); + size_t metasize = 0, imagesize = 0; + int c; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No Seama file passed\n"); + err = -EINVAL; + goto out; + } + seama_path = argv[2]; + + seama = fopen(seama_path, "w+"); + if (!seama) { + fprintf(stderr, "Couldn't open %s\n", seama_path); + err = -EACCES; + goto out; + } + fseek(seama, curr_offset, SEEK_SET); + + optind = 3; + while ((c = getopt(argc, argv, "m:f:b:")) != -1) { + switch (c) { + case 'm': + sbytes = fwrite(optarg, 1, strlen(optarg) + 1, seama); + if (sbytes < 0) { + fprintf(stderr, "Failed to write meta %s\n", optarg); + } else { + curr_offset += sbytes; + metasize += sbytes; + } + + sbytes = oseama_entity_align(seama, curr_offset, 4); + if (sbytes < 0) { + fprintf(stderr, "Failed to append zeros\n"); + } else { + curr_offset += sbytes; + metasize += sbytes; + } + + break; + case 'f': + case 'b': + break; + } + } + + optind = 3; + while ((c = getopt(argc, argv, "m:f:b:")) != -1) { + switch (c) { + case 'm': + break; + case 'f': + sbytes = oseama_entity_append_file(seama, optarg); + if (sbytes < 0) { + fprintf(stderr, "Failed to append file %s\n", optarg); + } else { + curr_offset += sbytes; + imagesize += sbytes; + } + break; + case 'b': + sbytes = strtol(optarg, NULL, 0) - curr_offset; + if (sbytes < 0) { + fprintf(stderr, "Current Seama entity length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0)); + } else { + sbytes = oseama_entity_append_zeros(seama, sbytes); + if (sbytes < 0) { + fprintf(stderr, "Failed to append zeros\n"); + } else { + curr_offset += sbytes; + imagesize += sbytes; + } + } + break; + } + if (err) + break; + } + + oseama_entity_write_hdr(seama, metasize, imagesize); + + fclose(seama); +out: + return err; +} + +/************************************************** + * Extract + **************************************************/ + +static void oseama_extract_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "e:o:")) != -1) { + switch (c) { + case 'e': + entity_idx = atoi(optarg); + break; + case 'o': + out_path = optarg; + break; + } + } +} + +static int oseama_extract_entity(FILE *seama, FILE *out) { + struct seama_entity_header hdr; + size_t bytes, metasize, imagesize, length; + uint8_t buf[1024]; + int i = 0; + int err = 0; + + while ((bytes = fread(&hdr, 1, sizeof(hdr), seama)) == sizeof(hdr)) { + if (be32_to_cpu(hdr.magic) != SEAMA_MAGIC) { + fprintf(stderr, "Invalid Seama magic: 0x%08x\n", be32_to_cpu(hdr.magic)); + err = -EINVAL; + break; + } + metasize = be16_to_cpu(hdr.metasize); + imagesize = be32_to_cpu(hdr.imagesize); + + if (i != entity_idx) { + fseek(seama, metasize + imagesize, SEEK_CUR); + i++; + continue; + } + + fseek(seama, -sizeof(hdr), SEEK_CUR); + + length = sizeof(hdr) + metasize + imagesize; + while ((bytes = fread(buf, 1, oseama_min(sizeof(buf), length), seama)) > 0) { + if (fwrite(buf, 1, bytes, out) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path); + err = -EIO; + break; + } + length -= bytes; + } + + if (length) { + fprintf(stderr, "Couldn't extract whole entity %d from %s (%zu B left)\n", entity_idx, seama_path, length); + err = -EIO; + break; + } + + break; + } + + return err; +} + +static int oseama_extract(int argc, char **argv) { + FILE *seama; + FILE *out; + struct seama_seal_header hdr; + size_t bytes; + uint16_t metasize; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No Seama file passed\n"); + err = -EINVAL; + goto out; + } + seama_path = argv[2]; + + optind = 3; + oseama_extract_parse_options(argc, argv); + if (entity_idx < 0) { + fprintf(stderr, "No entity specified\n"); + err = -EINVAL; + goto out; + } else if (!out_path) { + fprintf(stderr, "No output file specified\n"); + err = -EINVAL; + goto out; + } + + seama = fopen(seama_path, "r"); + if (!seama) { + fprintf(stderr, "Couldn't open %s\n", seama_path); + err = -EACCES; + goto out; + } + + out = fopen(out_path, "w"); + if (!out) { + fprintf(stderr, "Couldn't open %s\n", out_path); + err = -EACCES; + goto err_close_seama; + } + + bytes = fread(&hdr, 1, sizeof(hdr), seama); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", seama_path); + err = -EIO; + goto err_close_out; + } + metasize = be16_to_cpu(hdr.metasize); + + fseek(seama, metasize, SEEK_CUR); + + oseama_extract_entity(seama, out); + +err_close_out: + fclose(out); +err_close_seama: + fclose(seama); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() { + printf("Usage:\n"); + printf("\n"); + printf("Info about Seama seal (container):\n"); + printf("\toseama info <file> [options]\n"); + printf("\t-e\t\t\t\tprint info about specified entity only\n"); + printf("\n"); + printf("Create Seama entity:\n"); + printf("\toseama entity <file> [options]\n"); + printf("\t-m meta\t\t\t\tmeta into to put in header\n"); + printf("\t-f file\t\t\t\tappend content from file\n"); + printf("\t-b offset\t\t\tappend zeros till reaching absolute offset\n"); + printf("\n"); + printf("Extract from Seama seal (container):\n"); + printf("\toseama extract <file> [options]\n"); + printf("\t-e\t\t\t\tindex of entity to extract\n"); + printf("\t-o file\t\t\t\toutput file\n"); +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "info")) + return oseama_info(argc, argv); + else if (!strcmp(argv[1], "entity")) + return oseama_entity(argc, argv); + else if (!strcmp(argv[1], "extract")) + return oseama_extract(argc, argv); + } + + usage(); + return 0; +} diff --git a/tools/firmware-utils/src/otrx.c b/tools/firmware-utils/src/otrx.c new file mode 100644 index 00000000000..223e032f2b5 --- /dev/null +++ b/tools/firmware-utils/src/otrx.c @@ -0,0 +1,592 @@ +/* + * otrx + * + * Copyright (C) 2015-2017 Rafał Miłecki <zajec5@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <byteswap.h> +#include <endian.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if !defined(__BYTE_ORDER) +#error "Unknown byte order" +#endif + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le32(x) bswap_32(x) +#define le32_to_cpu(x) bswap_32(x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le32(x) (x) +#define le32_to_cpu(x) (x) +#else +#error "Unsupported endianness" +#endif + +#define TRX_MAGIC 0x30524448 +#define TRX_FLAGS_OFFSET 12 +#define TRX_MAX_PARTS 3 + +struct trx_header { + uint32_t magic; + uint32_t length; + uint32_t crc32; + uint16_t flags; + uint16_t version; + uint32_t offset[3]; +}; + +char *trx_path; +size_t trx_offset = 0; +char *partition[TRX_MAX_PARTS] = {}; + +static inline size_t otrx_min(size_t x, size_t y) { + return x < y ? x : y; +} + +/************************************************** + * CRC32 + **************************************************/ + +static const uint32_t crc32_tbl[] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +uint32_t otrx_crc32(uint32_t crc, uint8_t *buf, size_t len) { + while (len) { + crc = crc32_tbl[(crc ^ *buf) & 0xff] ^ (crc >> 8); + buf++; + len--; + } + + return crc; +} + +/************************************************** + * Check + **************************************************/ + +static void otrx_check_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "o:")) != -1) { + switch (c) { + case 'o': + trx_offset = atoi(optarg); + break; + } + } +} + +static int otrx_check(int argc, char **argv) { + FILE *trx; + struct trx_header hdr; + size_t bytes, length; + uint8_t buf[1024]; + uint32_t crc32; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No TRX file passed\n"); + err = -EINVAL; + goto out; + } + trx_path = argv[2]; + + optind = 3; + otrx_check_parse_options(argc, argv); + + trx = fopen(trx_path, "r"); + if (!trx) { + fprintf(stderr, "Couldn't open %s\n", trx_path); + err = -EACCES; + goto out; + } + + fseek(trx, trx_offset, SEEK_SET); + bytes = fread(&hdr, 1, sizeof(hdr), trx); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", trx_path); + err = -EIO; + goto err_close; + } + + if (le32_to_cpu(hdr.magic) != TRX_MAGIC) { + fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_close; + } + + length = le32_to_cpu(hdr.length); + if (length < sizeof(hdr)) { + fprintf(stderr, "Length read from TRX too low (%zu B)\n", length); + err = -EINVAL; + goto err_close; + } + + crc32 = 0xffffffff; + fseek(trx, trx_offset + TRX_FLAGS_OFFSET, SEEK_SET); + length -= TRX_FLAGS_OFFSET; + while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) { + crc32 = otrx_crc32(crc32, buf, bytes); + length -= bytes; + } + + if (length) { + fprintf(stderr, "Couldn't read last %zd B of data from %s\n", length, trx_path); + err = -EIO; + goto err_close; + } + + if (crc32 != le32_to_cpu(hdr.crc32)) { + fprintf(stderr, "Invalid data crc32: 0x%08x instead of 0x%08x\n", crc32, le32_to_cpu(hdr.crc32)); + err = -EINVAL; + goto err_close; + } + + printf("Found a valid TRX version %d\n", le32_to_cpu(hdr.version)); + +err_close: + fclose(trx); +out: + return err; +} + +/************************************************** + * Create + **************************************************/ + +static ssize_t otrx_create_append_file(FILE *trx, const char *in_path) { + FILE *in; + size_t bytes; + ssize_t length = 0; + uint8_t buf[1024]; + + in = fopen(in_path, "r"); + if (!in) { + fprintf(stderr, "Couldn't open %s\n", in_path); + return -EACCES; + } + + while ((bytes = fread(buf, 1, sizeof(buf), in)) > 0) { + if (fwrite(buf, 1, bytes, trx) != bytes) { + fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, trx_path); + length = -EIO; + break; + } + length += bytes; + } + + fclose(in); + + return length; +} + +static ssize_t otrx_create_append_zeros(FILE *trx, size_t length) { + uint8_t *buf; + + buf = malloc(length); + if (!buf) + return -ENOMEM; + memset(buf, 0, length); + + if (fwrite(buf, 1, length, trx) != length) { + fprintf(stderr, "Couldn't write %zu B to %s\n", length, trx_path); + free(buf); + return -EIO; + } + + free(buf); + + return length; +} + +static ssize_t otrx_create_align(FILE *trx, size_t curr_offset, size_t alignment) { + if (curr_offset & (alignment - 1)) { + size_t length = alignment - (curr_offset % alignment); + return otrx_create_append_zeros(trx, length); + } + + return 0; +} + +static int otrx_create_write_hdr(FILE *trx, struct trx_header *hdr) { + size_t bytes, length; + uint8_t buf[1024]; + uint32_t crc32; + + hdr->magic = cpu_to_le32(TRX_MAGIC); + hdr->version = 1; + + fseek(trx, 0, SEEK_SET); + bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx); + if (bytes != sizeof(struct trx_header)) { + fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path); + return -EIO; + } + + length = le32_to_cpu(hdr->length); + + crc32 = 0xffffffff; + fseek(trx, TRX_FLAGS_OFFSET, SEEK_SET); + length -= TRX_FLAGS_OFFSET; + while ((bytes = fread(buf, 1, otrx_min(sizeof(buf), length), trx)) > 0) { + crc32 = otrx_crc32(crc32, buf, bytes); + length -= bytes; + } + hdr->crc32 = cpu_to_le32(crc32); + + fseek(trx, 0, SEEK_SET); + bytes = fwrite(hdr, 1, sizeof(struct trx_header), trx); + if (bytes != sizeof(struct trx_header)) { + fprintf(stderr, "Couldn't write TRX header to %s\n", trx_path); + return -EIO; + } + + return 0; +} + +static int otrx_create(int argc, char **argv) { + FILE *trx; + struct trx_header hdr = {}; + ssize_t sbytes; + size_t curr_idx = 0; + size_t curr_offset = sizeof(hdr); + int c; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No TRX file passed\n"); + err = -EINVAL; + goto out; + } + trx_path = argv[2]; + + trx = fopen(trx_path, "w+"); + if (!trx) { + fprintf(stderr, "Couldn't open %s\n", trx_path); + err = -EACCES; + goto out; + } + fseek(trx, curr_offset, SEEK_SET); + + optind = 3; + while ((c = getopt(argc, argv, "f:A:a:b:")) != -1) { + switch (c) { + case 'f': + if (curr_idx >= TRX_MAX_PARTS) { + err = -ENOSPC; + fprintf(stderr, "Reached TRX partitions limit, no place for %s\n", optarg); + goto err_close; + } + + sbytes = otrx_create_append_file(trx, optarg); + if (sbytes < 0) { + fprintf(stderr, "Failed to append file %s\n", optarg); + } else { + hdr.offset[curr_idx++] = curr_offset; + curr_offset += sbytes; + } + + sbytes = otrx_create_align(trx, curr_offset, 4); + if (sbytes < 0) + fprintf(stderr, "Failed to append zeros\n"); + else + curr_offset += sbytes; + + break; + case 'A': + sbytes = otrx_create_append_file(trx, optarg); + if (sbytes < 0) { + fprintf(stderr, "Failed to append file %s\n", optarg); + } else { + curr_offset += sbytes; + } + + sbytes = otrx_create_align(trx, curr_offset, 4); + if (sbytes < 0) + fprintf(stderr, "Failed to append zeros\n"); + else + curr_offset += sbytes; + break; + case 'a': + sbytes = otrx_create_align(trx, curr_offset, strtol(optarg, NULL, 0)); + if (sbytes < 0) + fprintf(stderr, "Failed to append zeros\n"); + else + curr_offset += sbytes; + break; + case 'b': + sbytes = strtol(optarg, NULL, 0) - curr_offset; + if (sbytes < 0) { + fprintf(stderr, "Current TRX length is 0x%zx, can't pad it with zeros to 0x%lx\n", curr_offset, strtol(optarg, NULL, 0)); + } else { + sbytes = otrx_create_append_zeros(trx, sbytes); + if (sbytes < 0) + fprintf(stderr, "Failed to append zeros\n"); + else + curr_offset += sbytes; + } + break; + } + if (err) + break; + } + + sbytes = otrx_create_align(trx, curr_offset, 0x1000); + if (sbytes < 0) + fprintf(stderr, "Failed to append zeros\n"); + else + curr_offset += sbytes; + + hdr.length = curr_offset; + otrx_create_write_hdr(trx, &hdr); +err_close: + fclose(trx); +out: + return err; +} + +/************************************************** + * Extract + **************************************************/ + +static void otrx_extract_parse_options(int argc, char **argv) { + int c; + + while ((c = getopt(argc, argv, "c:e:o:1:2:3:")) != -1) { + switch (c) { + case 'o': + trx_offset = atoi(optarg); + break; + case '1': + partition[0] = optarg; + break; + case '2': + partition[1] = optarg; + break; + case '3': + partition[2] = optarg; + break; + } + } +} + +static int otrx_extract_copy(FILE *trx, size_t offset, size_t length, char *out_path) { + FILE *out; + size_t bytes; + uint8_t *buf; + int err = 0; + + out = fopen(out_path, "w"); + if (!out) { + fprintf(stderr, "Couldn't open %s\n", out_path); + err = -EACCES; + goto out; + } + + buf = malloc(length); + if (!buf) { + fprintf(stderr, "Couldn't alloc %zu B buffer\n", length); + err = -ENOMEM; + goto err_close; + } + + fseek(trx, offset, SEEK_SET); + bytes = fread(buf, 1, length, trx); + if (bytes != length) { + fprintf(stderr, "Couldn't read %zu B of data from %s\n", length, trx_path); + err = -ENOMEM; + goto err_free_buf; + }; + + bytes = fwrite(buf, 1, length, out); + if (bytes != length) { + fprintf(stderr, "Couldn't write %zu B to %s\n", length, out_path); + err = -ENOMEM; + goto err_free_buf; + } + + printf("Extracted 0x%zx bytes into %s\n", length, out_path); + +err_free_buf: + free(buf); +err_close: + fclose(out); +out: + return err; +} + +static int otrx_extract(int argc, char **argv) { + FILE *trx; + struct trx_header hdr; + size_t bytes; + int i; + int err = 0; + + if (argc < 3) { + fprintf(stderr, "No TRX file passed\n"); + err = -EINVAL; + goto out; + } + trx_path = argv[2]; + + optind = 3; + otrx_extract_parse_options(argc, argv); + + trx = fopen(trx_path, "r"); + if (!trx) { + fprintf(stderr, "Couldn't open %s\n", trx_path); + err = -EACCES; + goto out; + } + + fseek(trx, trx_offset, SEEK_SET); + bytes = fread(&hdr, 1, sizeof(hdr), trx); + if (bytes != sizeof(hdr)) { + fprintf(stderr, "Couldn't read %s header\n", trx_path); + err = -EIO; + goto err_close; + } + + if (le32_to_cpu(hdr.magic) != TRX_MAGIC) { + fprintf(stderr, "Invalid TRX magic: 0x%08x\n", le32_to_cpu(hdr.magic)); + err = -EINVAL; + goto err_close; + } + + for (i = 0; i < TRX_MAX_PARTS; i++) { + size_t length; + + if (!partition[i]) + continue; + if (!hdr.offset[i]) { + printf("TRX doesn't contain partition %d, can't extract %s\n", i + 1, partition[i]); + continue; + } + + if (i + 1 >= TRX_MAX_PARTS || !hdr.offset[i + 1]) + length = le32_to_cpu(hdr.length) - le32_to_cpu(hdr.offset[i]); + else + length = le32_to_cpu(hdr.offset[i + 1]) - le32_to_cpu(hdr.offset[i]); + + otrx_extract_copy(trx, trx_offset + le32_to_cpu(hdr.offset[i]), length, partition[i]); + } + +err_close: + fclose(trx); +out: + return err; +} + +/************************************************** + * Start + **************************************************/ + +static void usage() { + printf("Usage:\n"); + printf("\n"); + printf("Checking TRX file:\n"); + printf("\totrx check <file> [options]\tcheck if file is a valid TRX\n"); + printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n"); + printf("\n"); + printf("Creating new TRX file:\n"); + printf("\totrx create <file> [options] [partitions]\n"); + printf("\t-f file\t\t\t\t[partition] start new partition with content copied from file\n"); + printf("\t-A file\t\t\t\t[partition] append current partition with content copied from file\n"); + printf("\t-a alignment\t\t\t[partition] align current partition\n"); + printf("\t-b offset\t\t\t[partition] append zeros to partition till reaching absolute offset\n"); + printf("\n"); + printf("Extracting from TRX file:\n"); + printf("\totrx extract <file> [options]\textract partitions from TRX file\n"); + printf("\t-o offset\t\t\toffset of TRX data in file (default: 0)\n"); + printf("\t-1 file\t\t\t\tfile to extract 1st partition to (optional)\n"); + printf("\t-2 file\t\t\t\tfile to extract 2nd partition to (optional)\n"); + printf("\t-3 file\t\t\t\tfile to extract 3rd partition to (optional)\n"); +} + +int main(int argc, char **argv) { + if (argc > 1) { + if (!strcmp(argv[1], "check")) + return otrx_check(argc, argv); + else if (!strcmp(argv[1], "create")) + return otrx_create(argc, argv); + else if (!strcmp(argv[1], "extract")) + return otrx_extract(argc, argv); + } + + usage(); + return 0; +} diff --git a/tools/firmware-utils/src/pc1crypt.c b/tools/firmware-utils/src/pc1crypt.c index 9c2eb83f666..fe41b3dab5c 100644 --- a/tools/firmware-utils/src/pc1crypt.c +++ b/tools/firmware-utils/src/pc1crypt.c @@ -208,7 +208,7 @@ static int decrypt; #define ERRS(fmt, ...) do { \ int save = errno; \ fflush(0); \ - fprintf(stderr, "[%s] *** error: " fmt "\n", \ + fprintf(stderr, "[%s] *** error: " fmt ": %s\n", \ progname, ## __VA_ARGS__, strerror(save)); \ } while (0) diff --git a/tools/firmware-utils/src/ptgen.c b/tools/firmware-utils/src/ptgen.c index d94aabb5fcf..13e0eda6222 100644 --- a/tools/firmware-utils/src/ptgen.c +++ b/tools/firmware-utils/src/ptgen.c @@ -1,6 +1,6 @@ -/* +/* * ptgen - partition table generator - * Copyright (C) 2006 by Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2006 by Felix Fietkau <nbd@nbd.name> * * uses parts of afdisk * Copyright (C) 2002 by David Roetzel <david@roetzel.de> @@ -9,12 +9,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA @@ -26,25 +26,27 @@ #include <unistd.h> #include <stdlib.h> #include <stdio.h> +#include <stdint.h> #include <ctype.h> #include <fcntl.h> +#include <stdint.h> #if __BYTE_ORDER == __BIG_ENDIAN -#define cpu_to_le16(x) bswap_16(x) +#define cpu_to_le32(x) bswap_32(x) #elif __BYTE_ORDER == __LITTLE_ENDIAN -#define cpu_to_le16(x) (x) +#define cpu_to_le32(x) (x) #else #error unknown endianness! #endif /* Partition table entry */ -struct pte { - unsigned char active; - unsigned char chs_start[3]; - unsigned char type; - unsigned char chs_end[3]; - unsigned int start; - unsigned int length; +struct pte { + uint8_t active; + uint8_t chs_start[3]; + uint8_t type; + uint8_t chs_end[3]; + uint32_t start; + uint32_t length; }; struct partinfo { @@ -56,29 +58,31 @@ int verbose = 0; int active = 1; int heads = -1; int sectors = -1; +int kb_align = 0; struct partinfo parts[4]; char *filename = NULL; -/* +/* * parse the size argument, which is either * a simple number (K assumed) or * K, M or G * * returns the size in KByte */ -static long to_kbytes(const char *string) { +static long to_kbytes(const char *string) +{ int exp = 0; long result; char *end; result = strtoul(string, &end, 0); switch (tolower(*end)) { - case 'k' : - case '\0' : exp = 0; break; - case 'm' : exp = 1; break; - case 'g' : exp = 2; break; - default: return 0; + case 'k' : + case '\0' : exp = 0; break; + case 'm' : exp = 1; break; + case 'g' : exp = 2; break; + default: return 0; } if (*end) @@ -90,13 +94,16 @@ static long to_kbytes(const char *string) { } /* result: number + 1024^(exp) */ - return result * ((2 << ((10 * exp) - 1)) ?: 1); + if (exp == 0) + return result; + return result * (2 << ((10 * exp) - 1)); } /* convert the sector number into a CHS value for the partition table */ -static void to_chs(long sect, unsigned char chs[3]) { +static void to_chs(long sect, unsigned char chs[3]) +{ int c,h,s; - + s = (sect % sectors) + 1; sect = sect / sectors; h = sect % heads; @@ -111,17 +118,23 @@ static void to_chs(long sect, unsigned char chs[3]) { } /* round the sector number up to the next cylinder */ -static inline unsigned long round_to_cyl(long sect) { +static inline unsigned long round_to_cyl(long sect) +{ int cyl_size = heads * sectors; - return sect + cyl_size - (sect % cyl_size); + return sect + cyl_size - (sect % cyl_size); +} + +/* round the sector number up to the kb_align boundary */ +static inline unsigned long round_to_kb(long sect) { + return ((sect - 1) / kb_align + 1) * kb_align; } /* check the partition sizes and write the partition table */ -static int gen_ptable(int nr) +static int gen_ptable(uint32_t signature, int nr) { struct pte pte[4]; - unsigned long sect = 0; + unsigned long sect = 0; int i, fd, ret = -1, start, len; memset(pte, 0, sizeof(struct pte) * 4); @@ -130,17 +143,27 @@ static int gen_ptable(int nr) fprintf(stderr, "Invalid size in partition %d!\n", i); return -1; } + pte[i].active = ((i + 1) == active) ? 0x80 : 0; pte[i].type = parts[i].type; - pte[i].start = cpu_to_le16(start = sect + sectors); - sect = round_to_cyl(start + parts[i].size * 2); - pte[i].length = cpu_to_le16(len = sect - start); + + start = sect + sectors; + if (kb_align != 0) + start = round_to_kb(start); + pte[i].start = cpu_to_le32(start); + + sect = start + parts[i].size * 2; + if (kb_align == 0) + sect = round_to_cyl(sect); + pte[i].length = cpu_to_le32(len = sect - start); + to_chs(start, pte[i].chs_start); to_chs(start + len - 1, pte[i].chs_end); + if (verbose) - fprintf(stderr, "Partition %d: start=%ld, end=%ld, size=%ld\n", i, (long) start * 512, ((long) start + (long) len) * 512, (long) len * 512); - printf("%ld\n", ((long) start * 512)); - printf("%ld\n", ((long) len * 512)); + fprintf(stderr, "Partition %d: start=%ld, end=%ld, size=%ld\n", i, (long)start * 512, ((long)start + (long)len) * 512, (long)len * 512); + printf("%ld\n", (long)start * 512); + printf("%ld\n", (long)len * 512); } if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { @@ -148,6 +171,12 @@ static int gen_ptable(int nr) return -1; } + lseek(fd, 440, SEEK_SET); + if (write(fd, &signature, sizeof(signature)) != sizeof(signature)) { + fprintf(stderr, "write failed.\n"); + goto fail; + } + lseek(fd, 446, SEEK_SET); if (write(fd, pte, sizeof(struct pte) * 4) != sizeof(struct pte) * 4) { fprintf(stderr, "write failed.\n"); @@ -158,7 +187,7 @@ static int gen_ptable(int nr) fprintf(stderr, "write failed.\n"); goto fail; } - + ret = 0; fail: close(fd); @@ -167,8 +196,8 @@ fail: static void usage(char *prog) { - fprintf(stderr, "Usage: %s [-v] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [[-t <type>] -p <size>...] \n", prog); - exit(1); + fprintf(stderr, "Usage: %s [-v] -h <heads> -s <sectors> -o <outputfile> [-a 0..4] [-l <align kB>] [[-t <type>] -p <size>...] \n", prog); + exit(EXIT_FAILURE); } int main (int argc, char **argv) @@ -176,8 +205,9 @@ int main (int argc, char **argv) char type = 0x83; int ch; int part = 0; + uint32_t signature = 0x5452574F; /* 'OWRT' */ - while ((ch = getopt(argc, argv, "h:s:p:a:t:o:v")) != -1) { + while ((ch = getopt(argc, argv, "h:s:p:a:t:o:vl:S:")) != -1) { switch (ch) { case 'o': filename = optarg; @@ -186,35 +216,41 @@ int main (int argc, char **argv) verbose++; break; case 'h': - heads = (int) strtoul(optarg, NULL, 0); + heads = (int)strtoul(optarg, NULL, 0); break; case 's': - sectors = (int) strtoul(optarg, NULL, 0); + sectors = (int)strtoul(optarg, NULL, 0); break; case 'p': if (part > 3) { fprintf(stderr, "Too many partitions\n"); - exit(1); + exit(EXIT_FAILURE); } parts[part].size = to_kbytes(optarg); parts[part++].type = type; break; case 't': - type = (char) strtoul(optarg, NULL, 16); + type = (char)strtoul(optarg, NULL, 16); break; case 'a': - active = (int) strtoul(optarg, NULL, 0); + active = (int)strtoul(optarg, NULL, 0); if ((active < 0) || (active > 4)) active = 0; break; + case 'l': + kb_align = (int)strtoul(optarg, NULL, 0) * 2; + break; + case 'S': + signature = strtoul(optarg, NULL, 0); + break; case '?': default: usage(argv[0]); } } argc -= optind; - if (argc || (heads <= 0) || (sectors <= 0) || !filename) + if (argc || (heads <= 0) || (sectors <= 0) || !filename) usage(argv[0]); - - return gen_ptable(part); + + return gen_ptable(signature, part) ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/tools/firmware-utils/src/seama.c b/tools/firmware-utils/src/seama.c new file mode 100644 index 00000000000..05aee8e76aa --- /dev/null +++ b/tools/firmware-utils/src/seama.c @@ -0,0 +1,529 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright (C) 2008, Alpha Networks, Inc. + * Created by David Hsieh <david_hsieh@alphanetworks.com> + * All right reserved. + * + * (SEA)ttle i(MA)ge is the image which used in project seattle. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdarg.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <string.h> +#include <arpa/inet.h> + +#include "md5.h" +#include "seama.h" + +#define PROGNAME "seama" +#define VERSION "0.20" +#define MAX_SEAMA_META_SIZE 1024 +#define MAX_META 128 +#define MAX_IMAGE 128 + +extern int optind; +extern char * optarg; + +static int o_verbose = 0; /* verbose mode. */ +static char * o_dump = NULL; /* Seama file to dump. */ +static char * o_seal = NULL; /* Seal the input images when file name exist. */ +static char * o_extract = NULL; /* Extract the seama file. */ +static char * o_images[MAX_IMAGE];/* The image files to pack or seal */ +static int o_isize = 0; /* number of images */ +static char * o_meta[MAX_META]; /* meta data array */ +static int o_msize = 0; /* size of meta array */ + +static void verbose(const char * format, ...) +{ + va_list marker; + if (o_verbose) + { + va_start(marker, format); + vfprintf(stdout, format, marker); + va_end(marker); + } +} + +static void cleanup_exit(int exit_code) +{ + verbose("%s: exit with code %d\n", PROGNAME, exit_code); + exit(exit_code); +} + +static void show_usage(int exit_code) +{ + printf( PROGNAME " version " VERSION "\n" + "usage: " PROGNAME " [OPTIONS]\n" + " -h show this help message.\n" + " -v verbose mode.\n" + " -m {META data} META data.\n" + " -d {file} dump the info of the seama file.\n" + " -i {input file} image file name.\n" + " -s {file} Seal the images to the seama file.\n" + " -x {seama file} Extract the seama file.\n" + "\n" + " SEAMA can pack the input file (with -i) into a seama file.\n" + " ex: seama -i target.file\n" + " SEAMA can also seal multiple seama files into a single seama file.\n" + " ex: seama -s final.file -i taget1.seama -i target2.seama\n" + " To extract the raw image from SEAMA, you need to specify the meta.\n" + " The first image match the specified meta will be extract to\n" + " the output file which was specified with '-x'.\n" + " ex: seama -x output -i seama.image -m file=sealpac\n" + ); + cleanup_exit(exit_code); +} + +static int parse_args(int argc, char * argv[]) +{ + int opt; + + while ((opt = getopt(argc, argv, "hvd:s:i:m:x:")) > 0) + { + switch (opt) + { + default: show_usage(-1); break; + case 'h': show_usage(0); break; + case 'v': o_verbose++; break; + case 'd': o_dump = optarg; break; + case 's': o_seal = optarg; break; + case 'x': o_extract = optarg; break; + case 'i': + if (o_isize < MAX_IMAGE) o_images[o_isize++] = optarg; + else printf("Exceed the maximum acceptable image files.!\n"); + break; + case 'm': + if (o_msize < MAX_META) o_meta[o_msize++] = optarg; + else printf("Exceed the maximum acceptable META data.!\n"); + break; + } + } + return 0; +} + +/*******************************************************************/ + +static size_t calculate_digest(FILE * fh, size_t size, uint8_t * digest) +{ + MD5_CTX ctx; + size_t bytes_left, bytes_read, i; + uint8_t buf[MAX_SEAMA_META_SIZE]; + + bytes_left = size ? size : sizeof(buf); + bytes_read = 0; + + MD5_Init(&ctx); + while (!feof(fh) && !ferror(fh) && bytes_left > 0) + { + i = bytes_left < sizeof(buf) ? bytes_left : sizeof(buf); + i = fread(buf, sizeof(char), i, fh); + if (i > 0) + { + MD5_Update(&ctx, buf, i); + bytes_read += i; + } + if (size) bytes_left -= i; + } + MD5_Final(digest, &ctx); + return bytes_read; +} + +#define READ_BUFF_SIZE 8*1024 +static size_t copy_file(FILE * to, FILE * from) +{ + size_t i, fsize = 0; + uint8_t buf[READ_BUFF_SIZE]; + + while (!feof(from) && !ferror(from)) + { + i = fread(buf, sizeof(uint8_t), READ_BUFF_SIZE, from); + if (i > 0) + { + fsize += i; + fwrite(buf, sizeof(uint8_t), i, to); + } + } + return fsize; +} + +static int verify_seama(const char * fname, int msg) +{ + FILE * fh = NULL; + struct stat st; + seamahdr_t shdr; + uint8_t checksum[16]; + uint8_t digest[16]; + uint8_t buf[MAX_SEAMA_META_SIZE]; + size_t msize, isize, i; + int ret = -1; + +#define ERRBREAK(fmt, args...) { if (msg) printf(fmt, ##args); break; } + + do + { + if (stat(fname, &st) < 0) ERRBREAK("Unable to get the info of '%s'\n",fname); + if ((fh = fopen(fname, "r+"))==NULL) ERRBREAK("Unable to open '%s' for reading!\n",fname); + + /* Dump SEAMA header */ + if (msg) printf("FILE - %s (%d bytes)\n", fname, (int)st.st_size); + + /* SEAMA */ + while (!feof(fh) && !ferror(fh)) + { + /* read header */ + if (fread(&shdr, sizeof(shdr), 1, fh) != 1) break; + + /* Check the magic number */ + if (shdr.magic != htonl(SEAMA_MAGIC)) ERRBREAK("Invalid SEAMA magic. Probably no more SEAMA!\n"); + + /* Get the size */ + isize = ntohl(shdr.size); + msize = ntohs(shdr.metasize); + + /* The checksum exist only if size is greater than zero. */ + if (isize > 0) + { + if (fread(checksum, sizeof(checksum), 1, fh) != 1) + ERRBREAK("Error reading checksum !\n"); + } + + /* Check the META size. */ + if (msize > sizeof(buf)) ERRBREAK("META data in SEAMA header is too large!\n"); + + /* Read META data. */ + if (fread(buf, sizeof(char), msize, fh) != msize) + ERRBREAK("Unable to read SEAMA META data!\n"); + + /* dump header */ + if (msg) + { + printf("SEAMA ==========================================\n"); + printf(" magic : %08x\n", ntohl(shdr.magic)); + printf(" meta size : %zu bytes\n", msize); + for (i=0; i<msize; i+=(strlen((const char *)&buf[i])+1)) + printf(" meta data : %s\n", &buf[i]); + printf(" image size : %zu bytes\n", isize); + } + + /* verify checksum */ + if (isize > 0) + { + if (msg) + { + printf(" checksum : "); + for (i=0; i<16; i++) printf("%02X", checksum[i]); + printf("\n"); + } + + /* Calculate the checksum */ + calculate_digest(fh, isize, digest); + if (msg) + { + printf(" digest : "); + for (i=0; i<16; i++) printf("%02X", digest[i]); + printf("\n"); + } + + if (memcmp(checksum, digest, 16)!=0) ERRBREAK("!!ERROR!! checksum error !!\n"); + ret = 0; + } + } + if (msg) printf("================================================\n"); + } while (0); + if (fh) fclose(fh); + return ret; +} + +static size_t write_seama_header(FILE * fh, char * meta[], size_t msize, size_t size) +{ + seamahdr_t shdr; + size_t i; + uint16_t metasize = 0; + + /* Calculate the META size */ + for (i=0; i<msize; i++) metasize += (strlen(meta[i]) + 1); + //+++ let meta data end on 4 alignment by siyou. 2010/3/1 03:58pm + metasize = ((metasize+3)/4)*4; + verbose("SEAMA META : %d bytes\n", metasize); + + /* Fill up the header, all the data endian should be network byte order. */ + shdr.magic = htonl(SEAMA_MAGIC); + shdr.reserved = 0; + shdr.metasize = htons(metasize); + shdr.size = htonl(size); + + /* Write the header */ + return fwrite(&shdr, sizeof(seamahdr_t), 1, fh); +} + +static size_t write_checksum(FILE * fh, uint8_t * checksum) +{ + return fwrite(checksum, sizeof(uint8_t), 16, fh); +} + +static size_t write_meta_data(FILE * fh, char * meta[], size_t size) +{ + size_t i,j; + size_t ret = 0; + + for (i=0; i<size; i++) + { + verbose("SEAMA META data : %s\n", meta[i]); + j = fwrite(meta[i], sizeof(char), strlen(meta[i])+1, fh); + if (j != strlen(meta[i])+1) return 0; + ret += j; + } + //+++ let meta data end on 4 alignment by siyou. 2010/3/1 03:58pm + j = ((ret+3)/4)*4; + for ( ; ret < j; ret++) + fwrite("", sizeof(char), 1, fh); + + return ret; +} + +/*******************************************************************/ + +static void dump_seama(const char * fname) +{ + verify_seama(fname, 1); +} + +static void seal_files(const char * file) +{ + FILE * fh; + FILE * ifh; + size_t i; + + /* Each image should be seama. */ + for (i = 0; i < o_isize; i++) + { + if (verify_seama(o_images[i], 0) < 0) + { + printf("'%s' is not a seama file !\n",o_images[i]); + return; + } + } + + /* Open file for write */ + fh = fopen(file, "w+"); + if (fh) + { + /* Write the header. */ + write_seama_header(fh, o_meta, o_msize, 0); + write_meta_data(fh, o_meta, o_msize); + + /* Write image files */ + for (i=0; i<o_isize; i++) + { + ifh = fopen(o_images[i], "r+"); + if (ifh) + { + copy_file(fh, ifh); + fclose(ifh); + } + } + + fclose(fh); + } +} + +static void pack_files(void) +{ + FILE * fh; + FILE * ifh; + size_t i, fsize; + char filename[512]; + uint8_t digest[16]; + + for (i=0; i<o_isize; i++) + { + /* Open the input file. */ + ifh = fopen(o_images[i], "r+"); + if (ifh) + { + fsize = calculate_digest(ifh, 0, digest); + verbose("file size (%s) : %d\n", o_images[i], fsize); + rewind(ifh); + + /* Open the output file. */ + sprintf(filename, "%s.seama", o_images[i]); + fh = fopen(filename, "w+"); + if (fh) + { + write_seama_header(fh, o_meta, o_msize, fsize); + write_checksum(fh, digest); + write_meta_data(fh, o_meta, o_msize); + copy_file(fh, ifh); + fclose(fh); + } + fclose(ifh); + } + else + { + printf("Unable to open image file '%s'\n",o_images[i]); + } + } +} + +/**************************************************************************/ + +static int match_meta(const char * meta, size_t size) +{ + size_t i, j; + int match; + + for (i = 0; i < o_msize; i++) + { + for (match = 0, j = 0; j < size; j += (strlen(&meta[j])+1)) + if (strcmp(&meta[j], o_meta[i])==0) { match++; break; } + if (!match) return 0; + } + return 1; +} + + +static void extract_file(const char * output) +{ + FILE * ifh = NULL; + FILE * ofh = NULL; + size_t msize, isize, i, m; + seamahdr_t shdr; + uint8_t buf[MAX_SEAMA_META_SIZE]; + int done = 0; + + /* We need meta for searching the target image. */ + if (o_msize == 0) + { + printf("SEAMA: need meta for searching image.\n"); + return; + } + + /* Walk through each input file */ + for (i = 0; i < o_isize; i++) + { + /* verify the input file */ + if (verify_seama(o_images[i], 0) < 0) + { + printf("SEAMA: '%s' is not a seama file !\n", o_images[i]); + continue; + } + /* open the input file */ + ifh = fopen(o_images[i], "r"); + if (!ifh) continue; + /* read file */ + while (!feof(ifh) && !ferror(ifh)) + { + /* read header */ + fread(&shdr, sizeof(shdr), 1, ifh); + if (shdr.magic != htonl(SEAMA_MAGIC)) break; + /* Get the size */ + isize = ntohl(shdr.size); + msize = ntohs(shdr.metasize); + if (isize == 0) + { + while (msize > 0) + { + m = fread(buf, sizeof(char), (msize < MAX_SEAMA_META_SIZE) ? msize : MAX_SEAMA_META_SIZE, ifh); + if (m <= 0) break; + msize -= m; + } + continue; + } + /* read checksum */ + fread(buf, sizeof(char), 16, ifh); + if (msize > 0) + { + /* read META */ + fread(buf, sizeof(char), msize, ifh); + if (match_meta((const char *)buf, msize)) + { + printf("SEAMA: found image @ '%s', image size: %zu\n", o_images[i], isize); + /* open output file */ + ofh = fopen(output, "w"); + if (!ofh) printf("SEAMA: unable to open '%s' for writting.\n",output); + else + { + while (isize > 0) + { + m = fread(buf, sizeof(char), (isize < MAX_SEAMA_META_SIZE) ? isize : MAX_SEAMA_META_SIZE, ifh); + if (m <= 0) break; + fwrite(buf, sizeof(char), m, ofh); + isize -= m; + } + fclose(ofh); + } + done++; + break; + } + } + while (isize > 0) + { + m = fread(buf, sizeof(char), (isize < MAX_SEAMA_META_SIZE) ? isize : MAX_SEAMA_META_SIZE, ifh); + if (m <= 0) break; + isize -= m; + } + } + /* close the file. */ + fclose(ifh); + if (done) break; + } + return; +} + +/*******************************************************************/ +#ifdef RGBIN_BOX +int seama_main(int argc, char * argv[], char * env[]) +#else +int main(int argc, char * argv[], char * env[]) +#endif +{ + verbose("SEAMA version " VERSION "\n"); + + /* parse the arguments */ + if (parse_args(argc, argv) < 0) show_usage(9); + + /* Do the works */ + if (o_dump) dump_seama(o_dump); + else if (o_seal) seal_files(o_seal); + else if (o_extract) extract_file(o_extract); + else pack_files(); + + cleanup_exit(0); + return 0; +} diff --git a/tools/firmware-utils/src/seama.h b/tools/firmware-utils/src/seama.h new file mode 100644 index 00000000000..02683b6e98d --- /dev/null +++ b/tools/firmware-utils/src/seama.h @@ -0,0 +1,108 @@ +/* vi: set sw=4 ts=4: */ +/* + * (SEA)ttle i(MA)ge is the image which used in project seattle. + * + * Created by David Hsieh <david_hsieh@alphanetworks.com> + * Copyright (C) 2008-2009 Alpha Networks, Inc. + * + * This file is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either' + * version 2.1 of the License, or (at your option) any later version. + * + * The GNU C Library is distributed in the hope that it will be useful,' + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the GNU C Library; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA. + */ + +#ifndef __SEAMA_HEADER_FILE__ +#define __SEAMA_HEADER_FILE__ + +#include <stdint.h> + +#define SEAMA_MAGIC 0x5EA3A417 + +/* + * SEAMA looks like the following map. + * All the data of the header should be in network byte order. + * + * +-------------+-------------+------------ + * | SEAMA magic | ^ + * +-------------+-------------+ | + * | reserved | meta size | | + * +-------------+-------------+ header + * | image size (0 bytes) | | + * +-------------+-------------+ | + * ~ Meta data ~ v + * +-------------+-------------+------------ + * | SEAMA magic | ^ ^ + * +-------------+-------------+ | | + * | reserved | meta size | | | + * +-------------+-------------+ | | + * | image size | | | + * +-------------+-------------+ header | + * | | | | + * | 16 bytes of MD5 digest | | | + * | | | | + * | | | | + * +-------------+-------------+ | | + * ~ Meta data ~ v | + * +-------------+-------------+------- | + * | | | + * | Image of the 1st entity | | + * ~ ~ 1st entity + * | | | + * | | v + * +-------------+-------------+------------- + * | SEAMA magic | ^ ^ + * +-------------+-------------+ | | + * | reserved | meta size | | | + * +-------------+-------------+ | | + * | image size | | | + * +-------------+-------------+ header | + * | | | | + * | 16 bytes of MD5 digest | | | + * | | | | + * | | | | + * +-------------+-------------+ | | + * ~ Meta data ~ v | + * +-------------+-------------+------- | + * | | | + * | Image of the 2nd entity | | + * ~ ~ 2nd entity + * | | | + * | | v + * +-------------+-------------+------------- + */ + + +/* + * SEAMA header + * + * |<-------- 32 bits -------->| + * +-------------+-------------+ + * | SEAMA magic | + * +-------------+-------------+ + * | reserved | meta size | + * +-------------+-------------+ + * | image size | + * +-------------+-------------+ + */ +/* seama header */ +typedef struct seama_hdr seamahdr_t; +struct seama_hdr +{ + uint32_t magic; /* should always be SEAMA_MAGIC. */ + uint16_t reserved; /* reserved for */ + uint16_t metasize; /* size of the META data */ + uint32_t size; /* size of the image */ +} __attribute__ ((packed)); + + +#endif diff --git a/tools/firmware-utils/src/spw303v.c b/tools/firmware-utils/src/spw303v.c index ae34a1ebddc..684532d7e11 100644 --- a/tools/firmware-utils/src/spw303v.c +++ b/tools/firmware-utils/src/spw303v.c @@ -18,11 +18,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> -#include <time.h> #include <unistd.h> #include <sys/stat.h> diff --git a/tools/firmware-utils/src/srec2bin.c b/tools/firmware-utils/src/srec2bin.c index 1cffbaed969..5cc71bda220 100644 --- a/tools/firmware-utils/src/srec2bin.c +++ b/tools/firmware-utils/src/srec2bin.c @@ -513,7 +513,7 @@ int srec2bin(int argc,char *argv[],int verbose) return(1); } -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { debug = TRUE; debug = FALSE; diff --git a/tools/firmware-utils/src/tplink-safeloader.c b/tools/firmware-utils/src/tplink-safeloader.c new file mode 100644 index 00000000000..78092bc535c --- /dev/null +++ b/tools/firmware-utils/src/tplink-safeloader.c @@ -0,0 +1,2084 @@ +/* + Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +/* + tplink-safeloader + + Image generation tool for the TP-LINK SafeLoader as seen on + TP-LINK Pharos devices (CPE210/220/510/520) +*/ + + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> + +#include "md5.h" + + +#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + + +#define MAX_PARTITIONS 32 + +/** An image partition table entry */ +struct image_partition_entry { + const char *name; + size_t size; + uint8_t *data; +}; + +/** A flash partition table entry */ +struct flash_partition_entry { + char *name; + uint32_t base; + uint32_t size; +}; + +/** Firmware layout description */ +struct device_info { + const char *id; + const char *vendor; + const char *support_list; + char support_trail; + const char *soft_ver; + struct flash_partition_entry partitions[MAX_PARTITIONS+1]; + const char *first_sysupgrade_partition; + const char *last_sysupgrade_partition; +}; + +/** The content of the soft-version structure */ +struct __attribute__((__packed__)) soft_version { + uint32_t magic; + uint32_t zero; + uint8_t pad1; + uint8_t version_major; + uint8_t version_minor; + uint8_t version_patch; + uint8_t year_hi; + uint8_t year_lo; + uint8_t month; + uint8_t day; + uint32_t rev; + uint8_t pad2; +}; + + +static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde}; + + +/** + Salt for the MD5 hash + + Fortunately, TP-LINK seems to use the same salt for most devices which use + the new image format. +*/ +static const uint8_t md5_salt[16] = { + 0x7a, 0x2b, 0x15, 0xed, + 0x9b, 0x98, 0x59, 0x6d, + 0xe5, 0x04, 0xab, 0x44, + 0xac, 0x2a, 0x9f, 0x4e, +}; + + +/** Firmware layout table */ +static struct device_info boards[] = { + /** Firmware layout for the CPE210/220 */ + { + .id = "CPE210", + .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n", + .support_list = + "SupportList:\r\n" + "CPE210(TP-LINK|UN|N300-2):1.0\r\n" + "CPE210(TP-LINK|UN|N300-2):1.1\r\n" + "CPE210(TP-LINK|US|N300-2):1.1\r\n" + "CPE210(TP-LINK|EU|N300-2):1.1\r\n" + "CPE220(TP-LINK|UN|N300-2):1.1\r\n" + "CPE220(TP-LINK|US|N300-2):1.1\r\n" + "CPE220(TP-LINK|EU|N300-2):1.1\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"signature", 0x32000, 0x00400}, + {"os-image", 0x40000, 0x1c0000}, + {"file-system", 0x200000, 0x5b0000}, + {"soft-version", 0x7b0000, 0x00100}, + {"support-list", 0x7b1000, 0x00400}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "support-list", + }, + + /** Firmware layout for the CPE210 V2 */ + { + .id = "CPE210V2", + .vendor = "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n", + .support_list = + "SupportList:\r\n" + "CPE210(TP-LINK|EU|N300-2|00000000):2.0\r\n" + "CPE210(TP-LINK|EU|N300-2|45550000):2.0\r\n" + "CPE210(TP-LINK|EU|N300-2|55530000):2.0\r\n" + "CPE210(TP-LINK|UN|N300-2|00000000):2.0\r\n" + "CPE210(TP-LINK|UN|N300-2|45550000):2.0\r\n" + "CPE210(TP-LINK|UN|N300-2|55530000):2.0\r\n" + "CPE210(TP-LINK|US|N300-2|55530000):2.0\r\n" + "CPE210(TP-LINK|UN|N300-2):2.0\r\n" + "CPE210(TP-LINK|EU|N300-2):2.0\r\n" + "CPE210(TP-LINK|US|N300-2):2.0\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"device-info", 0x31400, 0x00400}, + {"signature", 0x32000, 0x00400}, + {"device-id", 0x33000, 0x00100}, + {"os-image", 0x40000, 0x1c0000}, + {"file-system", 0x200000, 0x5b0000}, + {"soft-version", 0x7b0000, 0x00100}, + {"support-list", 0x7b1000, 0x01000}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "support-list", + }, + + /** Firmware layout for the CPE510/520 */ + { + .id = "CPE510", + .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n", + .support_list = + "SupportList:\r\n" + "CPE510(TP-LINK|UN|N300-5):1.0\r\n" + "CPE510(TP-LINK|UN|N300-5):1.1\r\n" + "CPE510(TP-LINK|UN|N300-5):1.1\r\n" + "CPE510(TP-LINK|US|N300-5):1.1\r\n" + "CPE510(TP-LINK|EU|N300-5):1.1\r\n" + "CPE520(TP-LINK|UN|N300-5):1.1\r\n" + "CPE520(TP-LINK|US|N300-5):1.1\r\n" + "CPE520(TP-LINK|EU|N300-5):1.1\r\n" + "CPE510(TP-LINK|EU|N300-5|00000000):2.0\r\n" + "CPE510(TP-LINK|EU|N300-5|45550000):2.0\r\n" + "CPE510(TP-LINK|EU|N300-5|55530000):2.0\r\n" + "CPE510(TP-LINK|UN|N300-5|00000000):2.0\r\n" + "CPE510(TP-LINK|UN|N300-5|45550000):2.0\r\n" + "CPE510(TP-LINK|UN|N300-5|55530000):2.0\r\n" + "CPE510(TP-LINK|US|N300-5|55530000):2.0\r\n" + "CPE510(TP-LINK|UN|N300-5):2.0\r\n" + "CPE510(TP-LINK|EU|N300-5):2.0\r\n" + "CPE510(TP-LINK|US|N300-5):2.0\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"signature", 0x32000, 0x00400}, + {"os-image", 0x40000, 0x1c0000}, + {"file-system", 0x200000, 0x5b0000}, + {"soft-version", 0x7b0000, 0x00100}, + {"support-list", 0x7b1000, 0x00400}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "support-list", + }, + + { + .id = "WBS210", + .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n", + .support_list = + "SupportList:\r\n" + "WBS210(TP-LINK|UN|N300-2):1.20\r\n" + "WBS210(TP-LINK|US|N300-2):1.20\r\n" + "WBS210(TP-LINK|EU|N300-2):1.20\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"signature", 0x32000, 0x00400}, + {"os-image", 0x40000, 0x1c0000}, + {"file-system", 0x200000, 0x5b0000}, + {"soft-version", 0x7b0000, 0x00100}, + {"support-list", 0x7b1000, 0x00400}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "support-list", + }, + + { + .id = "WBS510", + .vendor = "CPE510(TP-LINK|UN|N300-5):1.0\r\n", + .support_list = + "SupportList:\r\n" + "WBS510(TP-LINK|UN|N300-5):1.20\r\n" + "WBS510(TP-LINK|US|N300-5):1.20\r\n" + "WBS510(TP-LINK|EU|N300-5):1.20\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"signature", 0x32000, 0x00400}, + {"os-image", 0x40000, 0x1c0000}, + {"file-system", 0x200000, 0x5b0000}, + {"soft-version", 0x7b0000, 0x00100}, + {"support-list", 0x7b1000, 0x00400}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "support-list", + }, + + /** Firmware layout for the C2600 */ + { + .id = "C2600", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C2600,product_ver:1.0.0,special_id:00000000}\r\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** + We use a bigger os-image partition than the stock images (and thus + smaller file-system), as our kernel doesn't fit in the stock firmware's + 2 MB os-image since kernel 4.14. + */ + .partitions = { + {"SBL1", 0x00000, 0x20000}, + {"MIBIB", 0x20000, 0x20000}, + {"SBL2", 0x40000, 0x20000}, + {"SBL3", 0x60000, 0x30000}, + {"DDRCONFIG", 0x90000, 0x10000}, + {"SSD", 0xa0000, 0x10000}, + {"TZ", 0xb0000, 0x30000}, + {"RPM", 0xe0000, 0x20000}, + {"fs-uboot", 0x100000, 0x70000}, + {"uboot-env", 0x170000, 0x40000}, + {"radio", 0x1b0000, 0x40000}, + {"os-image", 0x1f0000, 0x400000}, /* Stock: base 0x1f0000 size 0x200000 */ + {"file-system", 0x5f0000, 0x1900000}, /* Stock: base 0x3f0000 size 0x1b00000 */ + {"default-mac", 0x1ef0000, 0x00200}, + {"pin", 0x1ef0200, 0x00200}, + {"product-info", 0x1ef0400, 0x0fc00}, + {"partition-table", 0x1f00000, 0x10000}, + {"soft-version", 0x1f10000, 0x10000}, + {"support-list", 0x1f20000, 0x10000}, + {"profile", 0x1f30000, 0x10000}, + {"default-config", 0x1f40000, 0x10000}, + {"user-config", 0x1f50000, 0x40000}, + {"qos-db", 0x1f90000, 0x40000}, + {"usb-config", 0x1fd0000, 0x10000}, + {"log", 0x1fe0000, 0x20000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the C25v1 */ + { + .id = "ARCHER-C25-V1", + .support_list = + "SupportList:\n" + "{product_name:ArcherC25,product_ver:1.0.0,special_id:00000000}\n" + "{product_name:ArcherC25,product_ver:1.0.0,special_id:55530000}\n" + "{product_name:ArcherC25,product_ver:1.0.0,special_id:45550000}\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + /** + We use a bigger os-image partition than the stock images (and thus + smaller file-system), as our kernel doesn't fit in the stock firmware's + 1MB os-image. + */ + .partitions = { + {"factory-boot", 0x00000, 0x20000}, + {"fs-uboot", 0x20000, 0x10000}, + {"os-image", 0x30000, 0x180000}, /* Stock: base 0x30000 size 0x100000 */ + {"file-system", 0x1b0000, 0x620000}, /* Stock: base 0x130000 size 0x6a0000 */ + {"user-config", 0x7d0000, 0x04000}, + {"default-mac", 0x7e0000, 0x00100}, + {"device-id", 0x7e0100, 0x00100}, + {"extra-para", 0x7e0200, 0x00100}, + {"pin", 0x7e0300, 0x00100}, + {"support-list", 0x7e0400, 0x00400}, + {"soft-version", 0x7e0800, 0x00400}, + {"product-info", 0x7e0c00, 0x01400}, + {"partition-table", 0x7e2000, 0x01000}, + {"profile", 0x7e3000, 0x01000}, + {"default-config", 0x7e4000, 0x04000}, + {"merge-config", 0x7ec000, 0x02000}, + {"qos-db", 0x7ee000, 0x02000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C58v1 */ + { + .id = "ARCHER-C58-V1", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C58,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:Archer C58,product_ver:1.0.0,special_id:45550000}\r\n" + "{product_name:Archer C58,product_ver:1.0.0,special_id:55530000}\r\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + .partitions = { + {"fs-uboot", 0x00000, 0x10000}, + {"default-mac", 0x10000, 0x00200}, + {"pin", 0x10200, 0x00200}, + {"product-info", 0x10400, 0x00100}, + {"partition-table", 0x10500, 0x00800}, + {"soft-version", 0x11300, 0x00200}, + {"support-list", 0x11500, 0x00100}, + {"device-id", 0x11600, 0x00100}, + {"profile", 0x11700, 0x03900}, + {"default-config", 0x15000, 0x04000}, + {"user-config", 0x19000, 0x04000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0x648000}, + {"certyficate", 0x7e8000, 0x08000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C59v1 */ + { + .id = "ARCHER-C59-V1", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C59,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:Archer C59,product_ver:1.0.0,special_id:45550000}\r\n" + "{product_name:Archer C59,product_ver:1.0.0,special_id:52550000}\r\n" + "{product_name:Archer C59,product_ver:1.0.0,special_id:55530000}\r\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + .partitions = { + {"fs-uboot", 0x00000, 0x10000}, + {"default-mac", 0x10000, 0x00200}, + {"pin", 0x10200, 0x00200}, + {"device-id", 0x10400, 0x00100}, + {"product-info", 0x10500, 0x0fb00}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0xcb0000}, + {"partition-table", 0xe50000, 0x10000}, + {"soft-version", 0xe60000, 0x10000}, + {"support-list", 0xe70000, 0x10000}, + {"profile", 0xe80000, 0x10000}, + {"default-config", 0xe90000, 0x10000}, + {"user-config", 0xea0000, 0x40000}, + {"usb-config", 0xee0000, 0x10000}, + {"certificate", 0xef0000, 0x10000}, + {"qos-db", 0xf00000, 0x40000}, + {"log", 0xfe0000, 0x10000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C59v2 */ + { + .id = "ARCHER-C59-V2", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C59,product_ver:2.0.0,special_id:00000000}\r\n" + "{product_name:Archer C59,product_ver:2.0.0,special_id:45550000}\r\n" + "{product_name:Archer C59,product_ver:2.0.0,special_id:55530000}\r\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:2.0.0 Build 20161206 rel.7303\n", + + /** We're using a dynamic kernel/rootfs split here */ + .partitions = { + {"factory-boot", 0x00000, 0x20000}, + {"fs-uboot", 0x20000, 0x10000}, + {"default-mac", 0x30000, 0x00200}, + {"pin", 0x30200, 0x00200}, + {"device-id", 0x30400, 0x00100}, + {"product-info", 0x30500, 0x0fb00}, + {"firmware", 0x40000, 0xe10000}, + {"partition-table", 0xe50000, 0x10000}, + {"soft-version", 0xe60000, 0x10000}, + {"support-list", 0xe70000, 0x10000}, + {"profile", 0xe80000, 0x10000}, + {"default-config", 0xe90000, 0x10000}, + {"user-config", 0xea0000, 0x40000}, + {"usb-config", 0xee0000, 0x10000}, + {"certificate", 0xef0000, 0x10000}, + {"extra-para", 0xf00000, 0x10000}, + {"qos-db", 0xf10000, 0x30000}, + {"log", 0xfe0000, 0x10000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C60v1 */ + { + .id = "ARCHER-C60-V1", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C60,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:Archer C60,product_ver:1.0.0,special_id:45550000}\r\n" + "{product_name:Archer C60,product_ver:1.0.0,special_id:55530000}\r\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + .partitions = { + {"fs-uboot", 0x00000, 0x10000}, + {"default-mac", 0x10000, 0x00200}, + {"pin", 0x10200, 0x00200}, + {"product-info", 0x10400, 0x00100}, + {"partition-table", 0x10500, 0x00800}, + {"soft-version", 0x11300, 0x00200}, + {"support-list", 0x11500, 0x00100}, + {"device-id", 0x11600, 0x00100}, + {"profile", 0x11700, 0x03900}, + {"default-config", 0x15000, 0x04000}, + {"user-config", 0x19000, 0x04000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0x648000}, + {"certyficate", 0x7e8000, 0x08000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C60v2 */ + { + .id = "ARCHER-C60-V2", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:Archer C60,product_ver:2.0.0,special_id:42520000}\r\n" + "{product_name:Archer C60,product_ver:2.0.0,special_id:45550000}\r\n" + "{product_name:Archer C60,product_ver:2.0.0,special_id:55530000}\r\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:2.0.0\n", + + .partitions = { + {"factory-boot", 0x00000, 0x1fb00}, + {"default-mac", 0x1fb00, 0x00200}, + {"pin", 0x1fd00, 0x00100}, + {"product-info", 0x1fe00, 0x00100}, + {"device-id", 0x1ff00, 0x00100}, + {"fs-uboot", 0x20000, 0x10000}, + {"os-image", 0x30000, 0x180000}, + {"file-system", 0x1b0000, 0x620000}, + {"soft-version", 0x7d9500, 0x00100}, + {"support-list", 0x7d9600, 0x00100}, + {"extra-para", 0x7d9700, 0x00100}, + {"profile", 0x7d9800, 0x03000}, + {"default-config", 0x7dc800, 0x03000}, + {"partition-table", 0x7df800, 0x00800}, + {"user-config", 0x7e0000, 0x0c000}, + {"certificate", 0x7ec000, 0x04000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C5 */ + { + .id = "ARCHER-C5-V2", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:ArcherC5,product_ver:2.0.0,special_id:00000000}\r\n" + "{product_name:ArcherC5,product_ver:2.0.0,special_id:55530000}\r\n" + "{product_name:ArcherC5,product_ver:2.0.0,special_id:4A500000}\r\n", /* JP version */ + .support_trail = '\x00', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x40000}, + {"os-image", 0x40000, 0x200000}, + {"file-system", 0x240000, 0xc00000}, + {"default-mac", 0xe40000, 0x00200}, + {"pin", 0xe40200, 0x00200}, + {"product-info", 0xe40400, 0x00200}, + {"partition-table", 0xe50000, 0x10000}, + {"soft-version", 0xe60000, 0x00200}, + {"support-list", 0xe61000, 0x0f000}, + {"profile", 0xe70000, 0x10000}, + {"default-config", 0xe80000, 0x10000}, + {"user-config", 0xe90000, 0x50000}, + {"log", 0xee0000, 0x100000}, + {"radio_bk", 0xfe0000, 0x10000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the C7 */ + { + .id = "ARCHER-C7-V4", + .support_list = + "SupportList:\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:00000000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:41550000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:45550000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:4B520000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:42520000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:4A500000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:52550000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:54570000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:55530000}\n" + "{product_name:Archer C7,product_ver:4.0.0,special_id:43410000}\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + /** + We use a bigger os-image partition than the stock images (and thus + smaller file-system), as our kernel doesn't fit in the stock firmware's + 1MB os-image. + */ + .partitions = { + {"factory-boot", 0x00000, 0x20000}, + {"fs-uboot", 0x20000, 0x20000}, + {"os-image", 0x40000, 0x180000}, /* Stock: base 0x40000 size 0x120000 */ + {"file-system", 0x1c0000, 0xd40000}, /* Stock: base 0x160000 size 0xda0000 */ + {"default-mac", 0xf00000, 0x00200}, + {"pin", 0xf00200, 0x00200}, + {"device-id", 0xf00400, 0x00100}, + {"product-info", 0xf00500, 0x0fb00}, + {"soft-version", 0xf10000, 0x00100}, + {"extra-para", 0xf11000, 0x01000}, + {"support-list", 0xf12000, 0x0a000}, + {"profile", 0xf1c000, 0x04000}, + {"default-config", 0xf20000, 0x10000}, + {"user-config", 0xf30000, 0x40000}, + {"qos-db", 0xf70000, 0x40000}, + {"certificate", 0xfb0000, 0x10000}, + {"partition-table", 0xfc0000, 0x10000}, + {"log", 0xfd0000, 0x20000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C7 v5*/ + { + .id = "ARCHER-C7-V5", + .support_list = + "SupportList:\n" + "{product_name:Archer C7,product_ver:5.0.0,special_id:00000000}\n" + "{product_name:Archer C7,product_ver:5.0.0,special_id:55530000}\n", + + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + + /** + We use a bigger os-image partition than the stock images (and thus + smaller file-system), as our kernel doesn't fit in the stock firmware's + 1MB os-image. + */ + .partitions = { + {"factory-boot", 0x00000, 0x20000}, + {"fs-uboot", 0x20000, 0x20000}, + {"partition-table", 0x40000, 0x10000}, + {"radio", 0x50000, 0x10000}, + {"default-mac", 0x60000, 0x00200}, + {"pin", 0x60200, 0x00200}, + {"device-id", 0x60400, 0x00100}, + {"product-info", 0x60500, 0x0fb00}, + {"soft-version", 0x70000, 0x01000}, + {"extra-para", 0x71000, 0x01000}, + {"support-list", 0x72000, 0x0a000}, + {"profile", 0x7c000, 0x04000}, + {"user-config", 0x80000, 0x40000}, + + + {"os-image", 0xc0000, 0x180000}, /* Stock: base 0xc0000 size 0x120000 */ + {"file-system", 0x240000, 0xd80000}, /* Stock: base 0x1e0000 size 0xde0000 */ + + {"log", 0xfc0000, 0x20000}, + {"certificate", 0xfe0000, 0x10000}, + {"default-config", 0xff0000, 0x10000}, + {NULL, 0, 0} + + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the C9 */ + { + .id = "ARCHERC9", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:ArcherC9," + "product_ver:1.0.0," + "special_id:00000000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x40000}, + {"os-image", 0x40000, 0x200000}, + {"file-system", 0x240000, 0xc00000}, + {"default-mac", 0xe40000, 0x00200}, + {"pin", 0xe40200, 0x00200}, + {"product-info", 0xe40400, 0x00200}, + {"partition-table", 0xe50000, 0x10000}, + {"soft-version", 0xe60000, 0x00200}, + {"support-list", 0xe61000, 0x0f000}, + {"profile", 0xe70000, 0x10000}, + {"default-config", 0xe80000, 0x10000}, + {"user-config", 0xe90000, 0x50000}, + {"log", 0xee0000, 0x100000}, + {"radio_bk", 0xfe0000, 0x10000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the EAP120 */ + { + .id = "EAP120", + .vendor = "EAP120(TP-LINK|UN|N300-2):1.0\r\n", + .support_list = + "SupportList:\r\n" + "EAP120(TP-LINK|UN|N300-2):1.0\r\n", + .support_trail = '\xff', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"support-list", 0x31000, 0x00100}, + {"product-info", 0x31100, 0x00100}, + {"soft-version", 0x32000, 0x00100}, + {"os-image", 0x40000, 0x180000}, + {"file-system", 0x1c0000, 0x600000}, + {"user-config", 0x7c0000, 0x10000}, + {"backup-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the TL-WA850RE v2 */ + { + .id = "TLWA850REV2", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55530000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:00000000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:55534100}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:45550000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4B520000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:42520000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:4A500000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:43410000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:41550000}\n" + "{product_name:TL-WA850RE,product_ver:2.0.0,special_id:52550000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** + 576KB were moved from file-system to os-image + in comparison to the stock image + */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x150000}, + {"file-system", 0x170000, 0x240000}, + {"partition-table", 0x3b0000, 0x02000}, + {"default-mac", 0x3c0000, 0x00020}, + {"pin", 0x3c0100, 0x00020}, + {"product-info", 0x3c1000, 0x01000}, + {"soft-version", 0x3c2000, 0x00100}, + {"support-list", 0x3c3000, 0x01000}, + {"profile", 0x3c4000, 0x08000}, + {"user-config", 0x3d0000, 0x10000}, + {"default-config", 0x3e0000, 0x10000}, + {"radio", 0x3f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the TL-WA855RE v1 */ + { + .id = "TLWA855REV1", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:00000000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:55530000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:45550000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4B520000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:42520000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:4A500000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:43410000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:41550000}\n" + "{product_name:TL-WA855RE,product_ver:1.0.0,special_id:52550000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x150000}, + {"file-system", 0x170000, 0x240000}, + {"partition-table", 0x3b0000, 0x02000}, + {"default-mac", 0x3c0000, 0x00020}, + {"pin", 0x3c0100, 0x00020}, + {"product-info", 0x3c1000, 0x01000}, + {"soft-version", 0x3c2000, 0x00100}, + {"support-list", 0x3c3000, 0x01000}, + {"profile", 0x3c4000, 0x08000}, + {"user-config", 0x3d0000, 0x10000}, + {"default-config", 0x3e0000, 0x10000}, + {"radio", 0x3f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the TL-WR1043 v5 */ + { + .id = "TLWR1043NV5", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:45550000}\n" + "{product_name:TL-WR1043N,product_ver:5.0.0,special_id:55530000}\n", + .support_trail = '\x00', + .soft_ver = "soft_ver:1.0.0\n", + .partitions = { + {"factory-boot", 0x00000, 0x20000}, + {"fs-uboot", 0x20000, 0x20000}, + {"os-image", 0x40000, 0x180000}, + {"file-system", 0x1c0000, 0xd40000}, + {"default-mac", 0xf00000, 0x00200}, + {"pin", 0xf00200, 0x00200}, + {"device-id", 0xf00400, 0x00100}, + {"product-info", 0xf00500, 0x0fb00}, + {"soft-version", 0xf10000, 0x01000}, + {"extra-para", 0xf11000, 0x01000}, + {"support-list", 0xf12000, 0x0a000}, + {"profile", 0xf1c000, 0x04000}, + {"default-config", 0xf20000, 0x10000}, + {"user-config", 0xf30000, 0x40000}, + {"qos-db", 0xf70000, 0x40000}, + {"certificate", 0xfb0000, 0x10000}, + {"partition-table", 0xfc0000, 0x10000}, + {"log", 0xfd0000, 0x20000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the TL-WR1043 v4 */ + { + .id = "TLWR1043NDV4", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:TL-WR1043ND,product_ver:4.0.0,special_id:45550000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /* We're using a dynamic kernel/rootfs split here */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"firmware", 0x20000, 0xf30000}, + {"default-mac", 0xf50000, 0x00200}, + {"pin", 0xf50200, 0x00200}, + {"product-info", 0xf50400, 0x0fc00}, + {"soft-version", 0xf60000, 0x0b000}, + {"support-list", 0xf6b000, 0x04000}, + {"profile", 0xf70000, 0x04000}, + {"default-config", 0xf74000, 0x0b000}, + {"user-config", 0xf80000, 0x40000}, + {"partition-table", 0xfc0000, 0x10000}, + {"log", 0xfd0000, 0x20000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the TL-WR902AC v1 */ + { + .id = "TL-WR902AC-V1", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:45550000}\n" + "{product_name:TL-WR902AC,product_ver:1.0.0,special_id:55530000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** + 384KB were moved from file-system to os-image + in comparison to the stock image + */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0x5b0000}, + {"default-mac", 0x750000, 0x00200}, + {"pin", 0x750200, 0x00200}, + {"product-info", 0x750400, 0x0fc00}, + {"soft-version", 0x760000, 0x0b000}, + {"support-list", 0x76b000, 0x04000}, + {"profile", 0x770000, 0x04000}, + {"default-config", 0x774000, 0x0b000}, + {"user-config", 0x780000, 0x40000}, + {"partition-table", 0x7c0000, 0x10000}, + {"log", 0x7d0000, 0x20000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the TL-WR942N V1 */ + { + .id = "TLWR942NV1", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:TL-WR942N,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:TL-WR942N,product_ver:1.0.0,special_id:52550000}\r\n", + .support_trail = '\x00', + .soft_ver = NULL, + + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0xca0000}, + {"default-mac", 0xe40000, 0x00200}, + {"pin", 0xe40200, 0x00200}, + {"product-info", 0xe40400, 0x0fc00}, + {"partition-table", 0xe50000, 0x10000}, + {"soft-version", 0xe60000, 0x10000}, + {"support-list", 0xe70000, 0x10000}, + {"profile", 0xe80000, 0x10000}, + {"default-config", 0xe90000, 0x10000}, + {"user-config", 0xea0000, 0x40000}, + {"qos-db", 0xee0000, 0x40000}, + {"certificate", 0xf20000, 0x10000}, + {"usb-config", 0xfb0000, 0x10000}, + {"log", 0xfc0000, 0x20000}, + {"radio-bk", 0xfe0000, 0x10000}, + {"radio", 0xff0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system", + }, + + /** Firmware layout for the RE350 v1 */ + { + .id = "RE350-V1", + .vendor = "", + .support_list = + "SupportList:\n" + "{product_name:RE350,product_ver:1.0.0,special_id:45550000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:00000000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:41550000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:55530000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:43410000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:4b520000}\n" + "{product_name:RE350,product_ver:1.0.0,special_id:4a500000}\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** We're using a dynamic kernel/rootfs split here */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"firmware", 0x20000, 0x5e0000}, + {"partition-table", 0x600000, 0x02000}, + {"default-mac", 0x610000, 0x00020}, + {"pin", 0x610100, 0x00020}, + {"product-info", 0x611100, 0x01000}, + {"soft-version", 0x620000, 0x01000}, + {"support-list", 0x621000, 0x01000}, + {"profile", 0x622000, 0x08000}, + {"user-config", 0x630000, 0x10000}, + {"default-config", 0x640000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the RE355 */ + { + .id = "RE355", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:55530000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:45550000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:4A500000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:43410000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:41550000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:4B520000}\r\n" + "{product_name:RE355,product_ver:1.0.0,special_id:55534100}\r\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** + The flash partition table for RE355; + it is almost the same as the one used by the stock images, + 576KB were moved from file-system to os-image. + */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0x460000}, + {"partition-table", 0x600000, 0x02000}, + {"default-mac", 0x610000, 0x00020}, + {"pin", 0x610100, 0x00020}, + {"product-info", 0x611100, 0x01000}, + {"soft-version", 0x620000, 0x01000}, + {"support-list", 0x621000, 0x01000}, + {"profile", 0x622000, 0x08000}, + {"user-config", 0x630000, 0x10000}, + {"default-config", 0x640000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the RE450 */ + { + .id = "RE450", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:00000000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:55530000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:45550000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:4A500000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:43410000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:41550000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:4B520000}\r\n" + "{product_name:RE450,product_ver:1.0.0,special_id:55534100}\r\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /** + The flash partition table for RE450; + it is almost the same as the one used by the stock images, + 576KB were moved from file-system to os-image. + */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"os-image", 0x20000, 0x180000}, + {"file-system", 0x1a0000, 0x460000}, + {"partition-table", 0x600000, 0x02000}, + {"default-mac", 0x610000, 0x00020}, + {"pin", 0x610100, 0x00020}, + {"product-info", 0x611100, 0x01000}, + {"soft-version", 0x620000, 0x01000}, + {"support-list", 0x621000, 0x01000}, + {"profile", 0x622000, 0x08000}, + {"user-config", 0x630000, 0x10000}, + {"default-config", 0x640000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + /** Firmware layout for the RE450 v2 */ + { + .id = "RE450-V2", + .vendor = "", + .support_list = + "SupportList:\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:00000000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:55530000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:45550000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:4A500000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:43410000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:41550000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:41530000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:4B520000}\r\n" + "{product_name:RE450,product_ver:2.0.0,special_id:42520000}\r\n", + .support_trail = '\x00', + .soft_ver = NULL, + + /* We're using a dynamic kernel/rootfs split here */ + .partitions = { + {"fs-uboot", 0x00000, 0x20000}, + {"firmware", 0x20000, 0x5e0000}, + {"partition-table", 0x600000, 0x02000}, + {"default-mac", 0x610000, 0x00020}, + {"pin", 0x610100, 0x00020}, + {"product-info", 0x611100, 0x01000}, + {"soft-version", 0x620000, 0x01000}, + {"support-list", 0x621000, 0x01000}, + {"profile", 0x622000, 0x08000}, + {"user-config", 0x630000, 0x10000}, + {"default-config", 0x640000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + + {NULL, 0, 0} + }, + + .first_sysupgrade_partition = "os-image", + .last_sysupgrade_partition = "file-system" + }, + + {} +}; + +#define error(_ret, _errno, _str, ...) \ + do { \ + fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \ + strerror(_errno)); \ + if (_ret) \ + exit(_ret); \ + } while (0) + + +/** Stores a uint32 as big endian */ +static inline void put32(uint8_t *buf, uint32_t val) { + buf[0] = val >> 24; + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; +} + +/** Allocates a new image partition */ +static struct image_partition_entry alloc_image_partition(const char *name, size_t len) { + struct image_partition_entry entry = {name, len, malloc(len)}; + if (!entry.data) + error(1, errno, "malloc"); + + return entry; +} + +/** Frees an image partition */ +static void free_image_partition(struct image_partition_entry entry) { + free(entry.data); +} + +static time_t source_date_epoch = -1; +static void set_source_date_epoch() { + char *env = getenv("SOURCE_DATE_EPOCH"); + char *endptr = env; + errno = 0; + if (env && *env) { + source_date_epoch = strtoull(env, &endptr, 10); + if (errno || (endptr && *endptr != '\0')) { + fprintf(stderr, "Invalid SOURCE_DATE_EPOCH"); + exit(1); + } + } +} + +/** Generates the partition-table partition */ +static struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) { + struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800); + + char *s = (char *)entry.data, *end = (char *)(s+entry.size); + + *(s++) = 0x00; + *(s++) = 0x04; + *(s++) = 0x00; + *(s++) = 0x00; + + size_t i; + for (i = 0; p[i].name; i++) { + size_t len = end-s; + size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size); + + if (w > len-1) + error(1, 0, "flash partition table overflow?"); + + s += w; + } + + s++; + + memset(s, 0xff, end-s); + + return entry; +} + + +/** Generates a binary-coded decimal representation of an integer in the range [0, 99] */ +static inline uint8_t bcd(uint8_t v) { + return 0x10 * (v/10) + v%10; +} + + +/** Generates the soft-version partition */ +static struct image_partition_entry make_soft_version(uint32_t rev) { + struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version)); + struct soft_version *s = (struct soft_version *)entry.data; + + time_t t; + + if (source_date_epoch != -1) + t = source_date_epoch; + else if (time(&t) == (time_t)(-1)) + error(1, errno, "time"); + + struct tm *tm = localtime(&t); + + s->magic = htonl(0x0000000c); + s->zero = 0; + s->pad1 = 0xff; + + s->version_major = 0; + s->version_minor = 0; + s->version_patch = 0; + + s->year_hi = bcd((1900+tm->tm_year)/100); + s->year_lo = bcd(tm->tm_year%100); + s->month = bcd(tm->tm_mon+1); + s->day = bcd(tm->tm_mday); + s->rev = htonl(rev); + + s->pad2 = 0xff; + + return entry; +} + +static struct image_partition_entry make_soft_version_from_string(const char *soft_ver) { + /** String length _including_ the terminating zero byte */ + uint32_t ver_len = strlen(soft_ver) + 1; + /** Partition contains 64 bit header, the version string, and one additional null byte */ + size_t partition_len = 2*sizeof(uint32_t) + ver_len + 1; + struct image_partition_entry entry = alloc_image_partition("soft-version", partition_len); + + uint32_t *len = (uint32_t *)entry.data; + len[0] = htonl(ver_len); + len[1] = 0; + memcpy(&len[2], soft_ver, ver_len); + + entry.data[partition_len - 1] = 0; + + return entry; +} + +/** Generates the support-list partition */ +static struct image_partition_entry make_support_list(struct device_info *info) { + size_t len = strlen(info->support_list); + struct image_partition_entry entry = alloc_image_partition("support-list", len + 9); + + put32(entry.data, len); + memset(entry.data+4, 0, 4); + memcpy(entry.data+8, info->support_list, len); + entry.data[len+8] = info->support_trail; + + return entry; +} + +/** Creates a new image partition with an arbitrary name from a file */ +static struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof, struct flash_partition_entry *file_system_partition) { + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + error(1, errno, "unable to stat file `%s'", filename); + + size_t len = statbuf.st_size; + + if (add_jffs2_eof) + if (file_system_partition) + len = ALIGN(len + file_system_partition->base, 0x10000) + sizeof(jffs2_eof_mark) - file_system_partition->base; + else + len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark); + + struct image_partition_entry entry = alloc_image_partition(part_name, len); + + FILE *file = fopen(filename, "rb"); + if (!file) + error(1, errno, "unable to open file `%s'", filename); + + if (fread(entry.data, statbuf.st_size, 1, file) != 1) + error(1, errno, "unable to read file `%s'", filename); + + if (add_jffs2_eof) { + uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size; + + memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark)); + memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark)); + } + + fclose(file); + + return entry; +} + +/** Creates a new image partition from arbitrary data */ +static struct image_partition_entry put_data(const char *part_name, const char *datain, size_t len) { + + struct image_partition_entry entry = alloc_image_partition(part_name, len); + + memcpy(entry.data, datain, len); + + return entry; +} + +/** + Copies a list of image partitions into an image buffer and generates the image partition table while doing so + + Example image partition table: + + fwup-ptn partition-table base 0x00800 size 0x00800 + fwup-ptn os-image base 0x01000 size 0x113b45 + fwup-ptn file-system base 0x114b45 size 0x1d0004 + fwup-ptn support-list base 0x2e4b49 size 0x000d1 + + Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"), + the end of the partition table is marked with a zero byte. + + The firmware image must contain at least the partition-table and support-list partitions + to be accepted. There aren't any alignment constraints for the image partitions. + + The partition-table partition contains the actual flash layout; partitions + from the image partition table are mapped to the corresponding flash partitions during + the firmware upgrade. The support-list partition contains a list of devices supported by + the firmware image. + + The base offsets in the firmware partition table are relative to the end + of the vendor information block, so the partition-table partition will + actually start at offset 0x1814 of the image. + + I think partition-table must be the first partition in the firmware image. +*/ +static void put_partitions(uint8_t *buffer, const struct flash_partition_entry *flash_parts, const struct image_partition_entry *parts) { + size_t i, j; + char *image_pt = (char *)buffer, *end = image_pt + 0x800; + + size_t base = 0x800; + for (i = 0; parts[i].name; i++) { + for (j = 0; flash_parts[j].name; j++) { + if (!strcmp(flash_parts[j].name, parts[i].name)) { + if (parts[i].size > flash_parts[j].size) + error(1, 0, "%s partition too big (more than %u bytes)", flash_parts[j].name, (unsigned)flash_parts[j].size); + break; + } + } + + assert(flash_parts[j].name); + + memcpy(buffer + base, parts[i].data, parts[i].size); + + size_t len = end-image_pt; + size_t w = snprintf(image_pt, len, "fwup-ptn %s base 0x%05x size 0x%05x\t\r\n", parts[i].name, (unsigned)base, (unsigned)parts[i].size); + + if (w > len-1) + error(1, 0, "image partition table overflow?"); + + image_pt += w; + + base += parts[i].size; + } +} + +/** Generates and writes the image MD5 checksum */ +static void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) { + MD5_CTX ctx; + + MD5_Init(&ctx); + MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt)); + MD5_Update(&ctx, buffer, len); + MD5_Final(md5, &ctx); +} + + +/** + Generates the firmware image in factory format + + Image format: + + Bytes (hex) Usage + ----------- ----- + 0000-0003 Image size (4 bytes, big endian) + 0004-0013 MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14) + 0014-0017 Vendor information length (without padding) (4 bytes, big endian) + 0018-1013 Vendor information (4092 bytes, padded with 0xff; there seem to be older + (VxWorks-based) TP-LINK devices which use a smaller vendor information block) + 1014-1813 Image partition table (2048 bytes, padded with 0xff) + 1814-xxxx Firmware partitions +*/ +static void * generate_factory_image(struct device_info *info, const struct image_partition_entry *parts, size_t *len) { + *len = 0x1814; + + size_t i; + for (i = 0; parts[i].name; i++) + *len += parts[i].size; + + uint8_t *image = malloc(*len); + if (!image) + error(1, errno, "malloc"); + + memset(image, 0xff, *len); + put32(image, *len); + + if (info->vendor) { + size_t vendor_len = strlen(info->vendor); + put32(image+0x14, vendor_len); + memcpy(image+0x18, info->vendor, vendor_len); + } + + put_partitions(image + 0x1014, info->partitions, parts); + put_md5(image+0x04, image+0x14, *len-0x14); + + return image; +} + +/** + Generates the firmware image in sysupgrade format + + This makes some assumptions about the provided flash and image partition tables and + should be generalized when TP-LINK starts building its safeloader into hardware with + different flash layouts. +*/ +static void * generate_sysupgrade_image(struct device_info *info, const struct image_partition_entry *image_parts, size_t *len) { + size_t i, j; + size_t flash_first_partition_index = 0; + size_t flash_last_partition_index = 0; + const struct flash_partition_entry *flash_first_partition = NULL; + const struct flash_partition_entry *flash_last_partition = NULL; + const struct image_partition_entry *image_last_partition = NULL; + + /** Find first and last partitions */ + for (i = 0; info->partitions[i].name; i++) { + if (!strcmp(info->partitions[i].name, info->first_sysupgrade_partition)) { + flash_first_partition = &info->partitions[i]; + flash_first_partition_index = i; + } else if (!strcmp(info->partitions[i].name, info->last_sysupgrade_partition)) { + flash_last_partition = &info->partitions[i]; + flash_last_partition_index = i; + } + } + + assert(flash_first_partition && flash_last_partition); + assert(flash_first_partition_index < flash_last_partition_index); + + /** Find last partition from image to calculate needed size */ + for (i = 0; image_parts[i].name; i++) { + if (!strcmp(image_parts[i].name, info->last_sysupgrade_partition)) { + image_last_partition = &image_parts[i]; + break; + } + } + + assert(image_last_partition); + + *len = flash_last_partition->base - flash_first_partition->base + image_last_partition->size; + + uint8_t *image = malloc(*len); + if (!image) + error(1, errno, "malloc"); + + memset(image, 0xff, *len); + + for (i = flash_first_partition_index; i <= flash_last_partition_index; i++) { + for (j = 0; image_parts[j].name; j++) { + if (!strcmp(info->partitions[i].name, image_parts[j].name)) { + if (image_parts[j].size > info->partitions[i].size) + error(1, 0, "%s partition too big (more than %u bytes)", info->partitions[i].name, (unsigned)info->partitions[i].size); + memcpy(image + info->partitions[i].base - flash_first_partition->base, image_parts[j].data, image_parts[j].size); + break; + } + + assert(image_parts[j].name); + } + } + + return image; +} + +/** Generates an image according to a given layout and writes it to a file */ +static void build_image(const char *output, + const char *kernel_image, + const char *rootfs_image, + uint32_t rev, + bool add_jffs2_eof, + bool sysupgrade, + struct device_info *info) { + + size_t i; + + struct image_partition_entry parts[7] = {}; + + struct flash_partition_entry *firmware_partition = NULL; + struct flash_partition_entry *os_image_partition = NULL; + struct flash_partition_entry *file_system_partition = NULL; + size_t firmware_partition_index = 0; + + for (i = 0; info->partitions[i].name; i++) { + if (!strcmp(info->partitions[i].name, "firmware")) + { + firmware_partition = &info->partitions[i]; + firmware_partition_index = i; + } + } + + if (firmware_partition) + { + os_image_partition = &info->partitions[firmware_partition_index]; + file_system_partition = &info->partitions[firmware_partition_index + 1]; + + struct stat kernel; + if (stat(kernel_image, &kernel) < 0) + error(1, errno, "unable to stat file `%s'", kernel_image); + + if (kernel.st_size > firmware_partition->size) + error(1, 0, "kernel overflowed firmware partition\n"); + + for (i = MAX_PARTITIONS-1; i >= firmware_partition_index + 1; i--) + info->partitions[i+1] = info->partitions[i]; + + file_system_partition->name = "file-system"; + file_system_partition->base = firmware_partition->base + kernel.st_size; + + /* Align partition start to erase blocks for factory images only */ + if (!sysupgrade) + file_system_partition->base = ALIGN(firmware_partition->base + kernel.st_size, 0x10000); + + file_system_partition->size = firmware_partition->size - file_system_partition->base; + + os_image_partition->name = "os-image"; + os_image_partition->size = kernel.st_size; + } + + parts[0] = make_partition_table(info->partitions); + if (info->soft_ver) + parts[1] = make_soft_version_from_string(info->soft_ver); + else + parts[1] = make_soft_version(rev); + + parts[2] = make_support_list(info); + parts[3] = read_file("os-image", kernel_image, false, NULL); + parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof, file_system_partition); + + /* Some devices need the extra-para partition to accept the firmware */ + if (strcasecmp(info->id, "ARCHER-C25-V1") == 0 || + strcasecmp(info->id, "ARCHER-C59-V2") == 0 || + strcasecmp(info->id, "ARCHER-C60-V2") == 0 || + strcasecmp(info->id, "TLWR1043NV5") == 0) { + const char mdat[11] = {0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}; + parts[5] = put_data("extra-para", mdat, 11); + } else if (strcasecmp(info->id, "ARCHER-C7-V4") == 0 || strcasecmp(info->id, "ARCHER-C7-V5") == 0) { + const char mdat[11] = {0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0xca, 0x00, 0x01, 0x00, 0x00}; + parts[5] = put_data("extra-para", mdat, 11); + } + + size_t len; + void *image; + if (sysupgrade) + image = generate_sysupgrade_image(info, parts, &len); + else + image = generate_factory_image(info, parts, &len); + + FILE *file = fopen(output, "wb"); + if (!file) + error(1, errno, "unable to open output file"); + + if (fwrite(image, len, 1, file) != 1) + error(1, 0, "unable to write output file"); + + fclose(file); + + free(image); + + for (i = 0; parts[i].name; i++) + free_image_partition(parts[i]); +} + +/** Usage output */ +static void usage(const char *argv0) { + fprintf(stderr, + "Usage: %s [OPTIONS...]\n" + "\n" + "Options:\n" + " -h show this help\n" + "\n" + "Create a new image:\n" + " -B <board> create image for the board specified with <board>\n" + " -k <file> read kernel image from the file <file>\n" + " -r <file> read rootfs image from the file <file>\n" + " -o <file> write output to the file <file>\n" + " -V <rev> sets the revision number to <rev>\n" + " -j add jffs2 end-of-filesystem markers\n" + " -S create sysupgrade instead of factory image\n" + "Extract an old image:\n" + " -x <file> extract all oem firmware partition\n" + " -d <dir> destination to extract the firmware partition\n" + " -z <file> convert an oem firmware into a sysupgade file. Use -o for output file\n", + argv0 + ); +}; + + +static struct device_info *find_board(const char *id) +{ + struct device_info *board = NULL; + + for (board = boards; board->id != NULL; board++) + if (strcasecmp(id, board->id) == 0) + return board; + + return NULL; +} + +static int add_flash_partition( + struct flash_partition_entry *part_list, + size_t max_entries, + const char *name, + unsigned long base, + unsigned long size) +{ + int ptr; + /* check if the list has a free entry */ + for (ptr = 0; ptr < max_entries; ptr++, part_list++) { + if (part_list->name == NULL && + part_list->base == 0 && + part_list->size == 0) + break; + } + + if (ptr == max_entries) { + error(1, 0, "No free flash part entry available."); + } + + part_list->name = calloc(1, strlen(name) + 1); + if (!part_list->name) { + error(1, 0, "Unable to allocate memory"); + } + + memcpy((char *)part_list->name, name, strlen(name)); + part_list->base = base; + part_list->size = size; + + return 0; +} + +/** read the partition table into struct flash_partition_entry */ +static int read_partition_table( + FILE *file, long offset, + struct flash_partition_entry *entries, size_t max_entries, + int type) +{ + char buf[2048]; + char *ptr, *end; + const char *parthdr = NULL; + const char *fwuphdr = "fwup-ptn"; + const char *flashhdr = "partition"; + + /* TODO: search for the partition table */ + + switch(type) { + case 0: + parthdr = fwuphdr; + break; + case 1: + parthdr = flashhdr; + break; + default: + error(1, 0, "Invalid partition table"); + } + + if (fseek(file, offset, SEEK_SET) < 0) + error(1, errno, "Can not seek in the firmware"); + + if (fread(buf, 1, 2048, file) < 0) + error(1, errno, "Can not read fwup-ptn from the firmware"); + + buf[2047] = '\0'; + + /* look for the partition header */ + if (memcmp(buf, parthdr, strlen(parthdr)) != 0) { + fprintf(stderr, "DEBUG: can not find fwuphdr\n"); + return 1; + } + + ptr = buf; + end = buf + sizeof(buf); + while ((ptr + strlen(parthdr)) < end && + memcmp(ptr, parthdr, strlen(parthdr)) == 0) { + char *end_part; + char *end_element; + + char name[32] = { 0 }; + int name_len = 0; + unsigned long base = 0; + unsigned long size = 0; + + end_part = memchr(ptr, '\n', (end - ptr)); + if (end_part == NULL) { + /* in theory this should never happen, because a partition always ends with 0x09, 0x0D, 0x0A */ + break; + } + + for (int i = 0; i <= 4; i++) { + if (end_part <= ptr) + break; + + end_element = memchr(ptr, 0x20, (end_part - ptr)); + if (end_element == NULL) { + error(1, errno, "Ignoring the rest of the partition entries."); + break; + } + + switch (i) { + /* partition header */ + case 0: + ptr = end_element + 1; + continue; + /* name */ + case 1: + name_len = (end_element - ptr) > 31 ? 31 : (end_element - ptr); + strncpy(name, ptr, name_len); + name[name_len] = '\0'; + ptr = end_element + 1; + continue; + + /* string "base" */ + case 2: + ptr = end_element + 1; + continue; + + /* actual base */ + case 3: + base = strtoul(ptr, NULL, 16); + ptr = end_element + 1; + continue; + + /* string "size" */ + case 4: + ptr = end_element + 1; + /* actual size. The last element doesn't have a sepeartor */ + size = strtoul(ptr, NULL, 16); + /* the part ends with 0x09, 0x0d, 0x0a */ + ptr = end_part + 1; + add_flash_partition(entries, max_entries, name, base, size); + continue; + } + } + } + + return 0; +} + +static void write_partition( + FILE *input_file, + size_t firmware_offset, + struct flash_partition_entry *entry, + FILE *output_file) +{ + char buf[4096]; + size_t offset; + + fseek(input_file, entry->base + firmware_offset, SEEK_SET); + + for (offset = 0; sizeof(buf) + offset <= entry->size; offset += sizeof(buf)) { + if (fread(buf, sizeof(buf), 1, input_file) < 0) + error(1, errno, "Can not read partition from input_file"); + + if (fwrite(buf, sizeof(buf), 1, output_file) < 0) + error(1, errno, "Can not write partition to output_file"); + } + /* write last chunk smaller than buffer */ + if (offset < entry->size) { + offset = entry->size - offset; + if (fread(buf, offset, 1, input_file) < 0) + error(1, errno, "Can not read partition from input_file"); + if (fwrite(buf, offset, 1, output_file) < 0) + error(1, errno, "Can not write partition to output_file"); + } +} + +static int extract_firmware_partition(FILE *input_file, size_t firmware_offset, struct flash_partition_entry *entry, const char *output_directory) +{ + FILE *output_file; + char output[PATH_MAX]; + + snprintf(output, PATH_MAX, "%s/%s", output_directory, entry->name); + output_file = fopen(output, "wb+"); + if (output_file == NULL) { + error(1, errno, "Can not open output file %s", output); + } + + write_partition(input_file, firmware_offset, entry, output_file); + + fclose(output_file); + + return 0; +} + +/** extract all partitions from the firmware file */ +static int extract_firmware(const char *input, const char *output_directory) +{ + struct flash_partition_entry entries[16] = { 0 }; + size_t max_entries = 16; + size_t firmware_offset = 0x1014; + FILE *input_file; + + struct stat statbuf; + + /* check input file */ + if (stat(input, &statbuf)) { + error(1, errno, "Can not read input firmware %s", input); + } + + /* check if output directory exists */ + if (stat(output_directory, &statbuf)) { + error(1, errno, "Failed to stat output directory %s", output_directory); + } + + if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { + error(1, errno, "Given output directory is not a directory %s", output_directory); + } + + input_file = fopen(input, "rb"); + + if (read_partition_table(input_file, firmware_offset, entries, 16, 0) != 0) { + error(1, 0, "Error can not read the partition table (fwup-ptn)"); + } + + for (int i = 0; i < max_entries; i++) { + if (entries[i].name == NULL && + entries[i].base == 0 && + entries[i].size == 0) + continue; + + extract_firmware_partition(input_file, firmware_offset, &entries[i], output_directory); + } + + return 0; +} + +static struct flash_partition_entry *find_partition( + struct flash_partition_entry *entries, size_t max_entries, + const char *name, const char *error_msg) +{ + for (int i = 0; i < max_entries; i++, entries++) { + if (strcmp(entries->name, name) == 0) + return entries; + } + + error(1, 0, "%s", error_msg); + return NULL; +} + +static void write_ff(FILE *output_file, size_t size) +{ + char buf[4096]; + int offset; + + memset(buf, 0xff, sizeof(buf)); + + for (offset = 0; offset + sizeof(buf) < size ; offset += sizeof(buf)) { + if (fwrite(buf, sizeof(buf), 1, output_file) < 0) + error(1, errno, "Can not write 0xff to output_file"); + } + + /* write last chunk smaller than buffer */ + if (offset < size) { + offset = size - offset; + if (fwrite(buf, offset, 1, output_file) < 0) + error(1, errno, "Can not write partition to output_file"); + } +} + +static void convert_firmware(const char *input, const char *output) +{ + struct flash_partition_entry fwup[MAX_PARTITIONS] = { 0 }; + struct flash_partition_entry flash[MAX_PARTITIONS] = { 0 }; + struct flash_partition_entry *fwup_os_image = NULL, *fwup_file_system = NULL; + struct flash_partition_entry *flash_os_image = NULL, *flash_file_system = NULL; + struct flash_partition_entry *fwup_partition_table = NULL; + size_t firmware_offset = 0x1014; + FILE *input_file, *output_file; + + struct stat statbuf; + + /* check input file */ + if (stat(input, &statbuf)) { + error(1, errno, "Can not read input firmware %s", input); + } + + input_file = fopen(input, "rb"); + if (!input_file) + error(1, 0, "Can not open input firmware %s", input); + + output_file = fopen(output, "wb"); + if (!output_file) + error(1, 0, "Can not open output firmware %s", output); + + if (read_partition_table(input_file, firmware_offset, fwup, MAX_PARTITIONS, 0) != 0) { + error(1, 0, "Error can not read the partition table (fwup-ptn)"); + } + + fwup_os_image = find_partition(fwup, MAX_PARTITIONS, + "os-image", "Error can not find os-image partition (fwup)"); + fwup_file_system = find_partition(fwup, MAX_PARTITIONS, + "file-system", "Error can not find file-system partition (fwup)"); + fwup_partition_table = find_partition(fwup, MAX_PARTITIONS, + "partition-table", "Error can not find partition-table partition"); + + /* the flash partition table has a 0x00000004 magic haeder */ + if (read_partition_table(input_file, firmware_offset + fwup_partition_table->base + 4, flash, MAX_PARTITIONS, 1) != 0) + error(1, 0, "Error can not read the partition table (flash)"); + + flash_os_image = find_partition(flash, MAX_PARTITIONS, + "os-image", "Error can not find os-image partition (flash)"); + flash_file_system = find_partition(flash, MAX_PARTITIONS, + "file-system", "Error can not find file-system partition (flash)"); + + /* write os_image to 0x0 */ + write_partition(input_file, firmware_offset, fwup_os_image, output_file); + write_ff(output_file, flash_os_image->size - fwup_os_image->size); + + /* write file-system behind os_image */ + fseek(output_file, flash_file_system->base - flash_os_image->base, SEEK_SET); + write_partition(input_file, firmware_offset, fwup_file_system, output_file); + write_ff(output_file, flash_file_system->size - fwup_file_system->size); + + fclose(output_file); + fclose(input_file); +} + +int main(int argc, char *argv[]) { + const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL; + const char *extract_image = NULL, *output_directory = NULL, *convert_image = NULL; + bool add_jffs2_eof = false, sysupgrade = false; + unsigned rev = 0; + struct device_info *info; + set_source_date_epoch(); + + while (true) { + int c; + + c = getopt(argc, argv, "B:k:r:o:V:jSh:x:d:z:"); + if (c == -1) + break; + + switch (c) { + case 'B': + board = optarg; + break; + + case 'k': + kernel_image = optarg; + break; + + case 'r': + rootfs_image = optarg; + break; + + case 'o': + output = optarg; + break; + + case 'V': + sscanf(optarg, "r%u", &rev); + break; + + case 'j': + add_jffs2_eof = true; + break; + + case 'S': + sysupgrade = true; + break; + + case 'h': + usage(argv[0]); + return 0; + + case 'd': + output_directory = optarg; + break; + + case 'x': + extract_image = optarg; + break; + + case 'z': + convert_image = optarg; + break; + + default: + usage(argv[0]); + return 1; + } + } + + if (extract_image || output_directory) { + if (!extract_image) + error(1, 0, "No factory/oem image given via -x <file>. Output directory is only valid with -x"); + if (!output_directory) + error(1, 0, "Can not extract an image without output directory. Use -d <dir>"); + extract_firmware(extract_image, output_directory); + } else if (convert_image) { + if (!output) + error(1, 0, "Can not convert a factory/oem image into sysupgrade image without output file. Use -o <file>"); + convert_firmware(convert_image, output); + } else { + if (!board) + error(1, 0, "no board has been specified"); + if (!kernel_image) + error(1, 0, "no kernel image has been specified"); + if (!rootfs_image) + error(1, 0, "no rootfs image has been specified"); + if (!output) + error(1, 0, "no output filename has been specified"); + + info = find_board(board); + + if (info == NULL) + error(1, 0, "unsupported board %s", board); + + build_image(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade, info); + } + + return 0; +} diff --git a/tools/firmware-utils/src/trx.c b/tools/firmware-utils/src/trx.c index 8e95d98d7ae..dc5bb672aeb 100644 --- a/tools/firmware-utils/src/trx.c +++ b/tools/firmware-utils/src/trx.c @@ -68,7 +68,7 @@ uint32_t crc32buf(char *buf, size_t len); #define TRX_MAGIC 0x30524448 /* "HDR0" */ #define TRX_MAX_LEN 0x720000 -#define TRX_NO_HEADER 1 /* Do not write TRX header */ +#define TRX_NO_HEADER 1 /* Do not write TRX header */ struct trx_header { uint32_t magic; /* "HDR0" */ @@ -100,7 +100,7 @@ int main(int argc, char **argv) int c, i, append = 0; size_t n; ssize_t n2; - uint32_t cur_len, fsmark=0; + uint32_t cur_len, fsmark=0, magic; unsigned long maxlen = TRX_MAX_LEN; struct trx_header *p; char trx_version = 1; @@ -121,7 +121,7 @@ int main(int argc, char **argv) in = NULL; i = 0; - while ((c = getopt(argc, argv, "-:2o:m:a:x:b:f:A:F:")) != -1) { + while ((c = getopt(argc, argv, "-:2o:m:a:x:b:f:A:F:M:")) != -1) { switch (c) { case '2': /* take care that nothing was written to buf so far */ @@ -243,6 +243,15 @@ int main(int argc, char **argv) } break; + case 'M': + errno = 0; + magic = strtoul(optarg, &e, 0); + if (errno || (e == optarg) || *e) { + fprintf(stderr, "illegal numeric string\n"); + usage(); + } + p->magic = STORE32_LE(magic); + break; default: usage(); } @@ -262,7 +271,7 @@ int main(int argc, char **argv) cur_len += ROUND - n; } - /* for TRXv2 set bin-header Flags to 0xFF for CRC calculation like CFE does */ + /* for TRXv2 set bin-header Flags to 0xFF for CRC calculation like CFE does */ if (trx_version == 2) { if(cur_len - LOAD32_LE(p->offsets[3]) < sizeof(binheader)) { fprintf(stderr, "TRXv2 binheader too small!\n"); @@ -273,11 +282,10 @@ int main(int argc, char **argv) } p->crc32 = crc32buf((char *) &p->flag_version, - (fsmark)?fsmark:cur_len - offsetof(struct trx_header, flag_version)); + ((fsmark)?fsmark:cur_len) - offsetof(struct trx_header, flag_version)); p->crc32 = STORE32_LE(p->crc32); p->len = STORE32_LE((fsmark) ? fsmark : cur_len); - p->len = STORE32_LE(p->len); /* restore TRXv2 bin-header */ if (trx_version == 2) { @@ -290,7 +298,7 @@ int main(int argc, char **argv) } fclose(out); - + return EXIT_SUCCESS; } diff --git a/tools/firmware-utils/src/wndr3700.c b/tools/firmware-utils/src/wndr3700.c deleted file mode 100644 index 97b5f1d4558..00000000000 --- a/tools/firmware-utils/src/wndr3700.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * wndr3700.c - partially based on OpenWrt's add_header.c - * - * Copyright (C) 2009 Anael Orlinski <naouel@naouel.org> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License, - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* - * The add_header utility used by various vendors preprends the buf - * image with a header containing a CRC32 value which is generated for the - * model id + reserved space for CRC32 + buf, then replaces the reserved - * area with the actual CRC32. This replacement tool mimics this behavior. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stddef.h> -#include <unistd.h> -#include <errno.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <string.h> -#include <netinet/in.h> -#include <inttypes.h> - -#define BPB 8 /* bits/byte */ - -#define WNDR3700_MAGIC_LEN 4 - -static uint32_t crc32[1<<BPB]; -static char *magic = "3700"; - -static void init_crc32() -{ - const uint32_t poly = ntohl(0x2083b8ed); - int n; - - for (n = 0; n < 1<<BPB; n++) { - uint32_t crc = n; - int bit; - - for (bit = 0; bit < BPB; bit++) - crc = (crc & 1) ? (poly ^ (crc >> 1)) : (crc >> 1); - crc32[n] = crc; - } -} - -static uint32_t crc32buf(unsigned char *buf, size_t len) -{ - uint32_t crc = 0xFFFFFFFF; - - for (; len; len--, buf++) - crc = crc32[(uint8_t)crc ^ *buf] ^ (crc >> BPB); - return ~crc; -} - -struct header { - unsigned char magic[WNDR3700_MAGIC_LEN]; - uint32_t crc; - unsigned char stuff[56]; -}; - -static void usage(const char *) __attribute__ (( __noreturn__ )); - -static void usage(const char *mess) -{ - fprintf(stderr, "Error: %s\n", mess); - fprintf(stderr, "Usage: wndr3700 input_file output_file [magic]\n"); - fprintf(stderr, "\n"); - exit(1); -} - -int main(int argc, char **argv) -{ - off_t len; // of original buf - off_t buflen; // of the output file - int fd; - void *input_file; // pointer to the input file (mmmapped) - struct header header; - unsigned char *buf; // pointer to prefix + copy of original buf - - // verify parameters - - if (argc < 3) - usage("wrong number of arguments"); - - if (argc > 3) - magic = argv[3]; - - if (strlen(magic) != WNDR3700_MAGIC_LEN) { - fprintf(stderr, "Invalid magic: '%s'\n", magic); - exit(1); - } - - // mmap input_file - if ((fd = open(argv[1], O_RDONLY)) < 0 - || (len = lseek(fd, 0, SEEK_END)) < 0 - || (input_file = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0)) == (void *) (-1) - || close(fd) < 0) - { - fprintf(stderr, "Error loading file %s: %s\n", argv[1], strerror(errno)); - exit(1); - } - - buflen = len; - - init_crc32(); - - // preload header - memcpy(&header, input_file, sizeof(header)); - - memcpy(header.magic, magic, WNDR3700_MAGIC_LEN); - header.crc = 0; - - // create a firmware image in memory and copy the input_file to it - buf = malloc(buflen); - memcpy(buf, input_file, len); - - // CRC of temporary header - header.crc = htonl(crc32buf((unsigned char*)&header, sizeof(header))); - - memcpy(buf, &header, sizeof(header)); - - // write the buf - if ((fd = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC,0644)) < 0 - || write(fd, buf, buflen) != buflen - || close(fd) < 0) - { - fprintf(stderr, "Error storing file %s: %s\n", argv[2], strerror(errno)); - exit(2); - } - - free(buf); - - munmap(input_file,len); - - return 0; -} diff --git a/tools/firmware-utils/src/wrt400n.c b/tools/firmware-utils/src/wrt400n.c index a9a4908060c..1cf1debc893 100644 --- a/tools/firmware-utils/src/wrt400n.c +++ b/tools/firmware-utils/src/wrt400n.c @@ -7,12 +7,14 @@ * * Author: Sandeep Mistry */ +#include <arpa/inet.h> #include <fcntl.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> +#include <unistd.h> #include "cyg_crc.h" diff --git a/tools/firmware-utils/src/xorimage.c b/tools/firmware-utils/src/xorimage.c index b5ab83fa7a6..ca6e43742a1 100644 --- a/tools/firmware-utils/src/xorimage.c +++ b/tools/firmware-utils/src/xorimage.c @@ -21,7 +21,6 @@ #include <stdlib.h> #include <string.h> #include <stdint.h> -#include <time.h> #include <unistd.h> #include <sys/stat.h> diff --git a/tools/firmware-utils/src/zyimage.c b/tools/firmware-utils/src/zyimage.c new file mode 100644 index 00000000000..6aacf2ff1bd --- /dev/null +++ b/tools/firmware-utils/src/zyimage.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 Soul Trace <S-trace@list.ru> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#define _POSIX_SOURCE +#define _POSIX_C_SOURCE 199309L /* getopt */ +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <string.h> +#include <unistd.h> + +#define szbuf 32768 + +u_int32_t crc_tab[256]; + +u_int32_t chksum_crc32 (FILE *f) +{ + register unsigned long crc; + unsigned long i, j; + char *buffer = malloc(szbuf); + char *buf; + + crc = 0xFFFFFFFF; + while (!feof(f)) + { + j = fread(buffer, 1, szbuf, f); + buf = buffer; + for (i = 0; i < j; i++) + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *buf++) & 0xFF]; + } + free(buffer); + return crc; +} + +void chksum_crc32gentab () +{ + unsigned long crc, poly; + int i, j; + + poly = 0xEDB88320L; + for (i = 0; i < 256; i++) + { + crc = i; + for (j = 8; j > 0; j--) + { + if (crc & 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + crc_tab[i] = crc; + } +} + +void usage(char *progname) +{ + printf("Usage: %s [ -v Version ] [ -d Device_ID ] <input file>\n", progname); + exit(1); +} + +int main(int argc, char *argv[]) { + struct signature + { + const char magic[4]; + unsigned int device_id; + char firmware_version[48]; + unsigned int crc32; + } + sign = + { + { 'Z', 'N', 'B', 'G' }, + 1, + { "V.1.0.0(1.0.0)" }, + 0 + }; + FILE *f; + struct signature oldsign; + char *filename; + static const char *optString; + int opt; + + if (argc < 1) + usage(argv[0]); + + optString = "v:d:h"; + opt = getopt( argc, argv, optString ); + while( opt != -1 ) { + switch( opt ) { + case 'v': + if (optarg == NULL) + usage(argv[0]); + strncpy(sign.firmware_version, optarg, sizeof(sign.firmware_version)-1); + sign.firmware_version[sizeof(sign.firmware_version)-1]='\0'; /* Make sure that string is terminated correctly */ + break; + + case 'd': + sign.device_id = atoi(optarg); + if (sign.device_id == 0) + sign.device_id = (int)strtol(optarg, NULL, 16); + break; + + case '?': + case 'h': + usage(argv[0]); + break; + + default: + break; + } + + opt = getopt( argc, argv, optString ); + } + + chksum_crc32gentab(); + + filename=argv[optind]; + if (access(filename, W_OK) || access(filename, R_OK)) + { + printf("Not open input file %s\n", filename); + exit(1); + } + f = fopen(argv[optind], "r+"); + if (f != NULL) + { + fseek(f, sizeof(sign)*-1, SEEK_END); + fread(&oldsign, sizeof(oldsign), 1, f); + + if (strncmp(oldsign.magic,"ZNBG", sizeof(oldsign.magic)) == 0 ) + { + printf("Image is already signed as:\nDevice ID: 0x%08x\nFirmware version: %s\nImage CRC32: 0x%x\n", oldsign.device_id, oldsign.firmware_version, oldsign.crc32); + exit(0); + } + + fseek(f, 0, SEEK_SET); + sign.crc32 = chksum_crc32(f); + fwrite(&sign, sizeof(sign), 1, f); + fclose(f); + + printf("Image signed as:\nDevice ID: 0x%08x\nFirmware version: %s\nImage CRC32: 0x%x\n", sign.device_id, sign.firmware_version, sign.crc32); + } + return 0; +} diff --git a/tools/firmware-utils/src/zyxbcm.c b/tools/firmware-utils/src/zyxbcm.c new file mode 100644 index 00000000000..1a2926bfd31 --- /dev/null +++ b/tools/firmware-utils/src/zyxbcm.c @@ -0,0 +1,259 @@ +/* + * zyxbcm.c - based on Jonas Gorski's spw303v.c + * + * Copyright (C) 2014 Álvaro Fernández Rojas <noltari@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <arpa/inet.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <unistd.h> +#include <sys/stat.h> + +#define TAGVER_LEN 4 /* Length of Tag Version */ +#define SIG1_LEN 20 /* Company Signature 1 Length */ +#define SIG2_LEN 14 /* Company Signature 2 Lenght */ +#define BOARDID_LEN 16 /* Length of BoardId */ +#define ENDIANFLAG_LEN 2 /* Endian Flag Length */ +#define CHIPID_LEN 6 /* Chip Id Length */ +#define IMAGE_LEN 10 /* Length of Length Field */ +#define ADDRESS_LEN 12 /* Length of Address field */ +#define DUALFLAG_LEN 2 /* Dual Image flag Length */ +#define INACTIVEFLAG_LEN 2 /* Inactie Flag Length */ +#define RSASIG_LEN 20 /* Length of RSA Signature in tag */ +#define TAGINFO1_LEN 30 /* Length of vendor information field1 in tag */ +#define ZYX_TAGINFO1_LEN 20 /* Length of vendor information field1 in tag */ +#define FLASHLAYOUTVER_LEN 4 /* Length of Flash Layout Version String tag */ +#define TAGINFO2_LEN 16 /* Length of vendor information field2 in tag */ +#define CRC_LEN 4 /* Length of CRC in bytes */ + +#define IMAGETAG_CRC_START 0xFFFFFFFF + +struct bcm_tag { + char tagVersion[TAGVER_LEN]; // 0-3: Version of the image tag + char sig_1[SIG1_LEN]; // 4-23: Company Line 1 + char sig_2[SIG2_LEN]; // 24-37: Company Line 2 + char chipid[CHIPID_LEN]; // 38-43: Chip this image is for + char boardid[BOARDID_LEN]; // 44-59: Board name + char big_endian[ENDIANFLAG_LEN]; // 60-61: Map endianness -- 1 BE 0 LE + char totalLength[IMAGE_LEN]; // 62-71: Total length of image + char cfeAddress[ADDRESS_LEN]; // 72-83: Address in memory of CFE + char cfeLength[IMAGE_LEN]; // 84-93: Size of CFE + char flashImageStart[ADDRESS_LEN]; // 94-105: Address in memory of image start (kernel for OpenWRT, rootfs for stock firmware) + char flashRootLength[IMAGE_LEN]; // 106-115: Size of rootfs for flashing + char kernelAddress[ADDRESS_LEN]; // 116-127: Address in memory of kernel + char kernelLength[IMAGE_LEN]; // 128-137: Size of kernel + char dualImage[DUALFLAG_LEN]; // 138-139: Unused at present + char inactiveFlag[INACTIVEFLAG_LEN]; // 140-141: Unused at present + char rsa_signature[RSASIG_LEN]; // 142-161: RSA Signature (unused at present; some vendors may use this) + char information1[TAGINFO1_LEN]; // 162-191: Compilation and related information (not generated/used by OpenWRT) + char flashLayoutVer[FLASHLAYOUTVER_LEN]; // 192-195: Version flash layout + char fskernelCRC[CRC_LEN]; // 196-199: kernel+rootfs CRC32 + char information2[TAGINFO2_LEN]; // 200-215: Unused at present except Alice Gate where is is information + char imageCRC[CRC_LEN]; // 216-219: CRC32 of image less imagetag (kernel for Alice Gate) + char rootfsCRC[CRC_LEN]; // 220-223: CRC32 of rootfs partition + char kernelCRC[CRC_LEN]; // 224-227: CRC32 of kernel partition + char imageSequence[4]; // 228-231: Image sequence number + char rootLength[4]; // 232-235: steal from reserved1 to keep the real root length so we can use in the flash map even after we have change the rootLength to 0 to satisfy devices that check CRC on every boot + char headerCRC[CRC_LEN]; // 236-239: CRC32 of header excluding tagVersion + char reserved2[16]; // 240-255: Unused at present +}; + +struct zyxbcm_tag { + char tagVersion[TAGVER_LEN]; // 0-3: Version of the image tag + char sig_1[SIG1_LEN]; // 4-23: Company Line 1 + char sig_2[SIG2_LEN]; // 24-37: Company Line 2 + char chipid[CHIPID_LEN]; // 38-43: Chip this image is for + char boardid[BOARDID_LEN]; // 44-59: Board name + char big_endian[ENDIANFLAG_LEN]; // 60-61: Map endianness -- 1 BE 0 LE + char totalLength[IMAGE_LEN]; // 62-71: Total length of image + char cfeAddress[ADDRESS_LEN]; // 72-83: Address in memory of CFE + char cfeLength[IMAGE_LEN]; // 84-93: Size of CFE + char flashImageStart[ADDRESS_LEN]; // 94-105: Address in memory of image start (kernel for OpenWRT, rootfs for stock firmware) + char flashRootLength[IMAGE_LEN]; // 106-115: Size of rootfs for flashing + char kernelAddress[ADDRESS_LEN]; // 116-127: Address in memory of kernel + char kernelLength[IMAGE_LEN]; // 128-137: Size of kernel + char dualImage[DUALFLAG_LEN]; // 138-139: Unused at present + char inactiveFlag[INACTIVEFLAG_LEN]; // 140-141: Unused at present + char rsa_signature[RSASIG_LEN]; // 142-161: RSA Signature (unused at present; some vendors may use this) + char information1[ZYX_TAGINFO1_LEN]; // 162-181: Compilation and related information (not generated/used by OpenWRT) + char flashImageEnd[ADDRESS_LEN]; // 182-193: Address in memory of image end + char fskernelCRC[CRC_LEN]; // 194-197: kernel+rootfs CRC32 + char reserved1[2]; // 198-199: Unused at present + char information2[TAGINFO2_LEN]; // 200-215: Unused at present except Alice Gate where is is information + char imageCRC[CRC_LEN]; // 216-219: CRC32 of image less imagetag (kernel for Alice Gate) + char rootfsCRC[CRC_LEN]; // 220-223: CRC32 of rootfs partition + char kernelCRC[CRC_LEN]; // 224-227: CRC32 of kernel partition + char imageSequence[4]; // 228-231: Image sequence number + char rootLength[4]; // 232-235: steal from reserved1 to keep the real root length so we can use in the flash map even after we have change the rootLength to 0 to satisfy devices that check CRC on every boot + char headerCRC[CRC_LEN]; // 236-239: CRC32 of header excluding tagVersion + char reserved2[16]; // 240-255: Unused at present +}; + +static uint32_t crc32tab[256] = { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +uint32_t crc32(uint32_t crc, uint8_t *data, size_t len) +{ + while (len--) + crc = (crc >> 8) ^ crc32tab[(crc ^ *data++) & 0xFF]; + + return crc; +} + +void fix_header(void *buf) +{ + struct bcm_tag *bcmtag = buf; + struct zyxbcm_tag *zyxtag = buf; + uint8_t fskernel_crc[CRC_LEN]; + uint32_t crc; + uint64_t flash_start, rootfs_len, kernel_len; + + /* Backup values */ + flash_start = strtoul(bcmtag->flashImageStart, NULL, 10); + rootfs_len = strtoul(bcmtag->flashRootLength, NULL, 10); + kernel_len = strtoul(bcmtag->kernelLength, NULL, 10); + memcpy(fskernel_crc, bcmtag->fskernelCRC, CRC_LEN); + + /* Clear values */ + zyxtag->information1[ZYX_TAGINFO1_LEN - 1] = 0; + memset(zyxtag->flashImageEnd, 0, ADDRESS_LEN); + memset(zyxtag->fskernelCRC, 0, CRC_LEN); + memset(zyxtag->reserved1, 0, 2); + + /* Replace values */ + sprintf(zyxtag->flashImageEnd, "%lu", flash_start + rootfs_len + kernel_len); + memcpy(zyxtag->fskernelCRC, fskernel_crc, CRC_LEN); + + /* Update tag crc */ + crc = htonl(crc32(IMAGETAG_CRC_START, buf, 236)); + memcpy(zyxtag->headerCRC, &crc, 4); +} + +void usage(void) __attribute__ (( __noreturn__ )); + +void usage(void) +{ + fprintf(stderr, "Usage: zyxbcm [-i <inputfile>] [-o <outputfile>]\n"); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + char buf[1024]; /* keep this at 1k or adjust garbage calc below */ + FILE *in = stdin, *out = stdout; + char *ifn = NULL, *ofn = NULL; + size_t n; + int c, first_block = 1; + + while ((c = getopt(argc, argv, "i:o:h")) != -1) { + switch (c) { + case 'i': + ifn = optarg; + break; + case 'o': + ofn = optarg; + break; + case 'h': + default: + usage(); + } + } + + if (optind != argc || optind == 1) { + fprintf(stderr, "illegal arg \"%s\"\n", argv[optind]); + usage(); + } + + if (ifn && !(in = fopen(ifn, "r"))) { + fprintf(stderr, "can not open \"%s\" for reading\n", ifn); + usage(); + } + + if (ofn && !(out = fopen(ofn, "w"))) { + fprintf(stderr, "can not open \"%s\" for writing\n", ofn); + usage(); + } + + while ((n = fread(buf, 1, sizeof(buf), in)) > 0) { + if (n < sizeof(buf)) { + if (ferror(in)) { + FREAD_ERROR: + fprintf(stderr, "fread error\n"); + return EXIT_FAILURE; + } + } + + if (first_block && n >= 256) { + fix_header(buf); + first_block = 0; + } + + if (!fwrite(buf, n, 1, out)) { + FWRITE_ERROR: + fprintf(stderr, "fwrite error\n"); + return EXIT_FAILURE; + } + } + + if (ferror(in)) { + goto FREAD_ERROR; + } + + if (fflush(out)) { + goto FWRITE_ERROR; + } + + fclose(in); + fclose(out); + + return EXIT_SUCCESS; +} |
