From b36b9ec1a8aa04d72bde5fa9d57715d316058cad Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 6 Jun 2013 22:21:52 +0000 Subject: [PATCH] tools: add Broadcom cable modem firmware image creator Signed-off-by: Florian Fainelli git-svn-id: svn://svn.openwrt.org/openwrt/trunk@36873 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- tools/firmware-utils/Makefile | 1 + tools/firmware-utils/src/bcmalgo.c | 248 ++++++++++++++++++++++++ tools/firmware-utils/src/bcmalgo.h | 83 ++++++++ tools/firmware-utils/src/hcsmakeimage.c | 181 +++++++++++++++++ 4 files changed, 513 insertions(+) create mode 100644 tools/firmware-utils/src/bcmalgo.c create mode 100644 tools/firmware-utils/src/bcmalgo.h create mode 100644 tools/firmware-utils/src/hcsmakeimage.c diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile index 3fdf2d0060..70edd48df7 100644 --- a/tools/firmware-utils/Makefile +++ b/tools/firmware-utils/Makefile @@ -62,6 +62,7 @@ define Host/Compile $(call cc, mkcameofw, -Wall) $(call cc,seama md5) $(call cc,fix-u-media-header cyg_crc32,-Wall) + $(call cc,hcsmakeimage bcmalgo) endef define Host/Install diff --git a/tools/firmware-utils/src/bcmalgo.c b/tools/firmware-utils/src/bcmalgo.c new file mode 100644 index 0000000000..e7d3b113be --- /dev/null +++ b/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/tools/firmware-utils/src/bcmalgo.h b/tools/firmware-utils/src/bcmalgo.h new file mode 100644 index 0000000000..46647cf6d3 --- /dev/null +++ b/tools/firmware-utils/src/bcmalgo.h @@ -0,0 +1,83 @@ +#ifndef bcmutils_H +#define bcmutils_H + +typedef struct +{ + uint16_t magic; + uint16_t control; + uint16_t rev_maj; + uint16_t rev_min; + uint32_t build_date; + uint32_t filelen; + uint32_t ldaddress; + char filename[64]; + uint16_t hcs; + uint16_t her_znaet_chto; //v dushe ne ebu + uint32_t crc; +} ldr_header_t; + + +/** + * Reverses endianess of a 32bit int, if the ENDIAN_REVERSE_NEEDED defined at compile-time + * @param data + * @return + */ +uint32_t reverse_endian32 ( uint32_t data ); + +/** + * Reverses endianess of a 16bit int, if the ENDIAN_REVERSE_NEEDED defined at compile-time + * @param data + * @return + */ +uint16_t reverse_endian16 ( uint16_t data ); +/** + * Calculates the strange crc (used by bcm modems) of the file. Thnx fly out to Vector for the algorithm. + * @param filename + * @return + */ +uint32_t get_file_crc ( char* filename ); + +/** + * Calculates HCS of the header. + * @param hd + * @return + */ +uint16_t get_hcs ( ldr_header_t* hd ); + +/** + * Constructs the header of the image with the information given It also automagically calculates HCS and writes it there. + * @param magic - magic device bytes + * @param rev_maj - major revision + * @param rev_min - minor revision + * @param build_date - build date (seconds from EPOCH UTC) + * @param filelen - file length in bytes + * @param ldaddress - Load adress + * @param filename - filename + * @param crc_data - the crc of the data + * @return + */ +ldr_header_t* construct_header ( uint32_t magic, uint16_t rev_maj,uint16_t rev_min, uint32_t build_date, uint32_t filelen, uint32_t ldaddress, const char* filename, uint32_t crc_data ); + +/** + * Dumps header information to stdout. + * @param hd + */ +int dump_header ( ldr_header_t* hd ); + + +/** + * Returns a null terminated string describing what the control number meens + * DO NOT FREE IT!!! + * @param control + * @return + */ +char* get_control_info ( uint16_t control ); +#endif + +/** + * Calculates bcmCRC of a data buffer. + * @param filebuffer - pointer to buffer + * @param size - buffer size + * @return + */ +uint32_t get_buffer_crc ( char* filebuffer, size_t size ); diff --git a/tools/firmware-utils/src/hcsmakeimage.c b/tools/firmware-utils/src/hcsmakeimage.c new file mode 100644 index 0000000000..603ea882b1 --- /dev/null +++ b/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; +}