From: RISCi_ATOM Date: Sun, 19 Apr 2015 23:49:51 +0000 (+0000) Subject: added firmware-utils X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=e2edad779c3c916ad2122ae7cc422e408343c58b;p=librecmc%2Flibrecmc-fossil.git added firmware-utils --- diff --git a/trunk/tools/Makefile b/trunk/tools/Makefile index 563ce88c..558c8671 100644 --- a/trunk/tools/Makefile +++ b/trunk/tools/Makefile @@ -23,7 +23,7 @@ endif tools-$(BUILD_TOOLCHAIN) += gmp mpfr mpc libelf tools-y += m4 libtool autoconf automake flex bison pkg-config sed mklibs tools-y += sstrip genext2fs e2fsprogs mtd-utils mkimage -tools-y += patch-image patch quilt yaffs2 flock padjffs2 +tools-y += firmware-utils patch-image patch quilt yaffs2 flock padjffs2 tools-y += mm-macros missing-macros xz cmake scons bc findutils gengetopt patchelf tools-$(CONFIG_TARGET_orion_generic) += wrt350nv2-builder upslug2 tools-$(CONFIG_powerpc) += upx diff --git a/trunk/tools/firmware-utils/Makefile b/trunk/tools/firmware-utils/Makefile new file mode 100644 index 00000000..951d4dca --- /dev/null +++ b/trunk/tools/firmware-utils/Makefile @@ -0,0 +1,81 @@ +# +# Copyright (C) 2006-2012 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# +include $(TOPDIR)/rules.mk + +PKG_NAME := firmware-utils + +include $(INCLUDE_DIR)/host-build.mk +include $(INCLUDE_DIR)/kernel.mk + +define cc + $(HOSTCC) $(HOST_CFLAGS) -include endian.h $(HOST_LDFLAGS) -o $(HOST_BUILD_DIR)/bin/$(firstword $(1)) $(foreach src,$(1),src/$(src).c) $(2) +endef + +define Host/Compile + mkdir -p $(HOST_BUILD_DIR)/bin + $(call cc,addpattern) + $(call cc,trx) + $(call cc,motorola-bin) + $(call cc,dgfirmware) + $(call cc,mksenaofw md5) + $(call cc,trx2usr) + $(call cc,ptgen) + $(call cc,airlink) + $(call cc,srec2bin) + $(call cc,mkmylofw) + $(call cc,mkcsysimg) + $(call cc,mkzynfw) + $(call cc,lzma2eva,-lz) + $(call cc,mkcasfw) + $(call cc,mkfwimage,-lz) + $(call cc,mkfwimage2,-lz) + $(call cc,imagetag imagetag_cmdline cyg_crc32) + $(call cc,add_header) + $(call cc,makeamitbin) + $(call cc,encode_crc) + $(call cc,nand_ecc) + $(call cc,mkplanexfw sha1) + $(call cc,mktplinkfw md5) + $(call cc,mktplinkfw2 md5) + $(call cc,tplink-safeloader md5, -Wall) + $(call cc,pc1crypt) + $(call cc,osbridge-crc) + $(call cc,wrt400n cyg_crc32) + $(call cc,mkdniimg) + $(call cc,mktitanimg) + $(call cc,mkchkimg) + $(call cc,mkzcfw cyg_crc32) + $(call cc,spw303v) + $(call cc,zyxbcm) + $(call cc,trx2edips) + $(call cc,xorimage) + $(call cc,buffalo-enc buffalo-lib, -Wall) + $(call cc,buffalo-tag buffalo-lib, -Wall) + $(call cc,buffalo-tftp buffalo-lib, -Wall) + $(call cc,mkwrgimg md5, -Wall) + $(call cc,mkedimaximg) + $(call cc,mkbrncmdline) + $(call cc,mkbrnimg) + $(call cc,mkdapimg) + $(call cc, mkcameofw, -Wall) + $(call cc,seama md5) + $(call cc,fix-u-media-header cyg_crc32,-Wall) + $(call cc,hcsmakeimage bcmalgo) + $(call cc,mkporayfw, -Wall) + $(call cc,mkhilinkfw, -lcrypto) + $(call cc,mkdcs932, -Wall) + $(call cc,mkheader_gemtek,-lz) + $(call cc,mkrtn56uimg, -lz) + $(call cc,dgn3500sum) + $(call cc,edimax_fw_header, -Wall) +endef + +define Host/Install + $(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/* $(STAGING_DIR_HOST)/bin/ +endef + +$(eval $(call HostBuild)) diff --git a/trunk/tools/firmware-utils/src/add_header.c b/trunk/tools/firmware-utils/src/add_header.c new file mode 100644 index 00000000..6ad1527a --- /dev/null +++ b/trunk/tools/firmware-utils/src/add_header.c @@ -0,0 +1,138 @@ +/* + * add_header.c - partially based on libreCMC's motorola-bin.c + * + * Copyright (C) 2008 Imre Kaloz + * Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPB 8 /* bits/byte */ + +static uint32_t crc32[1<> 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 model[16]; + uint32_t crc; +}; + +static void usage(const char *) __attribute__ (( __noreturn__ )); + +static void usage(const char *mess) +{ + fprintf(stderr, "Error: %s\n", mess); + fprintf(stderr, "Usage: add_header model_id input_file output_file\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 != 4) + usage("wrong number of arguments"); + + // mmap input_file + if ((fd = open(argv[2], 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[2], strerror(errno)); + exit(1); + } + + buflen = len + sizeof(header); + + init_crc32(); + + // copy model name into header + strncpy(header.model, argv[1], sizeof(header.model)); + header.crc = 0; + + // create a firmware image in memory and copy the input_file to it + buf = malloc(buflen); + memcpy(buf, &header, sizeof(header)); + memcpy(&buf[sizeof(header)], input_file, len); + + // CRC of temporary header + buf + header.crc = htonl(crc32buf(buf, buflen)); + + memcpy(buf, &header, sizeof(header)); + + // write the buf + if ((fd = open(argv[3], 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[3], strerror(errno)); + exit(2); + } + + free(buf); + + munmap(input_file,len); + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/addpattern.c b/trunk/tools/firmware-utils/src/addpattern.c new file mode 100644 index 00000000..1d722856 --- /dev/null +++ b/trunk/tools/firmware-utils/src/addpattern.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2004 Manuel Novoa III + * + * 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 + */ + +/* July 29, 2004 + * + * This is a hacked replacement for the 'addpattern' utility used to + * create wrt54g .bin firmware files. It isn't pretty, but it does + * the job for me. + * + * Extensions: + * -v allows setting the version string on the command line. + * -{0|1} sets the (currently ignored) hw_ver flag in the header + * to 0 or 1 respectively. + */ + +/* January 12, 2005 + * + * Modified by rodent at rodent dot za dot net + * Support added for the new WRT54G v2.2 and WRT54GS v1.1 "flags" + * Without the flags set to 0x7, the above units will refuse to flash. + * + * Extensions: + * -{0|1|2} sets {0|1} sets hw_ver flag to 0/1. {2} sets hw_ver to 1 + * and adds the new hardware "flags" for the v2.2/v1.1 units +*/ + +/* January 1, 2007 + * + * Modified by juan.i.gonzalez at subdown dot net + * Support added for the AG241v2 and similar + * + * Extensions: + * -r #.# adds revision hardware flags. AG241v2 and similar. + * + * AG241V2 firmware sets the hw_ver to 0x44. + * + * Example: -r 2.0 + * + * Convert 2.0 to 20 to be an integer, and add 0x30 to skip special ASCII + * #define HW_Version ((HW_REV * 10) + 0x30) -> from cyutils.h +*/ + +#include +#include +#include +#include +#include +#include + +/**********************************************************************/ + +#define CODE_ID "U2ND" /* from code_pattern.h */ +#define CODE_PATTERN "W54S" /* from code_pattern.h */ +#define PBOT_PATTERN "PBOT" + +#define CYBERTAN_VERSION "v3.37.2" /* from cyutils.h */ + +/* WRT54G v2.2 and WRT54GS v1.1 "flags" (from 3.37.32 firmware cyutils.h) */ +#define SUPPORT_4712_CHIP 0x0001 +#define SUPPORT_INTEL_FLASH 0x0002 +#define SUPPORT_5325E_SWITCH 0x0004 +/* (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[8]; + char fwdate[3]; + char fwvern[3]; + char id[4]; /* U2ND */ + char hw_ver; /* 0: for 4702, 1: for 4712 -- new in 2.04.3 */ + + unsigned char sn; // Serial Number + unsigned char flags[2]; /* SUPPORT_ flags new for 3.37.2 (WRT54G v2.2 and WRT54GS v1.1) */ + unsigned char stable[2]; // The image is stable (for dual image) + unsigned char try1[2]; // Try to boot image first time (for dual image) + unsigned char try2[2]; // Try to boot image second time (for dual image) + unsigned char try3[2]; // Try to boot image third time (for dual_image) + unsigned char res3[2]; +} ; + +struct board_info { + char *id; + char *pattern; + char hw_ver; + char sn; + char flags[2]; +}; + +struct board_info boards[] = { + { + .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, + } +}; + +/**********************************************************************/ + +void usage(void) __attribute__ (( __noreturn__ )); + +void usage(void) +{ + fprintf(stderr, "Usage: addpattern [-i trxfile] [-o binfile] [-B board_id] [-p pattern] [-s serial] [-g] [-b] [-v v#.#.#] [-r #.#] [-{0|1|2|4|5}] -h\n"); + exit(EXIT_FAILURE); +} + +struct board_info *find_board(char *id) +{ + struct board_info *board; + + for (board = boards; board->id != NULL; board++) + if (strcasecmp(id, board->id) == 0) + return board; + + return NULL; +} + +int main(int argc, char **argv) +{ + char buf[1024]; /* keep this at 1k or adjust garbage calc below */ + struct code_header *hdr; + FILE *in = stdin; + FILE *out = stdout; + char *ifn = NULL; + char *ofn = NULL; + char *pattern = CODE_PATTERN; + char *pbotpat = PBOT_PATTERN; + char *version = CYBERTAN_VERSION; + char *board_id = NULL; + struct board_info *board = NULL; + int gflag = 0; + int pbotflag = 0; + int c; + int v0, v1, v2; + size_t off, n; + time_t t; + struct tm *ptm; + + fprintf(stderr, "mjn3's addpattern replacement - v0.81\n"); + + hdr = (struct code_header *) buf; + memset(hdr, 0, sizeof(struct code_header)); + + while ((c = getopt(argc, argv, "i:o:p:s:gbv:01245hr:B:")) != -1) { + switch (c) { + case 'i': + ifn = optarg; + break; + case 'o': + ofn = optarg; + break; + case 'p': + pattern = optarg; + break; + case 's': + hdr->sn = (unsigned char) atoi (optarg); + break; + case 'g': + gflag = 1; + break; + case 'b': + pbotflag = 1; + break; + case 'v': /* extension to allow setting version */ + version = optarg; + break; + case '0': + hdr->hw_ver = 0; + break; + case '1': + hdr->hw_ver = 1; + break; + case '2': /* new 54G v2.2 and 54GS v1.1 flags */ + hdr->hw_ver = 1; + hdr->flags[0] |= SUPPORT_4712_CHIP; + hdr->flags[0] |= SUPPORT_INTEL_FLASH; + hdr->flags[0] |= SUPPORT_5325E_SWITCH; + break; + case '4': + /* V4 firmware sets the flags to 0x1f */ + hdr->hw_ver = 0; + hdr->flags[0] = 0x1f; + break; + case '5': + /* V5 is appended to trxV2 image */ + hdr->stable[0] = 0x73; // force image to be stable + hdr->stable[1] = 0x00; + hdr->try1[0] = 0x74; // force try1 to be set + hdr->try1[1] = 0x00; + hdr->try2[0] = hdr->try2[1] = 0xFF; + hdr->try3[0] = hdr->try3[1] = 0xFF; + break; + case 'r': + hdr->hw_ver = (char)(atof(optarg)*10)+0x30; + break; + case 'B': + board_id = optarg; + break; + + case 'h': + default: + usage(); + } + } + + if (optind != argc || optind == 1) { + fprintf(stderr, "illegal arg \"%s\"\n", argv[optind]); + usage(); + } + + if (board_id) { + board = find_board(board_id); + if (board == NULL) { + fprintf(stderr, "unknown board \"%s\"\n", board_id); + usage(); + } + pattern = board->pattern; + hdr->hw_ver = board->hw_ver; + hdr->sn = board->sn; + hdr->flags[0] = board->flags[0]; + hdr->flags[1] = board->flags[1]; + } + + if (strlen(pattern) > 8) { + fprintf(stderr, "illegal pattern \"%s\"\n", pattern); + 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(); + } + + if (time(&t) == (time_t)(-1)) { + fprintf(stderr, "time call failed\n"); + return EXIT_FAILURE; + } + + ptm = localtime(&t); + + if (3 != sscanf(version, "v%d.%d.%d", &v0, &v1, &v2)) { + fprintf(stderr, "bad version string \"%s\"\n", version); + return EXIT_FAILURE; + } + + memcpy(hdr->magic, pattern, strlen(pattern)); + if (pbotflag) + 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)); + + off = sizeof(struct code_header); + + fprintf(stderr, "writing firmware v%d.%d.%d on %d/%d/%d (y/m/d)\n", + v0, v1, v2, + hdr->fwdate[0], hdr->fwdate[1], hdr->fwdate[2]); + + + while ((n = fread(buf + off, 1, sizeof(buf)-off, in) + off) > 0) { + off = 0; + if (n < sizeof(buf)) { + if (ferror(in)) { + FREAD_ERROR: + fprintf(stderr, "fread error\n"); + return EXIT_FAILURE; + } + if (gflag) { + gflag = sizeof(buf) - n; + memset(buf + n, 0xff, gflag); + fprintf(stderr, "adding %d bytes of garbage\n", gflag); + n = sizeof(buf); + } + } + 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; +} diff --git a/trunk/tools/firmware-utils/src/airlink.c b/trunk/tools/firmware-utils/src/airlink.c new file mode 100644 index 00000000..560a58df --- /dev/null +++ b/trunk/tools/firmware-utils/src/airlink.c @@ -0,0 +1,332 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +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/trunk/tools/firmware-utils/src/bcm_tag.h b/trunk/tools/firmware-utils/src/bcm_tag.h new file mode 100644 index 00000000..ecbd8891 --- /dev/null +++ b/trunk/tools/firmware-utils/src/bcm_tag.h @@ -0,0 +1,70 @@ +#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 libreCMC, 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 libreCMC) + 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/trunk/tools/firmware-utils/src/bcmalgo.c b/trunk/tools/firmware-utils/src/bcmalgo.c new file mode 100644 index 00000000..e7d3b113 --- /dev/null +++ b/trunk/tools/firmware-utils/src/bcmalgo.c @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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/trunk/tools/firmware-utils/src/bcmalgo.h b/trunk/tools/firmware-utils/src/bcmalgo.h new file mode 100644 index 00000000..46647cf6 --- /dev/null +++ b/trunk/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/trunk/tools/firmware-utils/src/buffalo-enc.c b/trunk/tools/firmware-utils/src/buffalo-enc.c new file mode 100644 index 00000000..59b66ab9 --- /dev/null +++ b/trunk/tools/firmware-utils/src/buffalo-enc.c @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2009-2011 Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include /* for getopt() */ +#include + +#include "buffalo-lib.h" + +#define ERR(fmt, args...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## args ); \ +} while (0) + +static char *progname; +static char *ifname; +static char *ofname; +static char *crypt_key = "Buffalo"; +static char *magic = "start"; +static int longstate; +static unsigned char seed = 'O'; + +static char *product; +static char *version; +static int do_decrypt; + +void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -d decrypt instead of encrypt\n" +" -i read input from the file \n" +" -o write output to the file \n" +" -l use longstate {en,de}cryption method\n" +" -k use for encryption (default: Buffalo)\n" +" -m set magic to \n" +" -p set product name to \n" +" -v set version to \n" +" -h show this screen\n" + ); + + exit(status); +} + +static int decrypt_file(void) +{ + struct enc_param ep; + ssize_t src_len; + unsigned char *buf = NULL; + int err; + int ret = -1; + + src_len = get_file_size(ifname); + if (src_len < 0) { + ERR("unable to get size of '%s'", ifname); + goto out; + } + + buf = malloc(src_len); + if (buf == NULL) { + ERR("no memory for the buffer"); + goto out; + } + + err = read_file_to_buf(ifname, buf, src_len); + if (err) { + ERR("unable to read from file '%s'", ifname); + goto out; + } + + memset(&ep, '\0', sizeof(ep)); + ep.key = (unsigned char *) crypt_key; + ep.longstate = longstate; + + err = decrypt_buf(&ep, buf, src_len); + if (err) { + ERR("unable to decrypt '%s'", ifname); + goto out; + } + + printf("Magic\t\t: '%s'\n", ep.magic); + printf("Seed\t\t: 0x%02x\n", ep.seed); + printf("Product\t\t: '%s'\n", ep.product); + printf("Version\t\t: '%s'\n", ep.version); + 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); + if (err) { + ERR("unable to write to file '%s'", ofname); + goto out; + } + + ret = 0; + +out: + free(buf); + return ret; +} + +static int encrypt_file(void) +{ + struct enc_param ep; + ssize_t src_len; + unsigned char *buf; + uint32_t hdrlen; + ssize_t totlen = 0; + int err; + int ret = -1; + + src_len = get_file_size(ifname); + if (src_len < 0) { + ERR("unable to get size of '%s'", ifname); + goto out; + } + + totlen = enc_compute_buf_len(product, version, src_len); + hdrlen = enc_compute_header_len(product, version); + + buf = malloc(totlen); + if (buf == NULL) { + ERR("no memory for the buffer"); + goto out; + } + + err = read_file_to_buf(ifname, &buf[hdrlen], src_len); + if (err) { + ERR("unable to read from file '%s'", ofname); + goto free_buf; + } + + memset(&ep, '\0', sizeof(ep)); + ep.key = (unsigned char *) crypt_key; + ep.seed = seed; + ep.longstate = longstate; + ep.csum = buffalo_csum(src_len, &buf[hdrlen], src_len); + ep.datalen = src_len; + strcpy((char *) ep.magic, magic); + strcpy((char *) ep.product, product); + strcpy((char *) ep.version, version); + + err = encrypt_buf(&ep, buf, &buf[hdrlen]); + if (err) { + ERR("invalid input file"); + goto free_buf; + } + + err = write_buf_to_file(ofname, buf, totlen); + if (err) { + ERR("unable to write to file '%s'", ofname); + goto free_buf; + } + + ret = 0; + +free_buf: + free(buf); +out: + return ret; +} + +static int check_params(void) +{ + int ret = -1; + + if (ifname == NULL) { + ERR("no input file specified"); + goto out; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto out; + } + + if (crypt_key == NULL) { + ERR("no key specified"); + goto out; + } else if (strlen(crypt_key) > BCRYPT_MAX_KEYLEN) { + ERR("key '%s' is too long", crypt_key); + goto out; + } + + if (strlen(magic) != (ENC_MAGIC_LEN - 1)) { + ERR("length of magic must be %d", ENC_MAGIC_LEN - 1); + goto out; + } + + if (!do_decrypt) { + if (product == NULL) { + ERR("no product specified"); + goto out; + } + + if (version == NULL) { + ERR("no version specified"); + goto out; + } + + if (strlen(product) > (ENC_PRODUCT_LEN - 1)) { + ERR("product name '%s' is too long", product); + goto out; + } + + if (strlen(version) > (ENC_VERSION_LEN - 1)) { + ERR("version '%s' is too long", version); + goto out; + } + } + + ret = 0; + +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + int err; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "adi:m:o:hlp:v:k:r:s:"); + if (c == -1) + break; + + switch (c) { + case 'd': + do_decrypt = 1; + break; + case 'i': + ifname = optarg; + break; + case 'l': + longstate = 1; + break; + case 'm': + magic = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'p': + product = optarg; + break; + case 'v': + version = optarg; + break; + case 'k': + crypt_key = optarg; + break; + case 's': + seed = strtoul(optarg, NULL, 16); + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + err = check_params(); + if (err) + goto out; + + if (do_decrypt) + err = decrypt_file(); + else + err = encrypt_file(); + + if (err) + goto out; + + res = EXIT_SUCCESS; + +out: + return res; +} diff --git a/trunk/tools/firmware-utils/src/buffalo-lib.c b/trunk/tools/firmware-utils/src/buffalo-lib.c new file mode 100644 index 00000000..add3a7e0 --- /dev/null +++ b/trunk/tools/firmware-utils/src/buffalo-lib.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2009-2011 Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include + +#include "buffalo-lib.h" + +static uint32_t crc32_table[256] = +{ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, + 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, + 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, + 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, + 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, + 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, + 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, + 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, + 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, + 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, + 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, + 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, + 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, + 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, + 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, + 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, + 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, + 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, + 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, + 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, + 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, + 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, + 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, + 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, + 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, + 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, + 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +int bcrypt_init(struct bcrypt_ctx *ctx, void *key, int keylen, + unsigned long state_len) +{ + unsigned char *state; + unsigned char *p = key; + unsigned long i, j; + unsigned long k = 0; + + state = malloc(state_len); + if (state == NULL) + return -1; + + ctx->i = 0; + ctx->j = 0; + ctx->state = state; + ctx->state_len = state_len; + + for (i = 0; i < state_len; i++) + state[i] = i; + + for(i = 0, j = 0; i < state_len; i++, j = (j + 1) % keylen) { + unsigned char t; + + t = state[i]; + k = (k + p[j] + t) % state_len; + state[i] = state[k]; + state[k] = t; + } + + return 0; +} + +int bcrypt_process(struct bcrypt_ctx *ctx, unsigned char *src, + unsigned char *dst, unsigned long len) +{ + unsigned char *state = ctx->state; + unsigned long state_len = ctx->state_len; + unsigned char i, j; + unsigned long k; + + i = ctx->i; + j = ctx->j; + + for (k = 0; k < len; k++) { + unsigned char t; + + i = (i + 1) % state_len; + j = (j + state[i]) % state_len; + t = state[j]; + state[j] = state[i]; + state[i] = t; + + dst[k] = src[k] ^ state[(state[i] + state[j]) % state_len]; + } + + ctx->i = i; + ctx->j = j; + + return len; +} + +void bcrypt_finish(struct bcrypt_ctx *ctx) +{ + if (ctx->state) + free(ctx->state); +} + +int bcrypt_buf(unsigned char seed, unsigned char *key, unsigned char *src, + unsigned char *dst, unsigned long len, int longstate) +{ + unsigned char bckey[BCRYPT_MAX_KEYLEN + 1]; + unsigned int keylen; + struct bcrypt_ctx ctx; + int ret; + + /* setup decryption key */ + keylen = strlen((char *) key); + bckey[0] = seed; + memcpy(&bckey[1], key, keylen); + + keylen++; + + ret = bcrypt_init(&ctx, bckey, keylen, + (longstate) ? len : BCRYPT_DEFAULT_STATE_LEN); + if (ret) + return ret; + + bcrypt_process(&ctx, src, dst, len); + bcrypt_finish(&ctx); + + return 0; +} + +uint32_t buffalo_csum(uint32_t csum, void *buf, unsigned long len) +{ + char *p = buf; + + while (len--) { + int i; + + csum ^= *p++; + for (i = 0; i < 8; i++) + csum = (csum >> 1) ^ ((csum & 1) ? 0xedb88320ul : 0); + } + + return csum; +} + +uint32_t buffalo_crc(void *buf, unsigned long len) +{ + unsigned char *p = buf; + unsigned long t = len; + uint32_t crc = 0; + + while (len--) + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *p++) & 0xFF]; + + while (t) { + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ t) & 0xFF]; + t >>= 8; + } + + return ~crc; +} + +unsigned long enc_compute_header_len(char *product, char *version) +{ + return ENC_MAGIC_LEN + 1 + strlen(product) + 1 + + strlen(version) + 1 + 3 * sizeof(uint32_t); +} + +unsigned long enc_compute_buf_len(char *product, char *version, + unsigned long datalen) +{ + unsigned long ret; + + ret = enc_compute_header_len(product, version); + ret += datalen + sizeof(uint32_t); + ret += (4 - ret % 4); + + return ret; +} + +static void put_be32(void *data, uint32_t val) +{ + unsigned char *p = data; + + p[0] = (val >> 24) & 0xff; + p[1] = (val >> 16) & 0xff; + p[2] = (val >> 8) & 0xff; + p[3] = val & 0xff; +} + +static uint32_t get_be32(void *data) +{ + unsigned char *p = data; + + return (((uint32_t)p[0]) << 24) | + (((uint32_t)p[1]) << 16) | + (((uint32_t)p[2]) << 8) | + ((uint32_t)p[3]); +} + +static int check_magic(void *magic) +{ + if (!memcmp("start", magic, ENC_MAGIC_LEN)) + return 0; + + if (!memcmp("asar1", magic, ENC_MAGIC_LEN)) + return 0; + + return -1; +} + +int encrypt_buf(struct enc_param *ep, unsigned char *hdr, + unsigned char *data) +{ + unsigned char *p; + uint32_t len; + int err; + int ret = -1; + unsigned char s; + + p = (unsigned char *) hdr; + + /* setup magic */ + len = strlen((char *) ep->magic) + 1; + memcpy(p, ep->magic, len); + p += len; + + /* setup seed */ + *p++ = ep->seed; + + /* put product len */ + len = strlen((char *) ep->product) + 1; + put_be32(p, len); + p += sizeof(uint32_t); + + /* copy and crypt product name */ + memcpy(p, ep->product, len); + err = bcrypt_buf(ep->seed, ep->key, p, p, len, ep->longstate); + if (err) + goto out; + s = *p; + p += len; + + /* put version length */ + len = strlen((char *) ep->version) + 1; + put_be32(p, len); + p += sizeof(uint32_t); + + /* copy and crypt version */ + memcpy(p, ep->version, len); + err = bcrypt_buf(s, ep->key, p, p, len, ep->longstate); + if (err) + goto out; + s = *p; + p += len; + + /* put data length */ + put_be32(p, ep->datalen); + + /* encrypt data */ + err = bcrypt_buf(s, ep->key, data, data, ep->datalen, ep->longstate); + if (err) + goto out; + + /* put checksum */ + put_be32(&data[ep->datalen], ep->csum); + + ret = 0; + +out: + return ret; +} + +int decrypt_buf(struct enc_param *ep, unsigned char *data, + unsigned long datalen) +{ + unsigned char *p; + uint32_t prod_len; + uint32_t ver_len; + uint32_t len; + uint32_t csum; + ssize_t remain; + int err; + int ret = -1; + +#define CHECKLEN(_l) do { \ + len = (_l); \ + if (remain < len) { \ + goto out; \ + } \ +} while (0) + +#define INCP() do { \ + p += len; \ + remain -= len; \ +} while (0) + + remain = datalen; + p = data; + + CHECKLEN(ENC_MAGIC_LEN); + err = check_magic(p); + if (err) + goto out; + memcpy(ep->magic, p, ENC_MAGIC_LEN); + INCP(); + + CHECKLEN(1); + ep->seed = *p; + INCP(); + + CHECKLEN(sizeof(uint32_t)); + prod_len = get_be32(p); + if (prod_len > ENC_PRODUCT_LEN) + goto out; + INCP(); + + CHECKLEN(prod_len); + memcpy(ep->product, p, prod_len); + INCP(); + + CHECKLEN(sizeof(uint32_t)); + ver_len = get_be32(p); + if (ver_len > ENC_VERSION_LEN) + goto out; + INCP(); + + CHECKLEN(ver_len); + memcpy(ep->version, p, ver_len); + INCP(); + + CHECKLEN(sizeof(uint32_t)); + ep->datalen = get_be32(p); + INCP(); + + /* decrypt data */ + CHECKLEN(ep->datalen); + err = bcrypt_buf(ep->version[0], ep->key, p, data, ep->datalen, + ep->longstate); + if (err) + goto out; + INCP(); + + CHECKLEN(sizeof(uint32_t)); + ep->csum = get_be32(p); + INCP(); + + csum = buffalo_csum(ep->datalen, data, ep->datalen); + if (csum != ep->csum) + goto out; + + /* decrypt product name */ + err = bcrypt_buf(ep->product[0], ep->key, ep->version, ep->version, + ver_len, ep->longstate); + if (err) + goto out; + + /* decrypt version */ + err = bcrypt_buf(ep->seed, ep->key, ep->product, ep->product, prod_len, + ep->longstate); + if (err) + goto out; + + ret = 0; +out: + return ret; + +#undef CHECKLEN +#undef INCP +} + +ssize_t get_file_size(char *name) +{ + struct stat st; + int err; + + err = stat(name, &st); + if (err) + return -1; + + return st.st_size; +} + +int read_file_to_buf(char *name, void *buf, ssize_t buflen) +{ + FILE *f; + size_t done; + int ret = -1; + + f = fopen(name, "r"); + if (f == NULL) + goto out; + + errno = 0; + done = fread(buf, buflen, 1, f); + if (done != 1) + goto close; + + ret = 0; + +close: + fclose(f); +out: + return ret; +} + +int write_buf_to_file(char *name, void *buf, ssize_t buflen) +{ + FILE *f; + size_t done; + int ret = -1; + + f = fopen(name, "w"); + if (f == NULL) + goto out; + + errno = 0; + done = fwrite(buf, buflen, 1, f); + if (done != 1) + goto close; + + ret = 0; + +close: + fflush(f); + fclose(f); + if (ret) + unlink(name); +out: + return ret; +} diff --git a/trunk/tools/firmware-utils/src/buffalo-lib.h b/trunk/tools/firmware-utils/src/buffalo-lib.h new file mode 100644 index 00000000..ba8a5081 --- /dev/null +++ b/trunk/tools/firmware-utils/src/buffalo-lib.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009-2011 Gabor Juhos + * + * 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 _BUFFALO_LIB_H +#define _BUFFALO_LIB_H + +#include + +#define ARRAY_SIZE(_a) (sizeof((_a)) / sizeof((_a)[0])) +#define BIT(_x) (1UL << (_x)) + +#define TAG_BRAND_LEN 32 +#define TAG_PRODUCT_LEN 32 +#define TAG_VERSION_LEN 8 +#define TAG_REGION_LEN 2 +#define TAG_LANGUAGE_LEN 8 +#define TAG_PLATFORM_LEN 8 +#define TAG_HWVER_LEN 4 +#define TAG_HWVER_VAL_LEN 4 + +struct buffalo_tag { + 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 len; + uint32_t crc; + uint32_t base1; + uint32_t base2; + uint32_t data_len; + uint8_t flag; + uint8_t unknown2[3]; +} __attribute ((packed)); + +struct buffalo_tag2 { + 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 len2; + uint8_t flag; + uint8_t unknown2[3]; +} __attribute ((packed)); + +#define ENC_PRODUCT_LEN 32 +#define ENC_VERSION_LEN 8 +#define ENC_MAGIC_LEN 6 + +unsigned long enc_compute_header_len(char *product, char *version); +unsigned long enc_compute_buf_len(char *product, char *version, + unsigned long datalen); + +struct enc_param { + unsigned char *key; + unsigned char magic[ENC_MAGIC_LEN]; + unsigned char product[ENC_PRODUCT_LEN]; + unsigned char version[ENC_VERSION_LEN]; + unsigned char seed; + int longstate; + unsigned datalen; + uint32_t csum; +}; + +int encrypt_buf(struct enc_param *ep, unsigned char *hdr, + unsigned char *data); +int decrypt_buf(struct enc_param *ep, unsigned char *data, + unsigned long datalen); + +#define BCRYPT_DEFAULT_STATE_LEN 256 +#define BCRYPT_MAX_KEYLEN 254 + +struct bcrypt_ctx { + unsigned long i; + unsigned long j; + unsigned char *state; + unsigned long state_len; +}; + +int bcrypt_init(struct bcrypt_ctx *ctx, void *key, int keylen, + unsigned long state_len); +int bcrypt_process(struct bcrypt_ctx *ctx, unsigned char *src, + unsigned char *dst, unsigned long len); +void bcrypt_finish(struct bcrypt_ctx *ctx); +int bcrypt_buf(unsigned char seed, unsigned char *key, unsigned char *src, + unsigned char *dst, unsigned long len, int longstate); + +uint32_t buffalo_csum(uint32_t csum, void *buf, unsigned long len); +uint32_t buffalo_crc(void *buf, unsigned long len); + +ssize_t get_file_size(char *name); +int read_file_to_buf(char *name, void *buf, ssize_t buflen); +int write_buf_to_file(char *name, void *buf, ssize_t buflen); + +#endif /* _BUFFALO_LIB_H */ diff --git a/trunk/tools/firmware-utils/src/buffalo-tag.c b/trunk/tools/firmware-utils/src/buffalo-tag.c new file mode 100644 index 00000000..b5db72ef --- /dev/null +++ b/trunk/tools/firmware-utils/src/buffalo-tag.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2009-2011 Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include /* for getopt() */ +#include + +#include "buffalo-lib.h" + +#define ERR(fmt, ...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +static char *region_table[] = { + "JP", "US", "EU", "AP", "TW", "KR" +}; + +#define MAX_INPUT_FILES 2 + +static char *progname; +static char *ifname[MAX_INPUT_FILES]; +static ssize_t fsize[MAX_INPUT_FILES]; +static int num_files; +static char *ofname; +static char *product; +static char *brand; +static char *language; +static char *hwver; +static char *platform; +static int flag; +static char *major; +static char *minor = "1.01"; +static int skipcrc; +static uint32_t base1; +static uint32_t base2; +static char *region_code; +static uint32_t region_mask; +static int num_regions; + +void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -a set platform to \n" +" -b set brand to \n" +" -c \n" +" -d \n" +" -f set flag to \n" +" -i read input from the file \n" +" -l set language to \n" +" -m set minor version to \n" +" -o write output to the file \n" +" -p set product to \n" +" -r set image region to \n" +" valid regions: JP, US, EU, AP, TW, KR, M_\n" +" -s skip CRC calculation\n" +" -v set major version to \n" +" -w set harwdware version to \n" +" -h show this screen\n" + ); + + exit(status); +} + +static int check_params(void) +{ + +#define CHECKSTR(_var, _name, _len) do { \ + if ((_var) == NULL) { \ + ERR("no %s specified", (_name)); \ + return -1; \ + } \ + if ((_len) > 0 && \ + strlen((_var)) > ((_len) - 1)) { \ + ERR("%s is too long", (_name)); \ + return -1; \ + } \ +} while (0) + + if (num_files == 0) + ERR("no input files specified"); + + CHECKSTR(ofname, "output file", 0); + CHECKSTR(brand, "brand", TAG_BRAND_LEN); + CHECKSTR(product, "product", TAG_PRODUCT_LEN); + CHECKSTR(platform, "platform", TAG_PLATFORM_LEN); + CHECKSTR(major, "major version", TAG_VERSION_LEN); + CHECKSTR(minor, "minor version", TAG_VERSION_LEN); + CHECKSTR(language, "language", TAG_LANGUAGE_LEN); + + if (hwver) + CHECKSTR(hwver, "hardware version", 2); + + if (num_regions == 0) { + ERR("no region code specified"); + return -1; + } + + return 0; + +#undef CHECKSTR +} + +static int process_region(char *reg) +{ + int i; + + if (strlen(reg) != 2) { + ERR("invalid region code '%s'", reg); + return -1; + } + + if (strcmp(reg, "M_") == 0) { + region_code = reg; + region_mask |= ~0; + num_regions = 32; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(region_table); i++) + if (strcmp(reg, region_table[i]) == 0) { + region_code = reg; + region_mask |= 1 << i; + num_regions++; + return 0; + } + + ERR("unknown region code '%s'", reg); + return -1; +} + +static int process_ifname(char *name) +{ + if (num_files >= ARRAY_SIZE(ifname)) { + ERR("too many input files specified"); + return -1; + } + + ifname[num_files++] = name; + return 0; +} + +static void fixup_tag(unsigned char *buf, ssize_t buflen) +{ + struct buffalo_tag *tag = (struct buffalo_tag *) 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->len = htonl(buflen); + tag->data_len = htonl(fsize[0]); + tag->base1 = htonl(base1); + tag->base2 = htonl(base2); + tag->flag = flag; + + if (hwver) { + memcpy(tag->hwv, "hwv", 3); + memcpy(tag->hwv_val, hwver, strlen(hwver)); + } + + if (!skipcrc) + tag->crc = htonl(buffalo_crc(buf, buflen)); +} + +static void fixup_tag2(unsigned char *buf, ssize_t buflen) +{ + struct buffalo_tag2 *tag = (struct buffalo_tag2 *) 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(buflen); + tag->len1 = htonl(fsize[0]); + tag->len2 = htonl(fsize[1]); + tag->flag = flag; + + if (hwver) { + memcpy(tag->hwv, "hwv", 3); + memcpy(tag->hwv_val, hwver, strlen(hwver)); + } + + if (!skipcrc) + tag->crc = htonl(buffalo_crc(buf, buflen)); +} + +static int tag_file(void) +{ + unsigned char *buf; + ssize_t offset; + ssize_t hdrlen; + ssize_t buflen; + int err; + int ret = -1; + int i; + + if (num_files == 1) + hdrlen = sizeof(struct buffalo_tag); + else + hdrlen = sizeof(struct buffalo_tag2); + + buflen = hdrlen; + + for (i = 0; i < num_files; i++) { + fsize[i] = get_file_size(ifname[i]); + if (fsize[i] < 0) { + ERR("unable to get size of '%s'", ifname[i]); + goto out; + } + buflen += fsize[i]; + } + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + offset = hdrlen; + for (i = 0; i < num_files; i++) { + err = read_file_to_buf(ifname[i], buf + offset, fsize[i]); + if (err) { + ERR("unable to read from file '%s'", ifname[i]); + goto free_buf; + } + + offset += fsize[i]; + } + + if (num_files == 1) + fixup_tag(buf, buflen); + else + fixup_tag2(buf, buflen); + + err = write_buf_to_file(ofname, buf, buflen); + if (err) { + ERR("unable to write to file '%s'", ofname); + goto free_buf; + } + + ret = 0; + +free_buf: + free(buf); +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + int err; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "a:b:c:d:f:hi:l:m:o:p:r:sv:w:"); + if (c == -1) + break; + + switch (c) { + case 'a': + platform = optarg; + break; + case 'b': + brand = optarg; + break; + case 'c': + base1 = strtoul(optarg, NULL, 16); + break; + case 'd': + base2 = strtoul(optarg, NULL, 16); + break; + case 'f': + flag = strtoul(optarg, NULL, 2); + break; + case 'i': + err = process_ifname(optarg); + if (err) + goto out; + break; + case 'l': + language = optarg; + break; + case 'm': + minor = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'p': + product = optarg; + break; + case 'r': + err = process_region(optarg); + if (err) + goto out; + break; + case 's': + skipcrc = 1; + break; + case 'v': + major = optarg; + break; + case 'w': + hwver = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + err = check_params(); + if (err) + goto out; + + err = tag_file(); + if (err) + goto out; + + res = EXIT_SUCCESS; + +out: + return res; +} diff --git a/trunk/tools/firmware-utils/src/buffalo-tftp.c b/trunk/tools/firmware-utils/src/buffalo-tftp.c new file mode 100644 index 00000000..087f9955 --- /dev/null +++ b/trunk/tools/firmware-utils/src/buffalo-tftp.c @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2009-2011 Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include /* for getopt() */ +#include + +#include "buffalo-lib.h" + +#define ERR(fmt, args...) do { \ + fflush(0); \ + fprintf(stderr, "[%s] *** error: " fmt "\n", \ + progname, ## args ); \ +} while (0) + +static char *progname; +static char *ifname; +static char *ofname; +static int do_decrypt; + +void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, +"\n" +"Options:\n" +" -d decrypt instead of encrypt\n" +" -i read input from the file \n" +" -o write output to the file \n" +" -h show this screen\n" + ); + + exit(status); +} + +static const unsigned char *crypt_key1 = (unsigned char *) + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; +static const unsigned char *crypt_key2 = (unsigned char *) + "XYZ0123hijklmnopqABCDEFGHrstuvabcdefgwxyzIJKLMSTUVW456789NOPQR"; + +static void crypt_header(unsigned char *buf, ssize_t len, + const unsigned char *key1, const unsigned char *key2) +{ + ssize_t i; + + for (i = 0; i < len; i++) { + unsigned int j; + + for (j = 0; key1[j]; j++) + if (buf[i] == key1[j]) { + buf[i] = key2[j]; + break; + } + } +} + +static int crypt_file(void) +{ + unsigned char *buf = NULL; + ssize_t src_len; + int err; + int ret = -1; + + src_len = get_file_size(ifname); + if (src_len < 0) { + ERR("unable to get size of '%s'", ifname); + goto out; + } + + buf = malloc(src_len); + if (buf == NULL) { + ERR("no memory for the buffer"); + goto out; + } + + err = read_file_to_buf(ifname, buf, src_len); + if (err) { + ERR("unable to read from file '%s'", ifname); + goto out; + } + + if (do_decrypt) + crypt_header(buf, 512, crypt_key2, crypt_key1); + else + crypt_header(buf, 512, crypt_key1, crypt_key2); + + err = write_buf_to_file(ofname, buf, src_len); + if (err) { + ERR("unable to write to file '%s'", ofname); + goto out; + } + + ret = 0; + +out: + free(buf); + return ret; +} + +static int check_params(void) +{ + int ret = -1; + + if (ifname == NULL) { + ERR("no input file specified"); + goto out; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto out; + } + + ret = 0; + +out: + return ret; +} + +int main(int argc, char *argv[]) +{ + int res = EXIT_FAILURE; + int err; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "di:o:h"); + if (c == -1) + break; + + switch (c) { + case 'd': + do_decrypt = 1; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + err = check_params(); + if (err) + goto out; + + err = crypt_file(); + if (err) + goto out; + + res = EXIT_SUCCESS; + +out: + return res; +} diff --git a/trunk/tools/firmware-utils/src/csysimg.h b/trunk/tools/firmware-utils/src/csysimg.h new file mode 100644 index 00000000..65ab062f --- /dev/null +++ b/trunk/tools/firmware-utils/src/csysimg.h @@ -0,0 +1,79 @@ +/* + * + * Copyright (C) 2007,2009 Gabor Juhos + * + * This program was based on the code found in various Linux + * source tarballs released by Edimax for it's devices. + * Original author: David Hsu + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#define SIG_LEN 4 + +#define ADM_CODE_ADDR 0x80500000 +#define ADM_WEBP_ADDR 0x10000 +#define ADM_WEBP_SIZE 0x10000 +#define ADM_BOOT_SIZE 0x8000 +#define ADM_CONF_SIZE 0x8000 +#define ADM_BOOT_SIG "\x00\x60\x1A\x40" + + +/* + * Generic signatures + */ +#define SIG_CSYS "CSYS" +#define SIG_CONF "HS\x00\x00" +#define SIG_BOOT_RTL "\x00\x00\x40\x21" + +/* + * Web page signatures + */ +#define SIG_BR6104K "WB4K" +#define SIG_BR6104KP "WBKP" +#define SIG_BR6104Wg "WBGW" +#define SIG_BR6104IPC "WBIP" +#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" +#define SIG_BR6541KP "4-KP" /* FIXME: valid? */ +#define SIG_BR6541WP "4-WP" /* FIXME: valid? */ +#define SIG_C54BSR4 SIG_BR6104IPC +#define SIG_EW7207APg "EWAS" +#define SIG_PS1205UWg "4000" +#define SIG_PS3205U "5010" +#define SIG_PS3205UWg "5011" +#define SIG_RALINK "RNRA" +#define SIG_5GXI "5GXI" /* fake signature */ + +#define SIG_H2BR4 SIG_BR6524K +#define SIG_H2WR54G SIG_BR6524WG + +#define SIG_XRT401D SIG_BR6104K +#define SIG_XRT402D SIG_BR6524K + +/* + * CSYS image file header + */ +struct csys_header { + unsigned char sig[SIG_LEN]; + uint32_t addr; + uint32_t size; +}; diff --git a/trunk/tools/firmware-utils/src/cyg_crc.h b/trunk/tools/firmware-utils/src/cyg_crc.h new file mode 100644 index 00000000..7b598034 --- /dev/null +++ b/trunk/tools/firmware-utils/src/cyg_crc.h @@ -0,0 +1,109 @@ +//========================================================================== +// +// crc.h +// +// Interface for the CRC algorithms. +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2002 Andrew Lunn +// +// eCos 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 or (at your option) any later version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Andrew Lunn +// Contributors: Andrew Lunn +// Date: 2002-08-06 +// Purpose: +// Description: +// +// This code is part of eCos (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#ifndef _SERVICES_CRC_CRC_H_ +#define _SERVICES_CRC_CRC_H_ + +#if 0 +#include +#else +#include +typedef uint32_t cyg_uint32; +typedef uint16_t cyg_uint16; +#endif + +#ifndef __externC +# ifdef __cplusplus +# define __externC extern "C" +# else +# define __externC extern +# endif +#endif + +// Compute a CRC, using the POSIX 1003 definition + +__externC cyg_uint32 +cyg_posix_crc32(unsigned char *s, int len); + +// Gary S. Brown's 32 bit CRC + +__externC cyg_uint32 +cyg_crc32(unsigned char *s, int len); + +// Gary S. Brown's 32 bit CRC, but accumulate the result from a +// previous CRC calculation + +__externC cyg_uint32 +cyg_crc32_accumulate(cyg_uint32 crc, unsigned char *s, int len); + +// Ethernet FCS Algorithm + +__externC cyg_uint32 +cyg_ether_crc32(unsigned char *s, int len); + +// Ethernet FCS algorithm, but accumulate the result from a previous +// CRC calculation. + +__externC cyg_uint32 +cyg_ether_crc32_accumulate(cyg_uint32 crc, unsigned char *s, int len); + +// 16 bit CRC with polynomial x^16+x^12+x^5+1 + +__externC cyg_uint16 +cyg_crc16(unsigned char *s, int len); + +#endif // _SERVICES_CRC_CRC_H_ + + + diff --git a/trunk/tools/firmware-utils/src/cyg_crc16.c b/trunk/tools/firmware-utils/src/cyg_crc16.c new file mode 100644 index 00000000..8b373528 --- /dev/null +++ b/trunk/tools/firmware-utils/src/cyg_crc16.c @@ -0,0 +1,110 @@ +//========================================================================== +// +// crc16.c +// +// 16 bit CRC with polynomial x^16+x^12+x^5+1 +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2002 Gary Thomas +// +// eCos 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 or (at your option) any later version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas,asl +// Date: 2001-01-31 +// Purpose: +// Description: +// +// This code is part of eCos (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#if 0 +#include +#else +#include "cyg_crc.h" +#endif + +// Table of CRC constants - implements x^16+x^12+x^5+1 +static const cyg_uint16 crc16_tab[] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, +}; + +cyg_uint16 +cyg_crc16(unsigned char *buf, int len) +{ + int i; + cyg_uint16 cksum; + + cksum = 0; + for (i = 0; i < len; i++) { + cksum = crc16_tab[((cksum>>8) ^ *buf++) & 0xFF] ^ (cksum << 8); + } + return cksum; +} + diff --git a/trunk/tools/firmware-utils/src/cyg_crc32.c b/trunk/tools/firmware-utils/src/cyg_crc32.c new file mode 100644 index 00000000..9462598a --- /dev/null +++ b/trunk/tools/firmware-utils/src/cyg_crc32.c @@ -0,0 +1,172 @@ +//========================================================================== +// +// crc32.c +// +// Gary S. Brown's 32 bit CRC +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2002 Gary Thomas +// +// eCos 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 or (at your option) any later version. +// +// eCos 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 eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas,asl +// Date: 2001-01-31 +// Purpose: +// Description: +// +// This code is part of eCos (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#if 0 +#include +#else +#include "cyg_crc.h" +#endif + + /* ====================================================================== */ + /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ + /* code or tables extracted from it, as desired without restriction. */ + /* */ + /* First, the polynomial itself and its table of feedback terms. The */ + /* polynomial is */ + /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ + /* */ + /* ====================================================================== */ + +static const cyg_uint32 crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL + }; + +/* This is the standard Gary S. Brown's 32 bit CRC algorithm, but + accumulate the CRC into the result of a previous CRC. */ +cyg_uint32 +cyg_crc32_accumulate(cyg_uint32 crc32val, unsigned char *s, int len) +{ + int i; + + for (i = 0; i < len; i++) { + crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); + } + return crc32val; +} + +/* This is the standard Gary S. Brown's 32 bit CRC algorithm */ +cyg_uint32 +cyg_crc32(unsigned char *s, int len) +{ + return (cyg_crc32_accumulate(0,s,len)); +} + +/* Return a 32-bit CRC of the contents of the buffer accumulating the + result from a previous CRC calculation. This uses the Ethernet FCS + algorithm.*/ +cyg_uint32 +cyg_ether_crc32_accumulate(cyg_uint32 crc32val, unsigned char *s, int len) +{ + int i; + + if (s == 0) return 0L; + + crc32val = crc32val ^ 0xffffffff; + for (i = 0; i < len; i++) { + crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); + } + return crc32val ^ 0xffffffff; +} + +/* Return a 32-bit CRC of the contents of the buffer, using the + Ethernet FCS algorithm. */ +cyg_uint32 +cyg_ether_crc32(unsigned char *s, int len) +{ + return cyg_ether_crc32_accumulate(0,s,len); +} + + diff --git a/trunk/tools/firmware-utils/src/dgfirmware.c b/trunk/tools/firmware-utils/src/dgfirmware.c new file mode 100644 index 00000000..5ff3b696 --- /dev/null +++ b/trunk/tools/firmware-utils/src/dgfirmware.c @@ -0,0 +1,376 @@ +#include +#include + + +#define IMG_SIZE 0x3e0000 + +#define KERNEL_START 0x020000 +#define KERNEL_SIZE 0x0b0000 + +#define ROOTFS_START 0x0d0000 +#define ROOTFS_SIZE 0x30ffb2 + +char* app_name; + + + + +void print_usage(void) +{ + fprintf(stderr, "usage: dgfirmware [] \n"); + fprintf(stderr, " firmware image filename\n"); + fprintf(stderr, " -h print this message\n"); + fprintf(stderr, " -f fix the checksum\n"); + fprintf(stderr, " -x extract the rootfs file to \n"); + fprintf(stderr, " -xk extract the kernel to \n"); + fprintf(stderr, " -m merge in rootfs fil\e from \n"); + fprintf(stderr, " -k merge in kernel from \n"); + fprintf(stderr, " -w write back the modified firmware\n"); +} + + +unsigned char* read_img(const char *fname) +{ + FILE *fp; + int size; + unsigned char *img; + + fp = fopen(fname, "rb"); + if (fp == NULL) { + perror(app_name); + exit(-1); + } + + fseek(fp, 0, SEEK_END); + size = ftell(fp); + + if (size != IMG_SIZE) { + fprintf(stderr, "%s: image file has wrong size\n", app_name); + fclose(fp); + exit(-1); + } + + rewind(fp); + + img = malloc(IMG_SIZE); + if (img == NULL) { + perror(app_name); + fclose(fp); + exit(-1); + } + + if (fread(img, 1, IMG_SIZE, fp) != IMG_SIZE) { + fprintf(stderr, "%s: can't read image file\n", app_name); + fclose(fp); + exit(-1); + } + + fclose(fp); + return img; +} + + +void write_img(unsigned char* img, const char *fname) +{ + FILE *fp; + + fp = fopen(fname, "wb"); + if (fp == NULL) { + perror(app_name); + exit(-1); + } + + if (fwrite(img, 1, IMG_SIZE, fp) != IMG_SIZE) { + fprintf(stderr, "%s: can't write image file\n", app_name); + fclose(fp); + exit(-1); + } +} + + +void write_rootfs(unsigned char* img, const char *fname) +{ + FILE *fp; + + fp = fopen(fname, "wb"); + if (fp == NULL) { + perror(app_name); + exit(-1); + } + + if (fwrite(img+ROOTFS_START, 1, ROOTFS_SIZE, fp) != ROOTFS_SIZE) { + fprintf(stderr, "%s: can't write image file\n", app_name); + fclose(fp); + exit(-1); + } +} + + +void write_kernel(unsigned char* img, const char *fname) +{ + FILE *fp; + + fp = fopen(fname, "wb"); + if (fp == NULL) { + perror(app_name); + exit(-1); + } + + if (fwrite(img+KERNEL_START, 1, KERNEL_SIZE, fp) != KERNEL_SIZE) { + fprintf(stderr, "%s: can't write kernel file\n", app_name); + fclose(fp); + exit(-1); + } +} + + +unsigned char* read_rootfs(unsigned char* img, const char *fname) +{ + FILE *fp; + int size; + int i; + + for (i=ROOTFS_START; i ROOTFS_SIZE) { + fprintf(stderr, "%s: rootfs image file is too big\n", app_name); + fclose(fp); + exit(-1); + } + + rewind(fp); + + if (fread(img+ROOTFS_START, 1, size, fp) != size) { + fprintf(stderr, "%s: can't read rootfs image file\n", app_name); + fclose(fp); + exit(-1); + } + + fclose(fp); + return img; +} + + +unsigned char* read_kernel(unsigned char* img, const char *fname) +{ + FILE *fp; + int size; + int i; + + for (i=KERNEL_START; i KERNEL_SIZE) { + fprintf(stderr, "%s: kernel binary file is too big\n", app_name); + fclose(fp); + exit(-1); + } + + rewind(fp); + + if (fread(img+KERNEL_START, 1, size, fp) != size) { + fprintf(stderr, "%s: can't read kernel file\n", app_name); + fclose(fp); + exit(-1); + } + + fclose(fp); + return img; +} + + +int get_checksum(unsigned char* img) +{ + short unsigned s; + + s = img[0x3dfffc] + (img[0x3dfffd]<<8); + + return s; +} + + +void set_checksum(unsigned char*img, unsigned short sum) +{ + img[0x3dfffc] = sum & 0xff; + img[0x3dfffd] = (sum>>8) & 0xff; +} + + +int compute_checksum(unsigned char* img) +{ + int i; + short s=0; + + for (i=0; i<0x3dfffc; i++) + s += img[i]; + + return s; +} + + +int main(int argc, char* argv[]) +{ + char *img_fname = NULL; + char *rootfs_fname = NULL; + char *kernel_fname = NULL; + char *new_img_fname = NULL; + + int do_fix_checksum = 0; + int do_write = 0; + int do_write_rootfs = 0; + int do_read_rootfs = 0; + int do_write_kernel = 0; + int do_read_kernel = 0; + + int i; + unsigned char *img; + unsigned short img_checksum; + unsigned short real_checksum; + + app_name = argv[0]; + + for (i=1; i= argc) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + do_write_rootfs = 1; + rootfs_fname = argv[i+1]; + i++; + } + else if (!strcmp(argv[i], "-xk")) { + if (i+1 >= argc) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + do_write_kernel = 1; + kernel_fname = argv[i+1]; + i++; + } + else if (!strcmp(argv[i], "-m")) { + if (i+1 >= argc) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + do_read_rootfs = 1; + rootfs_fname = argv[i+1]; + i++; + } + else if (!strcmp(argv[i], "-k")) { + if (i+1 >= argc) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + do_read_kernel = 1; + kernel_fname = argv[i+1]; + i++; + } + else if (!strcmp(argv[i], "-w")) { + if (i+1 >= argc) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + do_write = 1; + new_img_fname = argv[i+1]; + i++; + } + else if (img_fname != 0) { + fprintf(stderr, "%s: too many arguments\n", app_name); + return -1; + } + else { + img_fname = argv[i]; + } + } + + if (img_fname == NULL) { + fprintf(stderr, "%s: missing argument\n", app_name); + return -1; + } + + if ((do_read_rootfs && do_write_rootfs) || + (do_read_kernel && do_write_kernel)) { + fprintf(stderr, "%s: conflictuous options\n", app_name); + return -1; + } + + printf ("** Read firmware file\n"); + img = read_img(img_fname); + + printf ("Firmware product: %s\n", img+0x3dffbd); + printf ("Firmware version: 1.%02d.%02d\n", (img[0x3dffeb] & 0x7f), img[0x3dffec]); + + if (do_write_rootfs) { + printf ("** Write rootfs file\n"); + write_rootfs(img, rootfs_fname); + } + + if (do_write_kernel) { + printf ("** Write kernel file\n"); + write_kernel(img, kernel_fname); + } + + if (do_read_rootfs) { + printf ("** Read rootfs file\n"); + read_rootfs(img, rootfs_fname); + do_fix_checksum = 1; + } + + if (do_read_kernel) { + printf ("** Read kernel file\n"); + read_kernel(img, kernel_fname); + do_fix_checksum = 1; + } + + img_checksum = get_checksum(img); + real_checksum = compute_checksum(img); + + printf ("image checksum = %04x\n", img_checksum); + printf ("real checksum = %04x\n", real_checksum); + + if (do_fix_checksum) { + if (img_checksum != real_checksum) { + printf ("** Bad Checksum, fix it\n"); + set_checksum(img, real_checksum); + } + else { + printf ("** Checksum is correct, good\n"); + } + } + + if (do_write) { + printf ("** Write image file\n"); + write_img(img, new_img_fname); + } + + free(img); + return 0; +} + diff --git a/trunk/tools/firmware-utils/src/dgn3500sum.c b/trunk/tools/firmware-utils/src/dgn3500sum.c new file mode 100644 index 00000000..00a0c5f8 --- /dev/null +++ b/trunk/tools/firmware-utils/src/dgn3500sum.c @@ -0,0 +1,167 @@ +/* ************************************************************************** + + 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 + + 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 +#include +#include +#include + +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, sum1; + 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; + sum1 = 0; + 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/trunk/tools/firmware-utils/src/edimax_fw_header.c b/trunk/tools/firmware-utils/src/edimax_fw_header.c new file mode 100644 index 00000000..b85e3a17 --- /dev/null +++ b/trunk/tools/firmware-utils/src/edimax_fw_header.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2014 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include +#include + +#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 set end addr to \n" +" -f set force flag\n" +" -h show this screen\n" +" -i read input data from the file \n" +" -o write output to the file \n" +" -m set model to \n" +" -M set image magic to \n" +" -n set MTD device name to \n" +" -s set start address to \n" +" -t set image type to \n" +" -v set firmware version to \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/trunk/tools/firmware-utils/src/encode_crc.c b/trunk/tools/firmware-utils/src/encode_crc.c new file mode 100644 index 00000000..647fb92e --- /dev/null +++ b/trunk/tools/firmware-utils/src/encode_crc.c @@ -0,0 +1,151 @@ +/* ************************************************************************** + + This program creates a CRC checksum and encodes the file that is named + in the command line. + + Compile with: gcc encode_crc.c -Wall -o encode_crc + + Author: Michael Margraf (michael.margraf@freecom.com) + Copyright: Freecom Technology GmbH, Berlin, 2004 + www.freecom.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 +#include +#include +#include + +// ******************************************************************* +// CCITT polynom G(x)=x^16+x^12+x^5+1 +#define POLYNOM 0x1021 + +// CRC algorithm with MSB first +int make_crc16(int crc, char new) +{ + int i; + crc = crc ^ (((int)new) << 8); + + for(i=0; i<8; i++) { // work on 8 bits in "new" + crc <<= 1; // MSBs first + if(crc & 0x10000) crc ^= POLYNOM; + } + return crc & 0xFFFF; +} + +// ******************************************************************* +// Reads the file "filename" 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) +{ + if(argc < 3) { + printf("ERROR: Argument missing!\n\n"); + return 1; + } + + int count; // size of file in bytes + char *p, *master = readfile(argv[1], &count); + if(!master) { + printf("ERROR: File not found!\n"); + return 1; + } + + int crc = 0xFFFF, z; + + p = master; + for(z=0; z 2) { // with flag for device recognition ? + p = argv[2]; + for(z=strlen(p); z>0; z--) { + crc ^= (int)(*p); + *(p++) = (char)crc; // encode device flag + } + } + */ + + p = master; + for(z=0; z 3) { // add flag for device recognition ? + fwrite(argv[3], strlen(argv[3]), sizeof(char), fp); + } + else { + // Device is an FSG, so byte swap (IXP4xx is big endian) + crc16 = ((crc16 >> 8) & 0xFF) | ((crc16 << 8) & 0xFF00); + } + + fwrite(&crc16, 1, sizeof(short), fp); // first write CRC + + fwrite(master, count, sizeof(char), fp); // write content + fclose(fp); + + free(master); + return 0; +} diff --git a/trunk/tools/firmware-utils/src/fix-u-media-header.c b/trunk/tools/firmware-utils/src/fix-u-media-header.c new file mode 100644 index 00000000..21f184e6 --- /dev/null +++ b/trunk/tools/firmware-utils/src/fix-u-media-header.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include "cyg_crc.h" + +#include +#include + +#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 set board ID to \n" +" -i read input from the file \n" +" -F load factory defaults\n" +" -o write output to the file \n" +" -T set image type to \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/trunk/tools/firmware-utils/src/fw.h b/trunk/tools/firmware-utils/src/fw.h new file mode 100644 index 00000000..e37859c5 --- /dev/null +++ b/trunk/tools/firmware-utils/src/fw.h @@ -0,0 +1,70 @@ +/* + * * Copyright (C) 2007 Ubiquiti Networks, Inc. + * * + * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * */ + +#ifndef FW_INCLUDED +#define FW_INCLUDED + +#include + +#define MAGIC_HEADER "OPEN" +#define MAGIC_PART "PART" +#define MAGIC_END "END." + +#define MAGIC_LENGTH 4 + +typedef struct header { + char magic[MAGIC_LENGTH]; + char version[256]; + u_int32_t crc; + u_int32_t pad; +} __attribute__ ((packed)) header_t; + +typedef struct part { + char magic[MAGIC_LENGTH]; + char name[16]; + char pad[12]; + u_int32_t memaddr; + u_int32_t index; + u_int32_t baseaddr; + u_int32_t entryaddr; + u_int32_t data_size; + u_int32_t part_size; +} __attribute__ ((packed)) part_t; + +typedef struct part_crc { + u_int32_t crc; + u_int32_t pad; +} __attribute__ ((packed)) part_crc_t; + +typedef struct signature { + char magic[MAGIC_LENGTH]; + u_int32_t crc; + u_int32_t pad; +} __attribute__ ((packed)) signature_t; + +#define VERSION "1.2" + +#define INFO(...) fprintf(stdout, __VA_ARGS__) +#define ERROR(...) fprintf(stderr, "ERROR: "__VA_ARGS__) +#define WARN(...) fprintf(stderr, "WARN: "__VA_ARGS__) +#define DEBUG(...) do {\ + if (debug) \ + fprintf(stdout, "DEBUG: "__VA_ARGS__); \ +} while (0); + +#endif diff --git a/trunk/tools/firmware-utils/src/hcsmakeimage.c b/trunk/tools/firmware-utils/src/hcsmakeimage.c new file mode 100644 index 00000000..603ea882 --- /dev/null +++ b/trunk/tools/firmware-utils/src/hcsmakeimage.c @@ -0,0 +1,181 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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" ); +} + + +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; + } + struct timeval tm; + gettimeofday ( &tm,NULL ); + struct stat buf; + stat ( input,&buf ); + ldr_header_t* head = construct_header ( magicnum, (uint16_t) majrev, (uint16_t) minrev, ( uint32_t ) tm.tv_sec, ( 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/trunk/tools/firmware-utils/src/imagetag.c b/trunk/tools/firmware-utils/src/imagetag.c new file mode 100644 index 00000000..e34890f0 --- /dev/null +++ b/trunk/tools/firmware-utils/src/imagetag.c @@ -0,0 +1,489 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2008 Axel Gembe + * Copyright (C) 2009-2010 Daniel Dickinson + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bcm_tag.h" +#include "imagetag_cmdline.h" +#include "cyg_crc.h" + +#define DEADCODE 0xDEADC0DE + +/* Kernel header */ +struct kernelhdr { + uint32_t loadaddr; /* Kernel load address */ + uint32_t entry; /* Kernel entry point address */ + uint32_t lzmalen; /* Compressed length of the LZMA data that follows */ +}; + +static char pirellitab[NUM_PIRELLI][BOARDID_LEN] = PIRELLI_BOARDS; + +void int2tag(char *tag, uint32_t value) { + uint32_t network = htonl(value); + memcpy(tag, (char *)(&network), 4); +} + +uint32_t compute_crc32(uint32_t crc, FILE *binfile, size_t compute_start, size_t compute_len) +{ + uint8_t readbuf[1024]; + size_t read; + + fseek(binfile, compute_start, SEEK_SET); + + /* 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 = 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 = cyg_crc32_accumulate(crc, readbuf, read); + } + + return crc; +} + +size_t getlen(FILE *fp) +{ + size_t retval, curpos; + + if (!fp) + return 0; + + curpos = ftell(fp); + fseek(fp, 0, SEEK_END); + retval = ftell(fp); + fseek(fp, curpos, SEEK_SET); + + return retval; +} + +int tagfile(const char *kernel, const char *rootfs, const char *bin, \ + const struct gengetopt_args_info *args, \ + uint32_t flash_start, uint32_t image_offset, \ + uint32_t block_size, uint32_t load_address, uint32_t entry) +{ + struct bcm_tag tag; + 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, \ + rootfsend; + uint8_t readbuf[1024]; + uint32_t imagecrc = IMAGETAG_CRC_START; + uint32_t kernelcrc = IMAGETAG_CRC_START; + uint32_t rootfscrc = IMAGETAG_CRC_START; + uint32_t kernelfscrc = IMAGETAG_CRC_START; + uint32_t fwaddr = 0; + uint8_t crc_val; + const uint32_t deadcode = htonl(DEADCODE); + int i; + int is_pirelli = 0; + + + memset(&tag, 0, sizeof(struct bcm_tag)); + + if (!kernel || !rootfs) { + fprintf(stderr, "imagetag can't create an image without both kernel and rootfs\n"); + } + + if (kernel && !(kernelfile = fopen(kernel, "rb"))) { + fprintf(stderr, "Unable to open kernel \"%s\"\n", kernel); + return 1; + } + + if (rootfs && !(rootfsfile = fopen(rootfs, "rb"))) { + fprintf(stderr, "Unable to open rootfs \"%s\"\n", rootfs); + return 1; + } + + if (!bin || !(binfile = fopen(bin, "wb+"))) { + fprintf(stderr, "Unable to open output file \"%s\"\n", bin); + return 1; + } + + if ((args->cfe_given) && (args->cfe_arg)) { + if (!(cfefile = fopen(args->cfe_arg, "rb"))) { + fprintf(stderr, "Unable to open CFE file \"%s\"\n", args->cfe_arg); + } + } + + fwaddr = flash_start + image_offset; + if (cfefile) { + cfeoff = flash_start; + cfelen = getlen(cfefile); + /* Seek to the start of the file after tag */ + fseek(binfile, sizeof(tag), SEEK_SET); + + /* Write the cfe */ + while (cfefile && !feof(cfefile) && !ferror(cfefile)) { + read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), cfefile); + fwrite(readbuf, sizeof(uint8_t), read, binfile); + } + + } else { + cfeoff = 0; + cfelen = 0; + } + + if (!args->root_first_flag) { + /* Build the kernel address and length (doesn't need to be aligned, read only) */ + kerneloff = fwaddr + sizeof(tag); + + kernellen = getlen(kernelfile); + + if (!args->kernel_file_has_header_flag) { + /* Build the kernel header */ + khdr.loadaddr = htonl(load_address); + khdr.entry = htonl(entry); + khdr.lzmalen = htonl(kernellen); + + /* Increase the kernel size by the header size */ + kernellen += sizeof(khdr); + } + + /* Build the rootfs address and length */ + rootfsoff = kerneloff + kernellen; + /* align the start if requested */ + if (args->align_rootfs_flag) + rootfsoff = (rootfsoff % block_size) > 0 ? (((rootfsoff / block_size) + 1) * block_size) : 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); + + /* Seek to the start of the kernel */ + fseek(binfile, kerneloff - fwaddr + cfelen, SEEK_SET); + + /* Write the kernel header */ + fwrite(&khdr, sizeof(khdr), 1, binfile); + + /* Write the kernel */ + while (kernelfile && !feof(kernelfile) && !ferror(kernelfile)) { + read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), kernelfile); + fwrite(readbuf, sizeof(uint8_t), read, binfile); + } + + /* Write the RootFS */ + fseek(binfile, rootfsoff - fwaddr + cfelen, SEEK_SET); + while (rootfsfile && !feof(rootfsfile) && !ferror(rootfsfile)) { + read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), rootfsfile); + fwrite(readbuf, sizeof(uint8_t), read, binfile); + } + + /* Align image to specified erase block size and append deadc0de */ + printf("Data alignment to %dk with 'deadc0de' appended\n", block_size/1024); + 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 + */ + fflush(binfile); + + /* Compute the crc32 of the entire image (deadC0de included) */ + imagecrc = compute_crc32(imagecrc, binfile, kerneloff - fwaddr + cfelen, imagelen); + /* Compute the crc32 of the kernel and padding between kernel and rootfs) */ + kernelcrc = compute_crc32(kernelcrc, binfile, kerneloff - fwaddr + cfelen, kernellen + rootfsoffpadlen); + /* Compute the crc32 of the kernel and padding between kernel and rootfs) */ + kernelfscrc = compute_crc32(kernelfscrc, binfile, kerneloff - fwaddr + cfelen, kernellen + rootfsoffpadlen + rootfslen + sizeof(deadcode)); + /* Compute the crc32 of the flashImageStart to rootLength. + * The broadcom firmware assumes the rootfs starts the image, + * therefore uses the rootfs start 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, which is added to the kernel + * length to determine the length of image to flash and thus + * needs to be rootfs + deadcode + */ + rootfscrc = compute_crc32(rootfscrc, binfile, kerneloff - fwaddr + cfelen, rootfslen + sizeof(deadcode)); + + } else { + /* Build the kernel address and length (doesn't need to be aligned, read only) */ + rootfsoff = fwaddr + sizeof(tag); + oldrootfslen = getlen(rootfsfile); + 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); + + imagelen = cfelen + rootfslen + kernellen; + + /* Seek to the start of the kernel */ + fseek(binfile, kerneloff - fwaddr + cfelen, SEEK_SET); + + if (!args->kernel_file_has_header_flag) { + /* Build the kernel header */ + khdr.loadaddr = htonl(load_address); + khdr.entry = htonl(entry); + khdr.lzmalen = htonl(kernellen); + + /* Write the kernel header */ + fwrite(&khdr, sizeof(khdr), 1, binfile); + + /* Increase the kernel size by the header size */ + kernellen += sizeof(khdr); + } + + /* Write the kernel */ + while (kernelfile && !feof(kernelfile) && !ferror(kernelfile)) { + read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), kernelfile); + fwrite(readbuf, sizeof(uint8_t), read, binfile); + } + + /* Write the RootFS */ + fseek(binfile, rootfsoff - fwaddr + cfelen, SEEK_SET); + while (rootfsfile && !feof(rootfsfile) && !ferror(rootfsfile)) { + read = fread(readbuf, sizeof(uint8_t), sizeof(readbuf), rootfsfile); + fwrite(readbuf, sizeof(uint8_t), read, binfile); + } + + /* Flush the binfile buffer so that when we read from file, it contains + * everything in the buffer + */ + fflush(binfile); + + /* Compute the crc32 of the entire image (deadC0de included) */ + imagecrc = compute_crc32(imagecrc, binfile, sizeof(tag), imagelen); + /* Compute the crc32 of the kernel and padding between kernel and rootfs) */ + kernelcrc = compute_crc32(kernelcrc, binfile, kerneloff - fwaddr + cfelen, kernellen + rootfsoffpadlen); + kernelfscrc = compute_crc32(kernelfscrc, binfile, rootfsoff - fwaddr + cfelen, kernellen + rootfslen); + rootfscrc = compute_crc32(rootfscrc, binfile, rootfsoff - fwaddr + cfelen, rootfslen); + } + + /* Close the files */ + if (cfefile) { + fclose(cfefile); + } + fclose(kernelfile); + fclose(rootfsfile); + + /* Build the tag */ + strncpy(tag.tagVersion, args->tag_version_arg, sizeof(tag.tagVersion) - 1); + strncpy(tag.sig_1, args->signature_arg, sizeof(tag.sig_1) - 1); + strncpy(tag.sig_2, args->signature2_arg, sizeof(tag.sig_2) - 1); + strncpy(tag.chipid, args->chipid_arg, sizeof(tag.chipid) - 1); + strncpy(tag.boardid, args->boardid_arg, sizeof(tag.boardid) - 1); + strcpy(tag.big_endian, "1"); + sprintf(tag.totalLength, "%lu", imagelen); + + if (args->cfe_given) { + sprintf(tag.cfeAddress, "%lu", flash_start); + sprintf(tag.cfeLength, "%lu", cfelen); + } else { + /* We don't include CFE */ + strcpy(tag.cfeAddress, "0"); + strcpy(tag.cfeLength, "0"); + } + + sprintf(tag.kernelAddress, "%lu", kerneloff); + sprintf(tag.kernelLength, "%lu", kernellen + rootfsoffpadlen); + + if (args->root_first_flag) { + sprintf(tag.flashImageStart, "%lu", rootfsoff); + sprintf(tag.flashRootLength, "%lu", rootfslen); + } else { + sprintf(tag.flashImageStart, "%lu", kerneloff); + sprintf(tag.flashRootLength, "%lu", rootfslen + sizeof(deadcode)); + } + int2tag(tag.rootLength, oldrootfslen + sizeof(deadcode)); + + if (args->rsa_signature_given) { + strncpy(tag.rsa_signature, args->rsa_signature_arg, RSASIG_LEN); + } + + if (args->layoutver_given) { + strncpy(tag.flashLayoutVer, args->layoutver_arg, TAGLAYOUT_LEN); + } + + if (args->info1_given) { + strncpy(tag.information1, args->info1_arg, TAGINFO1_LEN); + } + + if (args->info2_given) { + strncpy(tag.information2, args->info2_arg, TAGINFO2_LEN); + } + + if (args->reserved2_given) { + strncpy(tag.reserved2, args->reserved2_arg, 16); + } + + if (args->altinfo_given) { + strncpy(&tag.information1[0], args->altinfo_arg, ALTTAGINFO_LEN); + } + + if (args->second_image_flag_given) { + if (strncmp(args->second_image_flag_arg, "2", DUALFLAG_LEN) != 0) { + strncpy(tag.dualImage, args->second_image_flag_arg, DUALFLAG_LEN); + } + } + + if (args->inactive_given) { + if (strncmp(args->inactive_arg, "2", INACTIVEFLAG_LEN) != 0) { + strncpy(tag.inactiveFlag, args->second_image_flag_arg, INACTIVEFLAG_LEN); + } + } + + for (i = 0; i < NUM_PIRELLI; i++) { + if (strncmp(args->boardid_arg, pirellitab[i], BOARDID_LEN) == 0) { + is_pirelli = 1; + break; + } + } + + if ( !is_pirelli ) { + int2tag(tag.imageCRC, kernelfscrc); + } else { + int2tag(tag.imageCRC, kernelcrc); + } + + int2tag(&(tag.rootfsCRC[0]), rootfscrc); + int2tag(tag.kernelCRC, kernelcrc); + int2tag(tag.fskernelCRC, kernelfscrc); + 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); + + fflush(binfile); + fclose(binfile); + + return 0; +} + +int main(int argc, char **argv) +{ + int c, i; + char *kernel, *rootfs, *bin; + uint32_t flash_start, image_offset, block_size, load_address, entry; + flash_start = image_offset = block_size = load_address = entry = 0; + struct gengetopt_args_info parsed_args; + + kernel = rootfs = bin = NULL; + + if (imagetag_cmdline(argc, argv, &parsed_args)) { + exit(1); + } + + printf("Broadcom 63xx image tagger - v2.0.0\n"); + printf("Copyright (C) 2008 Axel Gembe\n"); + printf("Copyright (C) 2009-2010 Daniel Dickinson\n"); + printf("Licensed under the terms of the Gnu General Public License\n"); + + kernel = parsed_args.kernel_arg; + rootfs = parsed_args.rootfs_arg; + bin = parsed_args.output_arg; + if (strlen(parsed_args.tag_version_arg) >= TAGVER_LEN) { + fprintf(stderr, "Error: Tag Version (tag_version,v) too long.\n"); + exit(1); + } + if (strlen(parsed_args.boardid_arg) >= BOARDID_LEN) { + fprintf(stderr, "Error: Board ID (boardid,b) too long.\n"); + exit(1); + } + if (strlen(parsed_args.chipid_arg) >= CHIPID_LEN) { + fprintf(stderr, "Error: Chip ID (chipid,c) too long.\n"); + exit(1); + } + if (strlen(parsed_args.signature_arg) >= SIG1_LEN) { + fprintf(stderr, "Error: Magic string (signature,a) too long.\n"); + exit(1); + } + if (strlen(parsed_args.signature2_arg) >= SIG2_LEN) { + fprintf(stderr, "Error: Second magic string (signature2,m) too long.\n"); + exit(1); + } + if (parsed_args.layoutver_given) { + if (strlen(parsed_args.layoutver_arg) > FLASHLAYOUTVER_LEN) { + fprintf(stderr, "Error: Flash layout version (layoutver,y) too long.\n"); + exit(1); + } + } + if (parsed_args.rsa_signature_given) { + if (strlen(parsed_args.rsa_signature_arg) > RSASIG_LEN) { + fprintf(stderr, "Error: RSA Signature (rsa_signature,r) too long.\n"); + exit(1); + } + } + + if (parsed_args.info1_given) { + if (strlen(parsed_args.info1_arg) >= TAGINFO1_LEN) { + fprintf(stderr, "Error: Vendor Information 1 (info1) too long.\n"); + exit(1); + } + } + + if (parsed_args.info2_given) { + if (strlen(parsed_args.info2_arg) >= TAGINFO2_LEN) { + fprintf(stderr, "Error: Vendor Information 2 (info2) too long.\n"); + exit(1); + } + } + + if (parsed_args.altinfo_given) { + if (strlen(parsed_args.altinfo_arg) >= ALTTAGINFO_LEN) { + fprintf(stderr, "Error: Vendor Information 1 (info1) too long.\n"); + 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); + + if (!parsed_args.kernel_file_has_header_flag) { + load_address = strtoul(parsed_args.load_addr_arg, NULL, 16); + entry = strtoul(parsed_args.entry_arg, NULL, 16); + if (load_address == 0) { + fprintf(stderr, "Error: Invalid value for load address\n"); + } + if (entry == 0) { + fprintf(stderr, "Error: Invalid value for entry\n"); + } + } + + return tagfile(kernel, rootfs, bin, &parsed_args, flash_start, image_offset, block_size, load_address, entry); +} diff --git a/trunk/tools/firmware-utils/src/imagetag.ggo b/trunk/tools/firmware-utils/src/imagetag.ggo new file mode 100644 index 00000000..04f608e8 --- /dev/null +++ b/trunk/tools/firmware-utils/src/imagetag.ggo @@ -0,0 +1,46 @@ +# Command line option parsing generator file for imagetag +# Supplied-To: gengetopt +# +# Copyright 2010 Daniel Dickinson +# +# This file is subject to the terms and conditions of the GNU General Public +# License. See the file "COPYING" in the main directory of this archive +# for more details. +# + +package "imagetag" +version "2.0.0" +purpose "Generate image with CFE imagetag for Broadcom 63xx routers." +description "Copyright (C) 2008 Axel Gembe +Copyright (C) 2009-2010 Daniel Dickinson +Licensed unter the terms of the Gnu General Public License. + +Given a root filesystem, a linux kernel, and an optional CFE, generates an image with an imagetag for a Broadcom 63xx-based router. Additional parameters to be specified depend on the specfic brand and model of router." +args "--file-name=imagetag_cmdline" + +option "kernel" i "File with LZMA compressed kernel to include in the image." string typestr="filename" required +option "rootfs" f "File with RootFS to include in the image." string typestr="filename" required +option "output" o "Name of output file." string typestr="filename" required +option "cfe" - "File with CFE to include in the image." string typestr="filename" optional +option "boardid" b "Board ID to set in the image (must match what router expects, e.g. \"96345GW2\")." string required +option "chipid" c "Chip ID to set in the image (must match the actual hardware, e.g. \"6345\")." string required +option "flash-start" s "Flash start address." string typestr="address" optional default="0xBFC00000" +option "image-offset" n "Offset from start address for the first byte after the CFE (in memory)." string typestr="offset" default="0x10000" optional +option "tag-version" v "Version number for imagetag format." string default="6" optional +option "signature" a "Magic string (signature), for boards that need it." string default="Broadcom Corporatio" optional +option "signature2" m "Second magic string (signature2)." string default="ver. 2.0" optional +option "block-size" k "Flash erase block size." string optional default="0x10000" +option "load-addr" l "Kernel load address." string typestr="address" required +option "entry" e "Address where the kernel entry point will be for booting." string typestr="address" required +option "layoutver" y "Flash layout version (version 2.2x of the Broadcom code requires this)." string optional +option "info1" 1 "String for first vendor information section." string optional +option "altinfo" - "String for vendor information section (alternate/pirelli)." string optional +option "info2" 2 "String for second vendor information section." string optional +option "root-first" - "Put the rootfs before the kernel (only for stock images, e.g. captured from the router's flash memory)." flag off +option "rsa-signature" r "String for RSA Signature section." string optional +option "second-image-flag" - "Dual Image Flag (2=not-specified)." values="0", "1", "2" default="2" typestr="flag-value" optional +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/trunk/tools/firmware-utils/src/imagetag_cmdline.c b/trunk/tools/firmware-utils/src/imagetag_cmdline.c new file mode 100644 index 00000000..86c90bbb --- /dev/null +++ b/trunk/tools/firmware-utils/src/imagetag_cmdline.c @@ -0,0 +1,1193 @@ +/* + File autogenerated by gengetopt version 2.22.5 + generated with the following command: + 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: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + +#include + +#include "imagetag_cmdline.h" + +const char *gengetopt_args_info_purpose = "Generate image with CFE imagetag for Broadcom 63xx routers."; + +const char *gengetopt_args_info_usage = "Usage: imagetag [OPTIONS]..."; + +const char *gengetopt_args_info_description = "Copyright (C) 2008 Axel Gembe\nCopyright (C) 2009-2010 Daniel Dickinson\nLicensed unter the terms of the Gnu General Public License.\n\nGiven a root filesystem, a linux kernel, and an optional CFE, generates an \nimage with an imagetag for a Broadcom 63xx-based router. Additional parameters \nto be specified depend on the specfic brand and model of router."; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + " -i, --kernel=filename File with LZMA compressed kernel to include in \n the image.", + " -f, --rootfs=filename File with RootFS to include in the image.", + " -o, --output=filename Name of output file.", + " --cfe=filename File with CFE to include in the image.", + " -b, --boardid=STRING Board ID to set in the image (must match what \n router expects, e.g. \"96345GW2\").", + " -c, --chipid=STRING Chip ID to set in the image (must match the \n actual hardware, e.g. \"6345\").", + " -s, --flash-start=address Flash start address. (default=`0xBFC00000')", + " -n, --image-offset=offset Offset from start address for the first byte \n after the CFE (in memory). \n (default=`0x10000')", + " -v, --tag-version=STRING Version number for imagetag format. \n (default=`6')", + " -a, --signature=STRING Magic string (signature), for boards that need \n it. (default=`Broadcom Corporatio')", + " -m, --signature2=STRING Second magic string (signature2). \n (default=`ver. 2.0')", + " -k, --block-size=STRING Flash erase block size. (default=`0x10000')", + " -l, --load-addr=address Kernel load address.", + " -e, --entry=address Address where the kernel entry point will be \n for booting.", + " -y, --layoutver=STRING Flash layout version (version 2.2x of the \n Broadcom code requires this).", + " -1, --info1=STRING String for first vendor information section.", + " --altinfo=STRING String for vendor information section \n (alternate/pirelli).", + " -2, --info2=STRING String for second vendor information section.", + " --root-first Put the rootfs before the kernel (only for \n stock images, e.g. captured from the router's \n flash memory). (default=off)", + " -r, --rsa-signature=STRING String for RSA Signature section.", + " --second-image-flag=flag-value\n Dual Image Flag (2=not-specified). (possible \n values=\"0\", \"1\", \"2\" default=`2')", + " --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 + , ARG_INT +} imagetag_cmdline_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +imagetag_cmdline_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params, const char *additional_error); + +static int +imagetag_cmdline_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error); + +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); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->kernel_given = 0 ; + args_info->rootfs_given = 0 ; + args_info->output_given = 0 ; + args_info->cfe_given = 0 ; + args_info->boardid_given = 0 ; + args_info->chipid_given = 0 ; + args_info->flash_start_given = 0 ; + args_info->image_offset_given = 0 ; + args_info->tag_version_given = 0 ; + args_info->signature_given = 0 ; + args_info->signature2_given = 0 ; + args_info->block_size_given = 0 ; + args_info->load_addr_given = 0 ; + args_info->entry_given = 0 ; + args_info->layoutver_given = 0 ; + args_info->info1_given = 0 ; + args_info->altinfo_given = 0 ; + args_info->info2_given = 0 ; + args_info->root_first_given = 0 ; + args_info->rsa_signature_given = 0 ; + args_info->second_image_flag_given = 0 ; + 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 +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->kernel_arg = NULL; + args_info->kernel_orig = NULL; + args_info->rootfs_arg = NULL; + args_info->rootfs_orig = NULL; + args_info->output_arg = NULL; + args_info->output_orig = NULL; + args_info->cfe_arg = NULL; + args_info->cfe_orig = NULL; + args_info->boardid_arg = NULL; + args_info->boardid_orig = NULL; + args_info->chipid_arg = NULL; + args_info->chipid_orig = NULL; + args_info->flash_start_arg = gengetopt_strdup ("0xBFC00000"); + args_info->flash_start_orig = NULL; + args_info->image_offset_arg = gengetopt_strdup ("0x10000"); + args_info->image_offset_orig = NULL; + args_info->tag_version_arg = gengetopt_strdup ("6"); + args_info->tag_version_orig = NULL; + args_info->signature_arg = gengetopt_strdup ("Broadcom Corporatio"); + args_info->signature_orig = NULL; + args_info->signature2_arg = gengetopt_strdup ("ver. 2.0"); + args_info->signature2_orig = NULL; + args_info->block_size_arg = gengetopt_strdup ("0x10000"); + args_info->block_size_orig = NULL; + args_info->load_addr_arg = NULL; + args_info->load_addr_orig = NULL; + args_info->entry_arg = NULL; + args_info->entry_orig = NULL; + args_info->layoutver_arg = NULL; + args_info->layoutver_orig = NULL; + args_info->info1_arg = NULL; + args_info->info1_orig = NULL; + args_info->altinfo_arg = NULL; + args_info->altinfo_orig = NULL; + args_info->info2_arg = NULL; + args_info->info2_orig = NULL; + args_info->root_first_flag = 0; + args_info->rsa_signature_arg = NULL; + args_info->rsa_signature_orig = NULL; + args_info->second_image_flag_arg = gengetopt_strdup ("2"); + args_info->second_image_flag_orig = NULL; + args_info->inactive_arg = gengetopt_strdup ("2"); + args_info->inactive_orig = NULL; + 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; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->kernel_help = gengetopt_args_info_help[2] ; + args_info->rootfs_help = gengetopt_args_info_help[3] ; + args_info->output_help = gengetopt_args_info_help[4] ; + args_info->cfe_help = gengetopt_args_info_help[5] ; + args_info->boardid_help = gengetopt_args_info_help[6] ; + args_info->chipid_help = gengetopt_args_info_help[7] ; + args_info->flash_start_help = gengetopt_args_info_help[8] ; + args_info->image_offset_help = gengetopt_args_info_help[9] ; + args_info->tag_version_help = gengetopt_args_info_help[10] ; + args_info->signature_help = gengetopt_args_info_help[11] ; + args_info->signature2_help = gengetopt_args_info_help[12] ; + args_info->block_size_help = gengetopt_args_info_help[13] ; + args_info->load_addr_help = gengetopt_args_info_help[14] ; + args_info->entry_help = gengetopt_args_info_help[15] ; + args_info->layoutver_help = gengetopt_args_info_help[16] ; + args_info->info1_help = gengetopt_args_info_help[17] ; + args_info->altinfo_help = gengetopt_args_info_help[18] ; + args_info->info2_help = gengetopt_args_info_help[19] ; + args_info->root_first_help = gengetopt_args_info_help[20] ; + args_info->rsa_signature_help = gengetopt_args_info_help[21] ; + args_info->second_image_flag_help = gengetopt_args_info_help[22] ; + 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 +imagetag_cmdline_print_version (void) +{ + printf ("%s %s\n", + (strlen(IMAGETAG_CMDLINE_PACKAGE_NAME) ? IMAGETAG_CMDLINE_PACKAGE_NAME : IMAGETAG_CMDLINE_PACKAGE), + IMAGETAG_CMDLINE_VERSION); +} + +static void print_help_common(void) { + imagetag_cmdline_print_version (); + + if (strlen(gengetopt_args_info_purpose) > 0) + printf("\n%s\n", gengetopt_args_info_purpose); + + if (strlen(gengetopt_args_info_usage) > 0) + printf("\n%s\n", gengetopt_args_info_usage); + + printf("\n"); + + if (strlen(gengetopt_args_info_description) > 0) + printf("%s\n\n", gengetopt_args_info_description); +} + +void +imagetag_cmdline_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +imagetag_cmdline_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +imagetag_cmdline_params_init(struct imagetag_cmdline_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct imagetag_cmdline_params * +imagetag_cmdline_params_create(void) +{ + struct imagetag_cmdline_params *params = + (struct imagetag_cmdline_params *)malloc(sizeof(struct imagetag_cmdline_params)); + imagetag_cmdline_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +imagetag_cmdline_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->kernel_arg)); + free_string_field (&(args_info->kernel_orig)); + free_string_field (&(args_info->rootfs_arg)); + free_string_field (&(args_info->rootfs_orig)); + free_string_field (&(args_info->output_arg)); + free_string_field (&(args_info->output_orig)); + free_string_field (&(args_info->cfe_arg)); + free_string_field (&(args_info->cfe_orig)); + free_string_field (&(args_info->boardid_arg)); + free_string_field (&(args_info->boardid_orig)); + free_string_field (&(args_info->chipid_arg)); + free_string_field (&(args_info->chipid_orig)); + free_string_field (&(args_info->flash_start_arg)); + free_string_field (&(args_info->flash_start_orig)); + free_string_field (&(args_info->image_offset_arg)); + free_string_field (&(args_info->image_offset_orig)); + free_string_field (&(args_info->tag_version_arg)); + free_string_field (&(args_info->tag_version_orig)); + free_string_field (&(args_info->signature_arg)); + free_string_field (&(args_info->signature_orig)); + free_string_field (&(args_info->signature2_arg)); + free_string_field (&(args_info->signature2_orig)); + free_string_field (&(args_info->block_size_arg)); + free_string_field (&(args_info->block_size_orig)); + free_string_field (&(args_info->load_addr_arg)); + free_string_field (&(args_info->load_addr_orig)); + free_string_field (&(args_info->entry_arg)); + free_string_field (&(args_info->entry_orig)); + free_string_field (&(args_info->layoutver_arg)); + free_string_field (&(args_info->layoutver_orig)); + free_string_field (&(args_info->info1_arg)); + free_string_field (&(args_info->info1_orig)); + free_string_field (&(args_info->altinfo_arg)); + free_string_field (&(args_info->altinfo_orig)); + free_string_field (&(args_info->info2_arg)); + free_string_field (&(args_info->info2_orig)); + free_string_field (&(args_info->rsa_signature_arg)); + free_string_field (&(args_info->rsa_signature_orig)); + free_string_field (&(args_info->second_image_flag_arg)); + free_string_field (&(args_info->second_image_flag_orig)); + free_string_field (&(args_info->inactive_arg)); + 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)); + + + + clear_given (args_info); +} + +/** + * @param val the value to check + * @param values the possible values + * @return the index of the matched value: + * -1 if no value matched, + * -2 if more than one value has matched + */ +static int +check_possible_values(const char *val, const char *values[]) +{ + int i, found, last; + size_t len; + + if (!val) /* otherwise strlen() crashes below */ + return -1; /* -1 means no argument for the option */ + + found = last = 0; + + for (i = 0, len = strlen(val); values[i]; ++i) + { + if (strncmp(val, values[i], len) == 0) + { + ++found; + last = i; + if (strlen(values[i]) == len) + return i; /* exact macth no need to check more */ + } + } + + if (found == 1) /* one match: OK */ + return last; + + return (found ? -2 : -1); /* return many values or none matched */ +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + int found = -1; + if (arg) { + if (values) { + found = check_possible_values(arg, values); + } + if (found >= 0) + fprintf(outfile, "%s=\"%s\" # %s\n", opt, arg, values[found]); + else + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +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", IMAGETAG_CMDLINE_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->kernel_given) + write_into_file(outfile, "kernel", args_info->kernel_orig, 0); + if (args_info->rootfs_given) + write_into_file(outfile, "rootfs", args_info->rootfs_orig, 0); + if (args_info->output_given) + write_into_file(outfile, "output", args_info->output_orig, 0); + if (args_info->cfe_given) + write_into_file(outfile, "cfe", args_info->cfe_orig, 0); + if (args_info->boardid_given) + write_into_file(outfile, "boardid", args_info->boardid_orig, 0); + if (args_info->chipid_given) + write_into_file(outfile, "chipid", args_info->chipid_orig, 0); + if (args_info->flash_start_given) + write_into_file(outfile, "flash-start", args_info->flash_start_orig, 0); + if (args_info->image_offset_given) + write_into_file(outfile, "image-offset", args_info->image_offset_orig, 0); + if (args_info->tag_version_given) + write_into_file(outfile, "tag-version", args_info->tag_version_orig, 0); + if (args_info->signature_given) + write_into_file(outfile, "signature", args_info->signature_orig, 0); + if (args_info->signature2_given) + write_into_file(outfile, "signature2", args_info->signature2_orig, 0); + if (args_info->block_size_given) + write_into_file(outfile, "block-size", args_info->block_size_orig, 0); + if (args_info->load_addr_given) + write_into_file(outfile, "load-addr", args_info->load_addr_orig, 0); + if (args_info->entry_given) + write_into_file(outfile, "entry", args_info->entry_orig, 0); + if (args_info->layoutver_given) + write_into_file(outfile, "layoutver", args_info->layoutver_orig, 0); + if (args_info->info1_given) + write_into_file(outfile, "info1", args_info->info1_orig, 0); + if (args_info->altinfo_given) + write_into_file(outfile, "altinfo", args_info->altinfo_orig, 0); + if (args_info->info2_given) + write_into_file(outfile, "info2", args_info->info2_orig, 0); + if (args_info->root_first_given) + write_into_file(outfile, "root-first", 0, 0 ); + 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, imagetag_cmdline_second_image_flag_values); + if (args_info->inactive_given) + 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; + return i; +} + +int +imagetag_cmdline_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", IMAGETAG_CMDLINE_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = imagetag_cmdline_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +imagetag_cmdline_free (struct gengetopt_args_info *args_info) +{ + imagetag_cmdline_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +int +imagetag_cmdline (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return imagetag_cmdline2 (argc, argv, args_info, 0, 1, 1); +} + +int +imagetag_cmdline_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params) +{ + int result; + result = imagetag_cmdline_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + imagetag_cmdline_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +imagetag_cmdline2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct imagetag_cmdline_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = imagetag_cmdline_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + imagetag_cmdline_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +imagetag_cmdline_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + int result = EXIT_SUCCESS; + + if (imagetag_cmdline_required2(args_info, prog_name, 0) > 0) + result = EXIT_FAILURE; + + if (result == EXIT_FAILURE) + { + imagetag_cmdline_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +imagetag_cmdline_required2 (struct gengetopt_args_info *args_info, const char *prog_name, const char *additional_error) +{ + int error = 0; + FIX_UNUSED (additional_error); + + /* checks for required options */ + if (! args_info->kernel_given) + { + fprintf (stderr, "%s: '--kernel' ('-i') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->rootfs_given) + { + fprintf (stderr, "%s: '--rootfs' ('-f') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->output_given) + { + fprintf (stderr, "%s: '--output' ('-o') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->boardid_given) + { + fprintf (stderr, "%s: '--boardid' ('-b') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->chipid_given) + { + fprintf (stderr, "%s: '--chipid' ('-c') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->load_addr_given) + { + fprintf (stderr, "%s: '--load-addr' ('-l') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + if (! args_info->entry_given) + { + fprintf (stderr, "%s: '--entry' ('-e') option required%s\n", prog_name, (additional_error ? additional_error : "")); + error = 1; + } + + + /* checks for dependences among options */ + + return error; +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @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 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 + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +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, + imagetag_cmdline_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (possible_values && (found = check_possible_values((value ? value : default_value), possible_values)) < 0) + { + if (short_opt != '-') + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s' (`-%c')%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: %s argument, \"%s\", for option `--%s'%s\n", + package_name, (found == -2) ? "ambiguous" : "invalid", value, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + 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; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + 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) { + case ARG_NO: + case ARG_FLAG: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +int +imagetag_cmdline_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + package_name = argv[0]; + + override = params->override; + initialize = params->initialize; + check_required = params->check_required; + check_ambiguity = params->check_ambiguity; + + if (initialize) + imagetag_cmdline_init (args_info); + + imagetag_cmdline_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "kernel", 1, NULL, 'i' }, + { "rootfs", 1, NULL, 'f' }, + { "output", 1, NULL, 'o' }, + { "cfe", 1, NULL, 0 }, + { "boardid", 1, NULL, 'b' }, + { "chipid", 1, NULL, 'c' }, + { "flash-start", 1, NULL, 's' }, + { "image-offset", 1, NULL, 'n' }, + { "tag-version", 1, NULL, 'v' }, + { "signature", 1, NULL, 'a' }, + { "signature2", 1, NULL, 'm' }, + { "block-size", 1, NULL, 'k' }, + { "load-addr", 1, NULL, 'l' }, + { "entry", 1, NULL, 'e' }, + { "layoutver", 1, NULL, 'y' }, + { "info1", 1, NULL, '1' }, + { "altinfo", 1, NULL, 0 }, + { "info2", 1, NULL, '2' }, + { "root-first", 0, NULL, 0 }, + { "rsa-signature", 1, NULL, 'r' }, + { "second-image-flag", 1, NULL, 0 }, + { "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:p:", long_options, &option_index); + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + imagetag_cmdline_print_help (); + imagetag_cmdline_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + 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.. */ + + + if (update_arg( (void *)&(args_info->kernel_arg), + &(args_info->kernel_orig), &(args_info->kernel_given), + &(local_args_info.kernel_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "kernel", 'i', + additional_error)) + goto failure; + + break; + case 'f': /* File with RootFS to include in the image.. */ + + + if (update_arg( (void *)&(args_info->rootfs_arg), + &(args_info->rootfs_orig), &(args_info->rootfs_given), + &(local_args_info.rootfs_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "rootfs", 'f', + additional_error)) + goto failure; + + break; + case 'o': /* Name of output file.. */ + + + if (update_arg( (void *)&(args_info->output_arg), + &(args_info->output_orig), &(args_info->output_given), + &(local_args_info.output_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "output", 'o', + additional_error)) + goto failure; + + break; + case 'b': /* Board ID to set in the image (must match what router expects, e.g. \"96345GW2\").. */ + + + if (update_arg( (void *)&(args_info->boardid_arg), + &(args_info->boardid_orig), &(args_info->boardid_given), + &(local_args_info.boardid_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "boardid", 'b', + additional_error)) + goto failure; + + break; + case 'c': /* Chip ID to set in the image (must match the actual hardware, e.g. \"6345\").. */ + + + if (update_arg( (void *)&(args_info->chipid_arg), + &(args_info->chipid_orig), &(args_info->chipid_given), + &(local_args_info.chipid_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "chipid", 'c', + additional_error)) + goto failure; + + break; + case 's': /* Flash start address.. */ + + + if (update_arg( (void *)&(args_info->flash_start_arg), + &(args_info->flash_start_orig), &(args_info->flash_start_given), + &(local_args_info.flash_start_given), optarg, 0, "0xBFC00000", ARG_STRING, + check_ambiguity, override, 0, 0, + "flash-start", 's', + additional_error)) + goto failure; + + break; + case 'n': /* Offset from start address for the first byte after the CFE (in memory).. */ + + + if (update_arg( (void *)&(args_info->image_offset_arg), + &(args_info->image_offset_orig), &(args_info->image_offset_given), + &(local_args_info.image_offset_given), optarg, 0, "0x10000", ARG_STRING, + check_ambiguity, override, 0, 0, + "image-offset", 'n', + additional_error)) + goto failure; + + break; + case 'v': /* Version number for imagetag format.. */ + + + if (update_arg( (void *)&(args_info->tag_version_arg), + &(args_info->tag_version_orig), &(args_info->tag_version_given), + &(local_args_info.tag_version_given), optarg, 0, "6", ARG_STRING, + check_ambiguity, override, 0, 0, + "tag-version", 'v', + additional_error)) + goto failure; + + break; + case 'a': /* Magic string (signature), for boards that need it.. */ + + + if (update_arg( (void *)&(args_info->signature_arg), + &(args_info->signature_orig), &(args_info->signature_given), + &(local_args_info.signature_given), optarg, 0, "Broadcom Corporatio", ARG_STRING, + check_ambiguity, override, 0, 0, + "signature", 'a', + additional_error)) + goto failure; + + break; + case 'm': /* Second magic string (signature2).. */ + + + if (update_arg( (void *)&(args_info->signature2_arg), + &(args_info->signature2_orig), &(args_info->signature2_given), + &(local_args_info.signature2_given), optarg, 0, "ver. 2.0", ARG_STRING, + check_ambiguity, override, 0, 0, + "signature2", 'm', + additional_error)) + goto failure; + + break; + case 'k': /* Flash erase block size.. */ + + + if (update_arg( (void *)&(args_info->block_size_arg), + &(args_info->block_size_orig), &(args_info->block_size_given), + &(local_args_info.block_size_given), optarg, 0, "0x10000", ARG_STRING, + check_ambiguity, override, 0, 0, + "block-size", 'k', + additional_error)) + goto failure; + + break; + case 'l': /* Kernel load address.. */ + + + if (update_arg( (void *)&(args_info->load_addr_arg), + &(args_info->load_addr_orig), &(args_info->load_addr_given), + &(local_args_info.load_addr_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "load-addr", 'l', + additional_error)) + goto failure; + + break; + case 'e': /* Address where the kernel entry point will be for booting.. */ + + + if (update_arg( (void *)&(args_info->entry_arg), + &(args_info->entry_orig), &(args_info->entry_given), + &(local_args_info.entry_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "entry", 'e', + additional_error)) + goto failure; + + break; + case 'y': /* Flash layout version (version 2.2x of the Broadcom code requires this).. */ + + + if (update_arg( (void *)&(args_info->layoutver_arg), + &(args_info->layoutver_orig), &(args_info->layoutver_given), + &(local_args_info.layoutver_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "layoutver", 'y', + additional_error)) + goto failure; + + break; + case '1': /* String for first vendor information section.. */ + + + if (update_arg( (void *)&(args_info->info1_arg), + &(args_info->info1_orig), &(args_info->info1_given), + &(local_args_info.info1_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "info1", '1', + additional_error)) + goto failure; + + break; + case '2': /* String for second vendor information section.. */ + + + if (update_arg( (void *)&(args_info->info2_arg), + &(args_info->info2_orig), &(args_info->info2_given), + &(local_args_info.info2_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "info2", '2', + additional_error)) + goto failure; + + break; + case 'r': /* String for RSA Signature section.. */ + + + if (update_arg( (void *)&(args_info->rsa_signature_arg), + &(args_info->rsa_signature_orig), &(args_info->rsa_signature_given), + &(local_args_info.rsa_signature_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "rsa-signature", 'r', + additional_error)) + 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.. */ + if (strcmp (long_options[option_index].name, "cfe") == 0) + { + + + if (update_arg( (void *)&(args_info->cfe_arg), + &(args_info->cfe_orig), &(args_info->cfe_given), + &(local_args_info.cfe_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "cfe", '-', + additional_error)) + goto failure; + + } + /* String for vendor information section (alternate/pirelli).. */ + else if (strcmp (long_options[option_index].name, "altinfo") == 0) + { + + + if (update_arg( (void *)&(args_info->altinfo_arg), + &(args_info->altinfo_orig), &(args_info->altinfo_given), + &(local_args_info.altinfo_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "altinfo", '-', + additional_error)) + goto failure; + + } + /* Put the rootfs before the kernel (only for stock images, e.g. captured from the router's flash memory).. */ + else if (strcmp (long_options[option_index].name, "root-first") == 0) + { + + + if (update_arg((void *)&(args_info->root_first_flag), 0, &(args_info->root_first_given), + &(local_args_info.root_first_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "root-first", '-', + additional_error)) + goto failure; + + } + /* Dual Image Flag (2=not-specified).. */ + else if (strcmp (long_options[option_index].name, "second-image-flag") == 0) + { + + + 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, imagetag_cmdline_second_image_flag_values, "2", ARG_STRING, + check_ambiguity, override, 0, 0, + "second-image-flag", '-', + additional_error)) + goto failure; + + } + /* Inactive Flag (2=not-specified).. */ + else if (strcmp (long_options[option_index].name, "inactive") == 0) + { + + + if (update_arg( (void *)&(args_info->inactive_arg), + &(args_info->inactive_orig), &(args_info->inactive_given), + &(local_args_info.inactive_given), optarg, imagetag_cmdline_inactive_values, "2", ARG_STRING, + check_ambiguity, override, 0, 0, + "inactive", '-', + additional_error)) + goto failure; + + } + /* String for second reserved section.. */ + else if (strcmp (long_options[option_index].name, "reserved2") == 0) + { + + + if (update_arg( (void *)&(args_info->reserved2_arg), + &(args_info->reserved2_orig), &(args_info->reserved2_given), + &(local_args_info.reserved2_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "reserved2", '-', + additional_error)) + goto failure; + + } + /* Indicates that the kernel file includes the kernel header with correct load address and entry point, so no changes are needed. */ + else if (strcmp (long_options[option_index].name, "kernel-file-has-header") == 0) + { + + + if (update_arg((void *)&(args_info->kernel_file_has_header_flag), 0, &(args_info->kernel_file_has_header_given), + &(local_args_info.kernel_file_has_header_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "kernel-file-has-header", '-', + additional_error)) + 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. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", IMAGETAG_CMDLINE_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + if (check_required) + { + error += imagetag_cmdline_required2 (args_info, argv[0], additional_error); + } + + imagetag_cmdline_release (&local_args_info); + + if ( error ) + return (EXIT_FAILURE); + + return 0; + +failure: + + imagetag_cmdline_release (&local_args_info); + return (EXIT_FAILURE); +} diff --git a/trunk/tools/firmware-utils/src/imagetag_cmdline.h b/trunk/tools/firmware-utils/src/imagetag_cmdline.h new file mode 100644 index 00000000..3f55c509 --- /dev/null +++ b/trunk/tools/firmware-utils/src/imagetag_cmdline.h @@ -0,0 +1,275 @@ +/** @file imagetag_cmdline.h + * @brief The header file for the command line option parser + * 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 */ + +#ifndef IMAGETAG_CMDLINE_H +#define IMAGETAG_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef IMAGETAG_CMDLINE_PACKAGE +/** @brief the program name (used for printing errors) */ +#define IMAGETAG_CMDLINE_PACKAGE "imagetag" +#endif + +#ifndef IMAGETAG_CMDLINE_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define IMAGETAG_CMDLINE_PACKAGE_NAME "imagetag" +#endif + +#ifndef IMAGETAG_CMDLINE_VERSION +/** @brief the program version */ +#define IMAGETAG_CMDLINE_VERSION "2.0.0" +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * kernel_arg; /**< @brief File with LZMA compressed kernel to include in the image.. */ + char * kernel_orig; /**< @brief File with LZMA compressed kernel to include in the image. original value given at command line. */ + const char *kernel_help; /**< @brief File with LZMA compressed kernel to include in the image. help description. */ + char * rootfs_arg; /**< @brief File with RootFS to include in the image.. */ + char * rootfs_orig; /**< @brief File with RootFS to include in the image. original value given at command line. */ + const char *rootfs_help; /**< @brief File with RootFS to include in the image. help description. */ + char * output_arg; /**< @brief Name of output file.. */ + char * output_orig; /**< @brief Name of output file. original value given at command line. */ + const char *output_help; /**< @brief Name of output file. help description. */ + char * cfe_arg; /**< @brief File with CFE to include in the image.. */ + char * cfe_orig; /**< @brief File with CFE to include in the image. original value given at command line. */ + const char *cfe_help; /**< @brief File with CFE to include in the image. help description. */ + char * boardid_arg; /**< @brief Board ID to set in the image (must match what router expects, e.g. \"96345GW2\").. */ + char * boardid_orig; /**< @brief Board ID to set in the image (must match what router expects, e.g. \"96345GW2\"). original value given at command line. */ + const char *boardid_help; /**< @brief Board ID to set in the image (must match what router expects, e.g. \"96345GW2\"). help description. */ + char * chipid_arg; /**< @brief Chip ID to set in the image (must match the actual hardware, e.g. \"6345\").. */ + char * chipid_orig; /**< @brief Chip ID to set in the image (must match the actual hardware, e.g. \"6345\"). original value given at command line. */ + const char *chipid_help; /**< @brief Chip ID to set in the image (must match the actual hardware, e.g. \"6345\"). help description. */ + char * flash_start_arg; /**< @brief Flash start address. (default='0xBFC00000'). */ + char * flash_start_orig; /**< @brief Flash start address. original value given at command line. */ + const char *flash_start_help; /**< @brief Flash start address. help description. */ + char * image_offset_arg; /**< @brief Offset from start address for the first byte after the CFE (in memory). (default='0x10000'). */ + char * image_offset_orig; /**< @brief Offset from start address for the first byte after the CFE (in memory). original value given at command line. */ + const char *image_offset_help; /**< @brief Offset from start address for the first byte after the CFE (in memory). help description. */ + char * tag_version_arg; /**< @brief Version number for imagetag format. (default='6'). */ + char * tag_version_orig; /**< @brief Version number for imagetag format. original value given at command line. */ + const char *tag_version_help; /**< @brief Version number for imagetag format. help description. */ + char * signature_arg; /**< @brief Magic string (signature), for boards that need it. (default='Broadcom Corporatio'). */ + char * signature_orig; /**< @brief Magic string (signature), for boards that need it. original value given at command line. */ + const char *signature_help; /**< @brief Magic string (signature), for boards that need it. help description. */ + char * signature2_arg; /**< @brief Second magic string (signature2). (default='ver. 2.0'). */ + char * signature2_orig; /**< @brief Second magic string (signature2). original value given at command line. */ + const char *signature2_help; /**< @brief Second magic string (signature2). help description. */ + char * block_size_arg; /**< @brief Flash erase block size. (default='0x10000'). */ + char * block_size_orig; /**< @brief Flash erase block size. original value given at command line. */ + const char *block_size_help; /**< @brief Flash erase block size. help description. */ + char * load_addr_arg; /**< @brief Kernel load address.. */ + char * load_addr_orig; /**< @brief Kernel load address. original value given at command line. */ + const char *load_addr_help; /**< @brief Kernel load address. help description. */ + char * entry_arg; /**< @brief Address where the kernel entry point will be for booting.. */ + char * entry_orig; /**< @brief Address where the kernel entry point will be for booting. original value given at command line. */ + const char *entry_help; /**< @brief Address where the kernel entry point will be for booting. help description. */ + char * layoutver_arg; /**< @brief Flash layout version (version 2.2x of the Broadcom code requires this).. */ + char * layoutver_orig; /**< @brief Flash layout version (version 2.2x of the Broadcom code requires this). original value given at command line. */ + const char *layoutver_help; /**< @brief Flash layout version (version 2.2x of the Broadcom code requires this). help description. */ + char * info1_arg; /**< @brief String for first vendor information section.. */ + char * info1_orig; /**< @brief String for first vendor information section. original value given at command line. */ + const char *info1_help; /**< @brief String for first vendor information section. help description. */ + char * altinfo_arg; /**< @brief String for vendor information section (alternate/pirelli).. */ + char * altinfo_orig; /**< @brief String for vendor information section (alternate/pirelli). original value given at command line. */ + const char *altinfo_help; /**< @brief String for vendor information section (alternate/pirelli). help description. */ + char * info2_arg; /**< @brief String for second vendor information section.. */ + char * info2_orig; /**< @brief String for second vendor information section. original value given at command line. */ + const char *info2_help; /**< @brief String for second vendor information section. help description. */ + int root_first_flag; /**< @brief Put the rootfs before the kernel (only for stock images, e.g. captured from the router's flash memory). (default=off). */ + const char *root_first_help; /**< @brief Put the rootfs before the kernel (only for stock images, e.g. captured from the router's flash memory). help description. */ + char * rsa_signature_arg; /**< @brief String for RSA Signature section.. */ + char * rsa_signature_orig; /**< @brief String for RSA Signature section. original value given at command line. */ + const char *rsa_signature_help; /**< @brief String for RSA Signature section. help description. */ + char * second_image_flag_arg; /**< @brief Dual Image Flag (2=not-specified). (default='2'). */ + char * second_image_flag_orig; /**< @brief Dual Image Flag (2=not-specified). original value given at command line. */ + const char *second_image_flag_help; /**< @brief Dual Image Flag (2=not-specified). help description. */ + char * inactive_arg; /**< @brief Inactive Flag (2=not-specified). (default='2'). */ + char * inactive_orig; /**< @brief Inactive Flag (2=not-specified). original value given at command line. */ + const char *inactive_help; /**< @brief Inactive Flag (2=not-specified). help description. */ + char * reserved2_arg; /**< @brief String for second reserved section.. */ + char * reserved2_orig; /**< @brief String for second reserved section. original value given at command line. */ + 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. */ + unsigned int kernel_given ; /**< @brief Whether kernel was given. */ + unsigned int rootfs_given ; /**< @brief Whether rootfs was given. */ + unsigned int output_given ; /**< @brief Whether output was given. */ + unsigned int cfe_given ; /**< @brief Whether cfe was given. */ + unsigned int boardid_given ; /**< @brief Whether boardid was given. */ + unsigned int chipid_given ; /**< @brief Whether chipid was given. */ + unsigned int flash_start_given ; /**< @brief Whether flash-start was given. */ + unsigned int image_offset_given ; /**< @brief Whether image-offset was given. */ + unsigned int tag_version_given ; /**< @brief Whether tag-version was given. */ + unsigned int signature_given ; /**< @brief Whether signature was given. */ + unsigned int signature2_given ; /**< @brief Whether signature2 was given. */ + unsigned int block_size_given ; /**< @brief Whether block-size was given. */ + unsigned int load_addr_given ; /**< @brief Whether load-addr was given. */ + unsigned int entry_given ; /**< @brief Whether entry was given. */ + unsigned int layoutver_given ; /**< @brief Whether layoutver was given. */ + unsigned int info1_given ; /**< @brief Whether info1 was given. */ + unsigned int altinfo_given ; /**< @brief Whether altinfo was given. */ + unsigned int info2_given ; /**< @brief Whether info2 was given. */ + unsigned int root_first_given ; /**< @brief Whether root-first was given. */ + unsigned int rsa_signature_given ; /**< @brief Whether rsa-signature was given. */ + unsigned int second_image_flag_given ; /**< @brief Whether second-image-flag was given. */ + 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 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) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @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 imagetag_cmdline (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @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 imagetag_cmdline_ext() instead + */ +int imagetag_cmdline2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int imagetag_cmdline_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct imagetag_cmdline_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int imagetag_cmdline_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int imagetag_cmdline_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void imagetag_cmdline_print_help(void); +/** + * Print the version + */ +void imagetag_cmdline_print_version(void); + +/** + * Initializes all the fields a imagetag_cmdline_params structure + * to their default values + * @param params the structure to initialize + */ +void imagetag_cmdline_params_init(struct imagetag_cmdline_params *params); + +/** + * Allocates dynamically a imagetag_cmdline_params structure and initializes + * all its fields to their default values + * @return the created and initialized imagetag_cmdline_params structure + */ +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 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 imagetag_cmdline_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int imagetag_cmdline_required (struct gengetopt_args_info *args_info, + const char *prog_name); + +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 +} +#endif /* __cplusplus */ +#endif /* IMAGETAG_CMDLINE_H */ diff --git a/trunk/tools/firmware-utils/src/lzma2eva.c b/trunk/tools/firmware-utils/src/lzma2eva.c new file mode 100644 index 00000000..0bc13fa4 --- /dev/null +++ b/trunk/tools/firmware-utils/src/lzma2eva.c @@ -0,0 +1,190 @@ +/* + lzma2eva - convert lzma-compressed file to AVM EVA bootloader format + Copyright (C) 2007 Enrik Berkhan + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include +#include /* crc32 */ + +#define checksum_add32(csum, data) \ + csum += ((uint8_t *)&data)[0]; \ + csum += ((uint8_t *)&data)[1]; \ + csum += ((uint8_t *)&data)[2]; \ + csum += ((uint8_t *)&data)[3]; + +void +usage(void) +{ + fprintf(stderr, "usage: lzma2eva \n"); + exit(1); +} + +void +pexit(const char *msg) +{ + perror(msg); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + + const char *infile, *outfile; + FILE *in, *out; + static const uint8_t buf[4096]; + size_t elems; + + uint8_t properties; + uint32_t dictsize; + uint64_t datasize; + + uint32_t magic = 0xfeed1281L; + uint32_t reclength = 0; + fpos_t reclengthpos; + uint32_t loadaddress = 0; + uint32_t type = 0x075a0201L; /* might be 7Z 2.1? */ + uint32_t checksum = 0; + + uint32_t compsize = 0; + fpos_t compsizepos; + uint32_t datasize32 = 0; + uint32_t datacrc32 = crc32(0, 0, 0); + + uint32_t zero = 0; + uint32_t entry = 0; + + if (argc != 5) + usage(); + + /* "parse" command line */ + loadaddress = strtoul(argv[1], 0, 0); + entry = strtoul(argv[2], 0, 0); + infile = argv[3]; + outfile = argv[4]; + + in = fopen(infile, "rb"); + if (!in) + pexit("fopen"); + out = fopen(outfile, "w+b"); + if (!out) + pexit("fopen"); + + /* read LZMA header */ + if (1 != fread(&properties, sizeof properties, 1, in)) + pexit("fread"); + if (1 != fread(&dictsize, sizeof dictsize, 1, in)) + pexit("fread"); + if (1 != fread(&datasize, sizeof datasize, 1, in)) + pexit("fread"); + + /* write EVA header */ + if (1 != fwrite(&magic, sizeof magic, 1, out)) + pexit("fwrite"); + if (fgetpos(out, &reclengthpos)) + pexit("fgetpos"); + if (1 != fwrite(&reclength, sizeof reclength, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&loadaddress, sizeof loadaddress, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&type, sizeof type, 1, out)) + pexit("fwrite"); + + /* write EVA LZMA header */ + if (fgetpos(out, &compsizepos)) + pexit("fgetpos"); + if (1 != fwrite(&compsize, sizeof compsize, 1, out)) + pexit("fwrite"); + /* XXX check length */ + datasize32 = (uint32_t)datasize; + if (1 != fwrite(&datasize32, sizeof datasize32, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&datacrc32, sizeof datacrc32, 1, out)) + pexit("fwrite"); + + /* write modified LZMA header */ + if (1 != fwrite(&properties, sizeof properties, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&dictsize, sizeof dictsize, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&zero, 3, 1, out)) + pexit("fwrite"); + + /* copy compressed data, calculate crc32 */ + while (0 < (elems = fread(&buf, sizeof buf[0], sizeof buf, in))) { + compsize += elems; + if (elems != fwrite(&buf, sizeof buf[0], elems, out)) + pexit("fwrite"); + datacrc32 = crc32(datacrc32, buf, elems); + } + if (ferror(in)) + pexit("fread"); + fclose(in); + + /* re-write record length */ + reclength = compsize + 24; + if (fsetpos(out, &reclengthpos)) + pexit("fsetpos"); + if (1 != fwrite(&reclength, sizeof reclength, 1, out)) + pexit("fwrite"); + + /* re-write EVA LZMA header including size and data crc */ + if (fsetpos(out, &compsizepos)) + pexit("fsetpos"); + if (1 != fwrite(&compsize, sizeof compsize, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&datasize32, sizeof datasize32, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&datacrc32, sizeof datacrc32, 1, out)) + pexit("fwrite"); + + /* calculate record checksum */ + checksum += reclength; + checksum += loadaddress; + checksum_add32(checksum, type); + checksum_add32(checksum, compsize); + checksum_add32(checksum, datasize32); + checksum_add32(checksum, datacrc32); + if (fseek(out, 0, SEEK_CUR)) + pexit("fseek"); + while (0 < (elems = fread(&buf, sizeof buf[0], sizeof buf, out))) { + size_t i; + for (i = 0; i < elems; ++i) + checksum += buf[i]; + } + if (ferror(out)) + pexit("fread"); + if (fseek(out, 0, SEEK_CUR)) + pexit("fseek"); + + checksum = ~checksum + 1; + if (1 != fwrite(&checksum, sizeof checksum, 1, out)) + pexit("fwrite"); + + /* write entry record */ + if (1 != fwrite(&zero, sizeof zero, 1, out)) + pexit("fwrite"); + if (1 != fwrite(&entry, sizeof entry, 1, out)) + pexit("fwrite"); + + if (fclose(out)) + pexit("fclose"); + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/makeamitbin.c b/trunk/tools/firmware-utils/src/makeamitbin.c new file mode 100644 index 00000000..5c334424 --- /dev/null +++ b/trunk/tools/firmware-utils/src/makeamitbin.c @@ -0,0 +1,316 @@ +/* + * makeamitbin - create firmware binaries for MGB100 + * + * Copyright (C) 2007 Volker Weiss + * Christian Welzel + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + + +#include +#include +#include + + +/* defaults: Level One WAP-0007 */ +static char *ascii1 = "DDC_RUS001"; +static char *ascii2 = "Queen"; + +static struct hdrinfo { + char *name; + unsigned long unknown; /* can probably be any number, maybe version number */ + int topalign; + unsigned int addr; + unsigned int size; +} hdrinfo[] = { + { "bios", 0xc76be111, 1, 0x3fa000, 0x006000 }, /* BIOS */ + { "recovery", 0xc76be222, 0, 0x3f0000, 0x004000 }, /* Recovery Loader */ + { "linux", 0xc76bee9d, 0, 0x000000, 0x100000 }, /* Linux */ + { "ramdisk", 0xc76bee9d, 0, 0x100000, 0x280000 }, /* ramdisk */ + { "amitconfig", 0xc76bee8b, 0, 0x380000, 0x060000 }, /* AMIT config */ + { "redboot", 0x00000000, 1, 0x3d0000, 0x030000 }, /* Redboot 128kB image */ + { "redbootlow", 0, 0, 0x3e0000, 0x18000 }, /* Redboot 1. part */ + { "redboothigh", 0, 0, 0x3fa000, 0x6000 }, /* Redboot 2. part */ + { "linux3g", 0xcb5f06b5, 0, 0x000000, 0x100000 }, /* Linux */ + { "ramdisk3g", 0xcb5f06b5, 0, 0x100000, 0x280000 }, /* ramdisk */ + { NULL } +}; + +/* +CHD2WLANU_R400b7 + +11e1 6bc7 +22e2 6bc7 +5dc3 47c8 +5cc3 47c8 +21c3 47c8 +*/ + +/* +20060106_DDC_WAP-0007_R400b4 + +11e1 6bc7 +22e2 6bc7 +9dee 6bc7 +9dee 6bc7 +8bee 6bc7 +*/ + +/* +WMU-6000FS_R400b6 + +11e1 6bc7 +22e2 6bc7 +6d2d 0fc8 +6c2d 0fc8 +542d 0fc8 +*/ + +/* +WAP-0007(R4.00b8)_2006-10-02 + +9979 5fc8 +22e2 6bc7 +c46e cec8 +c36e cec8 +a76e cec8 +*/ + + + +#define HDRSIZE 80 + +#define COPY_SHORT(d, o, v) d[o+0] = (unsigned char)((v) & 0xff); \ + d[o+1] = (unsigned char)(((v) >> 8) & 0xff) +#define COPY_LONG(d, o, v) d[o+0] = (unsigned char)((v) & 0xff); \ + d[o+1] = (unsigned char)(((v) >> 8) & 0xff); \ + d[o+2] = (unsigned char)(((v) >> 16) & 0xff); \ + d[o+3] = (unsigned char)(((v) >> 24) & 0xff) +#define READ_SHORT(d, o) ((unsigned short)(d[o+0]) + \ + (((unsigned short)(d[o+1])) << 8)) + +/* +00..0d ASCII product ID +0e..0f checksum of payload +10..1b ASCII Queen +1c..1f AMIT BIOS: 11e1 6bc7, Recovery Tool: 22e2 6bc7 + Linux: 5dc3 47c8, ramdisk: 5cc3 47c8 + AMIT FS: 21c3 47c8 VERSION NUMBER?????? +20..23 offset in flash aligned to segment boundary +24..27 length in flash aligned to segment boundary +28..2b offset in flash (payload) +2c..2f length (payload) +30..3f always 0 +40..47 always 4248 0101 5000 0001 (last maybe .....0501) +48..4b same as 20..23 +4c..4d always 0b00 +4e..4f inverted checksum of header +*/ + +unsigned short checksum(unsigned char *data, long size) +{ + long n; + unsigned short d, cs = 0; + for (n = 0; n < size; n += 2) + { + d = READ_SHORT(data, n); + cs += d; + if (cs < d) + cs++; + } + if (size & 1) + { + d = data[n]; + cs += d; + if (cs < d) + cs++; + } + return cs; +} + +void showhdr(unsigned char *hdr) +{ + int i, j; + for (j = 0; j < 5; j++) + { + for (i = 0; i < 16; i++) + { + printf("%02x ", (unsigned int)(hdr[j * 16 + i])); + } + printf(" "); + for (i = 0; i < 16; i++) + { + unsigned char d = hdr[j * 16 + i]; + printf("%c", (d >= ' ' && d < 127) ? d : '.'); + } + printf("\n"); + } +} + +void makehdr(unsigned char *hdr, struct hdrinfo *info, + unsigned char *data, long size, int last) +{ + unsigned int offset = info->addr + 0x10; + memset(hdr, 0, HDRSIZE); + if (info->topalign) + offset = info->addr + info->size - size; /* top align */ + strncpy((char *)hdr + 0x00, ascii1, 14); + strncpy((char *)hdr + 0x10, ascii2, 12); + COPY_LONG(hdr, 0x1c, info->unknown); + COPY_LONG(hdr, 0x20, info->addr); + COPY_LONG(hdr, 0x24, info->size); + COPY_LONG(hdr, 0x28, offset); + COPY_LONG(hdr, 0x2c, size); + COPY_LONG(hdr, 0x40, 0x01014842); + COPY_LONG(hdr, 0x44, last ? 0x01050050 : 0x01000050); + COPY_LONG(hdr, 0x48, info->addr); + COPY_SHORT(hdr, 0x4c, info->unknown == 0xcb5f06b5 ? 0x0016 : 0x000b); + COPY_SHORT(hdr, 0x0e, checksum(data, size)); + COPY_SHORT(hdr, 0x4e, ~checksum(hdr, HDRSIZE)); +} + +unsigned char *read_file(const char *name, long *size) +{ + FILE *f; + unsigned char *data = NULL; + *size = 0; + f = fopen(name, "r"); + if (f != NULL) + { + if (fseek(f, 0, SEEK_END) == 0) + { + *size = ftell(f); + if (*size != -1) + { + if (fseek(f, 0, SEEK_SET) == 0) + { + data = (unsigned char *)malloc(*size); + if (data != NULL) + { + if (fread(data, sizeof(char), *size, f) != *size) + { + free(data); + data = NULL; + } + } + } + } + } + fclose(f); + } + return data; +} + +struct hdrinfo *find_hdrinfo(const char *name) +{ + int n; + for (n = 0; hdrinfo[n].name != NULL; n++) + { + if (strcmp(name, hdrinfo[n].name) == 0) + return &hdrinfo[n]; + } + return NULL; +} + +void oferror(FILE *f) +{ + printf("file error\n"); + exit(2); +} + +void showhelp(void) +{ + printf("Syntax: makeamitbin [options]\n"); + printf("Options:\n"); + printf(" -1 ID1\tFirmware identifier 1, e.g. 'DDC_RUS001' for manufacturer LevelOne\n"); + printf(" -2 ID2\tFirmware identifier 2, 'Queen' in all known cases\n"); + printf(" -o FILE\tOutput file\n"); + printf(" -ids\t\tShow a list of known firmware identifiers.\n"); + exit(1); +} + +void show_fwids(void) +{ + printf("List of known firmware identifiers:\n"); + printf("Manufacturer\t\tProduct\t\tIdentifier\n"); + printf("=====================================================\n"); + printf("Conceptronic\t\tCHD2WLANU\tLLM_RUS001\n"); + printf("Pearl\t\t\tPE6643\t\tQueen\n"); + printf("Micronica\t\tMGB100\t\tQueen\n"); + printf("LevelOne\t\tWAP-0007\tDDC_RUS001\n"); + printf("SMC\t\t\tWAPS-G\t\tSMC_RUS001\n"); + printf("OvisLink (AirLive)\tWMU-6\t\tOVS_RUS001\n"); + printf("SafeCom SWSAPUR-5\tFMW\t\tSafeco_RPS001\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + unsigned char hdr[HDRSIZE]; + unsigned char *data; + FILE *of; + char *outfile = NULL; + char *type; + struct hdrinfo *info; + long size; + int last = 0; + int n; + for (n = 1; n < argc; n++) + { + if (strcmp(argv[n], "-1") == 0) + ascii1 = argv[n+1]; + if (strcmp(argv[n], "-2") == 0) + ascii2 = argv[n+1]; + if (strcmp(argv[n], "-o") == 0) + outfile = argv[n+1]; + if (strcmp(argv[n], "-ids") == 0) + show_fwids(); + } + if (ascii1 == NULL || ascii2 == NULL || outfile == NULL) + showhelp(); + of = fopen(outfile, "w"); + if (of == NULL) + oferror(of); + for (n = 1; n < argc; n++) + { + if (strncmp(argv[n], "-", 1) != 0) + { + type = argv[n++]; + if (n >= argc) + showhelp(); + last = ((n + 1) >= argc); /* dirty, options first! */ + info = find_hdrinfo(type); + if (info == NULL) + showhelp(); + data = read_file(argv[n], &size); + if (data == NULL) + showhelp(); + makehdr(hdr, info, data, size, last); + /* showhdr(hdr); */ + if (fwrite(hdr, HDRSIZE, 1, of) != 1) + oferror(of); + if (fwrite(data, size, 1, of) != 1) + oferror(of); + free(data); + } + else + n++; + } + if (fclose(of) != 0) + oferror(NULL); + return 0; +} diff --git a/trunk/tools/firmware-utils/src/md5.c b/trunk/tools/firmware-utils/src/md5.c new file mode 100644 index 00000000..20397603 --- /dev/null +++ b/trunk/tools/firmware-utils/src/md5.c @@ -0,0 +1,307 @@ + + +/* + *********************************************************************** + ** 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. ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** 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. ** + *********************************************************************** + */ + +#include +#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] ** + *********************************************************************** + */ + +/* 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 +#else +#define UL(x) x +#endif + +/* The routine MD5_Init initializes the message-digest context + mdContext. All fields are set to zero. + */ +void MD5_Init (mdContext) +MD5_CTX *mdContext; +{ + 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; +} + +/* 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; +{ + 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; + } + } +} + +/* 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; +{ + 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); +} + +/* Basic MD5 step. Transforms buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + 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; +} + +/* + *********************************************************************** + ** End of md5.c ** + ******************************** (cut) ******************************** + */ diff --git a/trunk/tools/firmware-utils/src/md5.h b/trunk/tools/firmware-utils/src/md5.h new file mode 100644 index 00000000..f7a0c964 --- /dev/null +++ b/trunk/tools/firmware-utils/src/md5.h @@ -0,0 +1,65 @@ +/* + *********************************************************************** + ** 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 ** + *********************************************************************** + */ + +/* + *********************************************************************** + ** 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 __MD5_INCLUDE__ + +/* 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_CTX; + +void MD5_Init (); +void MD5_Update (); +void MD5_Final (); + +#define __MD5_INCLUDE__ +#endif /* __MD5_INCLUDE__ */ diff --git a/trunk/tools/firmware-utils/src/mkbrncmdline.c b/trunk/tools/firmware-utils/src/mkbrncmdline.c new file mode 100644 index 00000000..207098bb --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkbrncmdline.c @@ -0,0 +1,168 @@ +/* + * mkbrncmdline.c - partially based on libreCMC's wndr3700.c + * + * Copyright (C) 2011 Tobias Diedrich + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void usage(const char *) __attribute__ (( __noreturn__ )); + +static void usage(const char *mess) +{ + fprintf(stderr, "Error: %s\n", mess); + fprintf(stderr, "Usage: mkbrncmdline -i input_file -o output_file [-a loadaddress] arg1 [argx ...]\n"); + fprintf(stderr, "\n"); + exit(1); +} + +static char *input_file = NULL; +static char *output_file = NULL; +static unsigned loadaddr = 0x80002000; + +static void parseopts(int *argc, char ***argv) +{ + char *endptr; + int res; + + while ((res = getopt(*argc, *argv, "a:i:o:")) != -1) { + switch (res) { + default: + usage("Unknown option"); + break; + case 'a': + loadaddr = strtoul(optarg, &endptr, 0); + if (endptr == optarg || *endptr != 0) + usage("loadaddress must be a decimal or hexadecimal 32-bit value"); + break; + case 'i': + input_file = optarg; + break; + case 'o': + output_file = optarg; + break; + } + } + *argc -= optind; + *argv += optind; +} + +static void emitload(int outfd, int reg, unsigned value) +{ + char buf[8] = { + 0x3c, 0x04 + reg, + value >> 24, value >> 16, + 0x34, 0x84 + reg + (reg << 5), + value >> 8, value, + }; + if (write(outfd, buf, sizeof(buf)) != sizeof(buf)) { + fprintf(stderr, "write: %s\n", strerror(errno)); + exit(1); + } +} + +int main(int argc, char **argv) +{ + int outfd; + int i; + int fd; + size_t len, skip, buf_len; + unsigned cmdline_addr; + unsigned s_ofs; + char *buf; + + parseopts(&argc, &argv); + + if (argc < 1) + usage("must specify at least one kernel cmdline argument"); + + if (input_file == NULL || output_file == NULL) + usage("must specify input and output file"); + + if ((outfd = open(output_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + { + fprintf(stderr, "Error opening '%s' for writing: %s\n", output_file, strerror(errno)); + exit(1); + } + + // mmap input_file + if ((fd = open(input_file, 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 mapping file '%s': %s\n", input_file, strerror(errno)); + exit(1); + } + + cmdline_addr = loadaddr + len; + + // Kernel args are passed in registers a0,a1,a2 and a3 + emitload(outfd, 0, 0); /* a0 = 0 */ + emitload(outfd, 1, 0); /* a1 = 0 */ + emitload(outfd, 2, cmdline_addr); /* a2 = &cmdline */ + emitload(outfd, 3, 0); /* a3 = 0 */ + skip = lseek(outfd, 0, SEEK_END); + + // write the kernel + if (write(outfd, input_file + skip, len - skip) != len -skip) { + fprintf(stderr, "write: %s\n", strerror(errno)); + exit(1); + } + + // write cmdline structure + buf_len = (argc + 1) * 4; + for (i=0; i> 24; + buf[i * 4 + 1] = s_ptr >> 16; + buf[i * 4 + 2] = s_ptr >> 8; + buf[i * 4 + 3] = s_ptr >> 0; + memcpy(&buf[s_ofs], argv[i], strlen(argv[i])); + s_ofs += strlen(argv[i]) + 1; + } + if (write(outfd, buf, buf_len) != buf_len) { + fprintf(stderr, "write: %s\n", strerror(errno)); + exit(1); + } + + + munmap(input_file, len); + close(outfd); + free(buf); + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/mkbrnimg.c b/trunk/tools/firmware-utils/src/mkbrnimg.c new file mode 100644 index 00000000..3c2a91d2 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkbrnimg.c @@ -0,0 +1,183 @@ +/* + * mkbrnimg.c - partially based on libreCMC's wndr3700.c + * + * Copyright (C) 2011 Tobias Diedrich + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPB 8 /* bits/byte */ + +static uint32_t crc32[1<> 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; +} + +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, "\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) { + switch (res) { + default: + usage("Unknown option"); + break; + case 'o': + output_file = optarg; + break; + case 'm': + magic = strtoul(optarg, &endptr, 0); + if (endptr == optarg || *endptr != 0) + usage("magic must be a decimal or hexadecimal 32-bit value"); + break; + case 's': + signature = optarg; + break; + } + } + *argc -= optind; + *argv += optind; +} + +static void appendfile(int outfd, char *path, int kernel) { + int fd; + size_t len, padded_len; + char *input_file; + uint32_t crc; + char padding[0x400]; + char footer[12]; + + memset(padding, 0xff, sizeof(padding)); + + // mmap input_file + if ((fd = open(path, 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 mapping file '%s': %s\n", path, strerror(errno)); + exit(1); + } + + // kernel should be lzma compressed image, not uImage + if (kernel && + (input_file[0] != (char)0x5d || + input_file[1] != (char)0x00 || + input_file[2] != (char)0x00 || + input_file[3] != (char)0x80)) { + fprintf(stderr, "lzma signature not found on kernel image.\n"); + exit(1); + } + + init_crc32(); + crc = crc32buf(input_file, len); + fprintf(stderr, "crc32 for '%s' is %08x.\n", path, crc); + + // write the file + write(outfd, input_file, len); + + // 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); + write(outfd, padding, padded_len - len); + + // write footer + footer[0] = (len >> 0) & 0xff; + footer[1] = (len >> 8) & 0xff; + footer[2] = (len >> 16) & 0xff; + footer[3] = (len >> 24) & 0xff; + footer[4] = (magic >> 0) & 0xff; + footer[5] = (magic >> 8) & 0xff; + footer[6] = (magic >> 16) & 0xff; + footer[7] = (magic >> 24) & 0xff; + footer[8] = (crc >> 0) & 0xff; + footer[9] = (crc >> 8) & 0xff; + footer[10] = (crc >> 16) & 0xff; + footer[11] = (crc >> 24) & 0xff; + write(outfd, footer, sizeof(footer)); + + munmap(input_file, len); +} + +int main(int argc, char **argv) +{ + int outfd; + int i; + + parseopts(&argc, &argv); + + if (argc < 1) + usage("wrong number of arguments"); + + if ((outfd = open(output_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) + { + fprintf(stderr, "Error opening '%s' for writing: %s\n", output_file, strerror(errno)); + exit(1); + } + + for (i=0; i + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include +#include + +#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 read kernel image from the file \n" +" -c use the kernel image as a combined image\n" +" -M set model to \n" +" -o write output to the file \n" +" -r read rootfs image from the file \n" +" -S set image signature to \n" +" -R set image region to \n" +" -V set image version to \n" +" -I set image size to \n" +" -K set kernel size to \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/trunk/tools/firmware-utils/src/mkcasfw.c b/trunk/tools/firmware-utils/src/mkcasfw.c new file mode 100644 index 00000000..dfcfd08f --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkcasfw.c @@ -0,0 +1,1030 @@ +/* + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include /* for __BYTE_ORDER */ +#if defined(__CYGWIN__) +# include +#endif + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +# define LE16_TO_HOST(x) (x) +# define LE32_TO_HOST(x) (x) +#else +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE16_TO_HOST(x) bswap_16(x) +# define LE32_TO_HOST(x) bswap_32(x) +#endif + +#define MAX_NUM_BLOCKS 2 +#define MAX_ARG_COUNT 32 +#define MAX_ARG_LEN 1024 +#define FILE_BUF_LEN (16*1024) +#define DEFAULT_PADC 0xFF + +#define DEFAULT_BLOCK_ALIGN 0x10000U + +#define CSUM_TYPE_NONE 0 +#define CSUM_TYPE_8 1 +#define CSUM_TYPE_16 2 +#define CSUM_TYPE_32 3 + +struct csum_state{ + int size; + uint32_t val; + uint32_t tmp; + int odd; +}; + +struct image_desc { + int need_file; + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ + + uint32_t csum; + uint32_t out_size; + uint8_t padc; +}; + +struct fwhdr_nfs { + uint32_t type; + uint32_t kernel_offs; + uint32_t kernel_size; + uint32_t fs_offs; + uint32_t fs_size; + uint32_t kernel_csum; + uint32_t fs_csum; + uint32_t id; +} __attribute__ ((packed)); + +struct fwhdr_cas { + uint32_t type; + uint32_t kernel_offs; + uint32_t kernel_size; + uint32_t id; + uint32_t kernel_csum; + uint32_t magic1; + uint32_t magic2; + uint32_t magic3; +} __attribute__ ((packed)); + +union file_hdr { + struct fwhdr_cas cas; + struct fwhdr_nfs nfs; +}; + +struct board_info { + char *model; + char *name; + int header_type; + uint32_t id; + uint32_t max_kernel_size; + uint32_t max_fs_size; +}; + +#define HEADER_TYPE_NFS 0 +#define HEADER_TYPE_CAS 1 + +#define KERNEL_SIZE_CAS (61*64*1024) +#define KERNEL_SIZE_NFS (52*64*1024) +#define FS_SIZE_NFS (9*64*1024) + +#define CAS_MAGIC1 0x5241AA55 +#define CAS_MAGIC2 0x524F4741 +#define CAS_MAGIC3 0xD3F22D4E + +/* Cellvision/SparkLAN products */ +#define MODEL_CAS_630 0x01000000 +#define MODEL_CAS_630W 0x01000000 +#define MODEL_CAS_670 0x01000000 +#define MODEL_CAS_670W 0x01000000 +#define MODEL_NFS_101U 0x01000000 +#define MODEL_NFS_101WU 0x01000003 +#define MODEL_NFS_202U 0x01000001 +#define MODEL_NFS_202WU 0x01000002 + +/* Corega products */ +#define MODEL_CG_NSADP 0x01000020 /* NFS-101U */ +#define MODEL_CG_NSADPCR 0x01000021 /* NFS-202U */ + +/* D-Link products */ +#define MODEL_DCS_950 0x01010102 /* CAS-630 */ +#define MODEL_DCS_950G 0x01020102 /* CAS-630W */ +#define MODEL_DNS_120 0x01000030 /* NFS-101U */ +#define MODEL_DNS_G120 0x01000032 /* NFS-101WU */ + +/* Digitus products */ +#define MODEL_DN_16021 MODEL_CAS_630 +#define MODEL_DN_16022 MODEL_CAS_630W +#define MODEL_DN_16030 MODEL_CAS_670 +#define MODEL_DN_16031 MODEL_CAS_670W +#define MODEL_DN_7013 MODEL_NFS_101U + +/* Lobos products */ +#define MODEL_LB_SS01TXU 0x00000000 + +/* Neu-Fusion products */ + +/* Ovislink products */ +#define MODEL_MU_5000FS 0x01000040 /* NFS-101U */ +#define MODEL_WL_5420CAM 0x020B0101 /* CAS-630W? */ +#define MODEL_WL_5460CAM 0x020B0001 /* CAS-670W */ + +/* Repotec products */ + +/* Sitecom products */ +#define MODEL_LN_350 /* unknown */ +#define MODEL_LN_403 0x01020402 +#define MODEL_WL_401 0x01010402 + +/* Surecom products */ +#define MODEL_EP_4001_MM 0x01030A02 /* CAS-630 */ +#define MODEL_EP_4002_MM 0x01020A02 /* CAS-630W */ +#define MODEL_EP_4011_MM 0x01010A02 /* CAS-670 */ +#define MODEL_EP_4012_MM 0x01000A02 /* CAS-670W */ +#define MODEL_EP_9812_U /* unknown */ + +/* Trendnet products */ +#define MODEL_TN_U100 0x01000081 /* NFS-101U */ +#define MODEL_TN_U200 0x01000082 /* NFS-202U */ + +/* + * Globals + */ +char *progname; +char *ofname; +int verblevel; +int keep_invalid_images; +int invalid_causes_error = 1; +union file_hdr header; + +struct image_desc kernel_image; +struct image_desc fs_image; + +struct board_info *board = NULL; + +#define BOARD(m, n, i, ks, fs, h) { \ + .model = (m), \ + .name = (n), \ + .id = (i), \ + .max_kernel_size = (ks), \ + .max_fs_size = (fs), \ + .header_type = (h) \ + } + +#define BOARD_CAS(m,n,i) \ + BOARD(m, n, i, KERNEL_SIZE_CAS, 0, HEADER_TYPE_CAS) +#define BOARD_NFS(m,n,i) \ + BOARD(m, n, i, KERNEL_SIZE_NFS, FS_SIZE_NFS, HEADER_TYPE_NFS) + +static struct board_info boards[] = { + /* Cellvision/Sparklan products */ + BOARD_CAS("CAS-630", "Cellvision CAS-630", MODEL_CAS_630), + BOARD_CAS("CAS-630W", "Cellvision CAS-630W", MODEL_CAS_630W), + BOARD_CAS("CAS-670", "Cellvision CAS-670", MODEL_CAS_670), + BOARD_CAS("CAS-670W", "Cellvision CAS-670W", MODEL_CAS_670W), + BOARD_NFS("NFS-101U", "Cellvision NFS-101U", MODEL_NFS_101U), + BOARD_NFS("NFS-101WU", "Cellvision NFS-101WU", MODEL_NFS_101WU), + BOARD_NFS("NFS-202U", "Cellvision NFS-202U", MODEL_NFS_202U), + BOARD_NFS("NFS-202WU", "Cellvision NFS-202WU", MODEL_NFS_202WU), + + /* Corega products */ + BOARD_NFS("CG-NSADP", "Corega CG-NSADP", MODEL_CG_NSADP), + BOARD_NFS("CG-NSADPCR", "Corega CG-NSADPCR", MODEL_CG_NSADPCR), + + /* D-Link products */ + BOARD_CAS("DCS-950", "D-Link DCS-950", MODEL_DCS_950), + BOARD_CAS("DCS-950G", "D-Link DCS-950G", MODEL_DCS_950G), + BOARD_NFS("DNS-120", "D-Link DNS-120", MODEL_DNS_120), + BOARD_NFS("DNS-G120", "D-Link DNS-G120", MODEL_DNS_G120), + + /* Digitus products */ + BOARD_NFS("DN-7013", "Digitus DN-7013", MODEL_DN_7013), + + /* Lobos products */ + BOARD_NFS("LB-SS01TXU", "Lobos LB-SS01TXU", MODEL_LB_SS01TXU), + + /* Ovislink products */ + BOARD_NFS("MU-5000FS", "Ovislink MU-5000FS", MODEL_MU_5000FS), + BOARD_CAS("WL-5420CAM", "Ovislink WL-5420 CAM", MODEL_WL_5420CAM), + BOARD_CAS("WL-5460CAM", "Ovislink WL-5460 CAM", MODEL_WL_5460CAM), + + /* Sitecom products */ + BOARD_CAS("LN-403", "Sitecom LN-403", MODEL_LN_403), + BOARD_CAS("WL-401", "Sitecom WL-401", MODEL_WL_401), + + /* Surecom products */ + BOARD_CAS("EP-4001-MM", "Surecom EP-4001-MM", MODEL_EP_4001_MM), + BOARD_CAS("EP-4002-MM", "Surecom EP-4002-MM", MODEL_EP_4002_MM), + BOARD_CAS("EP-4011-MM", "Surecom EP-4011-MM", MODEL_EP_4011_MM), + BOARD_CAS("EP-4012-MM", "Surecom EP-4012-MM", MODEL_EP_4012_MM), + + /* TrendNET products */ + BOARD_NFS("TN-U100", "TrendNET TN-U100", MODEL_TN_U100), + BOARD_NFS("TN-U200", "TrendNET TN-U200", MODEL_TN_U200), + + {.model = NULL} +}; + +/* + * 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 WARN(fmt, ...) do { \ + fprintf(stderr, "[%s] *** warning: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define DBG(lev, fmt, ...) do { \ + if (verblevel < lev) \ + break;\ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERR_FATAL -1 +#define ERR_INVALID_IMAGE -2 + +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" +" -B create image for the board specified with .\n" +" valid values:\n" + ); + for (board = boards; board->model != NULL; board++){ + fprintf(stream, +" %-12s: %s\n", + board->model, board->name); + }; + fprintf(stream, +" -d don't throw error on invalid images\n" +" -k keep invalid images\n" +" -K add kernel to the image\n" +" -C add custom filesystem to the image\n" +" -h show this screen\n" +"Parameters:\n" +" write output to the file \n" + ); + + exit(status); +} + +static inline uint32_t align(uint32_t base, uint32_t alignment) +{ + uint32_t ret; + + if (alignment) { + ret = (base + alignment - 1); + ret &= ~(alignment-1); + } else { + ret = base; + } + + return ret; +} + +/* + * argument parsing + */ +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 +str2u16(char *arg, uint16_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { + return -1; + } + + *val = t & 0xFFFF; + 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; +} + +int +parse_arg(char *arg, char *buf, char *argv[]) +{ + int res = 0; + size_t argl; + char *tok; + char **ap = &buf; + int i; + + memset(argv, 0, MAX_ARG_COUNT * sizeof(void *)); + + if ((arg == NULL)) { + /* no arguments */ + return 0; + } + + argl = strlen(arg); + if (argl == 0) { + /* no arguments */ + return 0; + } + + if (argl >= MAX_ARG_LEN) { + /* argument is too long */ + argl = MAX_ARG_LEN-1; + } + + memcpy(buf, arg, argl); + buf[argl] = '\0'; + + for (i = 0; i < MAX_ARG_COUNT; i++) { + tok = strsep(ap, ":"); + if (tok == NULL) { + break; + } +#if 0 + else if (tok[0] == '\0') { + break; + } +#endif + argv[i] = tok; + res++; + } + + return res; +} + + +int +required_arg(char c, char *arg) +{ + if (arg == NULL || *arg != '-') + return 0; + + ERR("option -%c requires an argument\n", c); + return ERR_FATAL; +} + + +int +is_empty_arg(char *arg) +{ + int ret = 1; + if (arg != NULL) { + if (*arg) ret = 0; + }; + return ret; +} + + +void +csum8_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + for ( ; len > 0; len --) { + css->val += *p++; + } +} + + +uint16_t +csum8_get(struct csum_state *css) +{ + uint8_t t; + + t = css->val; + return ~t + 1; +} + + +void +csum16_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + uint16_t t; + + if (css->odd) { + t = css->tmp + (p[0]<<8); + css->val += LE16_TO_HOST(t); + css->odd = 0; + len--; + p++; + } + + for ( ; len > 1; len -= 2, p +=2 ) { + t = p[0] + (p[1] << 8); + css->val += LE16_TO_HOST(t); + } + + if (len == 1) { + css->tmp = p[0]; + css->odd = 1; + } +} + + +uint16_t +csum16_get(struct csum_state *css) +{ + char pad = 0; + + csum16_update(&pad, 1, css); + return ~css->val + 1; +} + +void +csum32_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + uint32_t t; + + for ( ; len > 3; len -= 4, p += 4 ) { + t = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24); + css->val ^= t; + } +} + +uint32_t +csum32_get(struct csum_state *css) +{ + return css->val; +} + + +void +csum_init(struct csum_state *css, int size) +{ + css->val = 0; + css->tmp = 0; + css->odd = 0; + css->size = size; +} + +void +csum_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + switch (css->size) { + case CSUM_TYPE_8: + csum8_update(p,len,css); + break; + case CSUM_TYPE_16: + csum16_update(p,len,css); + break; + case CSUM_TYPE_32: + csum32_update(p,len,css); + break; + } +} + + +uint32_t +csum_get(struct csum_state *css) +{ + uint32_t ret; + + switch (css->size) { + case CSUM_TYPE_8: + ret = csum8_get(css); + break; + case CSUM_TYPE_16: + ret = csum16_get(css); + break; + case CSUM_TYPE_32: + ret = csum32_get(css); + } + + return ret; +} + + +/* + * routines to write data to the output file + */ +int +write_out_data(FILE *outfile, uint8_t *data, size_t len, + struct csum_state *css) +{ + errno = 0; + + fwrite(data, len, 1, outfile); + if (errno) { + ERRS("unable to write output file"); + return ERR_FATAL; + } + + if (css) { + csum_update(data, len, css); + } + + return 0; +} + + +int +write_out_padding(FILE *outfile, size_t len, uint8_t padc, + struct csum_state *css) +{ + uint8_t buf[512]; + size_t buflen = sizeof(buf); + int err; + + memset(buf, padc, buflen); + while (len > 0) { + if (len < buflen) + buflen = len; + + err = write_out_data(outfile, buf, buflen, css); + if (err) + return err; + + len -= buflen; + } + + return 0; +} + + +int +image_stat_file(struct image_desc *desc) +{ + struct stat st; + int err; + + if (desc->file_name == NULL) + return 0; + + err = stat(desc->file_name, &st); + if (err){ + ERRS("stat failed on %s", desc->file_name); + return ERR_FATAL; + } + + if (st.st_size > desc->out_size) { + WARN("file %s is too big, will be truncated to %d bytes\n", + desc->file_name, desc->out_size); + desc->file_size = desc->out_size; + return ERR_INVALID_IMAGE; + } + + + desc->file_size = st.st_size; + desc->out_size = align(desc->file_size,1); + return 0; +} + + +int +image_writeout_file(FILE *outfile, struct image_desc *desc, + struct csum_state *css) +{ + char buf[FILE_BUF_LEN]; + size_t buflen = sizeof(buf); + FILE *f; + size_t len; + int res; + + if (desc->file_name == NULL) + return 0; + + if (desc->file_size == 0) + return 0; + + errno = 0; + f = fopen(desc->file_name,"r"); + if (errno) { + ERRS("unable to open file: %s", desc->file_name); + return ERR_FATAL; + } + + len = desc->file_size; + while (len > 0) { + if (len < buflen) + buflen = len; + + /* read data from source file */ + errno = 0; + fread(buf, buflen, 1, f); + if (errno != 0) { + ERRS("unable to read from file: %s", desc->file_name); + res = ERR_FATAL; + break; + } + + res = write_out_data(outfile, buf, buflen, css); + if (res) + break; + + len -= buflen; + } + + fclose(f); + return res; +} + + +int +image_writeout(FILE *outfile, struct image_desc *desc) +{ + int res; + struct csum_state css; + size_t padlen; + + res = 0; + + if (!desc->file_size) + return 0; + + DBG(2, "writing image, file=%s, file_size=%d\n", + desc->file_name, desc->file_size); + + csum_init(&css, CSUM_TYPE_32); + + res = image_writeout_file(outfile, desc, &css); + if (res) + return res; + + /* write padding data if neccesary */ + padlen = desc->out_size - desc->file_size; + DBG(1,"padding desc, length=%d", padlen); + res = write_out_padding(outfile, padlen, desc->padc, &css); + + desc->csum = csum_get(&css); + + return res; +} + + +int +write_out_header(FILE *outfile) +{ + union file_hdr tmp; + int res; + + errno = 0; + if (fseek(outfile, 0, SEEK_SET) != 0) { + ERRS("fseek failed on output file"); + return ERR_FATAL; + } + + switch (board->header_type) { + case HEADER_TYPE_CAS: + tmp.cas.type = HOST_TO_LE32(header.cas.type); + tmp.cas.id = HOST_TO_LE32(header.cas.id); + tmp.cas.kernel_offs = HOST_TO_LE32(sizeof(tmp.cas)); + tmp.cas.kernel_size = HOST_TO_LE32(kernel_image.out_size); + tmp.cas.kernel_csum = HOST_TO_LE32(kernel_image.csum); + tmp.cas.magic1 = HOST_TO_LE32(CAS_MAGIC1); + tmp.cas.magic2 = HOST_TO_LE32(CAS_MAGIC2); + tmp.cas.magic3 = HOST_TO_LE32(CAS_MAGIC3); + res = write_out_data(outfile, (uint8_t *)&tmp.cas, + sizeof(tmp.cas), NULL); + break; + case HEADER_TYPE_NFS: + tmp.nfs.type = HOST_TO_LE32(header.nfs.type); + tmp.nfs.id = HOST_TO_LE32(header.nfs.id); + tmp.nfs.kernel_offs = HOST_TO_LE32(sizeof(tmp.nfs)); + tmp.nfs.kernel_size = HOST_TO_LE32(kernel_image.out_size); + tmp.nfs.kernel_csum = HOST_TO_LE32(kernel_image.csum); + tmp.nfs.fs_offs = HOST_TO_LE32(sizeof(tmp.nfs) + + kernel_image.out_size); + tmp.nfs.fs_size = HOST_TO_LE32(fs_image.out_size); + tmp.nfs.fs_csum = HOST_TO_LE32(fs_image.csum); + res = write_out_data(outfile, (uint8_t *)&tmp.nfs, + sizeof(tmp.nfs), NULL); + break; + } + + return res; +} + +int +write_out_images(FILE *outfile) +{ + struct image_desc *desc; + int i, res; + + res = image_writeout(outfile, &kernel_image); + if (res) + return res; + + res = image_writeout(outfile, &fs_image); + if (res) + return res; + + return 0; +} + + +struct board_info * +find_board(char *model) +{ + struct board_info *ret; + struct board_info *board; + + ret = NULL; + for (board = boards; board->model != NULL; board++){ + if (strcasecmp(model, board->model) == 0) { + ret = board; + break; + } + }; + + return ret; +} + + +int +parse_opt_board(char ch, char *arg) +{ + + DBG(1,"parsing board option: -%c %s", ch, arg); + + if (board != NULL) { + ERR("only one board option allowed"); + return ERR_FATAL; + } + + if (required_arg(ch, arg)) + return ERR_FATAL; + + board = find_board(arg); + if (board == NULL){ + ERR("invalid/unknown board specified: %s", arg); + return ERR_FATAL; + } + + switch (board->header_type) { + case HEADER_TYPE_CAS: + header.cas.type = HEADER_TYPE_CAS; + header.cas.id = board->id; + break; + case HEADER_TYPE_NFS: + header.nfs.type = HEADER_TYPE_NFS; + header.nfs.id = board->id; + break; + default: + ERR("internal error, unknown header type\n"); + return ERR_FATAL; + } + + return 0; +} + + +int +parse_opt_image(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + struct image_desc *desc = NULL; + int i; + + switch (ch) { + case 'K': + if (kernel_image.file_name) { + WARN("only one kernel option allowed"); + break; + } + desc = &kernel_image; + break; + case 'F': + if (fs_image.file_name) { + WARN("only one fs option allowed"); + break; + } + desc = &fs_image; + break; + } + + if (!desc) + return ERR_FATAL; + + argc = parse_arg(arg, buf, argv); + + i = 0; + p = argv[i++]; + if (!is_empty_arg(p)) { + desc->file_name = strdup(p); + if (desc->file_name == NULL) { + ERR("not enough memory"); + return ERR_FATAL; + } + } else { + ERR("no file specified for option %c", ch); + return ERR_FATAL; + } + + return 0; +} + + +int +process_images(void) +{ + struct image_desc *desc; + uint32_t offs = 0; + int i; + int res; + + kernel_image.out_size = board->max_kernel_size; + kernel_image.padc = DEFAULT_PADC; + res = image_stat_file(&kernel_image); + if (res) + return res; + + if (!fs_image.file_name) + return 0; + + fs_image.out_size = board->max_fs_size; + fs_image.padc = DEFAULT_PADC; + res = image_stat_file(&fs_image); + if (res) + return res; + + return 0; +} + + +int +main(int argc, char *argv[]) +{ + int optinvalid = 0; /* flag for invalid option */ + int c; + int res = ERR_FATAL; + + FILE *outfile; + + progname=basename(argv[0]); + + opterr = 0; /* could not print standard getopt error messages */ + while ( 1 ) { + optinvalid = 0; + + c = getopt(argc, argv, "B:C:dhK:r:vw:x:"); + if (c == -1) + break; + + switch (c) { + case 'B': + optinvalid = parse_opt_board(c,optarg); + break; + case 'd': + invalid_causes_error = 0; + break; + case 'C': + case 'K': + optinvalid = parse_opt_image(c,optarg); + break; + case 'k': + keep_invalid_images = 1; + break; + case 'v': + verblevel++; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + optinvalid = 1; + break; + } + if (optinvalid != 0 ){ + ERR("invalid option: -%c", optopt); + goto out; + } + } + + if (board == NULL) { + ERR("no board specified"); + goto out; + } + + if (optind == argc) { + ERR("no output file specified"); + goto out; + } + + ofname = argv[optind++]; + + if (optind < argc) { + ERR("invalid option: %s", argv[optind]); + goto out; + } + + res = process_images(); + if (res == ERR_FATAL) + goto out; + + if (res == ERR_INVALID_IMAGE) { + if (invalid_causes_error) + res = ERR_FATAL; + + if (keep_invalid_images == 0) { + WARN("generation of invalid images disabled", ofname); + goto out; + } + + WARN("generating invalid image", ofname); + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + res = ERR_FATAL; + goto out; + } + + if (write_out_header(outfile) != 0) { + res = ERR_FATAL; + goto out_flush; + } + + if (write_out_images(outfile) != 0) { + res = ERR_FATAL; + goto out_flush; + } + + if (write_out_header(outfile) != 0) { + res = ERR_FATAL; + goto out_flush; + } + + DBG(1,"Image file %s completed.", ofname); + +out_flush: + fflush(outfile); + fclose(outfile); + if (res == ERR_FATAL) { + unlink(ofname); + } +out: + if (res == ERR_FATAL) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/trunk/tools/firmware-utils/src/mkchkimg.c b/trunk/tools/firmware-utils/src/mkchkimg.c new file mode 100644 index 00000000..e152f7d4 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkchkimg.c @@ -0,0 +1,327 @@ +/* + * Make CHK Image + * + * This utility creates Netgear .chk files. + * + * Copyright (C) 2008 Dave C. Reeve + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#define BUF_LEN (2048) + +#define MAX_BOARD_ID_LEN (64) + +struct chk_header { + uint32_t magic; + uint32_t header_len; + uint8_t reserved[8]; + uint32_t kernel_chksum; + uint32_t rootfs_chksum; + uint32_t kernel_len; + uint32_t rootfs_len; + uint32_t image_chksum; + uint32_t header_chksum; + /* char board_id[] - upto MAX_BOARD_ID_LEN */ +}; + +static void __attribute__ ((format (printf, 2, 3))) +fatal_error (int maybe_errno, const char * format, ...) +{ + va_list ap; + + fprintf (stderr, "mkchkimg: "); + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); + + if (maybe_errno) { + fprintf (stderr, ": %s\n", strerror (maybe_errno)); + } else { + fprintf (stderr, "\n"); + } + + exit (EXIT_FAILURE); +} + +static void __attribute__ ((format (printf, 1, 2))) +message (const char * format, ...) +{ + va_list ap; + + fprintf (stderr, "mkchkimg: "); + va_start (ap, format); + vfprintf (stderr, format, ap); + va_end (ap); + fprintf (stderr, "\n"); +} + +struct ngr_checksum { + uint32_t c0; + uint32_t c1; +}; + +static inline void +netgear_checksum_init (struct ngr_checksum * c) +{ + c->c0 = c->c1 = 0; +} + +static inline void +netgear_checksum_add (struct ngr_checksum * c, unsigned char * buf, size_t len) +{ + size_t i; + + for (i=0; ic0 += buf[i] & 0xff; + c->c1 += c->c0; + } +} + +static inline unsigned long +netgear_checksum_fini (struct ngr_checksum * c) +{ + uint32_t b, checksum; + + b = (c->c0 & 65535) + ((c->c0 >> 16) & 65535); + c->c0 = ((b >> 16) + b) & 65535; + b = (c->c1 & 65535) + ((c->c1 >> 16) & 65535); + c->c1 = ((b >> 16) + b) & 65535; + checksum = ((c->c1 << 16) | c->c0); + return checksum; +} + +static void +print_help (void) +{ + fprintf (stderr, "Usage: mkchkimg -o output -k kernel [-f filesys] [-b board_id] [-r region]\n"); +} + +int +main (int argc, char * argv[]) +{ + int opt; + char * ptr; + size_t len; + size_t header_len; + struct chk_header * hdr; + struct ngr_checksum chk_part, chk_whole; + char buf[BUF_LEN]; + char * output_file, * kern_file, * fs_file; + FILE * out_fp, * kern_fp, * fs_fp; + char * board_id; + unsigned long region; + + /* Default values */ + board_id = "U12H072T00_NETGEAR"; + region = 1; /* 1=WW, 2=NA */ + output_file = NULL; + kern_file = NULL; + fs_file = NULL; + fs_fp = NULL; + + while ((opt = getopt (argc, argv, ":b:r:k:f:o:h")) != -1) { + switch (opt) { + case 'b': + /* Board Identity */ + if (strlen (optarg) > MAX_BOARD_ID_LEN) { + fatal_error (0, "Board lenght exceeds %d", + MAX_BOARD_ID_LEN); + } + board_id = optarg; + break; + + case 'r': + /* Region */ + errno = 0; + region = strtoul (optarg, &ptr, 0); + if (errno || ptr==optarg || *ptr!='\0') { + fatal_error (0, "Cannot parse region %s", optarg); + } + if (region > 0xff) { + fatal_error (0, "Region cannot exceed 0xff"); + } + break; + + case 'k': + /* Kernel */ + kern_file = optarg; + break; + + case 'f': + /* Filing System */ + fs_file = optarg; + break; + + case 'o': + /* Output file */ + output_file = optarg; + break; + + case 'h': + print_help (); + return EXIT_SUCCESS; + + case ':': + print_help (); + fatal_error (0, "Option -%c missing argument", optopt); + break; + + case '?': + print_help (); + fatal_error (0, "Unknown argument -%c", optopt); + break; + + default: + break; + } + } + + /* Check we have all the options expected */ + if (!kern_file) { + print_help (); + fatal_error (0, "Kernel file expected"); + } + if (!output_file) { + print_help (); + fatal_error (0, "Output file required"); + } + message ("Netgear CHK writer - v0.1"); + + /* Open the input file */ + kern_fp = fopen (kern_file, "r"); + if (!kern_fp) { + fatal_error (errno, "Cannot open %s", kern_file); + } + + /* Open the fs file, if specified */ + if (fs_file) { + fs_fp = fopen (fs_file, "r"); + if (!fs_fp) { + fatal_error (errno, "Cannot open %s", fs_file); + } + } + + /* Open the output file */ + out_fp = fopen (output_file, "w+"); + if (!out_fp) { + fatal_error (errno, "Cannot open %s", output_file); + } + + /* Write zeros when the chk header will be */ + buf[0] = '\0'; + header_len = sizeof (struct chk_header) + strlen (board_id); + if (fwrite (buf, 1, header_len, out_fp) != header_len) { + fatal_error (errno, "Cannot write header"); + } + + /* Allocate storage for header, we fill in as we go */ + hdr = malloc (sizeof (struct chk_header)); + if (!hdr) { + fatal_error (0, "malloc failed"); + } + bzero (hdr, sizeof (struct chk_header)); + + /* Fill in known values */ + hdr->magic = htonl (0x2a23245e); + hdr->header_len = htonl(header_len); + hdr->reserved[0] = (unsigned char)(region & 0xff); + 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 ? */ + message (" Board Id: %s", board_id); + message (" Region: %s", region == 1 ? "World Wide (WW)" + : (region == 2 ? "North America (NA)" : "Unknown")); + + /* Copy the trx file, calculating the checksum as we go */ + netgear_checksum_init (&chk_part); + netgear_checksum_init (&chk_whole); + while (!feof (kern_fp)) { + len = fread (buf, 1, BUF_LEN, kern_fp); + if (len < 1) { + break; + } + if (fwrite (buf, len, 1, out_fp) != 1) { + fatal_error (errno, "Write error"); + } + hdr->kernel_len += len; + netgear_checksum_add (&chk_part, (unsigned char *)buf, len); + netgear_checksum_add (&chk_whole, (unsigned char *)buf, len); + } + hdr->kernel_chksum = netgear_checksum_fini (&chk_part); + message (" Kernel Len: %u", hdr->kernel_len); + message ("Kernel Checksum: 0x%08x", hdr->kernel_chksum); + hdr->kernel_len = htonl (hdr->kernel_len); + hdr->kernel_chksum = htonl (hdr->kernel_chksum); + + /* Now copy the root fs, calculating the checksum as we go */ + if (fs_fp) { + netgear_checksum_init (&chk_part); + while (!feof (fs_fp)) { + len = fread (buf, 1, BUF_LEN, fs_fp); + if (len < 1) { + break; + } + if (fwrite (buf, len, 1, out_fp) != 1) { + fatal_error (errno, "Write error"); + } + hdr->rootfs_len += len; + netgear_checksum_add (&chk_part, (unsigned char *)buf, len); + netgear_checksum_add (&chk_whole, (unsigned char *)buf, len); + } + hdr->rootfs_chksum = (netgear_checksum_fini (&chk_part)); + message (" Rootfs Len: %u", hdr->rootfs_len); + message ("Rootfs Checksum: 0x%08x", hdr->rootfs_chksum); + hdr->rootfs_len = htonl (hdr->rootfs_len); + hdr->rootfs_chksum = htonl (hdr->rootfs_chksum); + } + + /* Calcautate the image checksum */ + hdr->image_chksum = netgear_checksum_fini (&chk_whole); + message (" Image Checksum: 0x%08x", hdr->image_chksum); + hdr->image_chksum = htonl (hdr->image_chksum); + + /* Calculate the header checksum */ + netgear_checksum_init (&chk_part); + netgear_checksum_add (&chk_part, (unsigned char *)hdr, + sizeof (struct chk_header)); + netgear_checksum_add (&chk_part, (unsigned char *)board_id, + strlen (board_id)); + hdr->header_chksum = htonl (netgear_checksum_fini (&chk_part)); + + /* Finally rewind the output and write headers */ + rewind (out_fp); + if (fwrite (hdr, sizeof (struct chk_header), 1, out_fp) != 1) { + fatal_error (errno, "Cannot write header"); + } + if (fwrite (board_id, strlen (board_id), 1, out_fp) != 1) { + fatal_error (errno, "Cannot write board id"); + } + + /* Success */ + return EXIT_SUCCESS; +} + diff --git a/trunk/tools/firmware-utils/src/mkcsysimg.c b/trunk/tools/firmware-utils/src/mkcsysimg.c new file mode 100644 index 00000000..c00096f8 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkcsysimg.c @@ -0,0 +1,1157 @@ +/* + * + * Copyright (C) 2007-2009 Gabor Juhos + * + * This program was based on the code found in various Linux + * source tarballs released by Edimax for it's devices. + * Original author: David Hsu + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include /* for __BYTE_ORDER */ +#if defined(__CYGWIN__) +# include +#endif + +#include "csysimg.h" + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +# define LE16_TO_HOST(x) (x) +# define LE32_TO_HOST(x) (x) +#else +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE16_TO_HOST(x) bswap_16(x) +# define LE32_TO_HOST(x) bswap_32(x) +#endif + +#define MAX_NUM_BLOCKS 8 +#define MAX_ARG_COUNT 32 +#define MAX_ARG_LEN 1024 +#define FILE_BUF_LEN (16*1024) +#define CSYS_PADC 0xFF + +#define BLOCK_TYPE_BOOT 0 +#define BLOCK_TYPE_CONF 1 +#define BLOCK_TYPE_WEBP 2 +#define BLOCK_TYPE_CODE 3 +#define BLOCK_TYPE_XTRA 4 + +#define DEFAULT_BLOCK_ALIGN 0x10000U + +#define CSUM_SIZE_NONE 0 +#define CSUM_SIZE_8 1 +#define CSUM_SIZE_16 2 + + +struct csum_state{ + int size; + uint16_t val; + uint16_t tmp; + int odd; +}; + + +struct csys_block { + int type; /* type of the block */ + + int need_file; + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ + + unsigned char sig[SIG_LEN]; + uint32_t addr; + int addr_set; + uint32_t align; + int align_set; + uint8_t padc; + + uint32_t size; + uint32_t size_hdr; + uint32_t size_csum; + uint32_t size_avail; + + struct csum_state *css; +}; + + +struct board_info { + char *model; + char *name; + uint32_t flash_size; + + char sig_boot[SIG_LEN]; + char sig_conf[SIG_LEN]; + char sig_webp[SIG_LEN]; + + uint32_t boot_size; + uint32_t conf_size; + uint32_t webp_size; + uint32_t webp_size_max; + uint32_t code_size; + + uint32_t addr_code; + uint32_t addr_webp; +}; + +#define BOARD(m, n, f, sigb, sigw, bs, cs, ws, ac, aw) {\ + .model = m, .name = n, .flash_size = f<<20, \ + .sig_boot = sigb, .sig_conf = SIG_CONF, .sig_webp = sigw, \ + .boot_size = bs, .conf_size = cs, \ + .webp_size = ws, .webp_size_max = 3*0x10000, \ + .addr_code = ac, .addr_webp = aw \ + } + +#define BOARD_ADM(m,n,f, sigw) BOARD(m,n,f, ADM_BOOT_SIG, sigw, \ + ADM_BOOT_SIZE, ADM_CONF_SIZE, ADM_WEBP_SIZE, \ + ADM_CODE_ADDR, ADM_WEBP_ADDR) + + +/* + * Globals + */ +char *progname; +char *ofname = NULL; +int verblevel = 0; +int invalid_causes_error = 1; +int keep_invalid_images = 0; + +struct board_info *board = NULL; + +struct csys_block *boot_block = NULL; +struct csys_block *conf_block = NULL; +struct csys_block *webp_block = NULL; +struct csys_block *code_block = NULL; + +struct csys_block blocks[MAX_NUM_BLOCKS]; +int num_blocks = 0; + +static struct board_info boards[] = { + /* The original Edimax products */ + BOARD_ADM("BR-6104K", "Edimax BR-6104K", 2, SIG_BR6104K), + BOARD_ADM("BR-6104KP", "Edimax BR-6104KP", 2, SIG_BR6104KP), + BOARD_ADM("BR-6104Wg", "Edimax BR-6104Wg", 2, SIG_BR6104Wg), + 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), + BOARD_ADM("BR-6541KP", "Edimax BR-6541K", 2, SIG_BR6541KP), + BOARD_ADM("BR-6541WP", "Edimax BR-6541WP", 4, SIG_BR6541WP), + BOARD_ADM("EW-7207APg", "Edimax EW-7207APg", 2, SIG_EW7207APg), + BOARD_ADM("PS-1205UWg", "Edimax PS-1205UWg", 2, SIG_PS1205UWg), + BOARD_ADM("PS-3205U", "Edimax PS-3205U", 2, SIG_PS3205U), + BOARD_ADM("PS-3205UWg", "Edimax PS-3205UWg", 2, SIG_PS3205UWg), + + /* Hawking products */ + BOARD_ADM("H2BR4", "Hawking H2BR4", 2, SIG_H2BR4), + BOARD_ADM("H2WR54G", "Hawking H2WR54G", 4, SIG_H2WR54G), + + /* Planet products */ + BOARD_ADM("XRT-401D", "Planet XRT-401D", 2, SIG_XRT401D), + BOARD_ADM("XRT-402D", "Planet XRT-402D", 2, SIG_XRT402D), + + /* Conceptronic products */ + BOARD_ADM("C54BSR4", "Conceptronic C54BSR4", 2, SIG_C54BSR4), + + /* OSBRiDGE products */ + BOARD_ADM("5GXi", "OSBDRiDGE 5GXi", 2, SIG_5GXI), + + {.model = NULL} +}; + +/* + * 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 WARN(fmt, ...) do { \ + fprintf(stderr, "[%s] *** warning: " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define DBG(lev, fmt, ...) do { \ + if (verblevel < lev) \ + break;\ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERR_FATAL -1 +#define ERR_INVALID_IMAGE -2 + +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" +" -B create image for the board specified with .\n" +" valid values:\n" + ); + for (board = boards; board->model != NULL; board++){ + fprintf(stream, +" %-12s: %s\n", + board->model, board->name); + }; + fprintf(stream, +" -d don't throw error on invalid images\n" +" -k keep invalid images\n" +" -b [:[:]]\n" +" add boot code to the image\n" +" -c [:[:]]\n" +" add configuration settings to the image\n" +" -r :[][:[:]]\n" +" add runtime code to the image\n" +" -w [:[][:[:]]]\n" +" add webpages to the image\n" +" -x [:[:]]\n" +" add extra data at the end of the image\n" +" -h show this screen\n" +"Parameters:\n" +" write output to the file \n" + ); + + exit(status); +} + +static inline uint32_t align(uint32_t base, uint32_t alignment) +{ + uint32_t ret; + + if (alignment) { + ret = (base + alignment - 1); + ret &= ~(alignment-1); + } else { + ret = base; + } + + return ret; +} + +/* + * argument parsing + */ +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 +str2u16(char *arg, uint16_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { + return -1; + } + + *val = t & 0xFFFF; + 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; +} + +int +str2sig(char *arg, uint32_t *sig) +{ + if (strlen(arg) != 4) + return -1; + + *sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24); + + return 0; +} + + +int +parse_arg(char *arg, char *buf, char *argv[]) +{ + int res = 0; + size_t argl; + char *tok; + char **ap = &buf; + int i; + + memset(argv, 0, MAX_ARG_COUNT * sizeof(void *)); + + if ((arg == NULL)) { + /* no arguments */ + return 0; + } + + argl = strlen(arg); + if (argl == 0) { + /* no arguments */ + return 0; + } + + if (argl >= MAX_ARG_LEN) { + /* argument is too long */ + argl = MAX_ARG_LEN-1; + } + + memcpy(buf, arg, argl); + buf[argl] = '\0'; + + for (i = 0; i < MAX_ARG_COUNT; i++) { + tok = strsep(ap, ":"); + if (tok == NULL) { + break; + } +#if 0 + else if (tok[0] == '\0') { + break; + } +#endif + argv[i] = tok; + res++; + } + + return res; +} + + +int +required_arg(char c, char *arg) +{ + if (arg == NULL || *arg != '-') + return 0; + + ERR("option -%c requires an argument\n", c); + return ERR_FATAL; +} + + +int +is_empty_arg(char *arg) +{ + int ret = 1; + if (arg != NULL) { + if (*arg) ret = 0; + }; + return ret; +} + + +void +csum8_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + for ( ; len > 0; len --) { + css->val += *p++; + } +} + + +uint16_t +csum8_get(struct csum_state *css) +{ + uint8_t t; + + t = css->val; + return ~t + 1; +} + + +void +csum16_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + uint16_t t; + + if (css->odd) { + t = css->tmp + (p[0]<<8); + css->val += LE16_TO_HOST(t); + css->odd = 0; + len--; + p++; + } + + for ( ; len > 1; len -= 2, p +=2 ) { + t = p[0] + (p[1] << 8); + css->val += LE16_TO_HOST(t); + } + + if (len == 1) { + css->tmp = p[0]; + css->odd = 1; + } +} + + +uint16_t +csum16_get(struct csum_state *css) +{ + char pad = 0; + + csum16_update(&pad, 1, css); + return ~css->val + 1; +} + + +void +csum_init(struct csum_state *css, int size) +{ + css->val = 0; + css->tmp = 0; + css->odd = 0; + css->size = size; +} + + +void +csum_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + switch (css->size) { + case CSUM_SIZE_8: + csum8_update(p,len,css); + break; + case CSUM_SIZE_16: + csum16_update(p,len,css); + break; + } +} + + +uint16_t +csum_get(struct csum_state *css) +{ + uint16_t ret; + + switch (css->size) { + case CSUM_SIZE_8: + ret = csum8_get(css); + break; + case CSUM_SIZE_16: + ret = csum16_get(css); + break; + } + + return ret; +} + + +/* + * routines to write data to the output file + */ +int +write_out_data(FILE *outfile, uint8_t *data, size_t len, + struct csum_state *css) +{ + errno = 0; + + fwrite(data, len, 1, outfile); + if (errno) { + ERRS("unable to write output file"); + return ERR_FATAL; + } + + if (css) { + csum_update(data, len, css); + } + + return 0; +} + + +int +write_out_padding(FILE *outfile, size_t len, uint8_t padc, + struct csum_state *css) +{ + uint8_t buf[512]; + size_t buflen = sizeof(buf); + int err; + + memset(buf, padc, buflen); + while (len > 0) { + if (len < buflen) + buflen = len; + + err = write_out_data(outfile, buf, buflen, css); + if (err) + return err; + + len -= buflen; + } + + return 0; +} + + +int +block_stat_file(struct csys_block *block) +{ + struct stat st; + int err; + + if (block->file_name == NULL) + return 0; + + err = stat(block->file_name, &st); + if (err){ + ERRS("stat failed on %s", block->file_name); + return ERR_FATAL; + } + + block->file_size = st.st_size; + return 0; +} + + +int +block_writeout_hdr(FILE *outfile, struct csys_block *block) +{ + struct csys_header hdr; + int res; + + if (block->size_hdr == 0) + return 0; + + /* setup header fields */ + memcpy(hdr.sig, block->sig, 4); + hdr.addr = HOST_TO_LE32(block->addr); + hdr.size = HOST_TO_LE32(block->size - block->size_hdr - block->size_csum); + + DBG(1,"writing header for block"); + res = write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr),NULL); + return res; + +} + + +int +block_writeout_file(FILE *outfile, struct csys_block *block) +{ + char buf[FILE_BUF_LEN]; + size_t buflen = sizeof(buf); + FILE *f; + size_t len; + int res; + + if (block->file_name == NULL) + return 0; + + if (block->file_size == 0) + return 0; + + errno = 0; + f = fopen(block->file_name,"r"); + if (errno) { + ERRS("unable to open file: %s", block->file_name); + return ERR_FATAL; + } + + len = block->file_size; + while (len > 0) { + if (len < buflen) + buflen = len; + + /* read data from source file */ + errno = 0; + fread(buf, buflen, 1, f); + if (errno != 0) { + ERRS("unable to read from file: %s", block->file_name); + res = ERR_FATAL; + break; + } + + res = write_out_data(outfile, buf, buflen, block->css); + if (res) + break; + + len -= buflen; + } + + fclose(f); + return res; +} + + +int +block_writeout_data(FILE *outfile, struct csys_block *block) +{ + int res; + size_t padlen; + + res = block_writeout_file(outfile, block); + if (res) + return res; + + /* write padding data if neccesary */ + padlen = block->size_avail - block->file_size; + DBG(1,"padding block, length=%d", padlen); + res = write_out_padding(outfile, padlen, block->padc, block->css); + + return res; +} + + +int +block_writeout_csum(FILE *outfile, struct csys_block *block) +{ + uint16_t csum; + int res; + + if (block->size_csum == 0) + return 0; + + DBG(1,"writing checksum for block"); + csum = HOST_TO_LE16(csum_get(block->css)); + res = write_out_data(outfile, (uint8_t *)&csum, block->size_csum, NULL); + + return res; +} + + +int +block_writeout(FILE *outfile, struct csys_block *block) +{ + int res; + struct csum_state css; + + res = 0; + + if (block == NULL) + return res; + + block->css = NULL; + + DBG(2, "writing block, file=%s, file_size=%d, space=%d", + block->file_name, block->file_size, block->size_avail); + res = block_writeout_hdr(outfile, block); + if (res) + return res; + + if (block->size_csum != 0) { + block->css = &css; + csum_init(&css, block->size_csum); + } + + res = block_writeout_data(outfile, block); + if (res) + return res; + + res = block_writeout_csum(outfile, block); + if (res) + return res; + + return res; +} + + +int +write_out_blocks(FILE *outfile) +{ + struct csys_block *block; + int i, res; + + res = block_writeout(outfile, boot_block); + if (res) + return res; + + res = block_writeout(outfile, conf_block); + if (res) + return res; + + res = block_writeout(outfile, webp_block); + if (res) + return res; + + res = block_writeout(outfile, code_block); + if (res) + return res; + + res = 0; + for (i=0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type != BLOCK_TYPE_XTRA) + continue; + + res = block_writeout(outfile, block); + if (res) + break; + } + + return res; +} + + +struct board_info * +find_board(char *model) +{ + struct board_info *ret; + struct board_info *board; + + ret = NULL; + for (board = boards; board->model != NULL; board++){ + if (strcasecmp(model, board->model) == 0) { + ret = board; + break; + } + }; + + return ret; +} + + +int +parse_opt_board(char ch, char *arg) +{ + + DBG(1,"parsing board option: -%c %s", ch, arg); + + if (board != NULL) { + ERR("only one board option allowed"); + return ERR_FATAL; + } + + if (required_arg(ch, arg)) + return ERR_FATAL; + + board = find_board(arg); + if (board == NULL){ + ERR("invalid/unknown board specified: %s", arg); + return ERR_FATAL; + } + + return 0; +} + + +int +parse_opt_block(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + struct csys_block *block; + int i; + + if ( num_blocks > MAX_NUM_BLOCKS ) { + ERR("too many blocks specified"); + return ERR_FATAL; + } + + block = &blocks[num_blocks]; + + /* setup default field values */ + block->need_file = 1; + block->padc = 0xFF; + + switch (ch) { + case 'b': + if (boot_block) { + WARN("only one boot block allowed"); + break; + } + block->type = BLOCK_TYPE_BOOT; + boot_block = block; + break; + case 'c': + if (conf_block) { + WARN("only one config block allowed"); + break; + } + block->type = BLOCK_TYPE_CONF; + conf_block = block; + break; + case 'w': + if (webp_block) { + WARN("only one web block allowed"); + break; + } + block->type = BLOCK_TYPE_WEBP; + block->size_hdr = sizeof(struct csys_header); + block->size_csum = CSUM_SIZE_8; + block->need_file = 0; + webp_block = block; + break; + case 'r': + if (code_block) { + WARN("only one runtime block allowed"); + break; + } + block->type = BLOCK_TYPE_CODE; + block->size_hdr = sizeof(struct csys_header); + block->size_csum = CSUM_SIZE_16; + code_block = block; + break; + case 'x': + block->type = BLOCK_TYPE_XTRA; + break; + default: + ERR("unknown block type \"%c\"", ch); + return ERR_FATAL; + } + + argc = parse_arg(arg, buf, argv); + + i = 0; + p = argv[i++]; + if (!is_empty_arg(p)) { + block->file_name = strdup(p); + if (block->file_name == NULL) { + ERR("not enough memory"); + return ERR_FATAL; + } + } else if (block->need_file){ + ERR("no file specified in %s", arg); + return ERR_FATAL; + } + + if (block->size_hdr) { + p = argv[i++]; + if (!is_empty_arg(p)) { + if (str2u32(p, &block->addr) != 0) { + ERR("invalid start address in %s", arg); + return ERR_FATAL; + } + block->addr_set = 1; + } + } + + p = argv[i++]; + if (!is_empty_arg(p)) { + if (str2u32(p, &block->align) != 0) { + ERR("invalid alignment value in %s", arg); + return ERR_FATAL; + } + block->align_set = 1; + } + + p = argv[i++]; + if (!is_empty_arg(p) && (str2u8(p, &block->padc) != 0)) { + ERR("invalid paddig character in %s", arg); + return ERR_FATAL; + } + + num_blocks++; + + return 0; +} + + +int +process_blocks(void) +{ + struct csys_block *block; + uint32_t offs = 0; + int i; + int res; + + res = 0; + /* collecting stats */ + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + res = block_stat_file(block); + if (res) + return res; + } + + /* bootloader */ + block = boot_block; + if (block) { + block->size = board->boot_size; + if (block->file_size > board->boot_size) { + WARN("boot block is too big"); + res = ERR_INVALID_IMAGE; + } + } + offs += board->boot_size; + + /* configuration data */ + block = conf_block; + if (block) { + block->size = board->conf_size; + if (block->file_size > board->conf_size) { + WARN("config block is too big"); + res = ERR_INVALID_IMAGE; + } + } + offs += board->conf_size; + + /* webpages */ + block = webp_block; + if (block) { + + memcpy(block->sig, board->sig_webp, 4); + + if (block->addr_set == 0) + block->addr = board->addr_webp; + + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; + + block->size = align(offs + block->file_size + block->size_hdr + + block->size_csum, block->align) - offs; + + if (block->size > board->webp_size_max) { + WARN("webpages block is too big"); + res = ERR_INVALID_IMAGE; + } + + DBG(2,"webpages start at %08x, size=%08x", offs, + block->size); + + offs += block->size; + if (offs > board->flash_size) { + WARN("webp block is too big"); + res = ERR_INVALID_IMAGE; + } + } + + /* runtime code */ + block = code_block; + if (block) { + memcpy(code_block->sig, SIG_CSYS, 4); + + if (block->addr_set == 0) + block->addr = board->addr_code; + + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; + + block->size = align(offs + block->file_size + + block->size_hdr + block->size_csum, + block->align) - offs; + + DBG(2,"code block start at %08x, size=%08x", offs, + block->size); + + offs += block->size; + if (offs > board->flash_size) { + WARN("code block is too big"); + res = ERR_INVALID_IMAGE; + } + } + + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type != BLOCK_TYPE_XTRA) + continue; + + if (block->align_set == 0) + block->align = DEFAULT_BLOCK_ALIGN; + + block->size = align(offs + block->file_size, + block->align) - offs; + + DBG(2,"file %s start at %08x, size=%08x, align=%08x", + block->file_name, offs, block->size, block->align); + + offs += block->size; + if (offs > board->flash_size) { + WARN("file %s is too big, size=%d, avail=%d", + block->file_name, block->file_size, + board->flash_size - offs); + res = ERR_INVALID_IMAGE; + } + } + + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + + block->size_avail = block->size - block->size_hdr - + block->size_csum; + + if (block->size_avail < block->file_size) { + WARN("file %s is too big, size=%d, avail=%d", + block->file_name, block->file_size, + block->size_avail); + res = ERR_INVALID_IMAGE; + } + } + + return res; +} + + +int +main(int argc, char *argv[]) +{ + int optinvalid = 0; /* flag for invalid option */ + int c; + int res = ERR_FATAL; + + FILE *outfile; + + progname=basename(argv[0]); + + opterr = 0; /* could not print standard getopt error messages */ + while ( 1 ) { + optinvalid = 0; + + c = getopt(argc, argv, "b:B:c:dhkr:vw:x:"); + if (c == -1) + break; + + switch (c) { + case 'b': + case 'c': + case 'r': + case 'x': + optinvalid = parse_opt_block(c,optarg); + break; + case 'w': + if (optarg != NULL && *optarg == '-') { + /* rollback */ + optind--; + optarg = NULL; + } + optinvalid = parse_opt_block(c,optarg); + break; + case 'd': + invalid_causes_error = 0; + break; + case 'k': + keep_invalid_images = 1; + break; + case 'B': + optinvalid = parse_opt_board(c,optarg); + break; + case 'v': + verblevel++; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + optinvalid = 1; + break; + } + if (optinvalid != 0 ){ + ERR("invalid option: -%c", optopt); + goto out; + } + } + + if (board == NULL) { + ERR("no board specified"); + goto out; + } + + if (optind == argc) { + ERR("no output file specified"); + goto out; + } + + ofname = argv[optind++]; + + if (optind < argc) { + ERR("invalid option: %s", argv[optind]); + goto out; + } + + res = process_blocks(); + if (res == ERR_FATAL) + goto out; + + if (res == ERR_INVALID_IMAGE) { + if (invalid_causes_error) + res = ERR_FATAL; + + if (keep_invalid_images == 0) { + WARN("generation of invalid images disabled", ofname); + goto out; + } + + WARN("generating invalid image", ofname); + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + res = ERR_FATAL; + goto out; + } + + if (write_out_blocks(outfile) != 0) { + res = ERR_FATAL; + goto out_flush; + } + + DBG(1,"Image file %s completed.", ofname); + +out_flush: + fflush(outfile); + fclose(outfile); + if (res == ERR_FATAL) { + unlink(ofname); + } +out: + if (res == ERR_FATAL) + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} diff --git a/trunk/tools/firmware-utils/src/mkdapimg.c b/trunk/tools/firmware-utils/src/mkdapimg.c new file mode 100644 index 00000000..ed662d8e --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkdapimg.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // htonl + +// Usage: mkdapimg [-p] [-m ] -s -i -o +// +// e.g.: mkdapimg -s RT3052-AP-DAP1350-3 -i sysupgarde.bin -o factory.bin +// +// If the model string 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/trunk/tools/firmware-utils/src/mkdcs932.c b/trunk/tools/firmware-utils/src/mkdcs932.c new file mode 100644 index 00000000..28c67aa3 --- /dev/null +++ b/trunk/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 + */ + +#include +#include +#include +#include +#include +#include +#include + +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/trunk/tools/firmware-utils/src/mkdniimg.c b/trunk/tools/firmware-utils/src/mkdniimg.c new file mode 100644 index 00000000..7230f09d --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkdniimg.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#define DNI_HDR_LEN 128 + +/* + * Globals + */ +static char *ifname; +static char *progname; +static char *ofname; +static char *version = "1.00.00"; +static char *region = ""; +static char *hd_id; + +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 "\n", \ + progname, ## __VA_ARGS__, strerror(save)); \ +} while (0) + +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" +" -B create image for the board specified with \n" +" -i read input from the file \n" +" -o write output to the file \n" +" -v set image version to \n" +" -r set image region to \n" +" -H set image hardware id to \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 pos, rem, i; + uint8_t csum; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "B:i:o:v:r:H: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 'H': + hd_id = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (board_id == NULL) { + ERR("no board 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); + pos = snprintf(buf, DNI_HDR_LEN, "device:%s\nversion:V%s\nregion:%s\n", + board_id, version, region); + rem = DNI_HDR_LEN - pos; + if (pos >= 0 && rem > 1 && hd_id) { + snprintf(buf + pos, rem, "hd_id:%s\n", hd_id); + } + + 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; + + out_flush: + 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/trunk/tools/firmware-utils/src/mkedimaximg.c b/trunk/tools/firmware-utils/src/mkedimaximg.c new file mode 100644 index 00000000..d8a017e6 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkedimaximg.c @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2011 Vasilis Tsiligiannis + * + * 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 +#include +#include +#include +#include +#include +#include +#include /* for __BYTE_ORDER */ + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +#else +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +#endif + +struct header +{ + unsigned char sign[4]; + unsigned int start; + unsigned int flash; + unsigned char model[4]; + unsigned int size; +} __attribute__ ((packed)); + +struct finfo +{ + char *name; + off_t size; +}; + +struct buf +{ + char *start; + size_t size; +}; + +static char *progname; + +static void usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + + fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); + fprintf(stream, + "\n" + "Options:\n" + " -s set image signature to \n" + " -m set model to \n" + " -i read input from file \n" + " -o write output to file \n" + " -f set flash address to \n" + " -S set start address to \n"); + + exit(status); +} + +static int strtou32(char *arg, unsigned int *val) +{ + char *endptr = NULL; + + errno = 0; + *val = strtoul(arg, &endptr, 0); + if (errno || (endptr == arg) || (*endptr && (endptr != NULL))) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + +static unsigned short fwcsum (struct buf *buf) { + int i; + unsigned short ret = 0; + + for (i = 0; i < buf->size / 2; i++) + ret -= ((unsigned short *) buf->start)[i]; + + return ret; +} + +static int fwread(struct finfo *finfo, struct buf *buf) +{ + FILE *f; + + f = fopen(finfo->name, "r"); + if (!f) { + fprintf(stderr, "could not open \"%s\" for reading\n", finfo->name); + usage(EXIT_FAILURE); + } + + buf->size = fread(buf->start, 1, finfo->size, f); + if (buf->size != finfo->size) { + fprintf(stderr, "unable to read from file \"%s\"\n", finfo->name); + usage(EXIT_FAILURE); + } + + fclose(f); + + return EXIT_SUCCESS; +} + +static int fwwrite(struct finfo *finfo, struct buf *buf) +{ + FILE *f; + + f = fopen(finfo->name, "w"); + if (!f) { + fprintf(stderr, "could not open \"%s\" for writing\n", finfo->name); + usage(EXIT_FAILURE); + } + + buf->size = fwrite(buf->start, 1, finfo->size, f); + if (buf->size != finfo->size) { + fprintf(stderr, "unable to write to file \"%s\"\n", finfo->name); + usage(EXIT_FAILURE); + } + + fclose(f); + + return EXIT_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct stat st; + struct header header; + struct buf ibuf, obuf; + struct finfo ifinfo, ofinfo; + unsigned short csum; + int c; + + ifinfo.name = ofinfo.name = NULL; + header.flash = header.size = header.start = 0; + progname = basename(argv[0]); + + while((c = getopt(argc, argv, "i:o:m:s:f:S:h")) != -1) { + switch (c) { + case 'i': + ifinfo.name = optarg; + break; + case 'o': + ofinfo.name = optarg; + break; + case 'm': + if (strlen(optarg) != 4) { + fprintf(stderr, "model must be 4 characters long\n"); + usage(EXIT_FAILURE); + } + memcpy(header.model, optarg, 4); + break; + case 's': + if (strlen(optarg) != 4) { + fprintf(stderr, "signature must be 4 characters long\n"); + usage(EXIT_FAILURE); + } + memcpy(header.sign, optarg, 4); + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case 'f': + if (!strtou32(optarg, &header.flash)) { + fprintf(stderr, "invalid flash address specified\n"); + usage(EXIT_FAILURE); + } + break; + case 'S': + if (!strtou32(optarg, &header.start)) { + fprintf(stderr, "invalid start address specified\n"); + usage(EXIT_FAILURE); + } + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if (ifinfo.name == NULL) { + fprintf(stderr, "no input file specified\n"); + usage(EXIT_FAILURE); + } + + if (ofinfo.name == NULL) { + fprintf(stderr, "no output file specified\n"); + usage(EXIT_FAILURE); + } + + if (stat(ifinfo.name, &st)) { + fprintf(stderr, "stat failed on %s\n", ifinfo.name); + usage(EXIT_FAILURE); + } + + if (header.sign == NULL) { + fprintf(stderr, "no signature specified\n"); + usage(EXIT_FAILURE); + } + + if (header.model == NULL) { + fprintf(stderr, "no model specified\n"); + usage(EXIT_FAILURE); + } + + if (!header.flash) { + fprintf(stderr, "no flash address specified\n"); + usage(EXIT_FAILURE); + } + + if (!header.start) { + fprintf(stderr, "no start address specified\n"); + usage(EXIT_FAILURE); + } + + ifinfo.size = st.st_size; + + obuf.size = ifinfo.size + sizeof(struct header) + sizeof(unsigned short); + if (obuf.size % sizeof(unsigned short)) + obuf.size++; + + obuf.start = malloc(obuf.size); + if (!obuf.start) { + fprintf(stderr, "no memory for buffer\n"); + usage(EXIT_FAILURE); + } + memset(obuf.start, 0, obuf.size); + + ibuf.size = ifinfo.size; + ibuf.start = obuf.start + sizeof(struct header); + + if (fwread(&ifinfo, &ibuf)) + usage(EXIT_FAILURE); + + header.flash = HOST_TO_LE32(header.flash); + header.size = HOST_TO_LE32(obuf.size - sizeof(struct header)); + header.start = HOST_TO_LE32(header.start); + memcpy (obuf.start, &header, sizeof(struct header)); + + csum = HOST_TO_LE16(fwcsum(&ibuf)); + memcpy(obuf.start + obuf.size - sizeof(unsigned short), + &csum, sizeof(unsigned short)); + + ofinfo.size = obuf.size; + + if (fwwrite(&ofinfo, &obuf)) + usage(EXIT_FAILURE); + + return EXIT_SUCCESS; +} diff --git a/trunk/tools/firmware-utils/src/mkfwimage.c b/trunk/tools/firmware-utils/src/mkfwimage.c new file mode 100644 index 00000000..e3a03c19 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkfwimage.c @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2007 Ubiquiti Networks, Inc. + * Copyright (C) 2008 Lukas Kuna + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fw.h" + +typedef struct fw_layout_data { + char name[PATH_MAX]; + u_int32_t kern_start; + u_int32_t kern_entry; + u_int32_t firmware_max_length; +} fw_layout_t; + +fw_layout_t fw_layout_data[] = { + { + .name = "XS2", + .kern_start = 0xbfc30000, + .kern_entry = 0x80041000, + .firmware_max_length= 0x00390000, + }, + { + .name = "XS5", + .kern_start = 0xbe030000, + .kern_entry = 0x80041000, + .firmware_max_length= 0x00390000, + }, + { + .name = "RS", + .kern_start = 0xbf030000, + .kern_entry = 0x80060000, + .firmware_max_length= 0x00B00000, + }, + { + .name = "RSPRO", + .kern_start = 0xbf030000, + .kern_entry = 0x80060000, + .firmware_max_length= 0x00F00000, + }, + { + .name = "LS-SR71", + .kern_start = 0xbf030000, + .kern_entry = 0x80060000, + .firmware_max_length= 0x00640000, + }, + { + .name = "XS2-8", + .kern_start = 0xa8030000, + .kern_entry = 0x80041000, + .firmware_max_length= 0x006C0000, + }, + { + .name = "XM", + .kern_start = 0x9f050000, + .kern_entry = 0x80002000, + .firmware_max_length= 0x006A0000, + }, + { .name = "", + }, +}; + +typedef struct part_data { + char partition_name[64]; + int partition_index; + u_int32_t partition_baseaddr; + u_int32_t partition_startaddr; + u_int32_t partition_memaddr; + u_int32_t partition_entryaddr; + u_int32_t partition_length; + + char filename[PATH_MAX]; + struct stat stats; +} part_data_t; + +#define MAX_SECTIONS 8 +#define DEFAULT_OUTPUT_FILE "firmware-image.bin" +#define DEFAULT_VERSION "UNKNOWN" + +#define OPTIONS "B:hv:m:o:r:k:" + +static int debug = 0; + +typedef struct image_info { + char magic[16]; + char version[256]; + char outputfile[PATH_MAX]; + u_int32_t part_count; + part_data_t parts[MAX_SECTIONS]; +} image_info_t; + +static void write_header(void* mem, const char *magic, const char* version) +{ + header_t* header = mem; + memset(header, 0, sizeof(header_t)); + + memcpy(header->magic, magic, MAGIC_LENGTH); + strncpy(header->version, version, sizeof(header->version)); + header->crc = htonl(crc32(0L, (unsigned char *)header, + sizeof(header_t) - 2 * sizeof(u_int32_t))); + header->pad = 0L; +} + + +static void write_signature(void* mem, u_int32_t sig_offset) +{ + /* write signature */ + signature_t* sign = (signature_t*)(mem + sig_offset); + memset(sign, 0, sizeof(signature_t)); + + memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH); + sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); + sign->pad = 0L; +} + +static int write_part(void* mem, part_data_t* d) +{ + char* addr; + int fd; + part_t* p = mem; + part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size; + + fd = open(d->filename, O_RDONLY); + if (fd < 0) + { + ERROR("Failed opening file '%s'\n", d->filename); + return -1; + } + + if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) + { + ERROR("Failed mmaping memory for file '%s'\n", d->filename); + close(fd); + return -2; + } + + memcpy(mem + sizeof(part_t), addr, d->stats.st_size); + munmap(addr, d->stats.st_size); + + memset(p->name, 0, sizeof(p->name)); + strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH); + strncpy(p->name, d->partition_name, sizeof(p->name)); + p->index = htonl(d->partition_index); + p->data_size = htonl(d->stats.st_size); + p->part_size = htonl(d->partition_length); + p->baseaddr = htonl(d->partition_baseaddr); + p->memaddr = htonl(d->partition_memaddr); + p->entryaddr = htonl(d->partition_entryaddr); + + crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t))); + crc->pad = 0L; + + return 0; +} + +static void usage(const char* progname) +{ + INFO("Version %s\n" + "Usage: %s [options]\n" + "\t-v \t - firmware version information, default: %s\n" + "\t-o \t - firmware output file, default: %s\n" + "\t-m \t - firmware magic, default: %s\n" + "\t-k \t\t - kernel file\n" + "\t-r \t\t - rootfs file\n" + "\t-B \t\t - choose firmware layout for specified board (XS2, XS5, RS, XM)\n" + "\t-h\t\t\t - this help\n", VERSION, + progname, DEFAULT_VERSION, DEFAULT_OUTPUT_FILE, MAGIC_HEADER); +} + +static void print_image_info(const image_info_t* im) +{ + int i = 0; + INFO("Firmware version: '%s'\n" + "Output file: '%s'\n" + "Part count: %u\n", + im->version, im->outputfile, + im->part_count); + + for (i = 0; i < im->part_count; ++i) + { + const part_data_t* d = &im->parts[i]; + INFO(" %10s: %8ld bytes (free: %8ld)\n", + d->partition_name, + d->stats.st_size, + d->partition_length - d->stats.st_size); + } +} + + + +static u_int32_t filelength(const char* file) +{ + FILE *p; + int ret = -1; + + if ( (p = fopen(file, "rb") ) == NULL) return (-1); + + fseek(p, 0, SEEK_END); + ret = ftell(p); + + fclose (p); + + return (ret); +} + +static int create_image_layout(const char* kernelfile, const char* rootfsfile, char* board_name, image_info_t* im) +{ + part_data_t* kernel = &im->parts[0]; + part_data_t* rootfs = &im->parts[1]; + + fw_layout_t* p; + + p = &fw_layout_data[0]; + while ((strlen(p->name) != 0) && (strncmp(p->name, board_name, sizeof(board_name)) != 0)) + p++; + if (p->name == NULL) { + printf("BUG! Unable to find default fw layout!\n"); + exit(-1); + } + + printf("board = %s\n", p->name); + strcpy(kernel->partition_name, "kernel"); + kernel->partition_index = 1; + kernel->partition_baseaddr = p->kern_start; + if ( (kernel->partition_length = filelength(kernelfile)) < 0) return (-1); + kernel->partition_memaddr = p->kern_entry; + kernel->partition_entryaddr = p->kern_entry; + strncpy(kernel->filename, kernelfile, sizeof(kernel->filename)); + + if (filelength(rootfsfile) + kernel->partition_length > p->firmware_max_length) + return (-2); + + strcpy(rootfs->partition_name, "rootfs"); + rootfs->partition_index = 2; + rootfs->partition_baseaddr = kernel->partition_baseaddr + kernel->partition_length; + rootfs->partition_length = p->firmware_max_length - kernel->partition_length; + rootfs->partition_memaddr = 0x00000000; + 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); + im->part_count = 2; + + return 0; +} + +/** + * Checks the availability and validity of all image components. + * Fills in stats member of the part_data structure. + */ +static int validate_image_layout(image_info_t* im) +{ + int i; + + if (im->part_count == 0 || im->part_count > MAX_SECTIONS) + { + ERROR("Invalid part count '%d'\n", im->part_count); + return -1; + } + + for (i = 0; i < im->part_count; ++i) + { + part_data_t* d = &im->parts[i]; + int len = strlen(d->partition_name); + if (len == 0 || len > 16) + { + ERROR("Invalid partition name '%s' of the part %d\n", + d->partition_name, i); + return -1; + } + if (stat(d->filename, &d->stats) < 0) + { + ERROR("Couldn't stat file '%s' from part '%s'\n", + d->filename, d->partition_name); + return -2; + } + if (d->stats.st_size == 0) + { + ERROR("File '%s' from part '%s' is empty!\n", + d->filename, d->partition_name); + return -3; + } + if (d->stats.st_size > d->partition_length) { + ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n", + d->filename, i, d->partition_length, + d->stats.st_size - d->partition_length); + return -4; + } + } + + return 0; +} + +static int build_image(image_info_t* im) +{ + char* mem; + char* ptr; + u_int32_t mem_size; + FILE* f; + int i; + + // build in-memory buffer + mem_size = sizeof(header_t) + sizeof(signature_t); + for (i = 0; i < im->part_count; ++i) + { + part_data_t* d = &im->parts[i]; + mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); + } + + mem = (char*)calloc(mem_size, 1); + if (mem == NULL) + { + ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size); + return -1; + } + + // write header + write_header(mem, im->magic, im->version); + ptr = mem + sizeof(header_t); + // write all parts + for (i = 0; i < im->part_count; ++i) + { + part_data_t* d = &im->parts[i]; + int rc; + if ((rc = write_part(ptr, d)) != 0) + { + ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name); + } + ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); + } + // write signature + write_signature(mem, mem_size - sizeof(signature_t)); + + // write in-memory buffer into file + if ((f = fopen(im->outputfile, "w")) == NULL) + { + ERROR("Can not create output file: '%s'\n", im->outputfile); + return -10; + } + + if (fwrite(mem, mem_size, 1, f) != 1) + { + ERROR("Could not write %d bytes into file: '%s'\n", + mem_size, im->outputfile); + return -11; + } + + free(mem); + fclose(f); + return 0; +} + + +int main(int argc, char* argv[]) +{ + char kernelfile[PATH_MAX]; + char rootfsfile[PATH_MAX]; + char board_name[PATH_MAX]; + int o, rc; + image_info_t im; + + memset(&im, 0, sizeof(im)); + memset(kernelfile, 0, sizeof(kernelfile)); + memset(rootfsfile, 0, sizeof(rootfsfile)); + memset(board_name, 0, sizeof(board_name)); + + strcpy(im.outputfile, DEFAULT_OUTPUT_FILE); + strcpy(im.version, DEFAULT_VERSION); + strncpy(im.magic, MAGIC_HEADER, sizeof(im.magic)); + + while ((o = getopt(argc, argv, OPTIONS)) != -1) + { + switch (o) { + case 'v': + if (optarg) + strncpy(im.version, optarg, sizeof(im.version)); + break; + case 'o': + if (optarg) + strncpy(im.outputfile, optarg, sizeof(im.outputfile)); + break; + case 'm': + if (optarg) + strncpy(im.magic, optarg, sizeof(im.magic)); + break; + case 'h': + usage(argv[0]); + return -1; + case 'k': + if (optarg) + strncpy(kernelfile, optarg, sizeof(kernelfile)); + break; + case 'r': + if (optarg) + strncpy(rootfsfile, optarg, sizeof(rootfsfile)); + break; + case 'B': + if (optarg) + strncpy(board_name, optarg, sizeof(board_name)); + break; + } + } + if (strlen(board_name) == 0) + strcpy(board_name, "XS2"); /* default to XS2 */ + + if (strlen(kernelfile) == 0) + { + ERROR("Kernel file is not specified, cannot continue\n"); + usage(argv[0]); + return -2; + } + + if (strlen(rootfsfile) == 0) + { + ERROR("Root FS file is not specified, cannot continue\n"); + usage(argv[0]); + return -2; + } + + if ((rc = create_image_layout(kernelfile, rootfsfile, board_name, &im)) != 0) + { + ERROR("Failed creating firmware layout description - error code: %d\n", rc); + return -3; + } + + if ((rc = validate_image_layout(&im)) != 0) + { + ERROR("Failed validating firmware layout - error code: %d\n", rc); + return -4; + } + + print_image_info(&im); + + if ((rc = build_image(&im)) != 0) + { + ERROR("Failed building image file '%s' - error code: %d\n", im.outputfile, rc); + return -5; + } + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/mkfwimage2.c b/trunk/tools/firmware-utils/src/mkfwimage2.c new file mode 100644 index 00000000..970d6e20 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkfwimage2.c @@ -0,0 +1,447 @@ +/* + * Copyright (C) 2007 Ubiquiti Networks, Inc. + * Copyright (C) 2008 Lukas Kuna + * Copyright (C) 2008 Gabor Juhos + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fw.h" + +#undef VERSION +#define VERSION "1.2-libreCMC.1" + +#define MAX_SECTIONS 8 +#define DEFAULT_OUTPUT_FILE "firmware-image.bin" +#define DEFAULT_VERSION "UNKNOWN" +#define DEFAULT_FLASH_BASE (0xbfc00000) + +#define FIRMWARE_MAX_LENGTH (0x390000) + +typedef struct part_data { + char partition_name[64]; + int partition_index; + u_int32_t partition_baseaddr; + u_int32_t partition_offset; + u_int32_t partition_memaddr; + u_int32_t partition_entryaddr; + u_int32_t partition_length; + + char filename[PATH_MAX]; + struct stat stats; +} part_data_t; + +typedef struct image_info { + char version[256]; + char outputfile[PATH_MAX]; + char magic[MAGIC_LENGTH]; + u_int32_t flash_baseaddr; + u_int32_t part_count; + part_data_t parts[MAX_SECTIONS]; +} image_info_t; + +static image_info_t im; +static int debug = 0; +static int zero_part_baseaddr = 0; + +static void write_header(void* mem, const char* version) +{ + header_t* header = mem; + memset(header, 0, sizeof(header_t)); + + memcpy(header->magic, im.magic, MAGIC_LENGTH); + strncpy(header->version, version, sizeof(header->version)); + header->crc = htonl(crc32(0L, (unsigned char *)header, + sizeof(header_t) - 2 * sizeof(u_int32_t))); + header->pad = 0L; +} + +static void write_signature(void* mem, u_int32_t sig_offset) +{ + /* write signature */ + signature_t* sign = (signature_t*)(mem + sig_offset); + memset(sign, 0, sizeof(signature_t)); + + memcpy(sign->magic, MAGIC_END, MAGIC_LENGTH); + sign->crc = htonl(crc32(0L,(unsigned char *)mem, sig_offset)); + sign->pad = 0L; +} + +static int write_part(void* mem, part_data_t* d) +{ + char* addr; + int fd; + part_t* p = mem; + part_crc_t* crc = mem + sizeof(part_t) + d->stats.st_size; + + fd = open(d->filename, O_RDONLY); + if (fd < 0) { + ERROR("Failed opening file '%s'\n", d->filename); + return -1; + } + + if ((addr=(char*)mmap(0, d->stats.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) { + ERROR("Failed mmaping memory for file '%s'\n", d->filename); + close(fd); + return -2; + } + + memcpy(mem + sizeof(part_t), addr, d->stats.st_size); + munmap(addr, d->stats.st_size); + + memset(p->name, 0, sizeof(p->name)); + strncpy(p->magic, MAGIC_PART, MAGIC_LENGTH); + strncpy(p->name, d->partition_name, sizeof(p->name)); + p->index = htonl(d->partition_index); + p->data_size = htonl(d->stats.st_size); + p->part_size = htonl(d->partition_length); + p->baseaddr = htonl(d->partition_baseaddr); + p->memaddr = htonl(d->partition_memaddr); + p->entryaddr = htonl(d->partition_entryaddr); + + crc->crc = htonl(crc32(0L, mem, d->stats.st_size + sizeof(part_t))); + crc->pad = 0L; + + return 0; +} + +static void usage(const char* progname) +{ + INFO("Version %s\n" + "Usage: %s [options]\n" + "\t-v \t - firmware version information, default: %s\n" + "\t-m \t\t - firmware magic, default: %s\n" + "\t-f \t\t - flash base address, default: 0x%08x\n" + "\t-o \t - firmware output file, default: %s\n" + "\t-p :::::\n " + "\t\t\t\t - create a partition from \n" + "\t-z\t\t\t - set partition offsets to zero\n" + "\t-h\t\t\t - this help\n", + VERSION, progname, DEFAULT_VERSION, MAGIC_HEADER, + DEFAULT_FLASH_BASE, DEFAULT_OUTPUT_FILE); +} + +static void print_image_info(void) +{ + int i; + + INFO("Firmware version : '%s'\n" + "Output file : '%s'\n" + "Part count : %u\n", + im.version, im.outputfile, im.part_count); + + for (i = 0; i < im.part_count; ++i) { + const part_data_t* d = &im.parts[i]; + INFO(" %10s: %08x %08x %08x %08x %8ld bytes (free: %8ld)\n", + d->partition_name, + d->partition_baseaddr, + d->partition_length, + d->partition_entryaddr, + d->partition_memaddr, + d->stats.st_size, + d->partition_length - d->stats.st_size); + } +} + +static int filelength(const char* file) +{ + FILE *p; + int ret = -1; + + if ( (p = fopen(file, "rb") ) == NULL) return (-1); + + fseek(p, 0, SEEK_END); + ret = ftell(p); + + fclose (p); + + return (ret); +} + +int str2u32(char *arg, u_int32_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 image_layout_add_partition(const char *part_desc) +{ + part_data_t *d; + char memaddr[16]; + char entryaddr[16]; + char offset[16]; + char length[16]; + int t; + + if (im.part_count >= MAX_SECTIONS) { + ERROR("Too many partitions specified\n"); + return (-1); + } + + d = &im.parts[im.part_count]; + 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]:%256s", + d->partition_name, + offset, + length, + memaddr, + entryaddr, + d->filename); + + if (t != 6) { + ERROR("Bad partition parameter %d, '%s'\n", t, part_desc); + return (-1); + } + + if (strlen(d->partition_name) == 0) { + ERROR("No partition name specified in '%s'\n", part_desc); + return (-1); + } + + if (str2u32(offset, &d->partition_offset)) { + ERROR("Bad offset value '%s'\n", offset); + return (-1); + } + + if (str2u32(length, &d->partition_length)) { + ERROR("Bad length value '%s'\n", length); + return (-1); + } + + if (d->partition_length == 0) { + int flen; + flen = filelength(d->filename); + if (flen < 0) { + ERROR("Unable to determine size of '%s'\n", + d->filename); + return (-1); + } + d->partition_length = flen; + } + + if (str2u32(memaddr, &d->partition_memaddr)) { + ERROR("Bad memaddr vaule '%s'\n", memaddr); + return (-1); + } + + if (str2u32(entryaddr, &d->partition_entryaddr)) { + ERROR("Bad entry address value '%s'\n", entryaddr); + return (-1); + } + + im.part_count++; + d->partition_index = im.part_count; + + return 0; +} + +static int image_layout_verify(void) +{ + u_int32_t offset; + int i; + + if (im.part_count == 0) { + ERROR("No partitions specified\n"); + return -1; + } + + offset = im.parts[0].partition_offset; + for (i = 0; i < im.part_count; i++) + { + part_data_t* d = &im.parts[i]; + + if (stat(d->filename, &d->stats) < 0) { + ERROR("Couldn't stat file '%s' from part '%s'\n", + d->filename, d->partition_name); + return -2; + } + + if (d->stats.st_size == 0) { + ERROR("File '%s' from part '%s' is empty!\n", + d->filename, d->partition_name); + return -3; + } + + if (d->stats.st_size > d->partition_length) { + ERROR("File '%s' too big (%d) - max size: 0x%08X (exceeds %lu bytes)\n", + d->filename, i, d->partition_length, + d->stats.st_size - d->partition_length); + return -4; + } + + if (d->partition_offset < offset) + d->partition_offset = offset; + + if (zero_part_baseaddr) { + d->partition_baseaddr = 0; + } else { + d->partition_baseaddr = + im.flash_baseaddr + d->partition_offset; + } + offset += d->partition_length; + } + + return 0; +} + +static int build_image(void) +{ + char* mem; + char* ptr; + u_int32_t mem_size; + FILE* f; + int i; + + /* build in-memory buffer */ + mem_size = sizeof(header_t) + sizeof(signature_t); + for (i = 0; i < im.part_count; ++i) { + part_data_t* d = &im.parts[i]; + mem_size += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); + } + + mem = (char*)calloc(mem_size, 1); + if (mem == NULL) { + ERROR("Cannot allocate memory chunk of size '%u'\n", mem_size); + return -1; + } + + /* write header */ + write_header(mem, im.version); + ptr = mem + sizeof(header_t); + + /* write all parts */ + for (i = 0; i < im.part_count; ++i) { + part_data_t* d = &im.parts[i]; + int rc; + if ((rc = write_part(ptr, d)) != 0) { + ERROR("ERROR: failed writing part %u '%s'\n", i, d->partition_name); + return -1; + } + ptr += sizeof(part_t) + d->stats.st_size + sizeof(part_crc_t); + } + + + /* write signature */ + write_signature(mem, mem_size - sizeof(signature_t)); + + /* write in-memory buffer into file */ + if ((f = fopen(im.outputfile, "w")) == NULL) { + ERROR("Can not create output file: '%s'\n", im.outputfile); + return -10; + } + + if (fwrite(mem, mem_size, 1, f) != 1) { + ERROR("Could not write %d bytes into file: '%s'\n", + mem_size, im.outputfile); + return -11; + } + + free(mem); + fclose(f); + return 0; +} + +int main(int argc, char* argv[]) +{ + int o, rc; + + memset(&im, 0, sizeof(im)); + + strcpy(im.outputfile, DEFAULT_OUTPUT_FILE); + strcpy(im.version, DEFAULT_VERSION); + memcpy(im.magic, MAGIC_HEADER, MAGIC_LENGTH); + im.flash_baseaddr = DEFAULT_FLASH_BASE; + + while ((o = getopt(argc, argv, "f:hm:o:p:v:z")) != -1) + { + switch (o) { + case 'f': + if (optarg) + if (str2u32(optarg, &im.flash_baseaddr)) { + ERROR("Invalid flash start address %s\n", optarg); + return -1; + } + break; + case 'h': + usage(argv[0]); + return -1; + case 'm': + if (optarg) { + if (strlen(optarg) != MAGIC_LENGTH) { + ERROR("Invalid magic %s\n", optarg); + return -1; + } + + memcpy(im.magic, optarg, MAGIC_LENGTH); + } + break; + case 'o': + if (optarg) + strncpy(im.outputfile, optarg, sizeof(im.outputfile)); + break; + case 'p': + if (optarg) { + if (image_layout_add_partition(optarg)) + return -1; + } + break; + case 'v': + if (optarg) + strncpy(im.version, optarg, sizeof(im.version)); + break; + case 'z': + zero_part_baseaddr = 1; + break; + } + } + + rc = image_layout_verify(); + if (rc) { + ERROR("Failed validating firmware layout - error code: %d\n", + rc); + return -4; + } + + print_image_info(); + + rc = build_image(); + if (rc) { + ERROR("Failed building image file '%s' - error code: %d\n", + im.outputfile, rc); + return -5; + } + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/mkheader_gemtek.c b/trunk/tools/firmware-utils/src/mkheader_gemtek.c new file mode 100644 index 00000000..9e618efb --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkheader_gemtek.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 Claudio Leite + * + * 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 + */ + +#include +#include +#include +#include + +#include /* 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 +# define le32 OSSwapHostToLittleInt32 +#else +# if defined(__linux__) +# include +# if __BYTE_ORDER == __BIG_ENDIAN +# define CPU_BIG_ENDIAN +# endif +# else +# include /* 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 [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/trunk/tools/firmware-utils/src/mkhilinkfw.c b/trunk/tools/firmware-utils/src/mkhilinkfw.c new file mode 100644 index 00000000..99322d4e --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkhilinkfw.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2013 Jeff Kent + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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/trunk/tools/firmware-utils/src/mkmylofw.c b/trunk/tools/firmware-utils/src/mkmylofw.c new file mode 100644 index 00000000..b5958ebe --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkmylofw.c @@ -0,0 +1,1297 @@ +/* + * Copyright (C) 2006-2008 Gabor Juhos + * + * 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include /* for __BYTE_ORDER */ + +#if defined(__CYGWIN__) +# include +#endif + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +#else +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +#endif + +#include "myloader.h" + +#define MAX_FW_BLOCKS 32 +#define MAX_ARG_COUNT 32 +#define MAX_ARG_LEN 1024 +#define FILE_BUF_LEN (16*1024) +#define PART_NAME_LEN 32 + +struct fw_block { + uint32_t addr; + uint32_t blocklen; /* length of the block */ + uint32_t flags; + + char *name; /* name of the file */ + uint32_t size; /* length of the file */ + uint32_t crc; /* crc value of the file */ +}; + +struct fw_part { + struct mylo_partition mylo; + char name[PART_NAME_LEN]; +}; + +#define BLOCK_FLAG_HAVEHDR 0x0001 + +struct cpx_board { + char *model; /* model number*/ + char *name; /* model name*/ + char *desc; /* description */ + uint16_t vid; /* vendor id */ + uint16_t did; /* device id */ + uint16_t svid; /* sub vendor id */ + uint16_t sdid; /* sub device id */ + uint32_t flash_size; /* size of flash */ + uint32_t part_offset; /* offset of the partition_table */ + uint32_t part_size; /* size of the partition_table */ +}; + +#define BOARD(_vid, _did, _svid, _sdid, _flash, _mod, _name, _desc, _po, _ps) { \ + .model = _mod, .name = _name, .desc = _desc, \ + .vid = _vid, .did = _did, .svid = _svid, .sdid = _sdid, \ + .flash_size = (_flash << 20), \ + .part_offset = _po, .part_size = _ps } + +#define CPX_BOARD(_did, _flash, _mod, _name, _desc, _po, _ps) \ + BOARD(VENID_COMPEX, _did, VENID_COMPEX, _did, _flash, _mod, _name, _desc, _po, _ps) + +#define CPX_BOARD_ADM(_did, _flash, _mod, _name, _desc) \ + CPX_BOARD(_did, _flash, _mod, _name, _desc, 0x10000, 0x10000) + +#define CPX_BOARD_AR71XX(_did, _flash, _mod, _name, _desc) \ + CPX_BOARD(_did, _flash, _mod, _name, _desc, 0x20000, 0x8000) + +#define CPX_BOARD_AR23XX(_did, _flash, _mod, _name, _desc) \ + CPX_BOARD(_did, _flash, _mod, _name, _desc, 0x10000, 0x10000) + +#define ALIGN(x,y) ((x)+((y)-1)) & ~((y)-1) + +char *progname; +char *ofname = NULL; + +uint32_t flash_size = 0; +int fw_num_partitions = 0; +int fw_num_blocks = 0; +int verblevel = 0; + +struct mylo_fw_header fw_header; +struct fw_part fw_parts[MYLO_MAX_PARTITIONS]; +struct fw_block fw_blocks[MAX_FW_BLOCKS]; +struct cpx_board *board; + +struct cpx_board boards[] = { + CPX_BOARD_ADM(DEVID_COMPEX_NP18A, 4, + "NP18A", "Compex NetPassage 18A", + "Dualband Wireless A+G Internet Gateway"), + CPX_BOARD_ADM(DEVID_COMPEX_NP26G8M, 2, + "NP26G8M", "Compex NetPassage 26G (8M)", + "Wireless-G Broadband Multimedia Gateway"), + CPX_BOARD_ADM(DEVID_COMPEX_NP26G16M, 4, + "NP26G16M", "Compex NetPassage 26G (16M)", + "Wireless-G Broadband Multimedia Gateway"), + CPX_BOARD_ADM(DEVID_COMPEX_NP27G, 4, + "NP27G", "Compex NetPassage 27G", + "Wireless-G 54Mbps eXtended Range Router"), + CPX_BOARD_ADM(DEVID_COMPEX_NP28G, 4, + "NP28G", "Compex NetPassage 28G", + "Wireless 108Mbps Super-G XR Multimedia Router with 4 USB Ports"), + CPX_BOARD_ADM(DEVID_COMPEX_NP28GHS, 4, + "NP28GHS", "Compex NetPassage 28G (HotSpot)", + "HotSpot Solution"), + CPX_BOARD_ADM(DEVID_COMPEX_WP18, 4, + "WP18", "Compex NetPassage WP18", + "Wireless-G 54Mbps A+G Dualband Access Point"), + CPX_BOARD_ADM(DEVID_COMPEX_WP54G, 4, + "WP54G", "Compex WP54G", + "Wireless-G 54Mbps XR Access Point"), + CPX_BOARD_ADM(DEVID_COMPEX_WP54Gv1C, 2, + "WP54Gv1C", "Compex WP54G rev.1C", + "Wireless-G 54Mbps XR Access Point"), + CPX_BOARD_ADM(DEVID_COMPEX_WP54AG, 4, + "WP54AG", "Compex WP54AG", + "Wireless-AG 54Mbps XR Access Point"), + CPX_BOARD_ADM(DEVID_COMPEX_WPP54G, 4, + "WPP54G", "Compex WPP54G", + "Outdoor Access Point"), + CPX_BOARD_ADM(DEVID_COMPEX_WPP54AG, 4, + "WPP54AG", "Compex WPP54AG", + "Outdoor Access Point"), + + CPX_BOARD_AR71XX(DEVID_COMPEX_WP543, 2, + "WP543", "Compex WP543", + "BareBoard"), + CPX_BOARD_AR71XX(DEVID_COMPEX_WPE72, 8, + "WPE72", "Compex WPE72", + "BareBoard"), + + CPX_BOARD_AR23XX(DEVID_COMPEX_NP25G, 4, + "NP25G", "Compex NetPassage 25G", + "Wireless 54Mbps XR Router"), + CPX_BOARD_AR23XX(DEVID_COMPEX_WPE53G, 4, + "WPE53G", "Compex NetPassage 25G", + "Wireless 54Mbps XR Access Point"), + {.model = NULL} +}; + +void +errmsgv(int syserr, const char *fmt, va_list arg_ptr) +{ + int save = errno; + + fflush(0); + fprintf(stderr, "[%s] Error: ", progname); + vfprintf(stderr, fmt, arg_ptr); + if (syserr != 0) { + fprintf(stderr, ": %s", strerror(save)); + } + fprintf(stderr, "\n"); +} + +void +errmsg(int syserr, const char *fmt, ...) +{ + va_list arg_ptr; + va_start(arg_ptr, fmt); + errmsgv(syserr, fmt, arg_ptr); + va_end(arg_ptr); +} + +void +dbgmsg(int level, const char *fmt, ...) +{ + va_list arg_ptr; + if (verblevel >= level) { + fflush(0); + va_start(arg_ptr, fmt); + vfprintf(stderr, fmt, arg_ptr); + fprintf(stderr, "\n"); + va_end(arg_ptr); + } +} + + +void +usage(int status) +{ + FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; + struct cpx_board *board; + + fprintf(stream, "Usage: %s [OPTION...] \n", progname); + fprintf(stream, +"\n" +" write output to the \n" +"\n" +"Options:\n" +" -B create firmware for the board specified with .\n" +" This option set vendor id, device id, subvendor id,\n" +" subdevice id, and flash size options to the right value.\n" +" valid values:\n"); + for (board = boards; board->model != NULL; board++){ + fprintf(stream, +" %-12s: %s\n", + board->model, board->name); + }; + fprintf(stream, +" -i :[:[:]]\n" +" create firmware for board with vendor id , device\n" +" id , subvendor id and subdevice id .\n" +" -r set board revision to .\n" +" -s set flash size to \n" +" -b :[:[]:]\n" +" define block at with length of .\n" +" valid values:\n" +" h : add crc header before the file data.\n" +" -p :[:[:[:[:]]]]\n" +" add partition at , with size of to the\n" +" partition table, set partition name to , partition \n" +" flags to and partition parameter to .\n" +" If the is specified content of the file will be \n" +" added to the firmware image.\n" +" valid values:\n" +" a: this is the active partition. The bootloader loads\n" +" the firmware from this partition.\n" +" h: the partition data have a header.\n" +" l: the partition data uses LZMA compression.\n" +" p: the bootloader loads data from this partition to\n" +" the RAM before decompress it.\n" +" -h show this screen\n" + ); + + exit(status); +} + +/* + * Code to compute the CRC-32 table. Borrowed from + * gzip-1.0.3/makecrc.c. + */ + +static uint32_t crc_32_tab[256]; + +void +init_crc_table(void) +{ + /* Not copyrighted 1990 Mark Adler */ + + uint32_t c; /* crc shift register */ + uint32_t e; /* polynomial exclusive-or pattern */ + int i; /* counter for all possible eight bit values */ + int k; /* byte being shifted into crc apparatus */ + + /* terms of polynomial defining this crc (except x^32): */ + static const int p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* Make exclusive-or pattern from polynomial */ + e = 0; + for (i = 0; i < sizeof(p)/sizeof(int); i++) + e |= 1L << (31 - p[i]); + + crc_32_tab[0] = 0; + + for (i = 1; i < 256; i++) { + c = 0; + for (k = i | 256; k != 1; k >>= 1) { + c = c & 1 ? (c >> 1) ^ e : c >> 1; + if (k & 1) + c ^= e; + } + crc_32_tab[i] = c; + } +} + + +void +update_crc(uint8_t *p, uint32_t len, uint32_t *crc) +{ + uint32_t t; + + t = *crc ^ 0xFFFFFFFFUL; + while (len--) { + t = crc_32_tab[(t ^ *p++) & 0xff] ^ (t >> 8); + } + *crc = t ^ 0xFFFFFFFFUL; +} + + +uint32_t +get_crc(uint8_t *p, uint32_t len) +{ + uint32_t crc; + + crc = 0; + update_crc(p ,len , &crc); + return crc; +} + + +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 +str2u16(char *arg, uint16_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { + return -1; + } + + *val = t & 0xFFFF; + return 0; +} + + +struct cpx_board * +find_board(char *model){ + struct cpx_board *board; + struct cpx_board *tmp; + + board = NULL; + for (tmp = boards; tmp->model != NULL; tmp++){ + if (strcasecmp(model, tmp->model) == 0) { + board = tmp; + break; + } + }; + + return board; +} + + +int +get_file_crc(struct fw_block *ff) +{ + FILE *f; + uint8_t buf[FILE_BUF_LEN]; + uint32_t readlen = sizeof(buf); + int res = -1; + size_t len; + + if ((ff->flags & BLOCK_FLAG_HAVEHDR) == 0) { + res = 0; + goto out; + } + + errno = 0; + f = fopen(ff->name,"r"); + if (errno) { + errmsg(1,"unable to open file %s", ff->name); + goto out; + } + + ff->crc = 0; + len = ff->size; + while (len > 0) { + if (len < readlen) + readlen = len; + + errno = 0; + fread(buf, readlen, 1, f); + if (errno) { + errmsg(1,"unable to read from file %s", ff->name); + goto out_close; + } + + update_crc(buf, readlen, &ff->crc); + len -= readlen; + } + + res = 0; + +out_close: + fclose(f); +out: + return res; +} + + +int +process_files(void) +{ + struct fw_block *b; + struct stat st; + int i; + + for (i = 0; i < fw_num_blocks; i++) { + b = &fw_blocks[i]; + if ((b->addr + b->blocklen) > flash_size) { + errmsg(0, "block at 0x%08X is too big", b->addr); + return -1; + } + if (b->name == NULL) + continue; + + if (stat(b->name, &st) < 0) { + errmsg(0, "stat failed on %s",b->name); + return -1; + } + if (b->blocklen == 0) { + b->blocklen = flash_size - b->addr; + } + if (st.st_size > b->blocklen) { + errmsg(0,"file %s is too big",b->name); + return -1; + } + + b->size = st.st_size; + } + + return 0; +} + + +int +process_partitions(void) +{ + struct mylo_partition *part; + int i; + + for (i = 0; i < fw_num_partitions; i++) { + part = &fw_parts[i].mylo; + + if (part->addr > flash_size) { + errmsg(0, "invalid partition at 0x%08X", part->addr); + return -1; + } + + if ((part->addr + part->size) > flash_size) { + errmsg(0, "partition at 0x%08X is too big", part->addr); + return -1; + } + } + + return 0; +} + + +/* + * routines to write data to the output file + */ +int +write_out_data(FILE *outfile, uint8_t *data, size_t len, uint32_t *crc) +{ + errno = 0; + + fwrite(data, len, 1, outfile); + if (errno) { + errmsg(1,"unable to write output file"); + return -1; + } + + if (crc) { + update_crc(data, len, crc); + } + + return 0; +} + + +int +write_out_desc(FILE *outfile, struct mylo_fw_blockdesc *desc, uint32_t *crc) +{ + return write_out_data(outfile, (uint8_t *)desc, + sizeof(*desc), crc); +} + + +int +write_out_padding(FILE *outfile, size_t len, uint8_t padc, uint32_t *crc) +{ + uint8_t buff[512]; + size_t buflen = sizeof(buff); + + memset(buff, padc, buflen); + + while (len > 0) { + if (len < buflen) + buflen = len; + + if (write_out_data(outfile, buff, buflen, crc)) + return -1; + + len -= buflen; + } + + return 0; +} + + +int +write_out_file(FILE *outfile, struct fw_block *block, uint32_t *crc) +{ + char buff[FILE_BUF_LEN]; + size_t buflen = sizeof(buff); + FILE *f; + size_t len; + + errno = 0; + + if (block->name == NULL) { + return 0; + } + + if ((block->flags & BLOCK_FLAG_HAVEHDR) != 0) { + struct mylo_partition_header ph; + + if (get_file_crc(block) != 0) + return -1; + + ph.crc = HOST_TO_LE32(block->crc); + ph.len = HOST_TO_LE32(block->size); + + if (write_out_data(outfile, (uint8_t *)&ph, sizeof(ph), crc) != 0) + return -1; + } + + f = fopen(block->name,"r"); + if (errno) { + errmsg(1,"unable to open file: %s", block->name); + return -1; + } + + len = block->size; + while (len > 0) { + if (len < buflen) + buflen = len; + + /* read data from source file */ + errno = 0; + fread(buff, buflen, 1, f); + if (errno != 0) { + errmsg(1,"unable to read from file: %s",block->name); + return -1; + } + + if (write_out_data(outfile, buff, buflen, crc) != 0) + return -1; + + len -= buflen; + } + + fclose(f); + + /* align next block on a 4 byte boundary */ + len = ALIGN(len,4) - block->size; + if (write_out_padding(outfile, len, 0xFF, crc)) + return -1; + + dbgmsg(1,"file %s written out", block->name); + return 0; +} + + +int +write_out_header(FILE *outfile, uint32_t *crc) +{ + struct mylo_fw_header hdr; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.magic = HOST_TO_LE32(MYLO_MAGIC_FIRMWARE); + hdr.crc = HOST_TO_LE32(fw_header.crc); + hdr.vid = HOST_TO_LE16(fw_header.vid); + hdr.did = HOST_TO_LE16(fw_header.did); + hdr.svid = HOST_TO_LE16(fw_header.svid); + hdr.sdid = HOST_TO_LE16(fw_header.sdid); + hdr.rev = HOST_TO_LE32(fw_header.rev); + hdr.fwhi = HOST_TO_LE32(fw_header.fwhi); + hdr.fwlo = HOST_TO_LE32(fw_header.fwlo); + hdr.flags = HOST_TO_LE32(fw_header.flags); + + if (fseek(outfile, 0, SEEK_SET) != 0) { + errmsg(1,"fseek failed on output file"); + return -1; + } + + return write_out_data(outfile, (uint8_t *)&hdr, sizeof(hdr), crc); +} + + +int +write_out_partitions(FILE *outfile, uint32_t *crc) +{ + struct mylo_partition_table p; + char part_names[MYLO_MAX_PARTITIONS][PART_NAME_LEN]; + int ret; + int i; + + if (fw_num_partitions == 0) + return 0; + + memset(&p, 0, sizeof(p)); + memset(part_names, 0, sizeof(part_names)); + + p.magic = HOST_TO_LE32(MYLO_MAGIC_PARTITIONS); + for (i = 0; i < fw_num_partitions; i++) { + struct mylo_partition *mp; + struct fw_part *fp; + + mp = &p.partitions[i]; + fp = &fw_parts[i]; + mp->flags = HOST_TO_LE16(fp->mylo.flags); + mp->type = HOST_TO_LE16(PARTITION_TYPE_USED); + mp->addr = HOST_TO_LE32(fp->mylo.addr); + mp->size = HOST_TO_LE32(fp->mylo.size); + mp->param = HOST_TO_LE32(fp->mylo.param); + + memcpy(part_names[i], fp->name, PART_NAME_LEN); + } + + ret = write_out_data(outfile, (uint8_t *)&p, sizeof(p), crc); + if (ret) + return ret; + + ret = write_out_data(outfile, (uint8_t *)part_names, sizeof(part_names), + crc); + return ret; +} + + +int +write_out_blocks(FILE *outfile, uint32_t *crc) +{ + struct mylo_fw_blockdesc desc; + struct fw_block *b; + uint32_t dlen; + int i; + + /* + * if at least one partition specified, write out block descriptor + * for the partition table + */ + if (fw_num_partitions > 0) { + desc.type = HOST_TO_LE32(FW_DESC_TYPE_USED); + desc.addr = HOST_TO_LE32(board->part_offset); + desc.dlen = HOST_TO_LE32(sizeof(struct mylo_partition_table) + + (MYLO_MAX_PARTITIONS * PART_NAME_LEN)); + desc.blen = HOST_TO_LE32(board->part_size); + + if (write_out_desc(outfile, &desc, crc) != 0) + return -1; + } + + /* + * write out block descriptors for each files + */ + for (i = 0; i < fw_num_blocks; i++) { + b = &fw_blocks[i]; + + /* detect block size */ + dlen = b->size; + if ((b->flags & BLOCK_FLAG_HAVEHDR) != 0) { + dlen += sizeof(struct mylo_partition_header); + } + + /* round up to 4 bytes */ + dlen = ALIGN(dlen, 4); + + /* setup the descriptor */ + desc.type = HOST_TO_LE32(FW_DESC_TYPE_USED); + desc.addr = HOST_TO_LE32(b->addr); + desc.dlen = HOST_TO_LE32(dlen); + desc.blen = HOST_TO_LE32(b->blocklen); + + if (write_out_desc(outfile, &desc, crc) != 0) + return -1; + } + + /* + * write out the null block descriptor + */ + memset(&desc, 0, sizeof(desc)); + if (write_out_desc(outfile, &desc, crc) != 0) + return -1; + + if (write_out_partitions(outfile, crc) != 0) + return -1; + + /* + * write out data for each blocks + */ + for (i = 0; i < fw_num_blocks; i++) { + b = &fw_blocks[i]; + if (write_out_file(outfile, b, crc) != 0) + return -1; + } + + return 0; +} + + +/* + * argument parsing + */ +int +parse_arg(char *arg, char *buf, char *argv[]) +{ + int res = 0; + size_t argl; + char *tok; + char **ap = &buf; + int i; + + if ((arg == NULL)) { + /* invalid argument string */ + return -1; + } + + argl = strlen(arg); + if (argl == 0) { + /* no arguments */ + return res; + } + + if (argl >= MAX_ARG_LEN) { + /* argument is too long */ + argl = MAX_ARG_LEN-1; + } + + memset(argv, 0, MAX_ARG_COUNT * sizeof(void *)); + memcpy(buf, arg, argl); + buf[argl] = '\0'; + + for (i = 0; i < MAX_ARG_COUNT; i++) { + tok = strsep(ap, ":"); + if (tok == NULL) { + break; + } +#if 0 + else if (tok[0] == '\0') { + break; + } +#endif + argv[i] = tok; + res++; + } + + return res; +} + + +int +required_arg(char c, char *arg) +{ + if ((optarg != NULL) && (*arg == '-')){ + errmsg(0,"option %c requires an argument\n", c); + return -1; + } + + return 0; +} + + +int +is_empty_arg(char *arg) +{ + int ret = 1; + if (arg != NULL) { + if (*arg) ret = 0; + }; + return ret; +} + + +int +parse_opt_flags(char ch, char *arg) +{ + if (required_arg(ch, arg)) { + goto err_out; + } + + if (str2u32(arg, &fw_header.flags) != 0) { + errmsg(0,"invalid firmware flags: %s", arg); + goto err_out; + } + + dbgmsg(1, "firmware flags set to %X bytes", fw_header.flags); + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_size(char ch, char *arg) +{ + if (required_arg(ch, arg)) { + goto err_out; + } + + if (str2u32(arg, &flash_size) != 0) { + errmsg(0,"invalid flash size: %s", arg); + goto err_out; + } + + dbgmsg(1, "flash size set to %d bytes", flash_size); + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_id(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + + if (required_arg(ch, arg)) { + goto err_out; + } + + argc = parse_arg(arg, buf, argv); + + /* processing vendor ID*/ + p = argv[0]; + if (is_empty_arg(p)) { + errmsg(0,"vendor id is missing from -%c %s",ch, arg); + goto err_out; + } else if (str2u16(p, &fw_header.vid) != 0) { + errmsg(0,"invalid vendor id: %s", p); + goto err_out; + } + + dbgmsg(1, "vendor id is set to 0x%04X", fw_header.vid); + + /* processing device ID*/ + p = argv[1]; + if (is_empty_arg(p)) { + errmsg(0,"device id is missing from -%c %s",ch, arg); + goto err_out; + } else if (str2u16(p, &fw_header.did) != 0) { + errmsg(0,"invalid device id: %s", p); + goto err_out; + } + + dbgmsg(1, "device id is set to 0x%04X", fw_header.did); + + /* processing sub vendor ID*/ + p = argv[2]; + if (is_empty_arg(p)) { + fw_header.svid = fw_header.vid; + } else if (str2u16(p, &fw_header.svid) != 0) { + errmsg(0,"invalid sub vendor id: %s", p); + goto err_out; + } + + dbgmsg(1, "sub vendor id is set to 0x%04X", fw_header.svid); + + /* processing device ID*/ + p = argv[3]; + if (is_empty_arg(p)) { + fw_header.sdid = fw_header.did; + } else if (str2u16(p, &fw_header.sdid) != 0) { + errmsg(0,"invalid sub device id: %s", p); + goto err_out; + } + + dbgmsg(1, "sub device id is set to 0x%04X", fw_header.sdid); + + /* processing revision */ + p = argv[4]; + if (is_empty_arg(p)) { + fw_header.rev = 0; + } else if (str2u32(arg, &fw_header.rev) != 0) { + errmsg(0,"invalid revision number: %s", p); + goto err_out; + } + + dbgmsg(1, "board revision is set to 0x%08X", fw_header.rev); + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_block(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + struct fw_block *b; + char *p; + + if (required_arg(ch, arg)) { + goto err_out; + } + + if (fw_num_blocks >= MAX_FW_BLOCKS) { + errmsg(0,"too many blocks specified"); + goto err_out; + } + + argc = parse_arg(arg, buf, argv); + dbgmsg(1,"processing block option %s, count %d", arg, argc); + + b = &fw_blocks[fw_num_blocks++]; + + /* processing block address */ + p = argv[0]; + if (is_empty_arg(p)) { + errmsg(0,"no block address specified in %s", arg); + goto err_out; + } else if (str2u32(p, &b->addr) != 0) { + errmsg(0,"invalid block address: %s", p); + goto err_out; + } + + /* processing block length */ + p = argv[1]; + if (is_empty_arg(p)) { + errmsg(0,"no block length specified in %s", arg); + goto err_out; + } else if (str2u32(p, &b->blocklen) != 0) { + errmsg(0,"invalid block length: %s", p); + goto err_out; + } + + if (argc < 3) { + dbgmsg(1,"empty block %s", arg); + goto success; + } + + /* processing flags */ + p = argv[2]; + if (is_empty_arg(p) == 0) { + for ( ; *p != '\0'; p++) { + switch (*p) { + case 'h': + b->flags |= BLOCK_FLAG_HAVEHDR; + break; + default: + errmsg(0, "invalid block flag \"%c\"", *p); + goto err_out; + } + } + } + + /* processing file name */ + p = argv[3]; + if (is_empty_arg(p)) { + errmsg(0,"file name missing in %s", arg); + goto err_out; + } + + b->name = strdup(p); + if (b->name == NULL) { + errmsg(0,"not enough memory"); + goto err_out; + } + +success: + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_partition(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + struct mylo_partition *part; + struct fw_part *fp; + + if (required_arg(ch, arg)) { + goto err_out; + } + + if (fw_num_partitions >= MYLO_MAX_PARTITIONS) { + errmsg(0, "too many partitions specified"); + goto err_out; + } + + fp = &fw_parts[fw_num_partitions++]; + part = &fp->mylo; + + argc = parse_arg(arg, buf, argv); + + /* processing partition address */ + p = argv[0]; + if (is_empty_arg(p)) { + errmsg(0,"partition address missing in -%c %s",ch, arg); + goto err_out; + } else if (str2u32(p, &part->addr) != 0) { + errmsg(0,"invalid partition address: %s", p); + goto err_out; + } + + /* processing partition size */ + p = argv[1]; + if (is_empty_arg(p)) { + errmsg(0,"partition size missing in -%c %s",ch, arg); + goto err_out; + } else if (str2u32(p, &part->size) != 0) { + errmsg(0,"invalid partition size: %s", p); + goto err_out; + } + + /* processing partition flags */ + p = argv[2]; + if (is_empty_arg(p) == 0) { + for ( ; *p != '\0'; p++) { + switch (*p) { + case 'a': + part->flags |= PARTITION_FLAG_ACTIVE; + break; + case 'p': + part->flags |= PARTITION_FLAG_PRELOAD; + break; + case 'l': + part->flags |= PARTITION_FLAG_LZMA; + break; + case 'h': + part->flags |= PARTITION_FLAG_HAVEHDR; + break; + default: + errmsg(0, "invalid partition flag \"%c\"", *p); + goto err_out; + } + } + } + + /* processing partition parameter */ + p = argv[3]; + if (is_empty_arg(p)) { + /* set default partition parameter */ + part->param = 0; + } else if (str2u32(p, &part->param) != 0) { + errmsg(0,"invalid partition parameter: %s", p); + goto err_out; + } + + p = argv[4]; + if (is_empty_arg(p)) { + /* set default partition parameter */ + fp->name[0] = '\0'; + } else { + strncpy(fp->name, p, PART_NAME_LEN); + } + +#if 1 + if (part->size == 0) { + part->size = flash_size - part->addr; + } + + /* processing file parameter */ + p = argv[5]; + if (is_empty_arg(p) == 0) { + struct fw_block *b; + + if (fw_num_blocks == MAX_FW_BLOCKS) { + errmsg(0,"too many blocks specified", p); + goto err_out; + } + b = &fw_blocks[fw_num_blocks++]; + b->name = strdup(p); + b->addr = part->addr; + b->blocklen = part->size; + if (part->flags & PARTITION_FLAG_HAVEHDR) { + b->flags |= BLOCK_FLAG_HAVEHDR; + } + } +#endif + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_board(char ch, char *arg) +{ + if (required_arg(ch, arg)) { + goto err_out; + } + + board = find_board(arg); + if (board == NULL){ + errmsg(0,"invalid/unknown board specified: %s", arg); + goto err_out; + } + + fw_header.vid = board->vid; + fw_header.did = board->did; + fw_header.svid = board->svid; + fw_header.sdid = board->sdid; + + flash_size = board->flash_size; + + return 0; + +err_out: + return -1; +} + + +int +parse_opt_rev(char ch, char *arg) +{ + if (required_arg(ch, arg)) { + return -1; + } + + if (str2u32(arg, &fw_header.rev) != 0) { + errmsg(0,"invalid revision number: %s", arg); + return -1; + } + + return 0; +} + + +/* + * main + */ +int +main(int argc, char *argv[]) +{ + int optinvalid = 0; /* flag for invalid option */ + int c; + int res = EXIT_FAILURE; + + FILE *outfile; + uint32_t crc; + + progname=basename(argv[0]); + + memset(&fw_header, 0, sizeof(fw_header)); + + /* init header defaults */ + fw_header.vid = VENID_COMPEX; + fw_header.did = DEVID_COMPEX_WP54G; + fw_header.svid = VENID_COMPEX; + fw_header.sdid = DEVID_COMPEX_WP54G; + fw_header.fwhi = 0x20000; + fw_header.fwlo = 0x20000; + fw_header.flags = 0; + + opterr = 0; /* could not print standard getopt error messages */ + while ((c = getopt(argc, argv, "b:B:f:hi:p:r:s:v")) != -1) { + optinvalid = 0; + switch (c) { + case 'b': + optinvalid = parse_opt_block(c,optarg); + break; + case 'B': + optinvalid = parse_opt_board(c,optarg); + break; + case 'f': + optinvalid = parse_opt_flags(c,optarg); + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case 'i': + optinvalid = parse_opt_id(c,optarg); + break; + case 'p': + optinvalid = parse_opt_partition(c,optarg); + break; + case 'r': + optinvalid = parse_opt_rev(c,optarg); + break; + case 's': + optinvalid = parse_opt_size(c,optarg); + break; + case 'v': + verblevel++; + break; + default: + optinvalid = 1; + break; + } + if (optinvalid != 0 ){ + errmsg(0, "invalid option: -%c", optopt); + goto out; + } + } + + if (optind == argc) { + errmsg(0, "no output file specified"); + goto out; + } + + ofname = argv[optind++]; + + if (optind < argc) { + errmsg(0, "invalid option: %s", argv[optind]); + goto out; + } + + if (!board) { + errmsg(0, "no board specified"); + goto out; + } + + if (flash_size == 0) { + errmsg(0, "no flash size specified"); + goto out; + } + + if (process_files() != 0) { + goto out; + } + + if (process_partitions() != 0) { + goto out; + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + errmsg(1, "could not open \"%s\" for writing", ofname); + goto out; + } + + crc = 0; + init_crc_table(); + + if (write_out_header(outfile, &crc) != 0) + goto out_flush; + + if (write_out_blocks(outfile, &crc) != 0) + goto out_flush; + + fw_header.crc = crc; + if (write_out_header(outfile, NULL) != 0) + goto out_flush; + + dbgmsg(1,"Firmware file %s completed.", ofname); + + res = EXIT_SUCCESS; + +out_flush: + fflush(outfile); + fclose(outfile); + if (res != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return res; +} diff --git a/trunk/tools/firmware-utils/src/mkplanexfw.c b/trunk/tools/firmware-utils/src/mkplanexfw.c new file mode 100644 index 00000000..1bdccb7c --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkplanexfw.c @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include "sha1.h" + +#if (__BYTE_ORDER == __BIG_ENDIAN) +# define HOST_TO_BE32(x) (x) +# define BE32_TO_HOST(x) (x) +#else +# define HOST_TO_BE32(x) bswap_32(x) +# define BE32_TO_HOST(x) bswap_32(x) +#endif + + +struct planex_hdr { + uint8_t sha1sum[20]; + char version[8]; + uint8_t unk1[2]; + uint32_t datalen; +} __attribute__ ((packed)); + +struct board_info { + char *id; + uint32_t seed; + uint8_t unk[2]; + uint32_t datalen; +}; + +/* + * Globals + */ +static char *ifname; +static char *progname; +static char *ofname; +static char *version = "1.00.00"; + +static char *board_id; +static struct board_info *board; + +static struct board_info boards[] = { + { + .id = "MZK-W04NU", + .seed = 2, + .unk = {0x04, 0x08}, + .datalen = 0x770000, + }, { + .id = "MZK-W300NH", + .seed = 4, + .unk = {0x00, 0x00}, + .datalen = 0x770000, + }, { + /* 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 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; +} + +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" +" -B create image for the board specified with \n" +" -i read input from the file \n" +" -o write output to the file \n" +" -v set image version to \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; + struct planex_hdr *hdr; + sha1_context ctx; + uint32_t seed; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "B:i:o:v: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 '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; + } + + err = stat(ifname, &st); + if (err){ + ERRS("stat failed on %s", ifname); + goto err; + } + + if (st.st_size > board->datalen) { + ERR("file '%s' is too big - max size: 0x%08X (exceeds %lu bytes)\n", + ifname, board->datalen, st.st_size - board->datalen); + goto err; + } + + buflen = board->datalen + 0x10000; + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto err; + } + + memset(buf, 0xff, buflen); + hdr = (struct planex_hdr *)buf; + + hdr->datalen = HOST_TO_BE32(board->datalen); + hdr->unk1[0] = board->unk[0]; + hdr->unk1[1] = board->unk[1]; + + snprintf(hdr->version, sizeof(hdr->version), "%s", version); + + infile = fopen(ifname, "r"); + if (infile == NULL) { + ERRS("could not open \"%s\" for reading", ifname); + goto err_free; + } + + errno = 0; + fread(buf + sizeof(*hdr), st.st_size, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto err_close_in; + } + + seed = HOST_TO_BE32(board->seed); + sha1_starts(&ctx); + sha1_update(&ctx, (uchar *) &seed, sizeof(seed)); + sha1_update(&ctx, buf + sizeof(*hdr), board->datalen); + sha1_finish(&ctx, hdr->sha1sum); + + 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; + + out_flush: + 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/trunk/tools/firmware-utils/src/mkporayfw.c b/trunk/tools/firmware-utils/src/mkporayfw.c new file mode 100644 index 00000000..802d5efa --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkporayfw.c @@ -0,0 +1,791 @@ +/* + * Builder/viewer/extractor utility for Poray firmware image files + * + * Copyright (C) 2013 Michel Stempin + * Copyright (C) 2013 Felix Kaechele + * Copyright (C) 2013 + * + * This tool is based on: + * TP-Link firmware upgrade tool. + * Copyright (C) 2009 Gabor Juhos + * + * Itself based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 create image for the board specified with \n" +" -H use hardware id specified with \n" +" -F use flash layout specified with \n" +" -f read firmware image from the file \n" +" -o write output to the 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("(== libreCMC default)\n"); + } else { + printf("(libreCMC 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/trunk/tools/firmware-utils/src/mkrtn56uimg.c b/trunk/tools/firmware-utils/src/mkrtn56uimg.c new file mode 100644 index 00000000..fe9ae2c8 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkrtn56uimg.c @@ -0,0 +1,294 @@ +/* + * + * Copyright (C) 2014 OpenWrt.org + * Copyright (C) 2014 Mikko Hissa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 generate a factory flash image \n" + " -s generate a sysupgrade flash image \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/trunk/tools/firmware-utils/src/mksenaofw.c b/trunk/tools/firmware-utils/src/mksenaofw.c new file mode 100644 index 00000000..7ea58f56 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mksenaofw.c @@ -0,0 +1,417 @@ +/* + * + * Copyright (C) 2012 OpenWrt.org + * Copyright (C) 2012 Mikko Hissa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#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 encode image file \n" + " -d decode image file \n" + " -o write output to the file \n" + " -t set image type to \n" + " valid image 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 set image version to \n" + " -r set image vendor id to \n" + " -p set image product id to \n" + " -m set encoding magic \n" + " -z enable image padding to \n" + " -b set image , 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 (header.firmware_type == 0) { + fprintf(stderr, "Firmware type must be defined\n"); + usage(progname, EXIT_FAILURE); + } else if (input_file == 0 || output_file == 0) { + fprintf(stderr, "Input and output files must be defined\n"); + usage(progname, EXIT_FAILURE); + } else 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); + } + + switch (mode) { + case NONE: + fprintf(stderr, "A mode must be defined\n"); + usage(progname, EXIT_FAILURE); + break; + case ENCODE: + if (encode_image(input_file, output_file, &header, pad ? block_size : 0) + < 0) + return EXIT_FAILURE; + break; + case DECODE: + if (decode_image(input_file, output_file) < 0) + return EXIT_FAILURE; + break; + } + + return EXIT_SUCCESS; +} diff --git a/trunk/tools/firmware-utils/src/mktitanimg.c b/trunk/tools/firmware-utils/src/mktitanimg.c new file mode 100644 index 00000000..cca4a0eb --- /dev/null +++ b/trunk/tools/firmware-utils/src/mktitanimg.c @@ -0,0 +1,1040 @@ +#include +#include +#include +#include +#include "mktitanimg.h" + + +struct checksumrecord +{ + unsigned int magic; + unsigned int chksum; /* The checksum for the complete header. + Excepting the + checksum block */ +}; +/*************************************************************************** + * void print_help(void) + ***************************************************************************/ +void print_help(void) +{ + static char* help_page[]= + { + "mknspimg version 1.0, Texas Instruments, 2004", + "Syntax:", + " mknspimg -o outfile -i image1 image2 -a align1 align2 [-v] [-b] [-p prod_id] [-r rel_id] [-s rel_name] [-f flags]", + "Example:", + " mknspimg -o nsp_image.bin -i kernel.bin files.img -a 0 4096", + "This generates 'nsp_image.bin' from two input files aligning first to 0 and second to 4096 bytes." + }; + + int num_lines = sizeof(help_page)/sizeof(char*); + int i; + for(i=0; i < num_lines; i++) { + printf("%s\n", help_page[i]); + } +} + +/*************************************************************************** + * void mknspimg_print_hdr(NSP_IMG_HDR* p_img_hdr) + ***************************************************************************/ +void mknspimg_print_hdr(struct nsp_img_hdr *hdr) +{ + struct nsp_img_hdr_chksum *chksum; + struct nsp_img_hdr_section_info *sect_info; + struct nsp_img_hdr_sections *section; + int i; + + printf("****************** NSP Image Summary ******************\n"); + printf("Magic: 0x%x\n", hdr->head.magic); + printf("Image Header Size: 0x%x bytes\n", hdr->head.hdr_size); + printf("Total Image Size: %d bytes\n", hdr->head.image_size); + printf("Product ID: 0x%x\n", hdr->head.prod_id); + printf("Release ID: 0x%x\n", hdr->head.rel_id); + printf("Version ID: 0x%x\n", hdr->head.version); + + printf("Offset Info: 0x%x\n", hdr->head.info_offset); + printf("Offset Sect info: 0x%x\n", hdr->head.sect_info_offset); + printf("Offset Sections: 0x%x\n", hdr->sect_info.sections_offset); + + chksum=(struct nsp_img_hdr_chksum *)(hdr+hdr->head.chksum_offset); + printf("Header Checksum: 0x%x\n", chksum->hdr_chksum); + + printf("+++ Section Information +++\n"); + printf("# of sections: %u\n", hdr->sect_info.num_sects); + section=&(hdr->sections); + for(i = 0; i < hdr->sect_info.num_sects; i++, section++) { + printf("+++++ Section %d +++++\n", i); + printf("Total size: %u bytes\n", section->total_size); + printf("Raw Size: %u bytes\n", section->raw_size); + printf("Offset: 0x%x\n", section->offset); + printf("Type: 0x%x\n", section->type); + printf("Name: %s\n", section->name); + } + printf("*******************************************************\n"); +} + +CMDLINE_CFG cmd_line_cfg = +{ + { + /* MIN MAX FLAGS OPTION */ + { 2, 2, (CMDLINE_OPTFLAG_ALLOW | CMDLINE_OPTFLAG_MANDAT) }, /* '-a' align1 align2 */ + { 0, 0, CMDLINE_OPTFLAG_ALLOW }, /* '-b' bootstrap */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-c' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-d' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-e' */ + { 1, 1, CMDLINE_OPTFLAG_ALLOW }, /* '-f' flags */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-g' */ + { 1, 1, CMDLINE_OPTFLAG_ALLOW }, /* '-h' */ + { 2, 2, (CMDLINE_OPTFLAG_ALLOW | CMDLINE_OPTFLAG_MANDAT) }, /* '-i arg1 arg2 ' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-j' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-k' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-l' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-m' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-n' */ + { 1, 1, (CMDLINE_OPTFLAG_ALLOW | CMDLINE_OPTFLAG_MANDAT) }, /* '-o arg' */ + { 1, 1, CMDLINE_OPTFLAG_ALLOW }, /* '-p' PROD_ID */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-q' */ + { 1, 1, CMDLINE_OPTFLAG_ALLOW }, /* '-r' REL_ID */ + { 1, 1, CMDLINE_OPTFLAG_ALLOW }, /* '-s' "Release XXX.XXX" */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-t' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-u' */ + { 0, 0, CMDLINE_OPTFLAG_ALLOW }, /* '-v' control VERBOSE/NON-VERBOSE mode */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-w' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-x' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* '-y' */ + { 0, 0, !CMDLINE_OPTFLAG_ALLOW } /* '-z' */ + }, + { 0, 0, !CMDLINE_OPTFLAG_ALLOW }, /* global arguments */ +}; + +/*************************************************************************** + * int nsp_img_write(void* image, char* file, int padding) + * Write out the image. + ***************************************************************************/ +int main(int argc, char* argv[], char* env[]) +{ + FILE* nsp_image = NULL; + int header_version=1; + int cmdline_err; + char* cmdline_error_msg; + + char* filen_kernel; + char* filen_files; + char* filen_out; + + int i,count; /* loop variables */ + int num_sects = 2; /* We require exactly two image with -i option + (see CMDLINE_CFG structure above) */ + int desc_count=0; + int total = 0; + + int header_size=0; + struct nsp_img_hdr_head *img_hdr_head; /* Start of image header */ + struct nsp_img_hdr_info *img_hdr_info; + struct nsp_img_hdr_section_info *img_hdr_section_info ; + struct nsp_img_hdr_sections *img_hdr_sections, *section; /* Section pointers */ + + + /* Configure the command line. */ + cmdline_configure(&cmd_line_cfg); + + /* Read and parse the command line. */ + cmdline_err = cmdline_read(argc, argv); + + /* Check for parsing errors. */ + if(cmdline_err != 0) { + /* Get the parse error message */ + cmdline_error_msg = cmdline_error(cmdline_err); + + /* Print it out */ + printf("%s\n", cmdline_error_msg); + + /* Print our help too */ + print_help(); + return -1; + } + if(cmdline_getopt_count('h') > 0) + { + header_version=atoi(argv[cmdline_getarg(cmdline_getarg_list('h'),0)]); + } + /* Set up arguments */ + filen_kernel = argv[cmdline_getarg(cmdline_getarg_list('i'),0)]; + filen_files = argv[cmdline_getarg(cmdline_getarg_list('i'),1)]; + filen_out = argv[cmdline_getarg(cmdline_getarg_list('o'),0)]; + /* Command line arguments have been parsed. Start doing our work. */ + + /* Caculate the header size, and allocate the memory, and assign the sub pointers */ + header_size = sizeof(struct nsp_img_hdr_head) + /* This has a single section + desc block already */ + (header_version==1?0:4) + + sizeof(struct nsp_img_hdr_info) + + sizeof(struct nsp_img_hdr_section_info) + + sizeof(struct nsp_img_hdr_sections) * num_sects ; + + img_hdr_head = (struct nsp_img_hdr_head *)malloc(header_size); + memset(img_hdr_head, 0x0, header_size); + img_hdr_info = (struct nsp_img_hdr_info*)((char *)img_hdr_head + sizeof(struct nsp_img_hdr_head) + (header_version==1?0:4)); + img_hdr_section_info = (struct nsp_img_hdr_section_info*)((char *)img_hdr_info + sizeof(struct nsp_img_hdr_info)); + img_hdr_sections = (struct nsp_img_hdr_sections*)((char *)img_hdr_section_info + sizeof(struct nsp_img_hdr_section_info)); + section = img_hdr_sections; + memset(img_hdr_head, 0xff, (void*)img_hdr_info - (void*)img_hdr_head); + + img_hdr_head->hdr_version = header_version; + img_hdr_head->hdr_size = header_size; + img_hdr_head->info_offset = (void*)img_hdr_info - (void*)img_hdr_head; + img_hdr_head->sect_info_offset = (void*)img_hdr_section_info - (void*)img_hdr_head; + + img_hdr_section_info->num_sects = num_sects; + img_hdr_section_info->sect_size = sizeof(struct nsp_img_hdr_sections); + img_hdr_section_info->sections_offset = (void*)img_hdr_sections - (void*)img_hdr_head; + +/* chksum = (struct nsp_img_hdr_chksum *) + ((unsigned int)image_hdr + header_size - sizeof(struct nsp_img_hdr_chksum));*/ + + /* Open the out file */ + nsp_image = fopen(filen_out,"wb+"); + if(nsp_image==NULL) { + printf("ERROR: can't open %s for writing.\n", filen_out); + return -1; + } + + /* Skip image header. We'll come back to it after we've written out the images. */ + fseek(nsp_image,header_size,SEEK_SET); + total = ftell(nsp_image); + total = header_size; + printf("total=%x\n",total); + { + int align; + int padding; + char * buf; + align = (header_version==1?0x10000:0x4000); + if(align==0) { + /* The user indicated no padding */ + padding = 0; + } else { + /* Calculate number padding bytes */ + if((total %align) ==0) + padding=0; + else + padding = align - (total % align); + } + if(padding>0) + { + buf=malloc(padding); + memset(buf, 0xff, padding); + if(fwrite((void*)buf,1,padding,nsp_image)!=padding) { + printf("ERROR: can't write to %s.\n", filen_out); + free(buf); + return -1; + } + free(buf); + + } + total+=padding; + + + } + /* Write out all specified images (with -i option) */ + for(i=0; i < num_sects; i++) { + char* file_name; /* input file name */ + FILE* filep; /* input file pointer */ + int padding; /* number of padding bytes to prepend */ + int align; /* align factor from command line */ + int result; /* intermediate result */ + char * buf; + + /* Open the specified image for reading */ + file_name = argv[cmdline_getarg(cmdline_getarg_list('i'),i)]; + filep = fopen(file_name, "rb"); + if(filep==NULL) { + printf("ERROR: can't open file %s for reading.\n", file_name); + return -1; + } + section->flags = ~0x00; + /* Determine file size */ + fseek(filep,0,SEEK_END); + section->raw_size=ftell(filep); + fseek(filep,0,SEEK_SET); + cs_calc_sum(filep,(unsigned long *)§ion->chksum,0); + fseek(filep,0,SEEK_SET); + + /* Retrieve the alignment constant */ + /* Set image offset from the beginning of the out file */ + section->offset=total;// + padding; + + //total += padding; + + /* Copy the image file into nsp_image */ + count = section->raw_size; + buf=malloc(count); + result=fread(buf, 1, count, filep); + fwrite(buf, 1, result, nsp_image); + free(buf); + + /* HACK: This is a hack to get the names and types to the files. + TODO: Fix this to be a real method */ + if(i==0){ + section->type=NSP_IMG_SECTION_TYPE_KERNEL; + strncpy(section->name, "kernel", 16); + } else if(i==1){ + section->type=NSP_IMG_SECTION_TYPE_FILESYSTEM_ROOT; + strncpy(section->name, "root", 16); + } + + /* Account for the total */ + align = strtoul(argv[cmdline_getarg(cmdline_getarg_list('a'),i)],NULL,0); + if(i==0){ + if(align==0 || (((section->raw_size+ section->offset)%align)==0)) + padding=0; + else + padding = align - ((section->raw_size+ section->offset) % align); + + section->total_size=section->raw_size + padding; + } + else{ + #define EXTRA_BLOCK 0x10000 + unsigned int squash_padding; + squash_padding = EXTRA_BLOCK - section->raw_size % EXTRA_BLOCK; + buf=malloc(EXTRA_BLOCK + 4); + memset(buf, 0, squash_padding); + fwrite(buf, 1, squash_padding, nsp_image); + memset(buf, 0, EXTRA_BLOCK + 4); + *((unsigned int *)buf)=0xdec0adde; + *((unsigned int *)(buf+EXTRA_BLOCK))=0xdec0adde; + fwrite(buf, 1, EXTRA_BLOCK+4, nsp_image); + free(buf); + + if(align==0 || (((section->raw_size + (EXTRA_BLOCK + 4 + squash_padding)) %align)==0)) + padding=0; + else + padding = align - ((section->raw_size + (EXTRA_BLOCK + 4 + squash_padding)) % align); + section->total_size=section->raw_size + (EXTRA_BLOCK + 4 + squash_padding) + padding; + } + if(padding>0){ + buf=malloc(padding); + memset(buf, 0xff, padding); + fwrite(buf, 1, padding, nsp_image); + free(buf); + } + printf("*****padding is %d\ttotal_size=%d\traw_size=%d\n",padding, section->total_size, section->raw_size); + + //total += section->raw_size; + total = section->total_size + section->offset; + printf("total=0x%x\n",total); + /* Close the input file */ + fclose(filep); + + /* Move the section pointer to the next slot */ + section++; + } + + /* Take care of the NSP image header fields */ + + /* head fields */ + img_hdr_head->magic = NSP_IMG_MAGIC_NUMBER; + img_hdr_head->boot_offset = img_hdr_sections->offset; + img_hdr_head->flags = ~0x00; /* Set to all 1's */ + + if(cmdline_getopt_count('b')) + img_hdr_head->flags &= ~(NSP_IMG_FLAG_FAILBACK_5 | NSP_IMG_FLAG_FAILBACK_1); + + if(cmdline_getopt_count('f')) + img_hdr_head->flags = strtoul(argv[cmdline_getarg(cmdline_getarg_list('f'),0)], 0, 16); + +#if 0 + img_hdr_head->hdr_version = 2; + img_hdr_head->hdr_size = header_size; +#endif + + if(cmdline_getopt_count('p')) + img_hdr_head->prod_id = strtoul(argv[cmdline_getarg(cmdline_getarg_list('p'),0)], 0, 16); + else + img_hdr_head->prod_id = 0x4C575943; + + if(cmdline_getopt_count('r')) + img_hdr_head->rel_id = strtoul(argv[cmdline_getarg(cmdline_getarg_list('r'),0)], 0, 0); + else + img_hdr_head->rel_id = 0x10203040; + + if(cmdline_getopt_count('s')) + img_hdr_head->version = strtoul(argv[cmdline_getarg(cmdline_getarg_list('s'),0)], 0, 0); + else + img_hdr_head->version = 0x0b040000; + img_hdr_head->image_size = total; +#if 0 + img_hdr_head->info_offset = (unsigned int)(&(image_hdr->info)) - + (unsigned int)image_hdr; + img_hdr_head->sect_info_offset= (unsigned int)(&(image_hdr->sect_info)) - + (unsigned int)image_hdr; +#endif +// image_hdr->head.chksum_offset = (unsigned int)chksum - (unsigned int)image_hdr; + img_hdr_head->chksum_offset = 0xffffffff; +// image_hdr->head.pad1 = 0xffffffff; + /* info fields */ + /* TODO: Fix. Do nothing yet */ +// strncpy(nsp_img_hdr.id.prod_info,NSP_PRODINFO_STRING,sizeof(NSP_PRODINFO_STRING)); + strcpy(img_hdr_info->image_filename, (const char *)basename(filen_out)); + /* section fields */ +#if 0 + img_hdr_section_info->num_sects= num_sects; + img_hdr_section_info->sect_size= sizeof(struct nsp_img_hdr_sections); + img_hdr_section_info->sections_offset= (unsigned int)(&(image_hdr->sections)) - + (unsigned int)image_hdr; +#endif + + /* Calculate checksum(s) */ +#if 0 + chksum->hdr_chksum = cs_calc_buf_sum((char*)image_hdr, + header_size - sizeof(struct nsp_img_hdr_chksum)); +#endif + /* Write out the NSP header. */ + fseek(nsp_image,0,SEEK_SET); + count = fwrite((void*)img_hdr_head, header_size, 1, nsp_image); + if(count!=1) { + printf("ERROR: can't write to %s.\n", filen_out); + return -1; + } + + /* Check if -v option was specified (no arg needed) */ + if(cmdline_getopt_count('v') > 0) + { + struct nsp_img_hdr_head head; + struct nsp_img_hdr *hdr; + + /* Rewind the file back to the beginning */ + fseek(nsp_image,0,SEEK_SET); + + /* Read header from the file */ + fread((void*)&head, sizeof(struct nsp_img_hdr_head), + 1, nsp_image); + + /* Get memory to store the complete header */ + hdr = (struct nsp_img_hdr *)malloc(head.hdr_size); + + /* Read header from the file */ + fseek(nsp_image,0,SEEK_SET); + fread((void*)hdr, head.hdr_size, 1, nsp_image); + + /* Print it out */ + mknspimg_print_hdr(hdr); + printf("Generated total %d bytes\n",total); + free(hdr); + } + + free(img_hdr_head); + + { + struct checksumrecord cr; + cr.magic=CKSUM_MAGIC_NUMBER; + cs_calc_sum(nsp_image, (unsigned long *)&cr.chksum, 0); + fseek(nsp_image,0, SEEK_END); + fwrite(&cr, 1, sizeof(cr), nsp_image); + } + { + FILE * non_web; + char fname[256]; + char * img_buf; + unsigned int len; + strcpy(fname, filen_out); + strcat(fname, ".non_web"); + non_web = fopen(fname,"wb+"); + fseek(nsp_image, 0, SEEK_END); + len = ftell(nsp_image); + img_buf=malloc(len); + fseek(nsp_image, 0, SEEK_SET); + fread(img_buf, 1, len, nsp_image); + img_buf[0xb] = 0x17; + fwrite(img_buf, 1, len-sizeof(struct checksumrecord), non_web); + fclose(non_web); + free(img_buf); + } + /* Close NSP image file */ + fclose(nsp_image); + + /* return result */ + return(0); +} + +#ifdef DMALLOC +#include +#endif /* DMALLOC */ + +#define BUFLEN (1 << 16) + +static unsigned long crctab[256] = +{ + 0x0, + 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, + 0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, + 0x5BD4B01B, 0x569796C2, 0x52568B75, 0x6A1936C8, 0x6ED82B7F, + 0x639B0DA6, 0x675A1011, 0x791D4014, 0x7DDC5DA3, 0x709F7B7A, + 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, + 0xBAEA46EF, 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, + 0xA4AD16EA, 0xA06C0B5D, 0xD4326D90, 0xD0F37027, 0xDDB056FE, + 0xD9714B49, 0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95, + 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1, 0xE13EF6F4, + 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, + 0x2AC12072, 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, + 0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, + 0x7C56B6B0, 0x71159069, 0x75D48DDE, 0x6B93DDDB, 0x6F52C06C, + 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, + 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, + 0xBB60ADFC, 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, + 0x832F1041, 0x87EE0DF6, 0x99A95DF3, 0x9D684044, 0x902B669D, + 0x94EA7B2A, 0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E, + 0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2, 0xC6BCF05F, + 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, + 0x644FC637, 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, + 0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F, 0x5C007B8A, + 0x58C1663D, 0x558240E4, 0x51435D53, 0x251D3B9E, 0x21DC2629, + 0x2C9F00F0, 0x285E1D47, 0x36194D42, 0x32D850F5, 0x3F9B762C, + 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, + 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, + 0xEBA91BBC, 0xEF68060B, 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, + 0xDA649D6F, 0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3, + 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7, 0xAE3AFBA2, + 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, + 0x857130C3, 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, + 0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C, 0x7B827D21, + 0x7F436096, 0x7200464F, 0x76C15BF8, 0x68860BFD, 0x6C47164A, + 0x61043093, 0x65C52D24, 0x119B4BE9, 0x155A565E, 0x18197087, + 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, + 0x2056CD3A, 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, + 0xCC2B1D17, 0xC8EA00A0, 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, + 0xDBEE767C, 0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18, + 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4, 0x89B8FD09, + 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, + 0xA2F33668, 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 +}; + +int cs_is_tagged(FILE *fp) +{ + char buf[8]; + + fseek(fp, -8, SEEK_END); + fread(buf, 8, 1, fp); + if(*(unsigned long*)buf == CKSUM_MAGIC_NUMBER) + return 1; + return 0; +} + +unsigned long cs_read_sum(FILE *fp) +{ + char buf[8]; + + fseek(fp, -8, SEEK_END); + fread(buf, 8, 1, fp); + return *((unsigned long*)&buf[4]); +} + +int cs_calc_sum(FILE *fp, unsigned long *res, int tagged) +{ + unsigned char buf[BUFLEN]; + unsigned long crc = 0; + uintmax_t length = 0; + size_t bytes_read; + + fseek(fp, 0, SEEK_SET); + + while((bytes_read = fread(buf, 1, BUFLEN, fp)) > 0) + { + unsigned char *cp = buf; + + if(length + bytes_read < length) + return 0; + + if(bytes_read != BUFLEN && tagged) + bytes_read -= 8; + + length += bytes_read; + while(bytes_read--) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF]; + } + + if(ferror(fp)) + return 0; + + for(; length; length >>= 8) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ length) & 0xFF]; + + crc = ~crc & 0xFFFFFFFF; + + *res = crc; + + return 1; +} + +unsigned long cs_calc_buf_sum(char *buf, int size) +{ + unsigned long crc = 0; + char *cp = buf; + unsigned long length = size; + + while(size--) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF]; + + for(; length; length >>= 8) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ length) & 0xFF]; + + crc = ~crc & 0xFFFFFFFF; + + return crc; +} + +unsigned long cs_calc_buf_sum_ds(char *buf, int buf_size, char *sign, int sign_len) +{ + unsigned long crc = 0; + char *cp = buf; + unsigned long length = buf_size+sign_len; + + while(buf_size--) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF]; + + cp = sign; + while(sign_len--) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ *cp++) & 0xFF]; + + + for(; length; length >>= 8) + crc =(crc << 8) ^ crctab[((crc >> 24) ^ length) & 0xFF]; + + crc = ~crc & 0xFFFFFFFF; + + return crc; +} + +int cs_set_sum(FILE *fp, unsigned long sum, int tagged) +{ + unsigned long magic = CKSUM_MAGIC_NUMBER; + + if(tagged) + fseek(fp, -8, SEEK_END); + else + fseek(fp, 0, SEEK_END); + + if(fwrite(&magic, 1, 4, fp) < 4) + return 0; + if(fwrite(&sum, 1, 4, fp) < 4) + return 0; + + return 1; +} + +void cs_get_sum(FILE *fp, unsigned long *sum) +{ + unsigned long magic = 0; + + fseek(fp, -8, SEEK_END); + + fread(&magic, 4, 1, fp); + fread(sum, 4, 1, fp); +} + +int cs_validate_file(char *filename) +{ + FILE *pFile = NULL; + unsigned long sum = 0, res = 0; + + if((pFile = fopen(filename, "r")) == NULL) + return 0; + + if(!cs_is_tagged(pFile)) + { + fclose(pFile); + return 0; + } + if(!cs_calc_sum(pFile, &sum, 1)) + { + fclose(pFile); + return 0; + } + cs_get_sum(pFile, &res); + fclose(pFile); + + if(sum != res) + return 0; + return 1; +} + +/* ********* Library internal data ********* */ +#define CMDLINE_TRUE 1 +#define CMDLINE_FALSE 0 + +typedef enum CMDLINE_ERR +{ + CMDLINE_ERR_OK = 0, /* No Error (OK) */ + CMDLINE_ERR_ERROR = -1, /* Unspecified error */ + CMDLINE_ERR_INVKEY = -3, /* Invalid option key */ + CMDLINE_ERR_MANYARG = -4, /* Too many arguments */ + CMDLINE_ERR_FEWARG = -5, /* Too few arguments */ + CMDLINE_ERR_ILLOPT = -6, /* Option not allowed (illegal option) */ + CMDLINE_ERR_NOMEM = -7, /* No memory */ + CMDLINE_ERR_OPTMIS = -8 /* A mandatory option is missing */ +} CMDLINE_ERR; + +/* Argument list */ +typedef struct CMDLINE_ARG +{ + int index; /* Index of the argument in the command line */ + struct CMDLINE_ARG* p_next; /* Next node in the linked list */ +} CMDLINE_ARG; + +/* Master control block for an option */ +typedef struct CMDLINE_ARGS +{ + int argc; /* Total count of arguments found */ + int optc; /* Total count of options found */ + CMDLINE_ARG* list; /* Argument list */ +} CMDLINE_ARGS; + +/* Master control block for all found arguments */ +typedef struct CMDLINE_DATA +{ + CMDLINE_ARGS opt_args[26]; /* Array of MCBs for each option ('a' through 'z') */ + CMDLINE_ARGS glb_args; /* Global arguments */ + int parsed; /* Internal flag to prevent client calls if library is not initialized */ +} CMDLINE_DATA; + +/* ********* Local Data ********* */ +static CMDLINE_CFG cmdline_cfg; +static CMDLINE_DATA cmdline_data; + +char* cmdline_errmsg = "CMDLINE ERROR"; + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +void* cmdline_getarg_list(char opt) +{ + int index = (opt - 'a'); + + /* Check the validity of the index */ + if((index < 0) || (index > 25)) + { + /* ERROR: Wrong option */ + return NULL; + } + + /* Return a pointer to the ARGS control structure */ + return((void*)(&cmdline_data.opt_args[index])); +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +int cmdline_getarg_count(void* list) +{ + CMDLINE_ARGS* p_args = (CMDLINE_ARGS*)list; + + /* Return number of arguments for this option */ + return(p_args->argc); +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +int cmdline_getopt_count(char opt) +{ + int index; + + /* Calculate index value */ + index = opt - 'a'; + if(index < 0 || index > 25) return -1; + + /* Return number of arguments for this option */ + return(cmdline_data.opt_args[index].optc); +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +int cmdline_getarg(void* list, int num) +{ + int i; + CMDLINE_ARGS* p_args = (CMDLINE_ARGS*)list; + CMDLINE_ARG* p_arg; + + /* Search the 'num' argument in the list for this option */ + for(i=0,p_arg=p_args->list; (p_arg!=NULL) && (iargc); i++, p_arg=p_arg->p_next) + { + /* if num matches i, we found it */ + if(i==num) return(p_arg->index); + } + /* We did not find the specified argument or the list was empty */ + return -1; +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +int cmdline_configure(CMDLINE_CFG* p_cfg) +{ + /* reset global data */ + memset(&cmdline_cfg,0,sizeof(cmdline_cfg)); + memset(&cmdline_data,0,sizeof(cmdline_data)); + + /* Copy the user's config structure */ + cmdline_cfg = *p_cfg; + return 0; +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +char* cmdline_error(int err) +{ + /* TODO: implement a table of error messages */ + return(cmdline_errmsg); +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +static void cmdline_print_args(CMDLINE_ARGS* p_arglist, char* argv[]) +{ + CMDLINE_ARG* p_arg; + + printf(" Number of times option was specified: %d\n", p_arglist->optc); + printf(" Number of Arguments: %d\n", p_arglist->argc); + + if(p_arglist->argc > 0) + { + printf(" Argument List: "); + + for(p_arg=p_arglist->list; p_arg != NULL; p_arg=p_arg->p_next) + printf("%s ", argv[p_arg->index]); + } + + printf("\n"); +} + +/* *************************************************************** +* Print all found command line options and their arguments +****************************************************************** */ +void cmdline_print(char* argv[]) +{ + int i; + + /* Check if the command line was parsed */ + if(cmdline_data.parsed != CMDLINE_TRUE) + { + printf("The command line has not been parsed yet.\n"); + return; + } + + /* Print out option arguments */ + for( i = 0; i < 26; i++ ) + { + /* Check if the option was specified */ + if(cmdline_data.opt_args[i].optc !=0 ) + { + /* Print out option name and arguments */ + printf("Option: -%c\n", (char)('a'+i)); + cmdline_print_args(&(cmdline_data.opt_args[i]), argv); + } + } + + /* Print out global arguments */ + printf("Global arguments:\n"); + cmdline_print_args(&(cmdline_data.glb_args), argv); +} + +/* *************************************************************** +* Print configuration +****************************************************************** */ +void cmdline_print_cfg(void) +{ + +} + +static void cmdline_argadd(CMDLINE_ARGS* p_arglist, CMDLINE_ARG* p_arg) +{ + CMDLINE_ARG* p_list; + CMDLINE_ARG* p_prev=NULL; + + /* See if we had anything in the list */ + if(p_arglist->argc == 0) + { + /* Link the argument in */ + p_arglist->list = p_arg; + } + else + { + /* Find the tail of the list */ + for(p_list=p_arglist->list; p_list != NULL; p_list=p_list->p_next) + p_prev = p_list; + + /* Link the argument in */ + p_prev->p_next=p_arg; + } + + /* Keep track of arg number */ + p_arglist->argc++; +} + +/* *************************************************************** +* cmdline_read() +* Read and parse command line arguments +****************************************************************** */ +int cmdline_read(int argc, char* argv[]) +{ + int i, option=0; + + /* Process every command line argument in argv[] array */ + for( i = 1; i < argc; i++ ) + { + /* Does the argument start with a dash? */ + if( *argv[i] == '-' ) + { + /* The argument must be two characters: a dash, and a letter */ + if( strlen(argv[i]) != 2 ) + { + /* ERROR: option syntax (needs to be a dash and one letter) */ + return(CMDLINE_ERR_ERROR); + } + + /* Check validity of the option key ('a' through 'z') */ + if( ((*(argv[i] + 1)) < 'a') || ((*(argv[i] + 1)) > 'z') ) + { + /* ERROR: option sysntax (invalid option key) */ + return(CMDLINE_ERR_INVKEY); + } + + /* Calculate the option index */ + option = (*(argv[i] + 1)) - 'a'; + if((option < 0) || (option > 25)) return(CMDLINE_ERR_INVKEY); + + /* Check to see if the option is allowed */ + if( cmdline_cfg.opts[option].flags & CMDLINE_OPTFLAG_ALLOW ) + { + /* Option allowed. */ + cmdline_data.opt_args[option].optc++; + continue; + } + else + { + /* ERROR: Option is not allowed */ + return(CMDLINE_ERR_ILLOPT); + } + } + else + { + /* Read the arguments for the option */ + CMDLINE_ARG* p_arg; + + /* Allocate space for the argument node */ + p_arg = (CMDLINE_ARG*)calloc(1,sizeof(CMDLINE_ARG)); + if( p_arg== NULL ) + { + /* ERROR: Can't allocate memory for the argument index */ + return(CMDLINE_ERR_NOMEM); + } + + /* Initialize the argument */ + p_arg->index = i; + p_arg->p_next = NULL; + + /* Check if we can add to the list of arguments for this option */ + if( (option < 0) /* Do we have to add to the global list? */ + || (cmdline_data.opt_args[option].argc == cmdline_cfg.opts[option].max) /* Did we reach MAX arguments? */ + ) + { + /* This option does not require arguments. Keep the argument in the global list. */ + cmdline_argadd(&(cmdline_data.glb_args), p_arg); + continue; + } + else + { + /* See if the current count has reached max for this option */ + if( cmdline_data.opt_args[option].argc == cmdline_cfg.opts[option].max ) + { + /* ERROR: too many arguments for an option */ + return(CMDLINE_ERR_MANYARG); + } + else + { + /* Link the argument to the arg list of the option */ + cmdline_argadd(&(cmdline_data.opt_args[option]), p_arg); + continue; + } + } + } + } + + /* ****** We read the complete command line. See if what we collected matches the configuration ******* */ + + /* Check every collected option against its configuration */ + for( i=0; i < 26; i++ ) + { + /* Check if this option was allowed */ + if(cmdline_cfg.opts[i].flags & CMDLINE_OPTFLAG_ALLOW) + { + /* See if it was mandatory */ + if(cmdline_cfg.opts[i].flags & CMDLINE_OPTFLAG_MANDAT) + { + /* Check if we really collected this option on the command line. */ + if(cmdline_data.opt_args[i].optc == 0) + { + /* ERROR: a missing mandatory option */ + return(CMDLINE_ERR_OPTMIS); + } + else + { + /* Option was there. Check how many args we got for it. */ + if(cmdline_data.opt_args[i].argc < cmdline_cfg.opts[i].min) + { + /* ERROR: too few arguments for an option */ + return(CMDLINE_ERR_FEWARG); + } + else + { + /* This mandatory option was proper. */ + continue; + } + } + } + else /* This is non-mandatory option: */ + { + /* Check if the option was specified on the command line */ + if(cmdline_data.opt_args[i].optc == 0) + { + /* option wasn't specified, go to the next */ + continue; + } + else + { + /* Option was there. Check how many args we collected for it. */ + if(cmdline_data.opt_args[i].argc < cmdline_cfg.opts[i].min) + { + /* ERROR: too few arguments for a non-mandatory option */ + return(CMDLINE_ERR_FEWARG); + } + else + { + /* This non-mandatory option was proper. */ + continue; + } + } + } + } + else /* Option was not allowed. */ + { + /* We should not get here as the non-allowed options should have been + trapped eariler. */ + } + } + + /* Command line was proper as far as the number of options and their arguments */ + cmdline_data.parsed = CMDLINE_TRUE; + return(CMDLINE_ERR_OK); +} diff --git a/trunk/tools/firmware-utils/src/mktitanimg.h b/trunk/tools/firmware-utils/src/mktitanimg.h new file mode 100644 index 00000000..9ff30f60 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mktitanimg.h @@ -0,0 +1,171 @@ +#ifndef __MKTITANIMG_H +#define __MKTITANIMG_H + +#ifndef CFGMGR_CKSUM_H +#define CFGMGR_CKSUM_H + +#define CKSUM_MAGIC_NUMBER 0xC453DE23 + +#include +#include +#include + +int cs_is_tagged(FILE*); +unsigned long cs_read_sum(FILE*); +int cs_calc_sum(FILE*, unsigned long*, int); +int cs_set_sum(FILE*, unsigned long, int); +void cs_get_sum(FILE*, unsigned long*); +unsigned long cs_calc_buf_sum(char*, int); +int cs_validate_file(char*); + +#endif +#ifndef ___CMDLINE_H___ +#define ___CMDLINE_H___ + +/* ********* Library Configuration ********* */ +typedef struct CMDLINE_OPT +{ + int min; /* Minimum number of arguments this option takes */ + int max; /* Maximum number of arguments this option takes */ + int flags; /* Controlling flags (whether to accept or not, etc) */ +} CMDLINE_OPT; + +typedef struct CMDLINE_CFG +{ + CMDLINE_OPT opts[26]; /* Options 'a' through 'z' */ + CMDLINE_OPT global; /* Global option (outside 'a'..'z') */ +} CMDLINE_CFG; +/* ******************************************** */ + +#define CMDLINE_OPTFLAG_ALLOW 0x1 /* The option is allowed */ +#define CMDLINE_OPTFLAG_MANDAT 0x2 /* The option is mandatory */ + +extern void cmdline_print(char* argv[]); + +extern int cmdline_configure(CMDLINE_CFG* p_cfg); +extern int cmdline_read(int argc, char* argv[]); + +extern void* cmdline_getarg_list(char opt); +extern int cmdline_getarg_count(void* list); +extern int cmdline_getopt_count(char opt); +extern int cmdline_getarg(void* list, int num); + +extern char* cmdline_error(int err); +#endif + + +#ifndef _NSPIMGHDR_H_ +#define _NSPIMGHDR_H_ + +/* This file describes the header format for the single image. The image is broken + up into several pieces. The image contains this header plus 1 or more sections. + Each section contains a binary block that could be a kernel, filesystem, etc. The + only garentee for this is that the very first section MUST be executable. Meaning + that the bootloader will be able to take the address of the header start, add the + header size, and execute that binary block. The header has its own checksum. It + starts hdr_size-4 bytes from the start of the header. + */ + +struct nsp_img_hdr_head +{ + unsigned int magic; /* Magic number to identify this image header */ + unsigned int boot_offset; /* Offset from start of header to kernel code. */ + unsigned int flags; /* Image flags. */ + unsigned int hdr_version; /* Version of this header. */ + unsigned int hdr_size; /* The complete size of all portions of the header */ + unsigned int prod_id; /* This product id */ + unsigned int rel_id; /* Which release this is */ + unsigned int version; /* name-MMM.nnn.ooo-rxx => 0xMMnnooxx. See comment + below */ + unsigned int image_size; /* Image size (including header) */ + unsigned int info_offset; /* Offset from start of header to info block */ + unsigned int sect_info_offset; /* Offset from start of header to section desc */ + unsigned int chksum_offset; /* Offset from start of header to chksum block */ +// unsigned int pad1; +}; + +/* The patch id is a machine readable value that takes the normal patch level, and encodes + the correct numbers inside of it. The format of the patches are name-MM.NN.oo-rxx.bin. + Convert MM, NN, oo, and xx into hex, and encode them as 0xMMNNooxx. Thus: + att-1.2.18-r14.bin => 0x0102120e */ + +/* The following are the flag bits for the above flags variable */ +/* List of NSP status flags: */ +#define NSP_IMG_FLAG_FAILBACK_MASK 0xF8000000 + +/* NSP Image status flag: Flag indicates individual sections image */ +#define NSP_IMG_FLAG_INDIVIDUAL 0x00000001 + +/* NSP Image status flag 1: Image contains a bootable image when this bit is 0 */ +#define NSP_IMG_FLAG_FAILBACK_1 0x08000000 + +/* NSP Image status flag 2: Image contains a non-bootable image when this bit is 0 */ +#define NSP_IMG_FLAG_FAILBACK_2 0x10000000 + +/* NSP Image status flag 3: PSPBoot has tried the image when this bit is 0 */ +#define NSP_IMG_FLAG_FAILBACK_3 0x20000000 + +/* NSP Image status flag 4: Image is now secondary image when this bit is 0 */ +#define NSP_IMG_FLAG_FAILBACK_4 0x40000000 + +/* NSP Image status flag 5: Image contains a valid image when this bit is 0 */ +#define NSP_IMG_FLAG_FAILBACK_5 0x80000000 + +/* NSP Single image magic number */ +#define NSP_IMG_MAGIC_NUMBER 0x4D544443 + + +struct nsp_img_hdr_info +{ + char release_name[64]; /* Name of release */ + char image_filename[64]; /* name-mm.nn.oo-rxx.bin format */ +}; + +struct nsp_img_hdr_section_info +{ + unsigned int num_sects; /* Number of section (and section desc blocks) in this + image */ + unsigned int sect_size; /* Size of a SINGLE section_desc block */ + unsigned int sections_offset; /* Offset to from start of header to the start of + the section blocks */ +}; + +/* There will be one of more of the following stuctures in the image header. Each + section will have one of these blocks. */ +struct nsp_img_hdr_sections +{ + unsigned int offset; /* Offset of section from start of NSP_IMG_HDR_HEAD */ + unsigned int total_size; /* Size of section (including pad size.) */ + unsigned int raw_size; /* Size of section only */ + unsigned int flags; /* Section flags */ + unsigned int chksum; /* Section checksum */ + unsigned int type; /* Section type. What kind of info does this section + describe */ + char name[16]; /* Reference name for this section. */ +}; +#define NSP_IMG_SECTION_TYPE_KERNEL (0x01) +#define NSP_IMG_SECTION_TYPE_FILESYSTEM_ROOT (0x02) +#define NSP_IMG_SECTION_TYPE_FILESYSTEM (0x03) + +struct nsp_img_hdr +{ + struct nsp_img_hdr_head head; /* Head portion */ + struct nsp_img_hdr_info info; /* Info */ + struct nsp_img_hdr_section_info sect_info; /* Section block */ + struct nsp_img_hdr_sections sections; /* 1 or more section_description blocks. More + section_desc blocks will be appended here + for each additional section needed */ +}; + +struct nsp_img_hdr_chksum +{ + unsigned int hdr_chksum; /* The checksum for the complete header. Excepting the + checksum block */ +}; + +struct nsp_img_hdr_sections *nsp_img_hdr_get_section_ptr_by_name(struct nsp_img_hdr *hdr, char *name); +unsigned int nsp_img_hdr_get_section_offset_by_name(struct nsp_img_hdr *hdr, char *name); +unsigned int nsp_img_hdr_get_section_size_by_name(struct nsp_img_hdr *hdr, char *name); + +#endif +#endif /* __MKTITANIMG_H */ diff --git a/trunk/tools/firmware-utils/src/mktplinkfw.c b/trunk/tools/firmware-utils/src/mktplinkfw.c new file mode 100644 index 00000000..81004451 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mktplinkfw.c @@ -0,0 +1,1170 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * This tool was based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include +#include + +#include "md5.h" + +#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + +#define HEADER_VERSION_V1 0x01000000 +#define HWID_GL_INET_V1 0x08000001 +#define HWID_GS_OOLITE_V1 0x3C000101 +#define HWID_TL_MR10U_V1 0x00100101 +#define HWID_TL_MR13U_V1 0x00130101 +#define HWID_TL_MR3020_V1 0x30200001 +#define HWID_TL_MR3220_V1 0x32200001 +#define HWID_TL_MR3220_V2 0x32200002 +#define HWID_TL_MR3420_V1 0x34200001 +#define HWID_TL_MR3420_V2 0x34200002 +#define HWID_TL_WA701N_V1 0x07010001 +#define HWID_TL_WA701N_V2 0x07010002 +#define HWID_TL_WA7210N_V2 0x72100002 +#define HWID_TL_WA7510N_V1 0x75100001 +#define HWID_TL_WA801ND_V1 0x08010001 +#define HWID_TL_WA830RE_V1 0x08300010 +#define HWID_TL_WA830RE_V2 0x08300002 +#define HWID_TL_WA801ND_V2 0x08010002 +#define HWID_TL_WA901ND_V1 0x09010001 +#define HWID_TL_WA901ND_V2 0x09010002 +#define HWID_TL_WDR4300_V1_IL 0x43008001 +#define HWID_TL_WDR4900_V1 0x49000001 +#define HWID_TL_WR703N_V1 0x07030101 +#define HWID_TL_WR720N_V3 0x07200103 +#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_WR743ND_V2 0x07430002 +#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_WR1043ND_V2 0x10430002 +#define HWID_TL_WR1041N_V2 0x10410002 +#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 */ +}; + +struct fw_header { + uint32_t version; /* header version */ + char vendor_name[24]; + char fw_version[36]; + uint32_t hw_id; /* hardware id */ + uint32_t hw_rev; /* hardware revision */ + uint32_t unk1; + uint8_t md5sum1[MD5SUM_LEN]; + uint32_t unk2; + uint8_t md5sum2[MD5SUM_LEN]; + uint32_t unk3; + uint32_t kernel_la; /* kernel load address */ + uint32_t kernel_ep; /* kernel entry point */ + uint32_t fw_length; /* total length of the firmware */ + uint32_t kernel_ofs; /* kernel data offset */ + uint32_t kernel_len; /* kernel data length */ + uint32_t rootfs_ofs; /* rootfs data offset */ + uint32_t rootfs_len; /* rootfs data length */ + uint32_t boot_ofs; /* bootloader data offset */ + uint32_t boot_len; /* bootloader data length */ + uint16_t ver_hi; + uint16_t ver_mid; + uint16_t ver_lo; + uint8_t pad[354]; +} __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 board_info { + char *id; + uint32_t hw_id; + uint32_t hw_rev; + char *layout_id; +}; + +/* + * Globals + */ +static char *ofname; +static char *progname; +static char *vendor = "TP-LINK Technologies"; +static char *version = "ver. 1.0"; +static char *fw_ver = "0.0.0"; + +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 char *opt_hw_rev; +static uint32_t hw_rev; +static int fw_ver_lo; +static int fw_ver_mid; +static int fw_ver_hi; +static 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; +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}; +static uint32_t fw_max_len; +static uint32_t reserved_space; + +static struct file_info inspect_info; +static int extract = 0; + +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] = { + 0x8c, 0xef, 0x33, 0x5b, 0xd5, 0xc5, 0xce, 0xfa, + 0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, +}; + +static struct flash_layout layouts[] = { + { + .id = "4M", + .fw_max_len = 0x3c0000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x140000, + }, { + .id = "4Mlzma", + .fw_max_len = 0x3c0000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x100000, + }, { + .id = "8M", + .fw_max_len = 0x7c0000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x140000, + }, { + .id = "8Mlzma", + .fw_max_len = 0x7c0000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x100000, + }, { + .id = "16M", + .fw_max_len = 0xf80000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x140000, + }, { + .id = "16Mlzma", + .fw_max_len = 0xf80000, + .kernel_la = 0x80060000, + .kernel_ep = 0x80060000, + .rootfs_ofs = 0x100000, + }, { + .id = "16Mppc", + .fw_max_len = 0xf80000, + .kernel_la = 0x00000000, + .kernel_ep = 0xc0000000, + .rootfs_ofs = 0x2a0000, + }, { + /* terminating entry */ + } +}; + +static struct board_info boards[] = { + { + .id = "TL-MR10Uv1", + .hw_id = HWID_TL_MR10U_V1, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-MR13Uv1", + .hw_id = HWID_TL_MR13U_V1, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .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-MR3220v2", + .hw_id = HWID_TL_MR3220_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-MR3420v1", + .hw_id = HWID_TL_MR3420_V1, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-MR3420v2", + .hw_id = HWID_TL_MR3420_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-WA701Nv1", + .hw_id = HWID_TL_WA701N_V1, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-WA701Nv2", + .hw_id = HWID_TL_WA701N_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-WA7210N", + .hw_id = HWID_TL_WA7210N_V2, + .hw_rev = 2, + .layout_id = "4Mlzma", + }, { + .id = "TL-WA7510N", + .hw_id = HWID_TL_WA7510N_V1, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-WA801NDv1", + .hw_id = HWID_TL_WA801ND_V1, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-WA830REv1", + .hw_id = HWID_TL_WA830RE_V1, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-WA830REv2", + .hw_id = HWID_TL_WA830RE_V2, + .hw_rev = 1, + .layout_id = "4M", + }, { + .id = "TL-WA801NDv2", + .hw_id = HWID_TL_WA801ND_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .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-WDR4300v1", + .hw_id = HWID_TL_WDR4300_V1_IL, + .hw_rev = 1, + .layout_id = "8Mlzma", + }, { + .id = "TL-WDR4900v1", + .hw_id = HWID_TL_WDR4900_V1, + .hw_rev = 1, + .layout_id = "16Mppc", + }, { + .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-WR743NDv2", + .hw_id = HWID_TL_WR743ND_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .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-WR1041Nv2", + .hw_id = HWID_TL_WR1041N_V2, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-WR1043NDv1", + .hw_id = HWID_TL_WR1043ND_V1, + .hw_rev = 1, + .layout_id = "8M", + }, { + .id = "TL-WR1043NDv2", + .hw_id = HWID_TL_WR1043ND_V2, + .hw_rev = 1, + .layout_id = "8Mlzma", + }, { + .id = "TL-WR2543Nv1", + .hw_id = HWID_TL_WR2543N_V1, + .hw_rev = 1, + .layout_id = "8Mlzma", + }, { + .id = "TL-WR703Nv1", + .hw_id = HWID_TL_WR703N_V1, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "TL-WR720Nv3", + .hw_id = HWID_TL_WR720N_V3, + .hw_rev = 1, + .layout_id = "4Mlzma", + }, { + .id = "GL-INETv1", + .hw_id = HWID_GL_INET_V1, + .hw_rev = 1, + .layout_id = "8Mlzma", + }, { + .id = "GS-OOLITEv1", + .hw_id = HWID_GS_OOLITE_V1, + .hw_rev = 1, + .layout_id = "16Mlzma", + }, { + /* 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 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; +} + +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, +"\n" +"Options:\n" +" -B create image for the board specified with \n" +" -c use combined kernel image\n" +" -E overwrite kernel entry point with (hexval prefixed with 0x)\n" +" -L overwrite kernel load address with (hexval prefixed with 0x)\n" +" -H use hardware id specified with \n" +" -W use hardware revision specified with \n" +" -F use flash layout specified with \n" +" -k read kernel image from the file \n" +" -r read rootfs image from the file \n" +" -a align the rootfs start on an bytes boundary\n" +" -R overwrite rootfs offset with (hexval prefixed with 0x)\n" +" -o write output to the file \n" +" -s strip padding from the end of the image\n" +" -j add jffs2 end-of-filesystem markers\n" +" -N set image vendor to \n" +" -V set image version to \n" +" -v set firmware version to \n" +" -i inspect given firmware file \n" +" -x extract kernel and rootfs while inspecting (requires -i)\n" +" -X reserve 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; + + 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 (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; + hw_rev = board->hw_rev; + } else { + if (layout_id == NULL) { + ERR("flash layout is not specified"); + 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; + } + + layout = find_layout(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 (reserved_space > layout->fw_max_len) { + ERR("reserved space is not valid"); + return -1; + } + + fw_max_len = layout->fw_max_len - reserved_space; + + 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 > + 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); + kernel_len = ALIGN(kernel_len, rootfs_align); + kernel_len -= sizeof(struct fw_header); + + DBG("kernel length aligned to %u", kernel_len); + + if (kernel_len + rootfs_info.file_size > + 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 > + (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; + } + + return 0; +} + +static 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); + 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) { + hdr->rootfs_ofs = htonl(rootfs_ofs); + hdr->rootfs_len = htonl(rootfs_info.file_size); + } + + hdr->ver_hi = htons(fw_ver_hi); + hdr->ver_mid = htons(fw_ver_mid); + hdr->ver_lo = htons(fw_ver_lo); + + 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); + } + 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; + } + + 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 (!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("(== libreCMC default)\n"); + else + printf("(libreCMC 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("(== libreCMC default)\n"); + else + printf("(libreCMC 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; iversion) != HEADER_VERSION_V1) { + ERR("file does not seem to have V1 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)); + 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) != 0) + inspect_fw_phexdec("Unknown value 3", hdr->unk3); + + printf("\n"); + + 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)); + } + + printf("\n"); + + inspect_fw_phexdec("Kernel data offset", + 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_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; + 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:xX:hsjv:"); + if (c == -1) + break; + + switch (c) { + case 'a': + sscanf(optarg, "0x%x", &rootfs_align); + break; + case 'B': + board_id = optarg; + 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 'L': + sscanf(optarg, "0x%x", &kernel_la); + break; + case 'V': + version = optarg; + break; + case 'v': + fw_ver = 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 'h': + usage(EXIT_SUCCESS); + break; + case 'X': + sscanf(optarg, "0x%x", &reserved_space); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + ret = check_options(); + if (ret) + goto out; + + if (!inspect_info.file_name) + ret = build_fw(); + else + ret = inspect_fw(); + + out: + return ret; +} + diff --git a/trunk/tools/firmware-utils/src/mktplinkfw2.c b/trunk/tools/firmware-utils/src/mktplinkfw2.c new file mode 100644 index 00000000..2e20f15b --- /dev/null +++ b/trunk/tools/firmware-utils/src/mktplinkfw2.c @@ -0,0 +1,977 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * This tool was based on: + * TP-Link WR941 V2 firmware checksum fixing tool. + * Copyright (C) 2008,2009 Wang Jian + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include +#include + +#include +#include + +#include "md5.h" + +#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + +#define MD5SUM_LEN 16 + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +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 unk1; /* 0x3c: 0x00000000 */ + 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: FIXME: seems to be unused */ + uint32_t boot_len; /* 0x88: FIXME: seems to be unused */ + 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)); + +struct flash_layout { + char *id; + uint32_t fw_max_len; + uint32_t kernel_la; + uint32_t kernel_ep; + uint32_t rootfs_ofs; +}; + +struct board_info { + char *id; + uint32_t hw_id; + uint32_t hw_rev; + char *layout_id; + uint32_t hdr_ver; + bool endian_swap; +}; + +/* + * Globals + */ +static char *ofname; +static 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 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 char *opt_hw_rev; +static uint32_t hw_rev; +static int fw_ver_lo; +static int fw_ver_mid; +static int fw_ver_hi; +static int sver_lo; +static int sver_hi; +static 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; +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}; + +static struct file_info inspect_info; +static int extract = 0; +static bool endian_swap = false; + +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, 0x5b, 0xd5, 0xc5, 0xce, 0xfa, + 0xa7, 0x9c, 0x28, 0xda, 0xb2, 0xe9, 0x0f, 0x42, +}; + +static struct flash_layout layouts[] = { + { + .id = "8Mltq", + .fw_max_len = 0x7a0000, + .kernel_la = 0x80002000, + .kernel_ep = 0x80002000, + .rootfs_ofs = 0x140000, + }, { + .id = "8Mmtk", + .fw_max_len = 0x7a0000, + .kernel_la = 0x80000000, + .kernel_ep = 0x80000000, + .rootfs_ofs = 0x140000, + }, { + /* terminating entry */ + } +}; + +static struct board_info boards[] = { + { + .id = "TD-W8970v1", + .hw_id = 0x89700001, + .hw_rev = 1, + .layout_id = "8Mltq", + }, { + .id = "ArcherC20i", + .hw_id = 0xc2000001, + .hw_rev = 58, + .layout_id = "8Mmtk", + .hdr_ver = 3, + .endian_swap = true, + }, { + /* 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 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; +} + +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, +"\n" +"Options:\n" +" -B create image for the board specified with \n" +" -c use combined kernel image\n" +" -E overwrite kernel entry point with (hexval prefixed with 0x)\n" +" -L overwrite kernel load address with (hexval prefixed with 0x)\n" +" -H use hardware id specified with \n" +" -W use hardware revision specified with \n" +" -F use flash layout specified with \n" +" -k read kernel image from the file \n" +" -r read rootfs image from the file \n" +" -a align the rootfs start on an bytes boundary\n" +" -R overwrite rootfs offset with (hexval prefixed with 0x)\n" +" -o write output to the file \n" +" -s strip padding from the end of the image\n" +" -j add jffs2 end-of-filesystem markers\n" +" -V set image version to \n" +" -v set firmware version to \n" +" -y set secondary version to \n" +" -i inspect given firmware file \n" +" -x extract kernel and rootfs while inspecting (requires -i)\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; + + 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 (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; + hw_rev = board->hw_rev; + if (board->hdr_ver) + hdr_ver = board->hdr_ver; + endian_swap = board->endian_swap; + } else { + if (layout_id == NULL) { + ERR("flash layout is not specified"); + 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; + } + + layout = find_layout(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); + kernel_len = ALIGN(kernel_len, rootfs_align); + kernel_len -= sizeof(struct fw_header); + + DBG("kernel length aligned to %u", kernel_len); + + 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; +} + +static 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(hw_id); + hdr->hw_rev = htonl(hw_rev); + + 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->unk1 = htonl(0); + 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 (endian_swap) { + hdr->kernel_la = bswap_32(hdr->kernel_la); + hdr->kernel_ep = bswap_32(hdr->kernel_ep); + } + + 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); + } + 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; + } + + 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 (!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("(== libreCMC default)\n"); + else + printf("(libreCMC 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("(== libreCMC default)\n"); + else + printf("(libreCMC 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; iversion))) { + 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)); + + 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)); + 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); + + 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)); + } + + 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)); + 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_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; + 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:xhsjv:y:T:e"); + if (c == -1) + break; + + switch (c) { + case 'a': + sscanf(optarg, "0x%x", &rootfs_align); + break; + case 'B': + board_id = optarg; + 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 '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': + endian_swap = true; + 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(); + else + ret = inspect_fw(); + + out: + return ret; +} + diff --git a/trunk/tools/firmware-utils/src/mkwrgimg.c b/trunk/tools/firmware-utils/src/mkwrgimg.c new file mode 100644 index 00000000..3915d146 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkwrgimg.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2011 Gabor Juhos + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 WRG_MAGIC 0x20040220 + +struct wrg_header { + char signature[32]; + uint32_t magic1; + uint32_t magic2; + 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 *dev_name; +static uint32_t offset; +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" +" -i read input from the file \n" +" -d set device name to \n" +" -o write output to the file \n" +" -O set offset to \n" +" -s set image signature to \n" +" -h show this screen\n" + ); + + exit(status); +} + +static void put_u32(void *data, uint32_t val) +{ + unsigned char *p = data; + + if (big_endian) { + 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 wrg_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 wrg_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:o:s:O:h"); + if (c == -1) + break; + + switch (c) { + case 'b': + big_endian = 1; + break; + case 'd': + dev_name = optarg; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 's': + signature = 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 (dev_name == NULL) { + ERR("no device name specified"); + goto err; + } + + err = stat(ifname, &st); + if (err){ + ERRS("stat failed on %s", ifname); + goto err; + } + + buflen = st.st_size + sizeof(struct wrg_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 wrg_header), st.st_size, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto close_in; + } + + header = (struct wrg_header *) buf; + memset(header, '\0', sizeof(struct wrg_header)); + + strncpy(header->signature, signature, sizeof(header->signature)); + strncpy(header->devname, dev_name, sizeof(header->signature)); + put_u32(&header->magic1, WRG_MAGIC); + put_u32(&header->magic2, WRG_MAGIC); + put_u32(&header->size, st.st_size); + put_u32(&header->offset, offset); + + get_digest(header, buf + sizeof(struct wrg_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/trunk/tools/firmware-utils/src/mkzcfw.c b/trunk/tools/firmware-utils/src/mkzcfw.c new file mode 100644 index 00000000..7674e706 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkzcfw.c @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2010 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#include "cyg_crc.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 + +#define MAGIC_FIRMWARE 0x6d726966 /* 'firm' */ +#define MAGIC_KERNEL 0x676d694b /* 'Kimg' */ +#define MAGIC_ROOTFS 0x676d6952 /* 'Rimg' */ + +struct file_info { + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ +}; + +struct fw_header { + uint32_t magic; + uint32_t length; + uint32_t unk1; + uint32_t unk2; +} __attribute__ ((packed)); + +struct fw_tail { + uint32_t hw_id; + uint32_t crc; +} __attribute__ ((packed)); + +struct board_info { + char *id; + uint32_t hw_id; + uint32_t kernel_len; + uint32_t rootfs_len; +}; + +/* + * Globals + */ +static char *ofname; +static char *progname; + +static char *board_id; +static struct board_info *board; +static struct file_info kernel_info; +static struct file_info rootfs_info; + + +static struct board_info boards[] = { + { + .id = "ZCN-1523H-2-8", + .hw_id = 0x66661523, + .kernel_len = 0x170000, + .rootfs_len = 0x610000, + }, { + .id = "ZCN-1523H-5-16", + .hw_id = 0x6615235A, + .kernel_len = 0x170000, + .rootfs_len = 0x610000, + }, { + /* 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 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" +" -B create image for the board specified with \n" +" -k read kernel image from the file \n" +" -r read rootfs image from the file \n" +" -o write output to the file \n" +" -h show this screen\n" + ); + + exit(status); +} + +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; + + if (board_id == NULL) { + ERR("no board specified"); + return -1; + } + + board = find_board(board_id); + if (board == NULL) { + ERR("unknown/unsupported board id \"%s\"", board_id); + return -1; + } + + if (kernel_info.file_name == NULL) { + ERR("no kernel image specified"); + return -1; + } + + ret = get_file_stat(&kernel_info); + if (ret) + return ret; + + if (kernel_info.file_size > board->kernel_len) { + ERR("kernel image is too big"); + return -1; + } + + 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_info.file_size > board->rootfs_len) { + ERR("rootfs image is too big"); + return -1; + } + + if (ofname == NULL) { + ERR("no output file specified"); + 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 int build_fw(void) +{ + int buflen; + char *buf; + char *p; + int ret = EXIT_FAILURE; + int writelen = 0; + uint32_t crc; + struct fw_header *hdr; + struct fw_tail *tail; + + buflen = 3 * sizeof(struct fw_header) + + kernel_info.file_size + rootfs_info.file_size + + 3 * sizeof(struct fw_tail); + + buf = malloc(buflen); + if (!buf) { + ERR("no memory for buffer\n"); + goto out; + } + + p = buf; + memset(p, 0, buflen); + + /* fill firmware header */ + hdr = (struct fw_header *) p; + hdr->magic = HOST_TO_LE32(MAGIC_FIRMWARE); + hdr->length = HOST_TO_LE32(buflen - sizeof(struct fw_header)); + p += sizeof(struct fw_header); + + /* fill kernel block header */ + hdr = (struct fw_header *) p; + hdr->magic = HOST_TO_LE32(MAGIC_KERNEL); + hdr->length = HOST_TO_LE32(kernel_info.file_size + + sizeof(struct fw_tail)); + p += sizeof(struct fw_header); + + /* read kernel data */ + ret = read_to_buf(&kernel_info, p); + if (ret) + goto out_free_buf; + + /* fill firmware tail */ + tail = (struct fw_tail *) (p + kernel_info.file_size); + tail->hw_id = HOST_TO_BE32(board->hw_id); + tail->crc = HOST_TO_BE32(cyg_crc32(p, kernel_info.file_size + + sizeof(struct fw_tail) - 4)); + + p += kernel_info.file_size + sizeof(struct fw_tail); + + /* fill rootfs block header */ + hdr = (struct fw_header *) p; + hdr->magic = HOST_TO_LE32(MAGIC_ROOTFS); + hdr->length = HOST_TO_LE32(rootfs_info.file_size + + sizeof(struct fw_tail)); + p += sizeof(struct fw_header); + + /* read rootfs data */ + ret = read_to_buf(&rootfs_info, p); + if (ret) + goto out_free_buf; + + /* fill firmware tail */ + tail = (struct fw_tail *) (p + rootfs_info.file_size); + tail->hw_id = HOST_TO_BE32(board->hw_id); + tail->crc = HOST_TO_BE32(cyg_crc32(p, rootfs_info.file_size + + sizeof(struct fw_tail) - 4)); + + p += rootfs_info.file_size + sizeof(struct fw_tail); + + /* fill firmware tail */ + tail = (struct fw_tail *) p; + tail->hw_id = HOST_TO_BE32(board->hw_id); + tail->crc = HOST_TO_BE32(cyg_crc32(buf + sizeof(struct fw_header), + buflen - sizeof(struct fw_header) - 4)); + + 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; + int err; + + FILE *outfile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "B:k:r:o:h"); + if (c == -1) + break; + + switch (c) { + case 'B': + board_id = optarg; + break; + case 'k': + kernel_info.file_name = optarg; + break; + case 'r': + rootfs_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 = build_fw(); + + out: + return ret; +} + diff --git a/trunk/tools/firmware-utils/src/mkzynfw.c b/trunk/tools/firmware-utils/src/mkzynfw.c new file mode 100644 index 00000000..58be0757 --- /dev/null +++ b/trunk/tools/firmware-utils/src/mkzynfw.c @@ -0,0 +1,1130 @@ +/* + * + * Copyright (C) 2007-2008 OpenWrt.org + * Copyright (C) 2007-2008 Gabor Juhos + * + * This code was based on the information of the ZyXEL's firmware + * image format written by Kolja Waschk, can be found at: + * http://www.ixo.de/info/zyxel_uclinux + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include +#include /* for __BYTE_ORDER */ +#if defined(__CYGWIN__) +# include +#endif + +#include "zynos.h" + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +# define LE16_TO_HOST(x) (x) +# define LE32_TO_HOST(x) (x) +# define HOST_TO_BE16(x) bswap_16(x) +# define HOST_TO_BE32(x) bswap_32(x) +# define BE16_TO_HOST(x) bswap_16(x) +# define BE32_TO_HOST(x) bswap_32(x) +#else +# define HOST_TO_BE16(x) (x) +# define HOST_TO_BE32(x) (x) +# define BE16_TO_HOST(x) (x) +# define BE32_TO_HOST(x) (x) +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE16_TO_HOST(x) bswap_16(x) +# define LE32_TO_HOST(x) bswap_32(x) +#endif + +#define ALIGN(x,y) (((x)+((y)-1)) & ~((y)-1)) + +#define MAX_NUM_BLOCKS 8 +#define MAX_ARG_COUNT 32 +#define MAX_ARG_LEN 1024 +#define FILE_BUF_LEN (16*1024) + + +struct csum_state{ + int odd; + uint32_t sum; + uint32_t tmp; +}; + +struct fw_block { + uint32_t align; /* alignment of this block */ + char *file_name; /* name of the file */ + uint32_t file_size; /* length of the file */ + char *mmap_name; /* name in the MMAP table */ + int type; /* block type */ + uint32_t padlen; + uint8_t padc; +}; + +#define BLOCK_TYPE_BOOTEXT 0 +#define BLOCK_TYPE_RAW 1 + +struct fw_mmap { + uint32_t addr; + uint32_t size; + uint32_t user_addr; + uint32_t user_size; +}; +#define MMAP_DATA_SIZE 1024 +#define MMAP_ALIGN 16 + +struct board_info { + char *name; /* model name */ + char *desc; /* description */ + uint16_t vendor; /* vendor id */ + uint16_t model; /* model id */ + uint32_t flash_base; /* flash base address */ + uint32_t flash_size; /* board flash size */ + uint32_t code_start; /* code start address */ + uint32_t romio_offs; /* offset of the firmware within the flash */ + uint32_t bootext_size; /* maximum size of bootext block */ +}; + +/* + * Globals + */ +char *progname; +char *ofname = NULL; +int verblevel = 0; + +struct board_info *board = NULL; + +struct fw_block blocks[MAX_NUM_BLOCKS]; +struct fw_block *bootext_block = NULL; +int num_blocks = 0; + +#define ADM5120_FLASH_BASE 0xBFC00000 +#define ADM5120_CODE_START 0x80008000 + +/* TODO: check values for AR7 */ +#define AR7_FLASH_BASE 0xB0000000 +#define AR7_CODE_START 0x94008000 + +#define ATHEROS_FLASH_BASE 0xBFC00000 +#define ATHEROS_CODE_START 0x80e00000 + +#define AR71XX_FLASH_BASE 0xBFC00000 +#define AR71XX_CODE_START 0x81E00000 + +#define BOARD(n, d, v, m, fb, fs, cs, fo) { \ + .name = (n), .desc=(d), \ + .vendor = (v), .model = (m), \ + .flash_base = (fb), .flash_size = (fs)<<20, \ + .code_start = (cs), .romio_offs = (fo), \ + .bootext_size = BOOTEXT_DEF_SIZE \ + } + +#define ADMBOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \ + ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x8000) + +#define ADMBOARD2(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \ + ADM5120_FLASH_BASE, fs, ADM5120_CODE_START, 0x10000) + +#define AR7BOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \ + AR7_FLASH_BASE, fs, AR7_CODE_START, 0x8000) + +#define ATHEROSBOARD1(n, d, m, fs) BOARD(n, d, ZYNOS_VENDOR_ID_ZYXEL, m, \ + ATHEROS_FLASH_BASE, fs, ATHEROS_CODE_START, 0x30000) + +#define AR71XXBOARD1(n, d, m, fs) { \ + .name = (n), .desc=(d), \ + .vendor = (ZYNOS_VENDOR_ID_ZYXEL), .model = (m), \ + .flash_base = (AR71XX_FLASH_BASE), .flash_size = (fs)<<20, \ + .code_start = (AR71XX_CODE_START), .romio_offs = (0x40000), \ + .bootext_size = 0x30000 \ + } + + +static struct board_info boards[] = { + /* + * Infineon/ADMtek ADM5120 based boards + */ + ADMBOARD2("ES-2024A", "ZyXEL ES-2024A", ZYNOS_MODEL_ES_2024A, 4), + ADMBOARD2("ES-2024PWR", "ZyXEL ES-2024PWR", ZYNOS_MODEL_ES_2024PWR, 4), + ADMBOARD2("ES-2108", "ZyXEL ES-2108", ZYNOS_MODEL_ES_2108, 4), + ADMBOARD2("ES-2108-F", "ZyXEL ES-2108-F", ZYNOS_MODEL_ES_2108_F, 4), + ADMBOARD2("ES-2108-G", "ZyXEL ES-2108-G", ZYNOS_MODEL_ES_2108_G, 4), + ADMBOARD2("ES-2108-LC", "ZyXEL ES-2108-LC", ZYNOS_MODEL_ES_2108_LC, 4), + ADMBOARD2("ES-2108PWR", "ZyXEL ES-2108PWR", ZYNOS_MODEL_ES_2108PWR, 4), + ADMBOARD1("HS-100", "ZyXEL HomeSafe 100", ZYNOS_MODEL_HS_100, 2), + ADMBOARD1("HS-100W", "ZyXEL HomeSafe 100W", ZYNOS_MODEL_HS_100W, 2), + ADMBOARD1("P-334", "ZyXEL Prestige 334", ZYNOS_MODEL_P_334, 2), + ADMBOARD1("P-334U", "ZyXEL Prestige 334U", ZYNOS_MODEL_P_334U, 4), + ADMBOARD1("P-334W", "ZyXEL Prestige 334W", ZYNOS_MODEL_P_334W, 2), + ADMBOARD1("P-334WH", "ZyXEL Prestige 334WH", ZYNOS_MODEL_P_334WH, 4), + ADMBOARD1("P-334WHD", "ZyXEL Prestige 334WHD", ZYNOS_MODEL_P_334WHD, 4), + ADMBOARD1("P-334WT", "ZyXEL Prestige 334WT", ZYNOS_MODEL_P_334WT, 4), + ADMBOARD1("P-335", "ZyXEL Prestige 335", ZYNOS_MODEL_P_335, 4), + ADMBOARD1("P-335Plus", "ZyXEL Prestige 335Plus", ZYNOS_MODEL_P_335PLUS, 4), + ADMBOARD1("P-335U", "ZyXEL Prestige 335U", ZYNOS_MODEL_P_335U, 4), + ADMBOARD1("P-335WT", "ZyXEL Prestige 335WT", ZYNOS_MODEL_P_335WT, 4), + + { + .name = "P-2602HW-D1A", + .desc = "ZyXEL P-2602HW-D1A", + .vendor = ZYNOS_VENDOR_ID_ZYXEL, + .model = ZYNOS_MODEL_P_2602HW_D1A, + .flash_base = AR7_FLASH_BASE, + .flash_size = 4*1024*1024, + .code_start = 0x94008000, + .romio_offs = 0x20000, + .bootext_size = BOOTEXT_DEF_SIZE, + }, + +#if 0 + /* + * Texas Instruments AR7 based boards + */ + AR7BOARD1("P-660H-61", "ZyXEL P-660H-61", ZYNOS_MODEL_P_660H_61, 2), + AR7BOARD1("P-660H-63", "ZyXEL P-660H-63", ZYNOS_MODEL_P_660H_63, 2), + AR7BOARD1("P-660H-D1", "ZyXEL P-660H-D1", ZYNOS_MODEL_P_660H_D1, 2), + AR7BOARD1("P-660H-D3", "ZyXEL P-660H-D3", ZYNOS_MODEL_P_660H_D3, 2), + AR7BOARD1("P-660HW-61", "ZyXEL P-660HW-61", ZYNOS_MODEL_P_660HW_61, 2), + AR7BOARD1("P-660HW-63", "ZyXEL P-660HW-63", ZYNOS_MODEL_P_660HW_63, 2), + AR7BOARD1("P-660HW-67", "ZyXEL P-660HW-67", ZYNOS_MODEL_P_660HW_67, 2), + AR7BOARD1("P-660HW-D1", "ZyXEL P-660HW-D1", ZYNOS_MODEL_P_660HW_D1, 2), + AR7BOARD1("P-660HW-D3", "ZyXEL P-660HW-D3", ZYNOS_MODEL_P_660HW_D3, 2), + AR7BOARD1("P-660R-61", "ZyXEL P-660R-61", ZYNOS_MODEL_P_660R_61, 2), + AR7BOARD1("P-660R-61C", "ZyXEL P-660R-61C", ZYNOS_MODEL_P_660R_61C, 2), + AR7BOARD1("P-660R-63", "ZyXEL P-660R-63", ZYNOS_MODEL_P_660R_63, 2), + AR7BOARD1("P-660R-63C", "ZyXEL P-660R-63C", ZYNOS_MODEL_P_660R_63C, 2), + AR7BOARD1("P-660R-67", "ZyXEL P-660R-67", ZYNOS_MODEL_P_660R_67, 2), + AR7BOARD1("P-660R-D1", "ZyXEL P-660R-D1", ZYNOS_MODEL_P_660R_D1, 2), + AR7BOARD1("P-660R-D3", "ZyXEL P-660R-D3", ZYNOS_MODEL_P_660R_D3, 2), +#endif + { + .name = "O2SURF", + .desc = "O2 DSL Surf & Phone", + .vendor = ZYNOS_VENDOR_ID_O2, + .model = ZYNOS_MODEL_O2SURF, + .flash_base = AR7_FLASH_BASE, + .flash_size = 8*1024*1024, + .code_start = 0x94014000, + .romio_offs = 0x40000, + .bootext_size = BOOTEXT_DEF_SIZE, + }, + + /* +:x + */ + ATHEROSBOARD1("NBG-318S", "ZyXEL NBG-318S", ZYNOS_MODEL_NBG_318S, 4), + + /* + * Atheros ar71xx based boards + */ + AR71XXBOARD1("NBG-460N", "ZyXEL NBG-460N", ZYNOS_MODEL_NBG_460N, 4), + + {.name = NULL} +}; + +/* + * 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 WARN(fmt, ...) do { \ + fprintf(stderr, "[%s] *** warning: " fmt "\n", \ + progname, ## __VA_ARGS__ ); \ +} while (0) + +#define DBG(lev, fmt, ...) do { \ + if (verblevel < lev) \ + break;\ + fprintf(stderr, "[%s] " fmt "\n", progname, ## __VA_ARGS__ ); \ +} while (0) + +#define ERR_FATAL -1 +#define ERR_INVALID_IMAGE -2 + +/* + * Helper routines + */ +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" +" -B create image for the board specified with .\n" +" valid values:\n" + ); + for (board = boards; board->name != NULL; board++){ + fprintf(stream, +" %-12s= %s\n", + board->name, board->desc); + }; + fprintf(stream, +" -b [:]\n" +" add boot extension block to the image\n" +" -r [:]\n" +" add raw block to the image\n" +" -o write output to the file \n" +" -h show this screen\n" + ); + + exit(status); +} + + +/* + * argument parsing + */ +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 +str2u16(char *arg, uint16_t *val) +{ + char *err = NULL; + uint32_t t; + + errno=0; + t = strtoul(arg, &err, 0); + if (errno || (err==arg) || ((err != NULL) && *err) || (t >= 0x10000)) { + return -1; + } + + *val = t & 0xFFFF; + 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; +} + +int +str2sig(char *arg, uint32_t *sig) +{ + if (strlen(arg) != 4) + return -1; + + *sig = arg[0] | (arg[1] << 8) | (arg[2] << 16) | (arg[3] << 24); + + return 0; +} + + +int +parse_arg(char *arg, char *buf, char *argv[]) +{ + int res = 0; + size_t argl; + char *tok; + char **ap = &buf; + int i; + + memset(argv, 0, MAX_ARG_COUNT * sizeof(void *)); + + if ((arg == NULL)) { + /* no arguments */ + return 0; + } + + argl = strlen(arg); + if (argl == 0) { + /* no arguments */ + return 0; + } + + if (argl >= MAX_ARG_LEN) { + /* argument is too long */ + argl = MAX_ARG_LEN-1; + } + + memcpy(buf, arg, argl); + buf[argl] = '\0'; + + for (i = 0; i < MAX_ARG_COUNT; i++) { + tok = strsep(ap, ":"); + if (tok == NULL) { + break; + } +#if 0 + else if (tok[0] == '\0') { + break; + } +#endif + argv[i] = tok; + res++; + } + + return res; +} + + +int +required_arg(char c, char *arg) +{ + if (arg == NULL || *arg != '-') + return 0; + + ERR("option -%c requires an argument\n", c); + return -1; +} + + +int +is_empty_arg(char *arg) +{ + int ret = 1; + if (arg != NULL) { + if (*arg) ret = 0; + }; + return ret; +} + + +void +csum_init(struct csum_state *css) +{ + css->odd = 0; + css->sum = 0; + css->tmp = 0; +} + + +void +csum_update(uint8_t *p, uint32_t len, struct csum_state *css) +{ + if (len == 0) + return; + + if (css->odd) { + css->sum += (css->tmp << 8) + p[0]; + if (css->sum > 0xFFFF) { + css->sum += 1; + css->sum &= 0xFFFF; + } + css->odd = 0; + len--; + p++; + } + + for ( ; len > 1; len -= 2, p +=2 ) { + css->sum += (p[0] << 8) + p[1]; + if (css->sum > 0xFFFF) { + css->sum += 1; + css->sum &= 0xFFFF; + } + } + + if (len == 1){ + css->tmp = p[0]; + css->odd = 1; + } +} + + +uint16_t +csum_get(struct csum_state *css) +{ + char pad = 0; + + csum_update(&pad, 1, css); + return css->sum; +} + +uint16_t +csum_buf(uint8_t *p, uint32_t len) +{ + struct csum_state css; + + csum_init(&css); + csum_update(p, len, &css); + return csum_get(&css); + +} + +/* + * routines to write data to the output file + */ +int +write_out_data(FILE *outfile, uint8_t *data, size_t len, + struct csum_state *css) +{ + errno = 0; + + fwrite(data, len, 1, outfile); + if (errno) { + ERR("unable to write output file"); + return -1; + } + + if (css) { + csum_update(data, len, css); + } + + return 0; +} + + +int +write_out_padding(FILE *outfile, size_t len, uint8_t padc, + struct csum_state *css) +{ + uint8_t buf[512]; + size_t buflen = sizeof(buf); + + memset(buf, padc, buflen); + while (len > 0) { + if (len < buflen) + buflen = len; + + if (write_out_data(outfile, buf, buflen, css)) + return -1; + + len -= buflen; + } + + return 0; +} + + +int +write_out_data_align(FILE *outfile, uint8_t *data, size_t len, size_t align, + struct csum_state *css) +{ + size_t padlen; + int res; + + res = write_out_data(outfile, data, len, css); + if (res) + return res; + + padlen = ALIGN(len,align) - len; + res = write_out_padding(outfile, padlen, 0xFF, css); + + return res; +} + + +int +write_out_header(FILE *outfile, struct zyn_rombin_hdr *hdr) +{ + struct zyn_rombin_hdr t; + + errno = 0; + if (fseek(outfile, 0, SEEK_SET) != 0) { + ERRS("fseek failed on output file"); + return -1; + } + + /* setup temporary header fields */ + memset(&t, 0, sizeof(t)); + t.addr = HOST_TO_BE32(hdr->addr); + memcpy(&t.sig, ROMBIN_SIGNATURE, ROMBIN_SIG_LEN); + t.type = hdr->type; + t.flags = hdr->flags; + t.osize = HOST_TO_BE32(hdr->osize); + t.csize = HOST_TO_BE32(hdr->csize); + t.ocsum = HOST_TO_BE16(hdr->ocsum); + t.ccsum = HOST_TO_BE16(hdr->ccsum); + t.mmap_addr = HOST_TO_BE32(hdr->mmap_addr); + + DBG(2, "hdr.addr = 0x%08x", hdr->addr); + DBG(2, "hdr.type = 0x%02x", hdr->type); + DBG(2, "hdr.osize = 0x%08x", hdr->osize); + DBG(2, "hdr.csize = 0x%08x", hdr->csize); + DBG(2, "hdr.flags = 0x%02x", hdr->flags); + DBG(2, "hdr.ocsum = 0x%04x", hdr->ocsum); + DBG(2, "hdr.ccsum = 0x%04x", hdr->ccsum); + DBG(2, "hdr.mmap_addr = 0x%08x", hdr->mmap_addr); + + return write_out_data(outfile, (uint8_t *)&t, sizeof(t), NULL); +} + + +int +write_out_mmap(FILE *outfile, struct fw_mmap *mmap, struct csum_state *css) +{ + struct zyn_mmt_hdr *mh; + uint8_t buf[MMAP_DATA_SIZE]; + uint32_t user_size; + char *data; + int res; + + memset(buf, 0, sizeof(buf)); + + mh = (struct zyn_mmt_hdr *)buf; + + /* TODO: needs to recreate the memory map too? */ + mh->count=0; + + /* Build user data section */ + data = buf+sizeof(*mh); + data += sprintf(data, "Vendor 1 %d", board->vendor); + *data++ = '\0'; + data += sprintf(data, "Model 1 %d", BE16_TO_HOST(board->model)); + *data++ = '\0'; + /* TODO: make hardware version configurable? */ + data += sprintf(data, "HwVerRange 2 %d %d", 0, 0); + *data++ = '\0'; + + user_size = (uint8_t *)data - buf; + mh->user_start= HOST_TO_BE32(mmap->addr+sizeof(*mh)); + mh->user_end= HOST_TO_BE32(mmap->addr+user_size); + mh->csum = HOST_TO_BE16(csum_buf(buf+sizeof(*mh), user_size)); + + res = write_out_data(outfile, buf, sizeof(buf), css); + + return res; +} + + +int +block_stat_file(struct fw_block *block) +{ + struct stat st; + int res; + + if (block->file_name == NULL) + return 0; + + res = stat(block->file_name, &st); + if (res){ + ERRS("stat failed on %s", block->file_name); + return res; + } + + block->file_size = st.st_size; + return 0; +} + + +int +read_magic(uint16_t *magic) +{ + FILE *f; + int res; + + errno = 0; + f = fopen(bootext_block->file_name,"r"); + if (errno) { + ERRS("unable to open file: %s", bootext_block->file_name); + return -1; + } + + errno = 0; + fread(magic, 2, 1, f); + if (errno != 0) { + ERRS("unable to read from file: %s", bootext_block->file_name); + res = -1; + goto err; + } + + res = 0; + +err: + fclose(f); + return res; +} + + +int +write_out_file(FILE *outfile, char *name, size_t len, struct csum_state *css) +{ + char buf[FILE_BUF_LEN]; + size_t buflen = sizeof(buf); + FILE *f; + int res; + + DBG(2, "writing out file, name=%s, len=%d", + name, len); + + errno = 0; + f = fopen(name,"r"); + if (errno) { + ERRS("unable to open file: %s", name); + return -1; + } + + while (len > 0) { + if (len < buflen) + buflen = len; + + /* read data from source file */ + errno = 0; + fread(buf, buflen, 1, f); + if (errno != 0) { + ERRS("unable to read from file: %s",name); + res = -1; + break; + } + + res = write_out_data(outfile, buf, buflen, css); + if (res) + break; + + len -= buflen; + } + + fclose(f); + return res; +} + + +int +write_out_block(FILE *outfile, struct fw_block *block, struct csum_state *css) +{ + int res; + + if (block == NULL) + return 0; + + if (block->file_name == NULL) + return 0; + + if (block->file_size == 0) + return 0; + + res = write_out_file(outfile, block->file_name, + block->file_size, css); + return res; +} + + +int +write_out_image(FILE *outfile) +{ + struct fw_block *block; + struct fw_mmap mmap; + struct zyn_rombin_hdr hdr; + struct csum_state css; + int i, res; + uint32_t offset; + uint32_t padlen; + uint16_t csum; + uint16_t t; + + /* setup header fields */ + memset(&hdr, 0, sizeof(hdr)); + hdr.addr = board->code_start; + hdr.type = OBJECT_TYPE_BOOTEXT; + hdr.flags = ROMBIN_FLAG_OCSUM; + + offset = board->romio_offs; + + res = write_out_header(outfile, &hdr); + if (res) + return res; + + offset += sizeof(hdr); + + csum_init(&css); + res = write_out_block(outfile, bootext_block, &css); + if (res) + return res; + + offset += bootext_block->file_size; + if (offset > (board->romio_offs + board->bootext_size)) { + ERR("bootext file '%s' is too big", bootext_block->file_name); + return -1; + } + + padlen = ALIGN(offset, MMAP_ALIGN) - offset; + res = write_out_padding(outfile, padlen, 0xFF, &css); + if (res) + return res; + + offset += padlen; + + mmap.addr = board->flash_base + offset; + res = write_out_mmap(outfile, &mmap, &css); + if (res) + return res; + + offset += MMAP_DATA_SIZE; + + if ((offset - board->romio_offs) < board->bootext_size) { + padlen = board->romio_offs + board->bootext_size - offset; + res = write_out_padding(outfile, padlen, 0xFF, &css); + if (res) + return res; + offset += padlen; + + DBG(2, "bootext end at %08x", offset); + } + + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type == BLOCK_TYPE_BOOTEXT) + continue; + + padlen = ALIGN(offset, block->align) - offset; + res = write_out_padding(outfile, padlen, 0xFF, &css); + if (res) + return res; + offset += padlen; + + res = write_out_block(outfile, block, &css); + if (res) + return res; + offset += block->file_size; + } + + padlen = ALIGN(offset, 4) - offset; + res = write_out_padding(outfile, padlen, 0xFF, &css); + if (res) + return res; + offset += padlen; + + csum = csum_get(&css); + hdr.mmap_addr = mmap.addr; + hdr.osize = 2; + + res = read_magic(&hdr.ocsum); + if (res) + return res; + hdr.ocsum = BE16_TO_HOST(hdr.ocsum); + + if (csum <= hdr.ocsum) + t = hdr.ocsum - csum; + else + t = hdr.ocsum - csum - 1; + + DBG(2, "ocsum=%04x, csum=%04x, fix=%04x", hdr.ocsum, csum, t); + + t = HOST_TO_BE16(t); + res = write_out_data(outfile, (uint8_t *)&t, 2, NULL); + if (res) + return res; + + + res = write_out_header(outfile, &hdr); + + return res; +} + + +struct board_info * +find_board(char *name) +{ + struct board_info *ret; + struct board_info *board; + + ret = NULL; + for (board = boards; board->name != NULL; board++){ + if (strcasecmp(name, board->name) == 0) { + ret = board; + break; + } + }; + + return ret; +} + + +int +parse_opt_board(char ch, char *arg) +{ + + DBG(1,"parsing board option: -%c %s", ch, arg); + + if (board != NULL) { + ERR("only one board option allowed"); + return -1; + } + + if (required_arg(ch, arg)) + return -1; + + board = find_board(arg); + if (board == NULL){ + ERR("invalid/unknown board specified: %s", arg); + return -1; + } + + return 0; +} + + +int +parse_opt_ofname(char ch, char *arg) +{ + + if (ofname != NULL) { + ERR("only one output file allowed"); + return -1; + } + + if (required_arg(ch, arg)) + return -1; + + ofname = arg; + + return 0; +} + + +int +parse_opt_block(char ch, char *arg) +{ + char buf[MAX_ARG_LEN]; + char *argv[MAX_ARG_COUNT]; + int argc; + char *p; + struct fw_block *block; + int i; + + if ( num_blocks >= MAX_NUM_BLOCKS ) { + ERR("too many blocks specified"); + return -1; + } + + block = &blocks[num_blocks++]; + + /* setup default field values */ + block->padc = 0xFF; + + switch(ch) { + case 'b': + if (bootext_block) { + ERR("only one boot extension block allowed"); + break; + } + block->type = BLOCK_TYPE_BOOTEXT; + bootext_block = block; + break; + case 'r': + block->type = BLOCK_TYPE_RAW; + break; + } + + argc = parse_arg(arg, buf, argv); + + i = 0; + p = argv[i++]; + if (is_empty_arg(p)) { + ERR("no file specified in %s", arg); + return -1; + } else { + block->file_name = strdup(p); + if (block->file_name == NULL) { + ERR("not enough memory"); + return -1; + } + } + + if (block->type == BLOCK_TYPE_BOOTEXT) + return 0; + + p = argv[i++]; + if (!is_empty_arg(p)) { + if (str2u32(p, &block->align) != 0) { + ERR("invalid block align in %s", arg); + return -1; + } + } + + return 0; +} + + +int +calc_block_offsets(int type, uint32_t *offset) +{ + struct fw_block *block; + uint32_t next_offs; + uint32_t avail; + int i, res; + + DBG(1,"calculating block offsets, starting with %lu", + *offset); + + res = 0; + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + + if (block->type != type) + continue; + + next_offs = ALIGN(*offset, block->align); + avail = board->flash_size - next_offs; + if (block->file_size > avail) { + ERR("file %s is too big, offset = %u, size=%u," + " avail = %u, align = %u", block->file_name, + (unsigned)next_offs, + (unsigned)block->file_size, + (unsigned)avail, + (unsigned)block->align); + res = -1; + break; + } + + block->padlen = next_offs - *offset; + *offset += block->file_size; + } + + return res; +} + +int +process_blocks(void) +{ + struct fw_block *block; + uint32_t offset; + int i; + int res; + + /* collecting file stats */ + for (i = 0; i < num_blocks; i++) { + block = &blocks[i]; + res = block_stat_file(block); + if (res) + return res; + } + + offset = board->romio_offs + bootext_block->file_size; + res = calc_block_offsets(BLOCK_TYPE_RAW, &offset); + + return res; +} + + +int +main(int argc, char *argv[]) +{ + int optinvalid = 0; /* flag for invalid option */ + int c; + int res = EXIT_FAILURE; + + FILE *outfile; + + progname=basename(argv[0]); + + opterr = 0; /* could not print standard getopt error messages */ + while ( 1 ) { + optinvalid = 0; + + c = getopt(argc, argv, "b:B:ho:r:v"); + if (c == -1) + break; + + switch (c) { + case 'b': + case 'r': + optinvalid = parse_opt_block(c,optarg); + break; + case 'B': + optinvalid = parse_opt_board(c,optarg); + break; + case 'o': + optinvalid = parse_opt_ofname(c,optarg); + break; + case 'v': + verblevel++; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + optinvalid = 1; + break; + } + if (optinvalid != 0 ) { + ERR("invalid option: -%c", optopt); + goto out; + } + } + + if (board == NULL) { + ERR("no board specified"); + goto out; + } + + if (ofname == NULL) { + ERR("no output file specified"); + goto out; + } + + if (optind < argc) { + ERR("invalid option: %s", argv[optind]); + goto out; + } + + if (process_blocks() != 0) { + goto out; + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto out; + } + + if (write_out_image(outfile) != 0) + goto out_flush; + + DBG(1,"Image file %s completed.", ofname); + + res = EXIT_SUCCESS; + +out_flush: + fflush(outfile); + fclose(outfile); + if (res != EXIT_SUCCESS) { + unlink(ofname); + } +out: + return res; +} diff --git a/trunk/tools/firmware-utils/src/motorola-bin.c b/trunk/tools/firmware-utils/src/motorola-bin.c new file mode 100644 index 00000000..33e9937e --- /dev/null +++ b/trunk/tools/firmware-utils/src/motorola-bin.c @@ -0,0 +1,227 @@ +/* + * motorola-bin.c + * + * Copyright (C) 2005-2006 Mike Baker, + * Imre Kaloz + * D. Hugh Redelmeier + * OpenWrt.org + * + * + * 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. + * + */ + +/* + * Motorola's firmware flashing code requires an extra header. + * The header is eight bytes (see struct motorola below). + * This program will take a firmware file and create a new one + * with this header: + * motorola-bin --wr850g WR850G_V403.stripped.trx WR850G_V403.trx + * + * Note: Motorola's firmware is distributed with this header. + * If you need to flash Motorola firmware on a router running libreCMC, + * you will to remove this header. Use the --strip flag: + * motorola-bin --strip WR850G_V403.trx WR850G_V403.stripped.trx + */ + +/* + * February 1, 2006 + * + * Add support for for creating WA840G and WE800G images + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BPB 8 /* bits/byte */ + +static uint32_t crc32[1<> 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 motorola { + uint32_t crc; // crc32 of the remainder + uint32_t flags; // unknown, 105770* +}; + +static const struct model { + char digit; /* a digit signifying model (historical) */ + const char *name; + uint32_t flags; +} models[] = { + { '1', "WR850G", 0x10577050LU }, + { '2', "WA840G", 0x10577040LU }, + { '3', "WE800G", 0x10577000LU }, + { '\0', NULL, 0 } +}; + +static void usage(const char *) __attribute__ (( __noreturn__ )); + +static void usage(const char *mess) +{ + const struct model *m; + + fprintf(stderr, "Error: %s\n", mess); + fprintf(stderr, "Usage: motorola-bin -device|--strip infile outfile\n"); + fprintf(stderr, "Known devices: "); + + for (m = models; m->digit != '\0'; m++) + fprintf(stderr, " %c - %s", m->digit, m->name); + + fprintf(stderr, "\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + off_t len; // of original firmware + int fd; + void *trx; // pointer to original firmware (mmmapped) + struct motorola *firmware; // pionter to prefix + copy of original firmware + uint32_t flags; + + // verify parameters + + if (argc != 4) + usage("wrong number of arguments"); + + // mmap trx file + if ((fd = open(argv[2], O_RDONLY)) < 0 + || (len = lseek(fd, 0, SEEK_END)) < 0 + || (trx = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0)) == (void *) (-1) + || close(fd) < 0) + { + fprintf(stderr, "Error loading file %s: %s\n", argv[2], strerror(errno)); + exit(1); + } + + init_crc32(); + + if (strcmp(argv[1], "--strip") == 0) + { + const char *ugh = NULL; + + if (len < sizeof(struct motorola)) { + ugh = "input file too short"; + } else { + const struct model *m; + + firmware = trx; + if (htonl(crc32buf(trx + offsetof(struct motorola, flags), len - offsetof(struct motorola, flags))) != firmware->crc) + ugh = "Invalid CRC"; + for (m = models; ; m++) { + if (m->digit == '\0') { + if (ugh == NULL) + ugh = "unrecognized flags field"; + break; + } + if (firmware->flags == htonl(m->flags)) { + fprintf(stderr, "Firmware for Motorola %s\n", m->name); + break; + } + } + } + + if (ugh != NULL) { + fprintf(stderr, "%s\n", ugh); + exit(3); + } else { + // all is well, write the file without the prefix + if ((fd = open(argv[3], O_CREAT|O_WRONLY|O_TRUNC,0644)) < 0 + || write(fd, trx + sizeof(struct motorola), len - sizeof(struct motorola)) != len - sizeof(struct motorola) + || close(fd) < 0) + { + fprintf(stderr, "Error storing file %s: %s\n", argv[3], strerror(errno)); + exit(2); + } + } + + } else { + // setup the firmware flags magic number + const struct model *m; + const char *df = argv[1]; + + if (*df != '-') + usage("first argument must start with -"); + if (*++df == '-') + ++df; /* allow but don't require second - */ + + for (m = models; ; m++) { + if (m->digit == '\0') + usage("unrecognized device specified"); + if ((df[0] == m->digit && df[1] == '\0') || strcasecmp(df, m->name) == 0) { + flags = m->flags; + break; + } + } + + + // create a firmware image in memory + // and copy the trx to it + firmware = malloc(sizeof(struct motorola) + len); + memcpy(&firmware[1], trx, len); + + // setup the motorola headers + firmware->flags = htonl(flags); + + // CRC of flags + firmware + firmware->crc = htonl(crc32buf((unsigned char *)&firmware->flags, sizeof(firmware->flags) + len)); + + // write the firmware + if ((fd = open(argv[3], O_CREAT|O_WRONLY|O_TRUNC,0644)) < 0 + || write(fd, firmware, sizeof(struct motorola) + len) != sizeof(struct motorola) + len + || close(fd) < 0) + { + fprintf(stderr, "Error storing file %s: %s\n", argv[3], strerror(errno)); + exit(2); + } + + free(firmware); + } + + munmap(trx,len); + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/myloader.h b/trunk/tools/firmware-utils/src/myloader.h new file mode 100644 index 00000000..7be1d493 --- /dev/null +++ b/trunk/tools/firmware-utils/src/myloader.h @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2006-2008 Gabor Juhos + * + * 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 _MYLOADER_H_ +#define _MYLOADER_H_ + +/* + * Firmware file format: + * + *
+ * [] + * ... + * [] + * + * [] + * ... + * [] + * + * + */ + +/* Myloader specific magic numbers */ +#define MYLO_MAGIC_FIRMWARE 0x4C594D00 +#define MYLO_MAGIC_20021103 0x20021103 +#define MYLO_MAGIC_20021107 0x20021107 + +#define MYLO_MAGIC_SYS_PARAMS MYLO_MAGIC_20021107 +#define MYLO_MAGIC_PARTITIONS MYLO_MAGIC_20021103 +#define MYLO_MAGIC_BOARD_PARAMS MYLO_MAGIC_20021103 + +/* + * Addresses of the data structures provided by MyLoader + */ +#define MYLO_MIPS_SYS_PARAMS 0x80000800 /* System Parameters */ +#define MYLO_MIPS_BOARD_PARAMS 0x80000A00 /* Board Parameters */ +#define MYLO_MIPS_PARTITIONS 0x80000C00 /* Partition Table */ +#define MYLO_MIPS_BOOT_PARAMS 0x80000E00 /* Boot Parameters */ + +/* Vendor ID's (seems to be same as the PCI vendor ID's) */ +#define VENID_COMPEX 0x11F6 + +/* Devices based on the ADM5120 */ +#define DEVID_COMPEX_NP27G 0x0078 +#define DEVID_COMPEX_NP28G 0x044C +#define DEVID_COMPEX_NP28GHS 0x044E +#define DEVID_COMPEX_WP54Gv1C 0x0514 +#define DEVID_COMPEX_WP54G 0x0515 +#define DEVID_COMPEX_WP54AG 0x0546 +#define DEVID_COMPEX_WPP54AG 0x0550 +#define DEVID_COMPEX_WPP54G 0x0555 + +/* Devices based on the Atheros AR2317 */ +#define DEVID_COMPEX_NP25G 0x05e6 +#define DEVID_COMPEX_WPE53G 0x05dc + +/* Devices based on the Atheros AR71xx */ +#define DEVID_COMPEX_WP543 0x0640 +#define DEVID_COMPEX_WPE72 0x0672 + +/* Devices based on the IXP422 */ +#define DEVID_COMPEX_WP18 0x047E +#define DEVID_COMPEX_NP18A 0x0489 + +/* Other devices */ +#define DEVID_COMPEX_NP26G8M 0x03E8 +#define DEVID_COMPEX_NP26G16M 0x03E9 + +struct mylo_fw_header { + uint32_t magic; /* must be MYLO_MAGIC_FIRMWARE */ + uint32_t crc; /* CRC of the whole firmware */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint16_t vid; /* vendor ID */ + uint16_t did; /* device ID */ + uint16_t svid; /* sub vendor ID */ + uint16_t sdid; /* sub device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; /* FIXME: firmware version high? */ + uint32_t fwlo; /* FIXME: firmware version low? */ + uint32_t flags; /* firmware flags */ +}; + +#define FW_FLAG_BOARD_PARAMS_WP 0x01 /* board parameters are write protected */ +#define FW_FLAG_BOOT_SECTOR_WE 0x02 /* enable of write boot sectors (below 64K) */ + +struct mylo_fw_blockdesc { + uint32_t type; /* block type */ + uint32_t addr; /* relative address to flash start */ + uint32_t dlen; /* size of block data in bytes */ + uint32_t blen; /* total size of block in bytes */ +}; + +#define FW_DESC_TYPE_UNUSED 0 +#define FW_DESC_TYPE_USED 1 + +struct mylo_partition { + uint16_t flags; /* partition flags */ + uint16_t type; /* type of the partition */ + uint32_t addr; /* relative address of the partition from the + flash start */ + uint32_t size; /* size of the partition in bytes */ + uint32_t param; /* if this is the active partition, the + MyLoader load code to this address */ +}; + +#define PARTITION_FLAG_ACTIVE 0x8000 /* this is the active partition, + * MyLoader loads firmware from here */ +#define PARTITION_FLAG_ISRAM 0x2000 /* FIXME: this is a RAM partition? */ +#define PARTIIION_FLAG_RAMLOAD 0x1000 /* FIXME: load this partition into the RAM? */ +#define PARTITION_FLAG_PRELOAD 0x0800 /* the partition data preloaded to RAM + * before decompression */ +#define PARTITION_FLAG_LZMA 0x0100 /* the partition data compressed with LZMA */ +#define PARTITION_FLAG_HAVEHDR 0x0002 /* the partition data have a header */ + +#define PARTITION_TYPE_FREE 0 +#define PARTITION_TYPE_USED 1 + +#define MYLO_MAX_PARTITIONS 8 /* maximum number of partitions in the + partition table */ + +struct mylo_partition_table { + uint32_t magic; /* must be MYLO_MAGIC_PARTITIONS */ + uint32_t res0; /* unknown/unused */ + uint32_t res1; /* unknown/unused */ + uint32_t res2; /* unknown/unused */ + struct mylo_partition partitions[MYLO_MAX_PARTITIONS]; +}; + +struct mylo_partition_header { + uint32_t len; /* length of the partition data */ + uint32_t crc; /* CRC value of the partition data */ +}; + +struct mylo_system_params { + uint32_t magic; /* must be MYLO_MAGIC_SYS_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t mylo_ver; + uint16_t vid; /* Vendor ID */ + uint16_t did; /* Device ID */ + uint16_t svid; /* Sub Vendor ID */ + uint16_t sdid; /* Sub Device ID */ + uint32_t rev; /* device revision */ + uint32_t fwhi; + uint32_t fwlo; + uint32_t tftp_addr; + uint32_t prog_start; + uint32_t flash_size; /* Size of boot FLASH in bytes */ + uint32_t dram_size; /* Size of onboard RAM in bytes */ +}; + + +struct mylo_eth_addr { + uint8_t mac[6]; + uint8_t csum[2]; +}; + +#define MYLO_ETHADDR_COUNT 8 /* maximum number of ethernet address + in the board parameters */ + +struct mylo_board_params { + uint32_t magic; /* must be MYLO_MAGIC_BOARD_PARAMS */ + uint32_t res0; + uint32_t res1; + uint32_t res2; + struct mylo_eth_addr addr[MYLO_ETHADDR_COUNT]; +}; + +#endif /* _MYLOADER_H_*/ diff --git a/trunk/tools/firmware-utils/src/nand_ecc.c b/trunk/tools/firmware-utils/src/nand_ecc.c new file mode 100644 index 00000000..b1e93051 --- /dev/null +++ b/trunk/tools/firmware-utils/src/nand_ecc.c @@ -0,0 +1,204 @@ +/* + * calculate ecc code for nand flash + * + * Copyright (C) 2008 yajin + * Copyright (C) 2009 Felix Fietkau + * + * 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 or + * (at your option) version 3 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include + +#define DEF_NAND_PAGE_SIZE 2048 +#define DEF_NAND_OOB_SIZE 64 +#define DEF_NAND_ECC_OFFSET 0x28 + +static int page_size = DEF_NAND_PAGE_SIZE; +static int oob_size = DEF_NAND_OOB_SIZE; +static int ecc_offset = DEF_NAND_ECC_OFFSET; + +/* + * Pre-calculated 256-way 1 byte column parity + */ +static const uint8_t nand_ecc_precalc_table[] = { + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a, + 0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f, + 0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c, + 0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69, + 0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03, + 0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66, + 0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65, + 0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00 +}; + +/** + * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block + * @dat: raw data + * @ecc_code: buffer for ECC + */ +int nand_calculate_ecc(const uint8_t *dat, + uint8_t *ecc_code) +{ + uint8_t idx, reg1, reg2, reg3, tmp1, tmp2; + int i; + + /* Initialize variables */ + reg1 = reg2 = reg3 = 0; + + /* Build up column parity */ + for(i = 0; i < 256; i++) { + /* Get CP0 - CP5 from table */ + idx = nand_ecc_precalc_table[*dat++]; + reg1 ^= (idx & 0x3f); + + /* All bit XOR = 1 ? */ + if (idx & 0x40) { + reg3 ^= (uint8_t) i; + reg2 ^= ~((uint8_t) i); + } + } + + /* Create non-inverted ECC code from line parity */ + tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */ + tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */ + tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */ + tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */ + tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */ + tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */ + tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */ + tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */ + + tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */ + tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */ + tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */ + tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */ + tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */ + tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */ + tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */ + tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */ + + /* Calculate final ECC code */ +#ifdef CONFIG_MTD_NAND_ECC_SMC + ecc_code[0] = ~tmp2; + ecc_code[1] = ~tmp1; +#else + ecc_code[0] = ~tmp1; + ecc_code[1] = ~tmp2; +#endif + ecc_code[2] = ((~reg1) << 2) | 0x03; + + return 0; +} + +/* + * usage: bb-nandflash-ecc start_address size + */ +void usage(const char *prog) +{ + fprintf(stderr, "Usage: %s [options] \n" + "Options:\n" + " -p NAND page size (default: %d)\n" + " -o NAND OOB size (default: %d)\n" + " -e NAND ECC offset (default: %d)\n" + "\n", prog, DEF_NAND_PAGE_SIZE, DEF_NAND_OOB_SIZE, + DEF_NAND_ECC_OFFSET); + exit(1); +} + +/*start_address/size does not include oob + */ +int main(int argc, char **argv) +{ + uint8_t *page_data = NULL; + uint8_t *ecc_data; + int infd = -1, outfd = -1; + int ret = 1; + ssize_t bytes; + int ch; + + while ((ch = getopt(argc, argv, "e:o:p:")) != -1) { + switch(ch) { + case 'p': + page_size = strtoul(optarg, NULL, 0); + break; + case 'o': + oob_size = strtoul(optarg, NULL, 0); + break; + case 'e': + ecc_offset = strtoul(optarg, NULL, 0); + break; + default: + usage(argv[0]); + } + } + argc -= optind; + if (argc < 2) + usage(argv[0]); + + argv += optind; + + infd = open(argv[0], O_RDONLY, 0); + if (infd < 0) { + perror("open input file"); + goto out; + } + + outfd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (outfd < 0) { + perror("open output file"); + goto out; + } + + page_data = malloc(page_size + oob_size); + + while ((bytes = read(infd, page_data, page_size)) == page_size) { + int j; + + ecc_data = page_data + page_size + ecc_offset; + for (j = 0; j < page_size / 256; j++) + { + nand_calculate_ecc(page_data + j * 256, ecc_data); + ecc_data += 3; + } + write(outfd, page_data, page_size + oob_size); + } + + ret = 0; +out: + if (infd >= 0) + close(infd); + if (outfd >= 0) + close(outfd); + if (page_data) + free(page_data); + return ret; +} + diff --git a/trunk/tools/firmware-utils/src/osbridge-crc.c b/trunk/tools/firmware-utils/src/osbridge-crc.c new file mode 100644 index 00000000..f2e3920a --- /dev/null +++ b/trunk/tools/firmware-utils/src/osbridge-crc.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * 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 +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +#if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define HOST_TO_LE16(x) (x) +# define HOST_TO_LE32(x) (x) +# define LE16_TO_HOST(x) (x) +# define LE32_TO_HOST(x) (x) +#else +# define HOST_TO_LE16(x) bswap_16(x) +# define HOST_TO_LE32(x) bswap_32(x) +# define LE16_TO_HOST(x) bswap_16(x) +# define LE32_TO_HOST(x) bswap_32(x) +#endif + +uint32_t crc32buf(char *buf, size_t len); + +/* + * Globals + */ +static char *ifname; +static char *progname; +static char *ofname; + +/* + * 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) + +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" +" -i read input from the file \n" +" -o write output to the file \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; + uint32_t *hdr; + uint32_t crc; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "i:o:h"); + if (c == -1) + break; + + switch (c) { + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + 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; + 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, buflen, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto err_close_in; + } + + crc = crc32buf(buf, buflen); + hdr = (uint32_t *)buf; + *hdr = HOST_TO_LE32(crc); + + 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; + + out_flush: + 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; +} + +/**********************************************************************/ +/* The following was grabbed and tweaked from the old snippets collection + * of public domain C code. */ + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static const uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +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 +}; + +#define UPDC32(octet,crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +uint32_t crc32buf(char *buf, size_t len) +{ + uint32_t crc; + + crc = 0xFFFFFFFF; + + for ( ; len; --len, ++buf) + { + crc = UPDC32(*buf, crc); + } + + return crc ^ 0xFFFFFFFF; +} + diff --git a/trunk/tools/firmware-utils/src/pc1crypt.c b/trunk/tools/firmware-utils/src/pc1crypt.c new file mode 100644 index 00000000..9c2eb83f --- /dev/null +++ b/trunk/tools/firmware-utils/src/pc1crypt.c @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2009 Gabor Juhos + * + * 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 code was based on: + * PC1 Cipher Algorithm ( Pukall Cipher 1 ) + * By Alexander PUKALL 1991 + * free code no restriction to use + * please include the name of the Author in the final software + * the Key is 128 bits + * http://membres.lycos.fr/pc1/ + * + */ + +#include +#include +#include +#include +#include /* for unlink() */ +#include +#include /* for getopt() */ +#include +#include +#include + +struct pc1_ctx { + unsigned short ax; + unsigned short bx; + unsigned short cx; + unsigned short dx; + unsigned short si; + unsigned short tmp; + unsigned short x1a2; + unsigned short x1a0[8]; + unsigned short res; + unsigned short i; + unsigned short inter; + unsigned short cfc; + unsigned short cfd; + unsigned short compte; + unsigned char cle[17]; + short c; +}; + +static void pc1_finish(struct pc1_ctx *pc1) +{ + /* erase all variables */ + memset(pc1, 0, sizeof(struct pc1_ctx)); +} + +static void pc1_code(struct pc1_ctx *pc1) +{ + pc1->dx = pc1->x1a2 + pc1->i; + pc1->ax = pc1->x1a0[pc1->i]; + pc1->cx = 0x015a; + pc1->bx = 0x4e35; + + pc1->tmp = pc1->ax; + pc1->ax = pc1->si; + pc1->si = pc1->tmp; + + pc1->tmp = pc1->ax; + pc1->ax = pc1->dx; + pc1->dx = pc1->tmp; + + if (pc1->ax != 0) { + pc1->ax = pc1->ax * pc1->bx; + } + + pc1->tmp = pc1->ax; + pc1->ax = pc1->cx; + pc1->cx = pc1->tmp; + + if (pc1->ax != 0) { + pc1->ax = pc1->ax * pc1->si; + pc1->cx = pc1->ax + pc1->cx; + } + + pc1->tmp = pc1->ax; + pc1->ax = pc1->si; + pc1->si = pc1->tmp; + pc1->ax = pc1->ax * pc1->bx; + pc1->dx = pc1->cx + pc1->dx; + + pc1->ax = pc1->ax + 1; + + pc1->x1a2 = pc1->dx; + pc1->x1a0[pc1->i] = pc1->ax; + + pc1->res = pc1->ax ^ pc1->dx; + pc1->i = pc1->i + 1; +} + +static void pc1_assemble(struct pc1_ctx *pc1) +{ + pc1->x1a0[0] = (pc1->cle[0] * 256) + pc1->cle[1]; + + pc1_code(pc1); + pc1->inter = pc1->res; + + pc1->x1a0[1] = pc1->x1a0[0] ^ ((pc1->cle[2]*256) + pc1->cle[3]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[2] = pc1->x1a0[1] ^ ((pc1->cle[4]*256) + pc1->cle[5]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[3] = pc1->x1a0[2] ^ ((pc1->cle[6]*256) + pc1->cle[7]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[4] = pc1->x1a0[3] ^ ((pc1->cle[8]*256) + pc1->cle[9]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[5] = pc1->x1a0[4] ^ ((pc1->cle[10]*256) + pc1->cle[11]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[6] = pc1->x1a0[5] ^ ((pc1->cle[12]*256) + pc1->cle[13]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->x1a0[7] = pc1->x1a0[6] ^ ((pc1->cle[14]*256) + pc1->cle[15]); + pc1_code(pc1); + pc1->inter = pc1->inter ^ pc1->res; + + pc1->i = 0; +} + +static unsigned char pc1_decrypt(struct pc1_ctx *pc1, short c) +{ + pc1_assemble(pc1); + pc1->cfc = pc1->inter >> 8; + pc1->cfd = pc1->inter & 255; /* cfc^cfd = random byte */ + + c = c ^ (pc1->cfc ^ pc1->cfd); + for (pc1->compte = 0; pc1->compte <= 15; pc1->compte++) { + /* we mix the plaintext byte with the key */ + pc1->cle[pc1->compte] = pc1->cle[pc1->compte] ^ c; + } + + return c; +} + +static unsigned char pc1_encrypt(struct pc1_ctx *pc1, short c) +{ + pc1_assemble(pc1); + pc1->cfc = pc1->inter >> 8; + pc1->cfd = pc1->inter & 255; /* cfc^cfd = random byte */ + + for (pc1->compte = 0; pc1->compte <= 15; pc1->compte++) { + /* we mix the plaintext byte with the key */ + pc1->cle[pc1->compte] = pc1->cle[pc1->compte] ^ c; + } + c = c ^ (pc1->cfc ^ pc1->cfd); + + return c; +} + +static void pc1_init(struct pc1_ctx *pc1) +{ + memset(pc1, 0, sizeof(struct pc1_ctx)); + + /* ('Remsaalps!123456') is the key used, you can change it */ + strcpy(pc1->cle, "Remsaalps!123456"); +} + +static void pc1_decrypt_buf(struct pc1_ctx *pc1, unsigned char *buf, + unsigned len) +{ + unsigned i; + + for (i = 0; i < len; i++) + buf[i] = pc1_decrypt(pc1, buf[i]); +} + +static void pc1_encrypt_buf(struct pc1_ctx *pc1, unsigned char *buf, + unsigned len) +{ + unsigned i; + + for (i = 0; i < len; i++) + buf[i] = pc1_encrypt(pc1, buf[i]); +} + +/* + * Globals + */ +static char *ifname; +static char *progname; +static char *ofname; +static int decrypt; + +/* + * 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) + +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" +" -d decrypt instead of encrypt" +" -i read input from the file \n" +" -o write output to the file \n" +" -h show this screen\n" + ); + + exit(status); +} + +#define BUFSIZE (64 * 1024) + +int main(int argc, char *argv[]) +{ + struct pc1_ctx pc1; + int res = EXIT_FAILURE; + int err; + struct stat st; + char *buf; + unsigned total; + + FILE *outfile, *infile; + + progname = basename(argv[0]); + + while ( 1 ) { + int c; + + c = getopt(argc, argv, "di:o:h"); + if (c == -1) + break; + + switch (c) { + case 'd': + decrypt = 1; + break; + case 'i': + ifname = optarg; + break; + case 'o': + ofname = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + 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; + } + + total = st.st_size; + buf = malloc(BUFSIZE); + 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; + } + + outfile = fopen(ofname, "w"); + if (outfile == NULL) { + ERRS("could not open \"%s\" for writing", ofname); + goto err_close_in; + } + + pc1_init(&pc1); + while (total > 0) { + unsigned datalen; + + if (total > BUFSIZE) + datalen = BUFSIZE; + else + datalen = total; + + errno = 0; + fread(buf, datalen, 1, infile); + if (errno != 0) { + ERRS("unable to read from file %s", ifname); + goto err_close_out; + } + + if (decrypt) + pc1_decrypt_buf(&pc1, buf, datalen); + else + pc1_encrypt_buf(&pc1, buf, datalen); + + errno = 0; + fwrite(buf, datalen, 1, outfile); + if (errno) { + ERRS("unable to write to file %s", ofname); + goto err_close_out; + } + + total -= datalen; + } + pc1_finish(&pc1); + + res = EXIT_SUCCESS; + + out_flush: + 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/trunk/tools/firmware-utils/src/ptgen.c b/trunk/tools/firmware-utils/src/ptgen.c new file mode 100644 index 00000000..6379ed7a --- /dev/null +++ b/trunk/tools/firmware-utils/src/ptgen.c @@ -0,0 +1,245 @@ +/* + * ptgen - partition table generator + * Copyright (C) 2006 by Felix Fietkau + * + * uses parts of afdisk + * Copyright (C) 2002 by David Roetzel + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define cpu_to_le16(x) bswap_16(x) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define cpu_to_le16(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 partinfo { + unsigned long size; + int type; +}; + +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) { + 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; + } + + if (*end) + end++; + + if (*end) { + fprintf(stderr, "garbage after end of number\n"); + return 0; + } + + /* result: number + 1024^(exp) */ + return result * ((2 << ((10 * exp) - 1)) ?: 1); +} + +/* convert the sector number into a CHS value for the partition table */ +static void to_chs(long sect, unsigned char chs[3]) { + int c,h,s; + + s = (sect % sectors) + 1; + sect = sect / sectors; + h = sect % heads; + sect = sect / heads; + c = sect; + + chs[0] = h; + chs[1] = s | ((c >> 2) & 0xC0); + chs[2] = c & 0xFF; + + return; +} + +/* round the sector number up to the next cylinder */ +static inline unsigned long round_to_cyl(long sect) { + int cyl_size = heads * sectors; + + 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(uint32_t signature, int nr) +{ + struct pte pte[4]; + unsigned long sect = 0; + int i, fd, ret = -1, start, len; + + memset(pte, 0, sizeof(struct pte) * 4); + for (i = 0; i < nr; i++) { + if (!parts[i].size) { + 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; + start = sect + sectors; + if (kb_align != 0) + start = round_to_kb(start); + pte[i].start = cpu_to_le16(start); + sect = start + parts[i].size * 2; + if (kb_align == 0) + sect = round_to_cyl(sect); + pte[i].length = cpu_to_le16(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)); + } + + if ((fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0) { + fprintf(stderr, "Can't open output file '%s'\n",filename); + 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"); + goto fail; + } + lseek(fd, 510, SEEK_SET); + if (write(fd, "\x55\xaa", 2) != 2) { + fprintf(stderr, "write failed.\n"); + goto fail; + } + + ret = 0; +fail: + close(fd); + return ret; +} + +static void usage(char *prog) +{ + fprintf(stderr, "Usage: %s [-v] -h -s -o [-a 0..4] [-l ] [[-t ] -p ...] \n", prog); + exit(1); +} + +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:vl:S:")) != -1) { + switch (ch) { + case 'o': + filename = optarg; + break; + case 'v': + verbose++; + break; + case 'h': + heads = (int) strtoul(optarg, NULL, 0); + break; + case 's': + sectors = (int) strtoul(optarg, NULL, 0); + break; + case 'p': + if (part > 3) { + fprintf(stderr, "Too many partitions\n"); + exit(1); + } + parts[part].size = to_kbytes(optarg); + parts[part++].type = type; + break; + case 't': + type = (char) strtoul(optarg, NULL, 16); + break; + case 'a': + 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) + usage(argv[0]); + + return gen_ptable(signature, part); +} diff --git a/trunk/tools/firmware-utils/src/seama.c b/trunk/tools/firmware-utils/src/seama.c new file mode 100644 index 00000000..944ed3a4 --- /dev/null +++ b/trunk/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 + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 : %d bytes\n", msize); + for (i=0; i 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 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: %d\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/trunk/tools/firmware-utils/src/seama.h b/trunk/tools/firmware-utils/src/seama.h new file mode 100644 index 00000000..02683b6e --- /dev/null +++ b/trunk/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 + * 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 + +#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/trunk/tools/firmware-utils/src/sha1.c b/trunk/tools/firmware-utils/src/sha1.c new file mode 100644 index 00000000..3ab13329 --- /dev/null +++ b/trunk/tools/firmware-utils/src/sha1.c @@ -0,0 +1,443 @@ +/* + * FIPS-180-1 compliant SHA-1 implementation + * + * Copyright (C) 2003-2006 Christophe Devine + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License, version 2.1 as published by the Free Software Foundation. + * + * This 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ +/* + * The SHA-1 standard was published by NIST in 1993. + * + * http://www.itl.nist.gov/fipspubs/fip180-1.htm + */ + +#ifndef _CRT_SECURE_NO_DEPRECATE +#define _CRT_SECURE_NO_DEPRECATE 1 +#endif + +#include +#include + +#include "sha1.h" + +/* + * 32-bit integer manipulation macros (big endian) + */ +#ifndef GET_UINT32_BE +#define GET_UINT32_BE(n,b,i) \ +{ \ + (n) = ( (ulong) (b)[(i) ] << 24 ) \ + | ( (ulong) (b)[(i) + 1] << 16 ) \ + | ( (ulong) (b)[(i) + 2] << 8 ) \ + | ( (ulong) (b)[(i) + 3] ); \ +} +#endif +#ifndef PUT_UINT32_BE +#define PUT_UINT32_BE(n,b,i) \ +{ \ + (b)[(i) ] = (uchar) ( (n) >> 24 ); \ + (b)[(i) + 1] = (uchar) ( (n) >> 16 ); \ + (b)[(i) + 2] = (uchar) ( (n) >> 8 ); \ + (b)[(i) + 3] = (uchar) ( (n) ); \ +} +#endif + +/* + * Core SHA-1 functions + */ +void sha1_starts( sha1_context *ctx ) +{ + ctx->total[0] = 0; + ctx->total[1] = 0; + + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; +} + +void sha1_process( sha1_context *ctx, uchar data[64] ) +{ + ulong temp, W[16], A, B, C, D, E; + + GET_UINT32_BE( W[0], data, 0 ); + GET_UINT32_BE( W[1], data, 4 ); + GET_UINT32_BE( W[2], data, 8 ); + GET_UINT32_BE( W[3], data, 12 ); + GET_UINT32_BE( W[4], data, 16 ); + GET_UINT32_BE( W[5], data, 20 ); + GET_UINT32_BE( W[6], data, 24 ); + GET_UINT32_BE( W[7], data, 28 ); + GET_UINT32_BE( W[8], data, 32 ); + GET_UINT32_BE( W[9], data, 36 ); + GET_UINT32_BE( W[10], data, 40 ); + GET_UINT32_BE( W[11], data, 44 ); + GET_UINT32_BE( W[12], data, 48 ); + GET_UINT32_BE( W[13], data, 52 ); + GET_UINT32_BE( W[14], data, 56 ); + GET_UINT32_BE( W[15], data, 60 ); + +#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) + +#define R(t) \ +( \ + temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ + W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ + ( W[t & 0x0F] = S(temp,1) ) \ +) + +#define P(a,b,c,d,e,x) \ +{ \ + e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ +} + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define F(x,y,z) (z ^ (x & (y ^ z))) +#define K 0x5A827999 + + P( A, B, C, D, E, W[0] ); + P( E, A, B, C, D, W[1] ); + P( D, E, A, B, C, W[2] ); + P( C, D, E, A, B, W[3] ); + P( B, C, D, E, A, W[4] ); + P( A, B, C, D, E, W[5] ); + P( E, A, B, C, D, W[6] ); + P( D, E, A, B, C, W[7] ); + P( C, D, E, A, B, W[8] ); + P( B, C, D, E, A, W[9] ); + P( A, B, C, D, E, W[10] ); + P( E, A, B, C, D, W[11] ); + P( D, E, A, B, C, W[12] ); + P( C, D, E, A, B, W[13] ); + P( B, C, D, E, A, W[14] ); + P( A, B, C, D, E, W[15] ); + P( E, A, B, C, D, R(16) ); + P( D, E, A, B, C, R(17) ); + P( C, D, E, A, B, R(18) ); + P( B, C, D, E, A, R(19) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0x6ED9EBA1 + + P( A, B, C, D, E, R(20) ); + P( E, A, B, C, D, R(21) ); + P( D, E, A, B, C, R(22) ); + P( C, D, E, A, B, R(23) ); + P( B, C, D, E, A, R(24) ); + P( A, B, C, D, E, R(25) ); + P( E, A, B, C, D, R(26) ); + P( D, E, A, B, C, R(27) ); + P( C, D, E, A, B, R(28) ); + P( B, C, D, E, A, R(29) ); + P( A, B, C, D, E, R(30) ); + P( E, A, B, C, D, R(31) ); + P( D, E, A, B, C, R(32) ); + P( C, D, E, A, B, R(33) ); + P( B, C, D, E, A, R(34) ); + P( A, B, C, D, E, R(35) ); + P( E, A, B, C, D, R(36) ); + P( D, E, A, B, C, R(37) ); + P( C, D, E, A, B, R(38) ); + P( B, C, D, E, A, R(39) ); + +#undef K +#undef F + +#define F(x,y,z) ((x & y) | (z & (x | y))) +#define K 0x8F1BBCDC + + P( A, B, C, D, E, R(40) ); + P( E, A, B, C, D, R(41) ); + P( D, E, A, B, C, R(42) ); + P( C, D, E, A, B, R(43) ); + P( B, C, D, E, A, R(44) ); + P( A, B, C, D, E, R(45) ); + P( E, A, B, C, D, R(46) ); + P( D, E, A, B, C, R(47) ); + P( C, D, E, A, B, R(48) ); + P( B, C, D, E, A, R(49) ); + P( A, B, C, D, E, R(50) ); + P( E, A, B, C, D, R(51) ); + P( D, E, A, B, C, R(52) ); + P( C, D, E, A, B, R(53) ); + P( B, C, D, E, A, R(54) ); + P( A, B, C, D, E, R(55) ); + P( E, A, B, C, D, R(56) ); + P( D, E, A, B, C, R(57) ); + P( C, D, E, A, B, R(58) ); + P( B, C, D, E, A, R(59) ); + +#undef K +#undef F + +#define F(x,y,z) (x ^ y ^ z) +#define K 0xCA62C1D6 + + P( A, B, C, D, E, R(60) ); + P( E, A, B, C, D, R(61) ); + P( D, E, A, B, C, R(62) ); + P( C, D, E, A, B, R(63) ); + P( B, C, D, E, A, R(64) ); + P( A, B, C, D, E, R(65) ); + P( E, A, B, C, D, R(66) ); + P( D, E, A, B, C, R(67) ); + P( C, D, E, A, B, R(68) ); + P( B, C, D, E, A, R(69) ); + P( A, B, C, D, E, R(70) ); + P( E, A, B, C, D, R(71) ); + P( D, E, A, B, C, R(72) ); + P( C, D, E, A, B, R(73) ); + P( B, C, D, E, A, R(74) ); + P( A, B, C, D, E, R(75) ); + P( E, A, B, C, D, R(76) ); + P( D, E, A, B, C, R(77) ); + P( C, D, E, A, B, R(78) ); + P( B, C, D, E, A, R(79) ); + +#undef K +#undef F + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void sha1_update( sha1_context *ctx, uchar *input, uint length ) +{ + ulong left, fill; + + if( ! length ) return; + + left = ctx->total[0] & 0x3F; + fill = 64 - left; + + ctx->total[0] += length; + ctx->total[0] &= 0xFFFFFFFF; + + if( ctx->total[0] < length ) + ctx->total[1]++; + + if( left && length >= fill ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, fill ); + sha1_process( ctx, ctx->buffer ); + length -= fill; + input += fill; + left = 0; + } + + while( length >= 64 ) + { + sha1_process( ctx, input ); + length -= 64; + input += 64; + } + + if( length ) + { + memcpy( (void *) (ctx->buffer + left), + (void *) input, length ); + } +} + +static uchar sha1_padding[64] = +{ + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +void sha1_finish( sha1_context *ctx, uchar digest[20] ) +{ + ulong last, padn; + ulong high, low; + uchar msglen[8]; + + high = ( ctx->total[0] >> 29 ) + | ( ctx->total[1] << 3 ); + low = ( ctx->total[0] << 3 ); + + PUT_UINT32_BE( high, msglen, 0 ); + PUT_UINT32_BE( low, msglen, 4 ); + + last = ctx->total[0] & 0x3F; + padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); + + sha1_update( ctx, sha1_padding, padn ); + sha1_update( ctx, msglen, 8 ); + + PUT_UINT32_BE( ctx->state[0], digest, 0 ); + PUT_UINT32_BE( ctx->state[1], digest, 4 ); + PUT_UINT32_BE( ctx->state[2], digest, 8 ); + PUT_UINT32_BE( ctx->state[3], digest, 12 ); + PUT_UINT32_BE( ctx->state[4], digest, 16 ); +} + +/* + * Output SHA-1(file contents), returns 0 if successful. + */ +int sha1_file( char *filename, uchar digest[20] ) +{ + FILE *f; + size_t n; + sha1_context ctx; + uchar buf[1024]; + + if( ( f = fopen( filename, "rb" ) ) == NULL ) + return( 1 ); + + sha1_starts( &ctx ); + + while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) + sha1_update( &ctx, buf, (uint) n ); + + sha1_finish( &ctx, digest ); + + fclose( f ); + return( 0 ); +} + +/* + * Output SHA-1(buf) + */ +void sha1_csum( uchar *buf, uint buflen, uchar digest[20] ) +{ + sha1_context ctx; + + sha1_starts( &ctx ); + sha1_update( &ctx, buf, buflen ); + sha1_finish( &ctx, digest ); +} + +/* + * Output HMAC-SHA-1(key,buf) + */ +void sha1_hmac( uchar *key, uint keylen, uchar *buf, uint buflen, + uchar digest[20] ) +{ + uint i; + sha1_context ctx; + uchar k_ipad[64]; + uchar k_opad[64]; + uchar tmpbuf[20]; + + memset( k_ipad, 0x36, 64 ); + memset( k_opad, 0x5C, 64 ); + + for( i = 0; i < keylen; i++ ) + { + if( i >= 64 ) break; + + k_ipad[i] ^= key[i]; + k_opad[i] ^= key[i]; + } + + sha1_starts( &ctx ); + sha1_update( &ctx, k_ipad, 64 ); + sha1_update( &ctx, buf, buflen ); + sha1_finish( &ctx, tmpbuf ); + + sha1_starts( &ctx ); + sha1_update( &ctx, k_opad, 64 ); + sha1_update( &ctx, tmpbuf, 20 ); + sha1_finish( &ctx, digest ); + + memset( k_ipad, 0, 64 ); + memset( k_opad, 0, 64 ); + memset( tmpbuf, 0, 20 ); + memset( &ctx, 0, sizeof( sha1_context ) ); +} + +#ifdef SELF_TEST +/* + * FIPS-180-1 test vectors + */ +static char *sha1_test_str[3] = +{ + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + NULL +}; + +static uchar sha1_test_sum[3][20] = +{ + { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, + 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, + { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, + 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, + { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, + 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } +}; + +/* + * Checkup routine + */ +int sha1_self_test( void ) +{ + int i, j; + uchar buf[1000]; + uchar sha1sum[20]; + sha1_context ctx; + + for( i = 0; i < 3; i++ ) + { + printf( " SHA-1 test #%d: ", i + 1 ); + + sha1_starts( &ctx ); + + if( i < 2 ) + sha1_update( &ctx, (uchar *) sha1_test_str[i], + strlen( sha1_test_str[i] ) ); + else + { + memset( buf, 'a', 1000 ); + for( j = 0; j < 1000; j++ ) + sha1_update( &ctx, (uchar *) buf, 1000 ); + } + + sha1_finish( &ctx, sha1sum ); + + if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) + { + printf( "failed\n" ); + return( 1 ); + } + + printf( "passed\n" ); + } + + printf( "\n" ); + return( 0 ); +} +#else +int sha1_self_test( void ) +{ + printf( "SHA-1 self-test not available\n\n" ); + return( 1 ); +} +#endif diff --git a/trunk/tools/firmware-utils/src/sha1.h b/trunk/tools/firmware-utils/src/sha1.h new file mode 100644 index 00000000..425267a3 --- /dev/null +++ b/trunk/tools/firmware-utils/src/sha1.h @@ -0,0 +1,57 @@ +#ifndef _SHA1_H +#define _SHA1_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _STD_TYPES +#define _STD_TYPES + +#define uchar unsigned char +#define uint unsigned int +#define ulong unsigned long int + +#endif + +typedef struct +{ + ulong total[2]; + ulong state[5]; + uchar buffer[64]; +} +sha1_context; + +/* + * Core SHA-1 functions + */ +void sha1_starts( sha1_context *ctx ); +void sha1_update( sha1_context *ctx, uchar *input, uint length ); +void sha1_finish( sha1_context *ctx, uchar digest[20] ); + +/* + * Output SHA-1(file contents), returns 0 if successful. + */ +int sha1_file( char *filename, uchar digest[20] ); + +/* + * Output SHA-1(buf) + */ +void sha1_csum( uchar *buf, uint buflen, uchar digest[20] ); + +/* + * Output HMAC-SHA-1(key,buf) + */ +void sha1_hmac( uchar *key, uint keylen, uchar *buf, uint buflen, + uchar digest[20] ); + +/* + * Checkup routine + */ +int sha1_self_test( void ); + +#ifdef __cplusplus +} +#endif + +#endif /* sha1.h */ diff --git a/trunk/tools/firmware-utils/src/spw303v.c b/trunk/tools/firmware-utils/src/spw303v.c new file mode 100644 index 00000000..202ffbad --- /dev/null +++ b/trunk/tools/firmware-utils/src/spw303v.c @@ -0,0 +1,242 @@ +/* + * spw303v.c - partially based on libreCMC's imagetag.c and addpattern.c + * + * Copyright (C) 2011 Jonas Gorski + * + * 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 +#include +#include +#include +#include +#include +#include + +#define IMAGE_LEN 10 /* Length of Length Field */ +#define ADDRESS_LEN 12 /* Length of Address field */ +#define TAGID_LEN 6 /* Length of tag ID */ +#define TAGINFO_LEN 20 /* Length of vendor information field in tag */ +#define TAGVER_LEN 4 /* Length of Tag Version */ +#define TAGLAYOUT_LEN 4 /* Length of FlashLayoutVer */ + + +struct spw303v_tag +{ + unsigned char tagVersion[4]; // tag version. Will be 2 here. + unsigned char signiture_1[20]; // text line for company info + unsigned char signiture_2[14]; // additional info (can be version number) + unsigned char chipId[6]; // chip id + unsigned char boardId[16]; // board id + unsigned char bigEndian[2]; // if = 1 - big, = 0 - little endia of the host + unsigned char totalImageLen[IMAGE_LEN]; // the sum of all the following length + unsigned char cfeAddress[ADDRESS_LEN]; // if non zero, cfe starting address + unsigned char cfeLen[IMAGE_LEN]; // if non zero, cfe size in clear ASCII text. + unsigned char rootfsAddress[ADDRESS_LEN]; // if non zero, filesystem starting address + unsigned char rootfsLen[IMAGE_LEN]; // if non zero, filesystem size in clear ASCII text. + unsigned char kernelAddress[ADDRESS_LEN]; // if non zero, kernel starting address + unsigned char kernelLen[IMAGE_LEN]; // if non zero, kernel size in clear ASCII text. + + unsigned char certf1Address[ADDRESS_LEN]; + unsigned char certf1Len[6]; + unsigned char certf2Address[ADDRESS_LEN]; + unsigned char certf2Len[6]; + unsigned char certf3Address[ADDRESS_LEN]; + unsigned char certf3Len[6]; + unsigned char httpsFileSize[4]; + unsigned char tr64FileSize[4]; + unsigned char tr69FileSize[4]; + unsigned char filesmap[4]; + + unsigned char imageSequence[4]; // incrments everytime an image is flashed + unsigned char reserved[4]; // reserved for later use + unsigned char imageCRC[4]; // 216-219: CRC32 of images + unsigned char reserved2[16]; // 220-235: Unused at present + unsigned char headerCRC[4]; // 236-239: CRC32 of header excluding tagVersion + unsigned char reserved3[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 +}; +#define IMAGETAG_CRC_START 0xFFFFFFFF + +#define IMAGETAG_MAGIC1_TCOM "AAAAAAAA Corporatio" + +static char fake_data[] = { + 0x18, 0x21, 0x21, 0x18, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21 ,0x18, + 0x21, 0x24, 0x21, 0x1b, 0x18, 0x18, 0x24, 0x24, 0x18, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x1b, 0x18, 0x18, 0x24, 0x24, 0x21, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, 0x18, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x18, + 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x21, 0x18, 0x21, 0x21, + 0x21, 0x21, 0x21, 0x21, +}; + + +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 spw303v_tag *tag = buf; + uint32_t crc; + /* Replace signature with custom t-com one */ + memset(tag->signiture_1, 0, 20); + memcpy(tag->signiture_1, IMAGETAG_MAGIC1_TCOM, strlen(IMAGETAG_MAGIC1_TCOM)); + + /* Clear cert fields to remove information_* data */ + memset(tag->certf1Address, 0, 74); + + /* replace image crc with modified one */ + crc = ntohl(*((uint32_t *)&tag->imageCRC)); + + crc = htonl(crc32(crc, fake_data, 64)); + + memcpy(tag->imageCRC, &crc, 4); + + /* Update tag crc */ + crc = htonl(crc32(IMAGETAG_CRC_START, buf, 236)); + memcpy(tag->headerCRC, &crc, 4); +} + + + +void usage(void) __attribute__ (( __noreturn__ )); + +void usage(void) +{ + fprintf(stderr, "Usage: spw303v [-i ] [-o ]\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; + FILE *out = stdout; + char *ifn = NULL; + char *ofn = NULL; + int c; + int v0, v1, v2; + size_t n; + int first_block = 1; + + uint32_t image_crc = IMAGETAG_CRC_START; + + 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; + } + + image_crc = crc32(image_crc, buf, n); + + 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; +} diff --git a/trunk/tools/firmware-utils/src/srec2bin.c b/trunk/tools/firmware-utils/src/srec2bin.c new file mode 100644 index 00000000..1cffbaed --- /dev/null +++ b/trunk/tools/firmware-utils/src/srec2bin.c @@ -0,0 +1,524 @@ +#include +#include +#include + +//Rev 0.1 Original +// 8 Jan 2001 MJH Added code to write data to Binary file +// note: outputfile is name.bin, where name is first part +// of input file. ie tmp.rec -> tmp.bin +// +// srec2bin +// +// TAG +// bit32u TAG_BIG = 0xDEADBE42; +// bit32u TAG_LITTLE = 0xFEEDFA42; +// +// File Structure +// +// TAG : 32 Bits +// [DATA RECORDS] +// +// Data Records Structure +// +// LENGTH : 32 Bits <- Length of DATA, excludes ADDRESS and CHECKSUM +// ADDRESS : 32 Bits +// DATA : 8 Bits * LENGTH +// CHECKSUM: 32 Bits <- 0 - (Sum of Length --> End of Data) +// +// Note : If Length == 0, Address will be Program Start +// +// +// +// +// + +#define MajRevNum 0 +#define MinRevNum 2 + + +#define EndianSwitch(x) ((x >> 24) | (x << 24) | ((x << 8) & (0x00FF0000)) | ((x >> 8) & (0x0000FF00)) ) + +typedef unsigned char bit8u; +typedef unsigned int bit32u; +typedef int bit32; + +#define FALSE 0 +#define TRUE (!FALSE) + + +bit32u CheckSum; +int RecStart; +int debug; +int verbose; + +FILE *OpenOutputFile( char *Name ); +FILE *fOut; +bit32u RecLength=0; + +bit32u AddressCurrent; + +bit32u gh(char *cp,int nibs); + +int BigEndian; + +int inputline; + +// char buf[16*1024]; + +char buffer[2048]; +char *cur_ptr; +int cur_line=0; +int cur_len=0; + +int s1s2s3_total=0; + +bit32u PBVal; +int PBValid; +bit32u PBAdr; + + +void dumpfTell(char *s, bit32u Value) +{ + int Length; + Length = (int) RecLength; + if (debug) + printf("[%s ] ftell()[0x%08lX] Length[0x%4X] Length[%4d] Value[0x%08x]\n", + s, ftell(fOut), Length, Length, Value); +} + +void DispHex(bit32u Hex) +{ +// printf("%X", Hex); +} + +void WaitDisplay(void) +{ + static int Count=0; + static int Index=0; + char iline[]={"-\\|/"}; + + Count++; + if ((Count % 32)==0) + { + if (verbose) + printf("%c%c",iline[Index++],8); + Index &= 3; + } +} + + +void binOut32 ( bit32u Data ) +{ +// On UNIX machine all 32bit writes need ENDIAN switched +// Data = EndianSwitch(Data); +// fwrite( &Data, sizeof(bit32u), 1, fOut); + + char sdat[4]; + int i; + + for(i=0;i<4;i++) + sdat[i]=(char)(Data>>(i*8)); + fwrite( sdat, 1, 4, fOut); + dumpfTell("Out32" , Data); +} + +// Only update RecLength on Byte Writes +// All 32 bit writes will be for Length etc + +void binOut8 ( bit8u Data ) +{ + int n; + dumpfTell("B4Data" , (bit32u) (Data & 0xFF) ); + n = fwrite( &Data, sizeof(bit8u), 1, fOut); + if (n != 1) + printf("Error in writing %X for Address 0x%8X\n", Data, AddressCurrent); + RecLength += 1; +} + +// Currently ONLY used for outputting Program Start + +void binRecStart(bit32u Address) +{ + RecLength = 0; + CheckSum = Address; + RecStart = TRUE; + + if (debug) + printf("[RecStart] CheckSum[0x%08X] Length[%4d] Address[0x%08X]\n", + CheckSum, RecLength, Address); + + + dumpfTell("RecLength", RecLength); + binOut32( RecLength ); + dumpfTell("Address", Address); + binOut32( Address ); +} + +void binRecEnd(void) +{ + long RecEnd; + + if (!RecStart) // if no record started, do not end it + { + return; + } + + RecStart = FALSE; + + + RecEnd = ftell(fOut); // Save Current position + + if (debug) + printf("[RecEnd ] CheckSum[0x%08X] Length[%4d] Length[0x%X] RecEnd[0x%08lX]\n", + CheckSum, RecLength, RecLength, RecEnd); + + fseek( fOut, -((long) RecLength), SEEK_CUR); // move back Start Of Data + + dumpfTell("Data ", -1); + + fseek( fOut, -4, SEEK_CUR); // move back Start Of Address + + dumpfTell("Address ", -1); + + fseek( fOut, -4, SEEK_CUR); // move back Start Of Length + + dumpfTell("Length ", -1); + + binOut32( RecLength ); + + fseek( fOut, RecEnd, SEEK_SET); // move to end of Record + + CheckSum += RecLength; + + CheckSum = ~CheckSum + 1; // Two's complement + + binOut32( CheckSum ); + + if (verbose) + printf("[Created Record of %d Bytes with CheckSum [0x%8X]\n", RecLength, CheckSum); +} + +void binRecOutProgramStart(bit32u Address) +{ + if (Address != (AddressCurrent+1)) + { + binRecEnd(); + binRecStart(Address); + } + AddressCurrent = Address; +} +void binRecOutByte(bit32u Address, bit8u Data) +{ + // If Address is one after Current Address, output Byte + // If not, close out last record, update Length, write checksum + // Then Start New Record, updating Current Address + + if (Address != (AddressCurrent+1)) + { + binRecEnd(); + binRecStart(Address); + } + AddressCurrent = Address; + CheckSum += Data; + binOut8( Data ); +} + +//============================================================================= +// SUPPORT FUNCTIONS +//============================================================================= +int readline(FILE *fil,char *buf,int len) +{ + int rlen; + + rlen=0; + if (len==0) return(0); + while(1) + { + if (cur_len==0) + { + cur_len=fread(buffer, 1, sizeof(buffer), fil); + if (cur_len==0) + { + if (rlen) + { + *buf=0; + return(rlen); + } + return(-1); + } + cur_ptr=buffer; + } + if (cur_len) + { + if (*cur_ptr=='\n') + { + *buf=0; + cur_ptr++; + cur_len--; + return(rlen); + } + else + { + if ((len>1)&&(*cur_ptr!='\r')) + { + *buf++=*cur_ptr++; + len--; + } + else + cur_ptr++; + + rlen++; + cur_len--; + } + } + else + { + *buf=0; + cur_ptr++; + cur_len--; + return(rlen); + } + } +} + + +int SRLerrorout(char *c1,char *c2) +{ + printf("\nERROR: %s - '%s'.",c1,c2); + return(FALSE); +} + + +int checksum(char *cp,int count) +{ + char *scp; + int cksum; + int dum; + + scp=cp; + while(*scp) + { + if (!isxdigit(*scp++)) + return(SRLerrorout("Invalid hex digits",cp)); + } + scp=cp; + + cksum=count; + + while(count) + { + cksum += gh(scp,2); + if (count == 2) + dum = ~cksum; + scp += 2; + count--; + } + cksum&=0x0ff; + // printf("\nCk:%02x",cksum); + return(cksum==0x0ff); +} + +bit32u gh(char *cp,int nibs) +{ + int i; + bit32u j; + + j=0; + + for(i=0;i='a')&&(*cp<='z')) *cp &= 0x5f; + if ((*cp>='0')&&(*cp<='9')) + j += (*cp-0x30); + else + if ((*cp>='A')&&(*cp<='F')) + j += (*cp-0x37); + else + SRLerrorout("Bad Hex char", cp); + cp++; + } + return(j); +} + + +//============================================================================= +// PROCESS SREC LINE +//============================================================================= + +int srecLine(char *pSrecLine) +{ + char *scp,ch; + int itmp,count,dat; + bit32u adr; + static bit32u RecordCounter=0; + + cur_line++; + scp=pSrecLine; + + if (*pSrecLine!='S') + return(SRLerrorout("Not an Srecord file",scp)); + pSrecLine++; + if (strlen(pSrecLine)<4) + return(SRLerrorout("Srecord too short",scp)); + + ch=*pSrecLine++; + + count=gh(pSrecLine,2); + + pSrecLine += 2; + + // if(debug) + // printf("count %d, strlen(pSrecLine) = %d, pSrecLine =[%s]\n", count, strlen(pSrecLine), pSrecLine); + RecordCounter++; + DispHex(RecordCounter); + + if ((count*2) != strlen(pSrecLine)) return(SRLerrorout("Count field larger than record",scp)); + + if (!checksum(pSrecLine, count)) return(SRLerrorout("Bad Checksum",scp)); + + switch(ch) + { + case '0': if (count<3) return(SRLerrorout("Invalid Srecord count field",scp)); + itmp=gh(pSrecLine,4); pSrecLine+=4; count-=2; + if (itmp) return(SRLerrorout("Srecord 1 address not zero",scp)); + break; + case '1': if (count<3) return(SRLerrorout("Invalid Srecord count field",scp)); + return(SRLerrorout("Srecord Not valid for MIPS",scp)); + break; + case '2': if (count<4) return(SRLerrorout("Invalid Srecord count field",scp)); + return(SRLerrorout("Srecord Not valid for MIPS",scp)); + break; + case '3': if (count<5) return(SRLerrorout("Invalid Srecord count field",scp)); + adr=gh(pSrecLine,8); pSrecLine+=8; count-=4; + count--; + while(count) + { + dat=gh(pSrecLine,2); pSrecLine+=2; count--; + binRecOutByte(adr, (char) (dat & 0xFF)); + adr++; + } + s1s2s3_total++; + break; + case '4': return(SRLerrorout("Invalid Srecord type",scp)); + break; + case '5': if (count<3) return(SRLerrorout("Invalid Srecord count field",scp)); + itmp=gh(pSrecLine,4); pSrecLine+=4; count-=2; + if (itmp|=s1s2s3_total) return(SRLerrorout("Incorrect number of S3 Record processed",scp)); + break; + case '6': return(SRLerrorout("Invalid Srecord type",scp)); + break; + case '7': // PROGRAM START + if (count<5) return(SRLerrorout("Invalid Srecord count field",scp)); + adr=gh(pSrecLine,8); pSrecLine+=8; count-=4; + if (count!=1) return(SRLerrorout("Invalid Srecord count field",scp)); + binRecOutProgramStart(adr); + break; + case '8': if (count<4) return(SRLerrorout("Invalid Srecord count field",scp)); + return(SRLerrorout("Srecord Not valid for MIPS",scp)); + break; + case '9': if (count<3) return(SRLerrorout("Invalid Srecord count field",scp)); + return(SRLerrorout("Srecord Not valid for MIPS",scp)); + break; + default: + break; + } + return(TRUE); +} + + +//============================================================================= +// MAIN LOGIC, READS IN LINE AND OUTPUTS BINARY +//============================================================================= + +int srec2bin(int argc,char *argv[],int verbose) +{ + int i,rlen,sts; + FILE *fp; + char ac; + char buff[256]; + bit32u TAG_BIG = 0xDEADBE42; + bit32u TAG_LITTLE = 0xFEEDFA42; + + bit32u Tag; + + + if(argc < 3) + { + printf("\nError: \n\n"); + return(0); + } + + if (argc > 3) BigEndian=TRUE; else BigEndian=FALSE; + + if (BigEndian) + Tag = TAG_BIG; + else + Tag = TAG_LITTLE; + + if (verbose) + printf("\nEndian: %s, Tag is 0x%8X\n",(BigEndian)?"BIG":"LITTLE", Tag); + + fp = fopen(argv[1],"rt"); + + if (fp==NULL) + { + printf("\nError: Opening input file, %s.", argv[1]); + return(0); + } + + fOut = fopen( argv[2], "wb"); + + if (fOut==NULL) + { + printf("\nError: Opening Output file, %s.", argv[2]); + if(fp) fclose(fp); + return(0); + } + + RecStart = FALSE; + + AddressCurrent = 0xFFFFFFFFL; + + // Setup Tag + + dumpfTell("Tag", Tag); + + binOut32(Tag); + + + inputline=0; + sts=TRUE; + + rlen = readline(fp,buff,sizeof buff); + + while( (sts) && (rlen != -1)) + { + if (strlen(buff)) + { + sts &= srecLine(buff); + WaitDisplay(); + } + rlen = readline(fp,buff,sizeof buff); + } + + + // printf("PC: 0x%08X, Length 0x%08X, Tag 0x%08X\n", ProgramStart, RecLength, TAG_LITTLE); + + binRecEnd(); + + if(fp) fclose(fp); + if(fOut) fclose(fOut); + + return(1); +} + +main(int argc, char *argv[]) +{ + debug = TRUE; + debug = FALSE; + verbose = FALSE; + srec2bin(argc,argv,verbose); + return 0; +} + diff --git a/trunk/tools/firmware-utils/src/tplink-safeloader.c b/trunk/tools/firmware-utils/src/tplink-safeloader.c new file mode 100644 index 00000000..9c5bb54f --- /dev/null +++ b/trunk/tools/firmware-utils/src/tplink-safeloader.c @@ -0,0 +1,546 @@ +/* + Copyright (c) 2014, Matthias Schiffer + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "md5.h" + + +#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); }) + + +/** 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 { + const char *name; + uint32_t base; + uint32_t size; +}; + + +/** 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, +}; + + +/** Vendor information for CPE210/220/510/520 */ +static const unsigned char cpe510_vendor[] = "\x00\x00\x00\x1f""CPE510(TP-LINK|UN|N300-5):1.0\r\n"; + + +/** + The flash partition table for CPE210/220/510/520; + it is the same as the one used by the stock images. +*/ +static const struct flash_partition_entry cpe510_partitions[] = { + {"fs-uboot", 0x00000, 0x20000}, + {"partition-table", 0x20000, 0x02000}, + {"default-mac", 0x30000, 0x00020}, + {"product-info", 0x31100, 0x00100}, + {"signature", 0x32000, 0x00400}, + {"os-image", 0x40000, 0x170000}, + {"soft-version", 0x1b0000, 0x00100}, + {"support-list", 0x1b1000, 0x00400}, + {"file-system", 0x1c0000, 0x600000}, + {"user-config", 0x7c0000, 0x10000}, + {"default-config", 0x7d0000, 0x10000}, + {"log", 0x7e0000, 0x10000}, + {"radio", 0x7f0000, 0x10000}, + {NULL, 0, 0} +}; + +/** + The support list for CPE210/220/510/520 + + The stock images also contain strings for two more devices: BS510 and BS210. + At the moment, there exists no public information about these devices. +*/ +static const unsigned char cpe510_support_list[] = + "\x00\x00\x00\xc8\x00\x00\x00\x00" + "SupportList:\r\n" + "CPE510(TP-LINK|UN|N300-5):1.0\r\n" + "CPE520(TP-LINK|UN|N300-5):1.0\r\n" + "CPE210(TP-LINK|UN|N300-2):1.0\r\n" + "CPE220(TP-LINK|UN|N300-2):1.0\r\n" + "\r\n\xff"; + +#define error(_ret, _errno, _str, ...) \ + do { \ + fprintf(stderr, _str ": %s\n", ## __VA_ARGS__, \ + strerror(_errno)); \ + if (_ret) \ + exit(_ret); \ + } while (0) + + +/** Allocates a new image partition */ +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 */ +void free_image_partition(struct image_partition_entry entry) { + free(entry.data); +} + +/** Generates the partition-table partition */ +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 */ +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 (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; +} + +/** Generates the support-list partition */ +struct image_partition_entry make_support_list(const unsigned char *support_list, size_t len) { + struct image_partition_entry entry = alloc_image_partition("support-list", len); + memcpy(entry.data, support_list, len); + return entry; +} + +/** Creates a new image partition with an arbitrary name from a file */ +struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) { + 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) + 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; +} + + +/** + 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. +*/ +void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) { + size_t i; + char *image_pt = (char *)buffer, *end = image_pt + 0x800; + + size_t base = 0x800; + for (i = 0; parts[i].name; i++) { + 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; + } + + image_pt++; + + memset(image_pt, 0xff, end-image_pt); +} + +/** Generates and writes the image MD5 checksum */ +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-1013 Vendor information (4096 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 +*/ +void * generate_factory_image(const unsigned char *vendor, size_t vendor_len, 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"); + + image[0] = *len >> 24; + image[1] = *len >> 16; + image[2] = *len >> 8; + image[3] = *len; + + memcpy(image+0x14, vendor, vendor_len); + memset(image+0x14+vendor_len, 0xff, 4096-vendor_len); + + put_partitions(image + 0x1014, 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. +*/ +void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) { + const struct flash_partition_entry *flash_os_image = &flash_parts[5]; + const struct flash_partition_entry *flash_soft_version = &flash_parts[6]; + const struct flash_partition_entry *flash_support_list = &flash_parts[7]; + const struct flash_partition_entry *flash_file_system = &flash_parts[8]; + + const struct image_partition_entry *image_os_image = &image_parts[3]; + const struct image_partition_entry *image_soft_version = &image_parts[1]; + const struct image_partition_entry *image_support_list = &image_parts[2]; + const struct image_partition_entry *image_file_system = &image_parts[4]; + + assert(strcmp(flash_os_image->name, "os-image") == 0); + assert(strcmp(flash_soft_version->name, "soft-version") == 0); + assert(strcmp(flash_support_list->name, "support-list") == 0); + assert(strcmp(flash_file_system->name, "file-system") == 0); + + assert(strcmp(image_os_image->name, "os-image") == 0); + assert(strcmp(image_soft_version->name, "soft-version") == 0); + assert(strcmp(image_support_list->name, "support-list") == 0); + assert(strcmp(image_file_system->name, "file-system") == 0); + + if (image_os_image->size > flash_os_image->size) + error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size); + if (image_file_system->size > flash_file_system->size) + error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size); + + *len = flash_file_system->base - flash_os_image->base + image_file_system->size; + + uint8_t *image = malloc(*len); + if (!image) + error(1, errno, "malloc"); + + memset(image, 0xff, *len); + + memcpy(image, image_os_image->data, image_os_image->size); + memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size); + memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size); + memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size); + + return image; +} + + +/** Generates an image for CPE210/220/510/520 and writes it to a file */ +static void do_cpe510(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) { + struct image_partition_entry parts[6] = {}; + + parts[0] = make_partition_table(cpe510_partitions); + parts[1] = make_soft_version(rev); + parts[2] = make_support_list(cpe510_support_list, sizeof(cpe510_support_list)-1); + parts[3] = read_file("os-image", kernel_image, false); + parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof); + + size_t len; + void *image; + if (sysupgrade) + image = generate_sysupgrade_image(cpe510_partitions, parts, &len); + else + image = generate_factory_image(cpe510_vendor, sizeof(cpe510_vendor)-1, 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); + + size_t i; + for (i = 0; parts[i].name; i++) + free_image_partition(parts[i]); +} + + +/** Usage output */ +void usage(const char *argv0) { + fprintf(stderr, + "Usage: %s [OPTIONS...]\n" + "\n" + "Options:\n" + " -B create image for the board specified with \n" + " -k read kernel image from the file \n" + " -r read rootfs image from the file \n" + " -o write output to the file \n" + " -V sets the revision number to \n" + " -j add jffs2 end-of-filesystem markers\n" + " -S create sysupgrade instead of factory image\n" + " -h show this help\n", + argv0 + ); +}; + + +int main(int argc, char *argv[]) { + const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL; + bool add_jffs2_eof = false, sysupgrade = false; + unsigned rev = 0; + + while (true) { + int c; + + c = getopt(argc, argv, "B:k:r:o:V:jSh"); + 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; + + default: + usage(argv[0]); + return 1; + } + } + + 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"); + + if (strcmp(board, "CPE510") == 0) + do_cpe510(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade); + else + error(1, 0, "unsupported board %s", board); + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/trx.c b/trunk/tools/firmware-utils/src/trx.c new file mode 100644 index 00000000..aa1f5be4 --- /dev/null +++ b/trunk/tools/firmware-utils/src/trx.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2004 Manuel Novoa III + * + * 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 + */ + +/* July 29, 2004 + * + * This is a hacked replacement for the 'trx' utility used to create + * wrt54g .trx firmware files. It isn't pretty, but it does the job + * for me. + * + * As an extension, you can specify a larger maximum length for the + * .trx file using '-m'. It will be rounded up to be a multiple of 4K. + * NOTE: This space will be malloc()'d. + * + * August 16, 2004 + * + * Sigh... Make it endian-neutral. + * + * TODO: Support '-b' option to specify offsets for each file. + * + * February 19, 2005 - mbm + * + * Add -a (align offset) and -b (absolute offset) + * + * March 24, 2010 - markus + * + * extend trx header struct for new version + * assume v1 for as default + * Add option -2 to allow v2 header + */ + +#include +#include +#include +#include +#include +#include +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define STORE32_LE(X) bswap_32(X) +#define LOAD32_LE(X) bswap_32(X) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define STORE32_LE(X) (X) +#define LOAD32_LE(X) (X) +#else +#error unkown endianness! +#endif + +uint32_t crc32buf(char *buf, size_t len); + +/**********************************************************************/ +/* from trxhdr.h */ + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_MAX_LEN 0x720000 +#define TRX_NO_HEADER 1 /* Do not write TRX header */ + +struct trx_header { + uint32_t magic; /* "HDR0" */ + uint32_t len; /* Length of file including header */ + uint32_t crc32; /* 32-bit CRC from flag_version to end of file */ + uint32_t flag_version; /* 0:15 flags, 16:31 version */ + uint32_t offsets[4]; /* Offsets of partitions from start of header */ +}; + +/**********************************************************************/ + +void usage(void) __attribute__ (( __noreturn__ )); + +void usage(void) +{ + fprintf(stderr, "Usage:\n"); + fprintf(stderr, " trx [-2] [-o outfile] [-m maxlen] [-a align] [-b absolute offset] [-x relative offset]\n"); + fprintf(stderr, " [-f file] [-f file [-f file [-f file (v2 only)]]]\n"); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + FILE *out = stdout; + FILE *in; + char *ofn = NULL; + char *buf; + char *e; + int c, i, append = 0; + size_t n; + ssize_t n2; + uint32_t cur_len, fsmark=0; + unsigned long maxlen = TRX_MAX_LEN; + struct trx_header *p; + char trx_version = 1; + unsigned char binheader[32]; + + fprintf(stderr, "mjn3's trx replacement - v0.81.1\n"); + + if (!(buf = malloc(maxlen))) { + fprintf(stderr, "malloc failed\n"); + return EXIT_FAILURE; + } + + p = (struct trx_header *) buf; + + p->magic = STORE32_LE(TRX_MAGIC); + cur_len = sizeof(struct trx_header) - 4; /* assume v1 header */ + + in = NULL; + i = 0; + + while ((c = getopt(argc, argv, "-:2o:m:a:x:b:f:A:F:")) != -1) { + switch (c) { + case '2': + /* take care that nothing was written to buf so far */ + if (cur_len != sizeof(struct trx_header) - 4) { + fprintf(stderr, "-2 has to be used before any other argument!\n"); + } + else { + trx_version = 2; + cur_len += 4; + } + break; + case 'F': + fsmark = cur_len; + case 'A': + append = 1; + /* fall through */ + case 'f': + case 1: + if (!append) + p->offsets[i++] = STORE32_LE(cur_len); + + if (!(in = fopen(optarg, "r"))) { + fprintf(stderr, "can not open \"%s\" for reading\n", optarg); + usage(); + } + n = fread(buf + cur_len, 1, maxlen - cur_len, in); + if (!feof(in)) { + fprintf(stderr, "fread failure or file \"%s\" too large\n",optarg); + fclose(in); + return EXIT_FAILURE; + } + fclose(in); +#undef ROUND +#define ROUND 4 + if (n & (ROUND-1)) { + memset(buf + cur_len + n, 0, ROUND - (n & (ROUND-1))); + n += ROUND - (n & (ROUND-1)); + } + cur_len += n; + append = 0; + + break; + case 'o': + ofn = optarg; + if (ofn && !(out = fopen(ofn, "w"))) { + fprintf(stderr, "can not open \"%s\" for writing\n", ofn); + usage(); + } + + break; + case 'm': + errno = 0; + maxlen = strtoul(optarg, &e, 0); + if (errno || (e == optarg) || *e) { + fprintf(stderr, "illegal numeric string\n"); + usage(); + } +#undef ROUND +#define ROUND 0x1000 + if (maxlen & (ROUND-1)) { + maxlen += (ROUND - (maxlen & (ROUND-1))); + } + if (maxlen < ROUND) { + fprintf(stderr, "maxlen too small (or wrapped)\n"); + usage(); + } + if (maxlen > TRX_MAX_LEN) { + fprintf(stderr, "WARNING: maxlen exceeds default maximum! Beware of overwriting nvram!\n"); + } + if (!(buf = realloc(buf,maxlen))) { + fprintf(stderr, "realloc failed"); + return EXIT_FAILURE; + } + p = (struct trx_header *) buf; + break; + case 'a': + errno = 0; + n = strtoul(optarg, &e, 0); + if (errno || (e == optarg) || *e) { + fprintf(stderr, "illegal numeric string\n"); + usage(); + } + if (cur_len & (n-1)) { + n = n - (cur_len & (n-1)); + memset(buf + cur_len, 0, n); + cur_len += n; + } + break; + case 'b': + errno = 0; + n = strtoul(optarg, &e, 0); + if (errno || (e == optarg) || *e) { + fprintf(stderr, "illegal numeric string\n"); + usage(); + } + if (n < cur_len) { + fprintf(stderr, "WARNING: current length exceeds -b %d offset\n",(int) n); + } else { + memset(buf + cur_len, 0, n - cur_len); + cur_len = n; + } + break; + case 'x': + errno = 0; + n2 = strtol(optarg, &e, 0); + if (errno || (e == optarg) || *e) { + fprintf(stderr, "illegal numeric string\n"); + usage(); + } + if (n2 < 0) { + if (-n2 > cur_len) { + fprintf(stderr, "WARNING: current length smaller then -x %d offset\n",(int) n2); + cur_len = 0; + } else + cur_len += n2; + } else { + memset(buf + cur_len, 0, n2); + cur_len += n2; + } + + break; + default: + usage(); + } + } + p->flag_version = STORE32_LE((trx_version << 16)); + + if (!in) { + fprintf(stderr, "we require atleast one filename\n"); + usage(); + } + +#undef ROUND +#define ROUND 0x1000 + n = cur_len & (ROUND-1); + if (n) { + memset(buf + cur_len, 0, ROUND - n); + cur_len += ROUND - n; + } + + /* 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"); + return EXIT_FAILURE; + } + memcpy(binheader, buf + LOAD32_LE(p->offsets[3]), sizeof(binheader)); /* save header */ + memset(buf + LOAD32_LE(p->offsets[3]) + 22, 0xFF, 8); /* set stable and try1-3 to 0xFF */ + } + + p->crc32 = crc32buf((char *) &p->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); + + /* restore TRXv2 bin-header */ + if (trx_version == 2) { + memcpy(buf + LOAD32_LE(p->offsets[3]), binheader, sizeof(binheader)); + } + + if (!fwrite(buf, cur_len, 1, out) || fflush(out)) { + fprintf(stderr, "fwrite failed\n"); + return EXIT_FAILURE; + } + + fclose(out); + + return EXIT_SUCCESS; +} + +/**********************************************************************/ +/* The following was grabbed and tweaked from the old snippets collection + * of public domain C code. */ + +/**********************************************************************\ +|* Demonstration program to compute the 32-bit CRC used as the frame *| +|* check sequence in ADCCP (ANSI X3.66, also known as FIPS PUB 71 *| +|* and FED-STD-1003, the U.S. versions of CCITT's X.25 link-level *| +|* protocol). The 32-bit FCS was added via the Federal Register, *| +|* 1 June 1982, p.23798. I presume but don't know for certain that *| +|* this polynomial is or will be included in CCITT V.41, which *| +|* defines the 16-bit CRC (often called CRC-CCITT) polynomial. FIPS *| +|* PUB 78 says that the 32-bit FCS reduces otherwise undetected *| +|* errors by a factor of 10^-5 over 16-bit FCS. *| +\**********************************************************************/ + +/* Copyright (C) 1986 Gary S. Brown. You may use this program, or + code or tables extracted from it, as desired without restriction.*/ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* 1. The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ +/* be they sixteen or thirty-two bits wide. You simply choose the */ +/* appropriate table. Alternatively, because the table can be */ +/* generated at runtime, you can start by generating the table for */ +/* the polynomial in question and use exactly the same "updcrc", */ +/* if your application needn't simultaneously handle two CRC */ +/* polynomials. (Note, however, that XMODEM is strange.) */ +/* */ +/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ +/* of course, 32-bit entries work OK if the high 16 bits are zero. */ +/* */ +/* 4. The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static const uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +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 +}; + +#define UPDC32(octet,crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +uint32_t crc32buf(char *buf, size_t len) +{ + uint32_t crc; + + crc = 0xFFFFFFFF; + + for ( ; len; --len, ++buf) + { + crc = UPDC32(*buf, crc); + } + + return crc; +} diff --git a/trunk/tools/firmware-utils/src/trx2edips.c b/trunk/tools/firmware-utils/src/trx2edips.c new file mode 100644 index 00000000..f8d068d5 --- /dev/null +++ b/trunk/tools/firmware-utils/src/trx2edips.c @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define STORE32_LE(X) bswap_32(X) +#define LOAD32_LE(X) bswap_32(X) +#elif __BYTE_ORDER == __LITTLE_ENDIAN +#define STORE32_LE(X) (X) +#define LOAD32_LE(X) (X) +#else +#error unkown endianness! +#endif + +/**********************************************************************/ +/* from trxhdr.h */ + +#define TRX_MAGIC 0x30524448 /* "HDR0" */ +#define TRX_VERSION 1 +#define TRX_MAX_LEN 0x5A0000 +#define TRX_NO_HEADER 1 /* Do not write TRX header */ + +struct trx_header { + uint32_t magic; /* "HDR0" */ + uint32_t len; /* Length of file including header */ + uint32_t crc32; /* 32-bit CRC from flag_version to end of file */ + uint32_t flag_version; /* 0:15 flags, 16:31 version */ + uint32_t offsets[3]; /* Offsets of partitions from start of header */ +}; + + +struct edimax_header { + uint32_t sign; /* signature for header */ + uint32_t length; /* start address but doesn't seems to be used... */ + uint32_t start_addr; /* length of data, not used too ...*/ +}; + + +#define EDIMAX_PS16 0x36315350 /* "PS16" */ +#define EDIMAX_HDR_LEN 0xc + + +/**********************************************************************/ +static const uint32_t crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ +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 +}; + +#define UPDC32(octet, crc) (crc_32_tab[((crc) ^ (octet)) & 0xff] ^ ((crc) >> 8)) + +uint32_t crc32buf(char *buf, size_t len) +{ + uint32_t crc; + + crc = 0xFFFFFFFF; + + for (; len; --len, ++buf) + crc = UPDC32(*buf, crc); + + return crc; +} + + +int main(int argc, char *argv[]) +{ + FILE *fpIn = NULL; + FILE *fpOut = NULL; + struct edimax_header eh; + size_t res; + int length; + + char *buf; + struct trx_header *p; + + if (argc != 3) { + printf("Usage: %s \n", argv[0]); + return -1; + } + + fpIn = fopen(argv[1], "rb"); + if (fpIn == NULL) { + fprintf(stderr, "Unable to open %s\n", argv[1]); + return EXIT_FAILURE; + } + /* compute the length of the file */ + fseek(fpIn, 0, SEEK_END); + length = ftell(fpIn); + /* alloc enough memory to store the file */ + buf = (char *)malloc(length); + if (!buf) { + fprintf(stderr, "malloc of buffers failed\n"); + return EXIT_FAILURE; + } + + rewind(fpIn); + /* read the whole file*/ + res = fread(buf, 1, length, fpIn); + + p = (struct trx_header *)buf; + if (LOAD32_LE(p->magic) != TRX_MAGIC) { + fprintf(stderr, "Not a trx file...%x\n", LOAD32_LE(p->magic)); + return EXIT_FAILURE; + } + + fclose(fpIn); + + fpOut = fopen(argv[2], "wb+"); + if (fpOut == NULL) { + fprintf(stderr, "Unable to open %s\n", argv[2]); + return EXIT_FAILURE; + } + /* make the 3 partition beeing 12 bytes closer from the header */ + memcpy(buf + LOAD32_LE(p->offsets[2]) - EDIMAX_HDR_LEN, buf + LOAD32_LE(p->offsets[2]), length - LOAD32_LE(p->offsets[2])); + /* recompute the crc32 check */ + p->crc32 = STORE32_LE(crc32buf((char *) &p->flag_version, length - offsetof(struct trx_header, flag_version))); + + eh.sign = STORE32_LE(EDIMAX_PS16); + eh.length = STORE32_LE(length); + eh.start_addr = STORE32_LE(0x80500000); + + /* write the modified file */ + fwrite(&eh, sizeof(struct edimax_header), 1, fpOut); + fwrite(buf, sizeof(char), length, fpOut); + fclose(fpOut); +} + diff --git a/trunk/tools/firmware-utils/src/trx2usr.c b/trunk/tools/firmware-utils/src/trx2usr.c new file mode 100644 index 00000000..96c0ab28 --- /dev/null +++ b/trunk/tools/firmware-utils/src/trx2usr.c @@ -0,0 +1,186 @@ +/* + * trx2usr - Convert a TRX firmware image to a U.S. Robotics firmware + * image by prepending a 28-byte header. + * + * This program was modeled after the usr-hdr.c program from the GPL'ed + * firmware for the U.S. Robotics Wireless MAXg Router (USR5461). The + * output file of this program can be uploaded via the web interface + * of the original U.S. Robotics firmware. Note that this program only + * works on a little-endian host platform. + * + * Copyright (C) 2006 Dick Streefland + * + * This is free software, licensed under the terms of the GNU General + * Public License as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#define TRX_MAGIC "HDR0" + +#define USR_MAGIC 0x30525355 // "USR0" +#define EPI_VERSION 0x06235d03 +#define COMPAT_ID 1 // USR5461 +#define HARDWARE_REV 1 + +#define CRC32_INIT 0xffffffff +#define CHUNK (64*1024) + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; + +struct usr_header +{ + uint32 magic; // "USR0" + uint32 len; // file length without this header + uint32 crc32; // CRC32 of the file without header + uint32 version; // EPI_VERSION + uint16 compatibility_id; // COMPAT_ID + uint16 hardware_revision; // HARDWARE_REV + uint32 reserved[2]; +}; + +static const uint32 crc_32_tab [] = // CRC polynomial 0xedb88320 +{ + 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 char buf[CHUNK]; + +static uint32 crc32(uint32 crc, uint8* p, size_t n) +{ + while (n--) + { + crc = crc_32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8); + } + return crc; +} + +static int trx2usr(FILE* trx, FILE* usr) +{ + struct usr_header hdr; + size_t n; + + hdr.magic = USR_MAGIC; + hdr.len = 0; + hdr.crc32 = CRC32_INIT; + hdr.version = EPI_VERSION; + hdr.compatibility_id = COMPAT_ID; + hdr.hardware_revision = HARDWARE_REV; + hdr.reserved[0] = 0; + hdr.reserved[1] = 0; + fwrite(& hdr, sizeof(hdr), 1, usr); + while ((n = fread(buf, 1, CHUNK, trx))) + { + if (hdr.len == 0 && strncmp(buf, TRX_MAGIC, strlen(TRX_MAGIC)) != 0) + { + break; + } + fwrite(& buf, 1, n, usr); + hdr.len += n; + hdr.crc32 = crc32( hdr.crc32, (uint8 *) & buf, n); + } + fseek(usr, 0L, SEEK_SET); + fwrite(& hdr, sizeof(hdr), 1, usr); + if (n != 0) + { + fprintf(stderr, "Input is not a TRX file\n"); + return 1; + } + if (hdr.len == 0) + { + fprintf(stderr, "Empty input\n"); + return 1; + } + if (ferror(trx)) + { + fprintf(stderr, "Read error\n"); + return 1; + } + if (ferror(usr)) + { + fprintf(stderr, "Write error\n"); + return 1; + } + return 0; +} + +extern int main(int argc, char *argv[]) +{ + FILE* in; + FILE* out; + int ret; + + if (argc != 3) + { + fprintf(stderr, "Usage: trx2usr \n"); + exit(2); + } + in = fopen(argv[1], "rb"); + if (!in) + { + fprintf(stderr, "Cannot open \"%s\": %s\n", argv[1], strerror(errno)); + exit(1); + } + out = fopen(argv[2], "wb"); + if (!out) + { + fprintf(stderr, "Cannot create \"%s\": %s\n", argv[2], strerror(errno)); + exit(1); + } + ret = trx2usr(in, out); + fclose(in); + fclose(out); + if (ret) + { + unlink(argv[2]); + } + return ret; +} diff --git a/trunk/tools/firmware-utils/src/wrt400n.c b/trunk/tools/firmware-utils/src/wrt400n.c new file mode 100644 index 00000000..69ecadd4 --- /dev/null +++ b/trunk/tools/firmware-utils/src/wrt400n.c @@ -0,0 +1,334 @@ +/* + * WRT400n - Firmware Generation Creator + * + * Creates a firmware image for the Linksys WRT400n router, + * that can be uploaded via the firmware upload page, + * from a kernel image file and root fs file + * + * Author: Sandeep Mistry + */ +#include +#include +#include +#include +#include +#include + +#include "cyg_crc.h" + +// https://dev.librecmc.org/browser/trunk/target/linux/rdc-2.6/files/drivers/mtd/maps/rdc3210.c +static 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 +}; + +static uint32_t crc32(uint8_t* 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[(uint8_t) (s0 & 0xFF) ^ buf[i]]; + } + sum = ~s0; + return sum; +} + +#define HEADERSIZE 60 +#define MAGIC "GMTKRT400N" + +// global variables +uint8_t kernelbuf[0x100000]; // kernel - lzma - uImage +uint8_t rootfsbuf[0x2FFFC4]; // root - squashfs + +uint8_t buf[0x400000]; // buffer for image + + +// Header format: +// +// GPL Tarball: http://downloads.linksysbycisco.com/downloads/WRT400N_1.0.01.19_US.tar,0.gz +// File: WRT400N_1.0.01.19_US/FW_WRT400N_1.0.01.19_US_20081229/GTK/user/include/fw_upgrade.h +// +// +// Struct: +// typedef struct +// { +// UINT32 checksum; /* CRC32 */ +// UINT8 magic[11]; /* The value of GTIMG_MAGIC */ +// UINT32 kernel_length; /* The length of the kernel image */ +// //UINT32 kernel_entry_point; /* Kernel's entry point for RedBoot's information */ +// UINT32 kernel_upgrade_flag; /* Set to 1 if we need to upgrade the kernel parition of the Flash */ +// UINT32 rootfs_length; /* The length of the rootfs image */ +// //UINT32 rootfs_entry_point; /* Not in use */ +// UINT32 rootfs_upgrade_flag; /* Set to 1 if we need to upgrade the rootfs parition of the Flash */ +// +// // Add 3 items by Vic Yu, 2006-05/10 +// UINT32 kernel_checksum; +// UINT32 rootfs_checksum; +// UINT32 fw_totalsize; +// UINT32 reserved[4]; +// }imghdr_t , *pLinuxFWHeader_T; +// +// +// Description +// - checksum: CRC32 of kernel and root fs, back to back +// - magic: GMTKRT400N +// - kernel_length: kernel length in bytes +// - kernel_upgrade_flag: should we upgrade the kernel - set to 1 +// - rootfs_length: root fs length in byte +// - rootfs_upgrade_flag: should we upgrade the root fs - set to 1 +// - kernel_checksum: Gary S. Brown's 32 bit CRC algorithm for kernel, with remaining bits +// set to 0xFF upto 0x100000 bytes (total length) +// - rootfs_checksum: Gary S. Brown's 32 bit CRC algorithm for root fs, with remaining bits +// set to 0xFF upto 0x2FFFC4 bytes (total length) +// - fw_totalsize: total firmware image file length (header length + kernel length + root fs length) +// - reserved[4]: reserved ??? - set to all 0xFF + + + +int main(int argc, char *argv[]) +{ + // file descriptors ... + int kernelfd = -1; + int rootfsfd = -1; + int outfd = -1; + + char* kernelfilename = NULL; + char* rootfsfilename = NULL; + char* outputfilename = NULL; + + // file sizes + uint32_t kernelsize = 0; + uint32_t rootfssize = 0; + uint32_t totalsize = 0; + + // header flags + uint32_t kernelflag = 0; + uint32_t rootfsflag = 0; + + // checksums + uint32_t kernelchecksum = 0; + uint32_t rootfschecksum = 0; + uint32_t crc = 0; + + if(argc != 4) + { + printf("Usage:\n\t%s \n", argv[0]); + return 1; + } + + kernelfilename = argv[1]; + rootfsfilename = argv[2]; + outputfilename = argv[3]; + + // Fill the kernel, rootfs, main buffer + memset(kernelbuf, 0xFF, sizeof(kernelbuf)); + memset(rootfsbuf, 0xFF, sizeof(rootfsbuf)); + memset(buf, 0xFF, sizeof(buf)); + + // open the kernel .. + kernelfd = open(kernelfilename, O_RDONLY); + + if(kernelfd == -1) + { + printf("Error: opening '%s'\n", kernelfilename); + goto done; + } + + // read in the kernel ... + kernelsize = read(kernelfd, kernelbuf, sizeof(kernelbuf)); + + if(kernelsize == -1) + { + printf("Error: reading '%s'\n", kernelfilename); + goto done; + } + + // calculate the kernel checksum ... + kernelchecksum = cyg_crc32_accumulate(0, kernelbuf, sizeof(kernelbuf)); + + // print out stats + printf("%s: size %d (0x%x), crc32 = 0x%x\n", kernelfilename, kernelsize, kernelsize, kernelchecksum); + + + // open the root fs .. + rootfsfd = open(rootfsfilename, O_RDONLY); + + if(rootfsfd == -1) + { + printf("Error: opening '%s'\n", rootfsfilename); + goto done; + } + + // read in the root fs .. + rootfssize = read(rootfsfd, rootfsbuf, sizeof(rootfsbuf)); + + if(rootfssize == -1) + { + printf("Error: reading '%s'\n", rootfsfilename); + goto done; + } + + // calculate the root fs checksum ... + rootfschecksum = cyg_crc32_accumulate(0, rootfsbuf, sizeof(rootfsbuf)); + + // print out stats + printf("%s: size %d (0x%x), crc32 = 0x%x\n", rootfsfilename, rootfssize, rootfssize, rootfschecksum); + + + // now for the header ... + + totalsize = HEADERSIZE; + + // copy over kernel + memcpy(buf + totalsize, kernelbuf, kernelsize); + totalsize += kernelsize; + + // copy over root fs + memcpy(buf + totalsize, rootfsbuf, rootfssize); + totalsize += rootfssize; + + // calculate crc + crc = crc32(buf + HEADERSIZE, totalsize - HEADERSIZE); + + // print some stats out + printf("crc = 0x%x, total size = %d (0x%x)\n", crc, totalsize, totalsize); + + // copy crc into header + crc = htonl(crc); + memcpy(buf, &crc, sizeof(crc)); + + // copy over magic + strcpy((char *)buf + 4, MAGIC); + + // copy over kernel size + kernelsize = htonl(kernelsize); + memcpy(buf + 16, &kernelsize, sizeof(kernelsize)); + + // copy over kernal flag + kernelflag = htonl(0x1); + memcpy(buf + 20, &kernelflag, sizeof(kernelflag)); + + // copy over root fs size + rootfssize = htonl(rootfssize); + memcpy(buf + 24, &rootfssize, sizeof(rootfssize)); + + // copy over root fs flag + rootfsflag = htonl(0x1); + memcpy(buf + 28, &rootfsflag, sizeof(rootfsflag)); + + // copy over kernel check sum + kernelchecksum = htonl(kernelchecksum); + memcpy(buf + 32, &kernelchecksum, sizeof(kernelchecksum)); + + // copy over root fs checksum + rootfschecksum = htonl(rootfschecksum); + memcpy(buf + 36, &rootfschecksum, sizeof(rootfschecksum)); + + // copy over total size + totalsize = htonl(totalsize); + memcpy(buf + 40, &totalsize, sizeof(totalsize)); + + // undo the htonl (for write) + totalsize = htonl(totalsize); + + + // write out the file from the buffer + outfd = open(outputfilename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if(outfd == -1) + { + printf("ERROR: opening '%s' for write\n", outputfilename); + } + + write(outfd, buf, totalsize); + +done: + // close open fd's + + if(kernelfd != -1) + { + close(kernelfd); + kernelfd = -1; + } + + if(rootfsfd != -1) + { + close(rootfsfd); + rootfsfd = -1; + } + + if(outfd != -1) + { + close(outfd); + outfd = -1; + } + + return 0; +} diff --git a/trunk/tools/firmware-utils/src/xorimage.c b/trunk/tools/firmware-utils/src/xorimage.c new file mode 100644 index 00000000..e33f5d5d --- /dev/null +++ b/trunk/tools/firmware-utils/src/xorimage.c @@ -0,0 +1,135 @@ +/* + * xorimage.c - partially based on libreCMC's addpattern.c + * + * 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 +#include +#include +#include +#include +#include +#include + +static char default_pattern[] = "12345678"; + + +int xor_data(uint8_t *data, size_t len, const uint8_t *pattern, int p_len, int p_off) +{ + int offset = p_off; + while (len--) { + *data ^= pattern[offset]; + data++; + offset = (offset + 1) % p_len; + } + return offset; +} + + +void usage(void) __attribute__ (( __noreturn__ )); + +void usage(void) +{ + fprintf(stderr, "Usage: xorimage [-i infile] [-o outfile] [-p ]\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; + FILE *out = stdout; + char *ifn = NULL; + char *ofn = NULL; + const char *pattern = default_pattern; + int c; + int v0, v1, v2; + size_t n; + int p_len, p_off = 0; + + while ((c = getopt(argc, argv, "i:o:p:h")) != -1) { + switch (c) { + case 'i': + ifn = optarg; + break; + case 'o': + ofn = optarg; + break; + case 'p': + pattern = 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(); + } + + p_len = strlen(pattern); + + if (p_len == 0) { + fprintf(stderr, "pattern cannot be empty\n"); + 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; + } + } + + p_off = xor_data(buf, n, pattern, p_len, p_off); + + 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; +} diff --git a/trunk/tools/firmware-utils/src/zynos.h b/trunk/tools/firmware-utils/src/zynos.h new file mode 100644 index 00000000..e16913db --- /dev/null +++ b/trunk/tools/firmware-utils/src/zynos.h @@ -0,0 +1,225 @@ +/* + * + * Copyright (C) 2007-2008 OpenWrt.org + * Copyright (C) 2007-2008 Gabor Juhos + * + * This code was based on the information of the ZyXEL's firmware + * image format written by Kolja Waschk, can be found at: + * http://www.ixo.de/info/zyxel_uclinux + * + * 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 _ZYNOS_H +#define _ZYNOS_H + +#define BOOTBASE_NAME_LEN 32 +#define BOOTBASE_MAC_LEN 6 +#define BOOTBASE_FEAT_LEN 22 + +#define BOOTEXT_DEF_SIZE 0x18000 + +struct zyn_bootbase_info { + char vendor[BOOTBASE_NAME_LEN]; /* Vendor name */ + char model[BOOTBASE_NAME_LEN]; /* Model name */ + uint32_t bootext_addr; /* absolute address of the Boot Extension */ + uint16_t res0; /* reserved/unknown */ + uint8_t sys_type; /* system type */ + uint8_t res1; /* reserved/unknown */ + uint16_t model_id; /* model id */ + uint8_t feat_other[BOOTBASE_FEAT_LEN]; /* other feature bits */ + uint8_t feat_main; /* main feature bits */ + uint8_t res2; /* reserved/unknown */ + uint8_t mac[BOOTBASE_MAC_LEN]; /* mac address */ + uint8_t country; /* default country code */ + uint8_t dbgflag; /* debug flag */ +} __attribute__((packed)); + +#define ROMBIN_SIG_LEN 3 +#define ROMBIN_VER_LEN 15 + +struct zyn_rombin_hdr { + uint32_t addr; /* load address of the object */ + uint16_t res0; /* unknown/unused */ + char sig[ROMBIN_SIG_LEN]; /* magic, must be "SIG" */ + uint8_t type; /* type of the object */ + uint32_t osize; /* size of the uncompressed data */ + uint32_t csize; /* size of the compressed data */ + uint8_t flags; /* various flags */ + uint8_t res1; /* unknown/unused */ + uint16_t ocsum; /* csum of the uncompressed data */ + uint16_t ccsum; /* csum of the compressed data */ + char ver[ROMBIN_VER_LEN]; + uint32_t mmap_addr; /* address of the Memory Map Table*/ + uint32_t res2; /* unknown/unused*/ + uint8_t res3; /* unknown/unused*/ +} __attribute__((packed)); + +#define ROMBIN_SIGNATURE "SIG" + +/* Rombin flag bits */ +#define ROMBIN_FLAG_01 0x01 +#define ROMBIN_FLAG_02 0x02 +#define ROMBIN_FLAG_04 0x04 +#define ROMBIN_FLAG_08 0x08 +#define ROMBIN_FLAG_10 0x10 +#define ROMBIN_FLAG_CCSUM 0x20 /* compressed checksum is valid */ +#define ROMBIN_FLAG_OCSUM 0x40 /* original checksum is valid */ +#define ROMBIN_FLAG_COMPRESSED 0x80 /* the binary is compressed */ + +/* Object types */ +#define OBJECT_TYPE_ROMIMG 0x01 +#define OBJECT_TYPE_ROMBOOT 0x02 +#define OBJECT_TYPE_BOOTEXT 0x03 +#define OBJECT_TYPE_ROMBIN 0x04 +#define OBJECT_TYPE_ROMDIR 0x05 +#define OBJECT_TYPE_6 0x06 +#define OBJECT_TYPE_ROMMAP 0x07 +#define OBJECT_TYPE_RAM 0x80 +#define OBJECT_TYPE_RAMCODE 0x81 +#define OBJECT_TYPE_RAMBOOT 0x82 + +/* + * Memory Map Table header + */ +struct zyn_mmt_hdr { + uint16_t count; + uint32_t user_start; + uint32_t user_end; + uint16_t csum; + uint8_t res[12]; +} __attribute__((packed)); + +#define OBJECT_NAME_LEN 8 + +struct zyn_mmt_item { + uint8_t type; /* type of the object */ + uint8_t name[OBJECT_NAME_LEN]; /* name of the object */ + uint8_t res0; /* unused/unknown */ + uint32_t addr; + uint32_t size; /* size of the object */ + uint8_t res1[3]; /* unused/unknown */ + uint8_t type2; +} __attribute__((packed)); + +/* + * Vendor IDs + */ +#define ZYNOS_VENDOR_ID_ZYXEL 0 +#define ZYNOS_VENDOR_ID_NETGEAR 1 +#define ZYNOS_VENDOR_ID_DLINK 2 +#define ZYNOS_VENDOR_ID_03 3 +#define ZYNOS_VENDOR_ID_LUCENT 4 +#define ZYNOS_VENDOR_ID_O2 10 + +/* + * Model IDs (in big-endian format) + */ +#define MID(x) (((x) & 0xFF) << 8) | (((x) & 0xFF00) >> 8) + +/* + * Infineon/ADMtek ADM5120 based models + */ +#define ZYNOS_MODEL_ES_2024A MID( 221) +#define ZYNOS_MODEL_ES_2024PWR MID( 4097) +#define ZYNOS_MODEL_ES_2108 MID(61952) +#define ZYNOS_MODEL_ES_2108_F MID(44801) +#define ZYNOS_MODEL_ES_2108_G MID(62208) +#define ZYNOS_MODEL_ES_2108_LC MID(64512) +#define ZYNOS_MODEL_ES_2108PWR MID(62464) +#define ZYNOS_MODEL_HS_100 MID(61855) +#define ZYNOS_MODEL_HS_100W ZYNOS_MODEL_HS_100 +#define ZYNOS_MODEL_P_334 MID(62879) +#define ZYNOS_MODEL_P_334U MID(56735) +#define ZYNOS_MODEL_P_334W MID(62367) +#define ZYNOS_MODEL_P_334WH MID(57344) +#define ZYNOS_MODEL_P_334WHD MID(57600) +#define ZYNOS_MODEL_P_334WT MID(61343) +#define ZYNOS_MODEL_P_335 MID(60831) +#define ZYNOS_MODEL_P_335PLUS MID( 9472) +#define ZYNOS_MODEL_P_335U MID(56479) +#define ZYNOS_MODEL_P_335WT ZYNOS_MODEL_P_335 + +/* + * Texas Instruments AR7 based models + */ +#define ZYNOS_MODEL_P_2602H_61C MID( 3229) +#define ZYNOS_MODEL_P_2602H_63C MID( 3485) +#define ZYNOS_MODEL_P_2602H_D1A /* n.a. */ +#define ZYNOS_MODEL_P_2602H_D3A /* n.a. */ +#define ZYNOS_MODEL_P_2602HW_61C /* n.a. */ +#define ZYNOS_MODEL_P_2602HW_63 /* n.a. */ +#define ZYNOS_MODEL_P_2602HW_63C ZYNOS_MODEL_P_2602H_63C +#define ZYNOS_MODEL_P_2602HW_D1A MID( 6301) +#define ZYNOS_MODEL_P_2602HW_D3A /* n.a. */ +#define ZYNOS_MODEL_P_2602HWL_61 MID( 1181) +#define ZYNOS_MODEL_P_2602HWL_61C ZYNOS_MODEL_P_2602H_61C +#define ZYNOS_MODEL_P_2602HWL_63C ZYNOS_MODEL_P_2602H_63C +#define ZYNOS_MODEL_P_2602HWL_D1A ZYNOS_MODEL_P_2602HW_D1A +#define ZYNOS_MODEL_P_2602HWL_D3A MID( 7581) +#define ZYNOS_MODEL_P_2602HWN_D7A MID(30464) +#define ZYNOS_MODEL_P_2602HWNLI_D7A MID( 6813) + +#define ZYNOS_MODEL_P_2602R_61 MID( 2205) +#define ZYNOS_MODEL_P_2602R_63 MID( 3997) +#define ZYNOS_MODEL_P_2602R_D1A /* n.a. */ +#define ZYNOS_MODEL_P_2602R_D3A /* n.a. */ +#define ZYNOS_MODEL_P_2602RL_D1A MID( 6045) +#define ZYNOS_MODEL_P_2602RL_D3A MID( 7069) + +#define ZYNOS_MODEL_P_660H_61 MID(19346) +#define ZYNOS_MODEL_P_660H_63 MID(22162) +#define ZYNOS_MODEL_P_660H_67 /* n.a. */ +#define ZYNOS_MODEL_P_660H_D1 MID( 7066) +#define ZYNOS_MODEL_P_660H_D3 MID(13210) + +#define ZYNOS_MODEL_P_660HW_61 ZYNOS_MODEL_P_660H_61 +#define ZYNOS_MODEL_P_660HW_63 ZYNOS_MODEL_P_660H_63 +#define ZYNOS_MODEL_P_660HW_67 ZYNOS_MODEL_P_660HW_63 +#define ZYNOS_MODEL_P_660HW_D1 MID( 9114) +#define ZYNOS_MODEL_P_660HW_D3 MID(12698) + +#define ZYNOS_MODEL_P_660R_61 MID(20882) +#define ZYNOS_MODEL_P_660R_61C MID( 1178) +#define ZYNOS_MODEL_P_660R_63 MID(21138) +#define ZYNOS_MODEL_P_660R_63C MID( 922) +#define ZYNOS_MODEL_P_660R_67 ZYNOS_MODEL_P_660R_63 +#define ZYNOS_MODEL_P_660R_67C /* n.a. */ +#define ZYNOS_MODEL_P_660R_D1 MID( 7322) +#define ZYNOS_MODEL_P_660R_D3 MID(10138) + +#define ZYNOS_MODEL_P_661H_61 MID(19346) +#define ZYNOS_MODEL_P_661H_63 MID( 1946) +#define ZYNOS_MODEL_P_661H_D1 MID(10650) +#define ZYNOS_MODEL_P_661H_D3 MID(12442) + +#define ZYNOS_MODEL_P_661HW_61 ZYNOS_MODEL_P_661H_61 +#define ZYNOS_MODEL_P_661HW_63 ZYNOS_MODEL_P_661H_63 +#define ZYNOS_MODEL_P_661HW_D1 MID(10906) +#define ZYNOS_MODEL_P_661HW_D3 MID(14746) + +#define ZYNOS_MODEL_P_662H_61 MID(22418) +#define ZYNOS_MODEL_P_662H_63 /* n.a. */ +#define ZYNOS_MODEL_P_662H_67 /* n.a. */ +#define ZYNOS_MODEL_P_662H_D1 /* n.a. */ +#define ZYNOS_MODEL_P_662H_D3 /* n.a. */ + +#define ZYNOS_MODEL_P_662HW_61 /* n.a. */ +#define ZYNOS_MODEL_P_662HW_63 MID(22674) +#define ZYNOS_MODEL_P_662HW_67 /* n.a. */ +#define ZYNOS_MODEL_P_662HW_D1 MID(10394) +#define ZYNOS_MODEL_P_662HW_D3 MID(12954) + +/* OEM boards */ +#define ZYNOS_MODEL_O2SURF ZYNOS_MODEL_P_2602HWN_D7A + +/* Atheros AR2318 based boards */ +#define ZYNOS_MODEL_NBG_318S MID(59392) + +/* Atheros AR71xx based boards */ +#define ZYNOS_MODEL_NBG_460N MID(61441) + +#endif /* _ZYNOS_H */ diff --git a/trunk/tools/firmware-utils/src/zyxbcm.c b/trunk/tools/firmware-utils/src/zyxbcm.c new file mode 100644 index 00000000..58205b29 --- /dev/null +++ b/trunk/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 + * + * 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 +#include +#include +#include +#include +#include +#include + +#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 libreCMC, 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 libreCMC) + 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 libreCMC, 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 libreCMC) + 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 ] [-o ]\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; +}