Index: mkamsboot.h =================================================================== --- mkamsboot.h (revision 0) +++ mkamsboot.h (revision 0) @@ -0,0 +1,49 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: mkamsboot.c 20688 2009-04-11 20:46:28Z kugel $ + * + * mkamsboot.c - a tool for merging bootloader code into an Sansa V2 + * (AMS) firmware file + * + * Copyright (C) 2008 Dave Chapman + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef MKAMSBOOT_H +#define MKAMSBOOT_H + +#include +#include + +/* load, check and pack a rockbox bootloader */ +/* mallocs a buffer for the packed binary */ +unsigned char* load_rockbox_file(char* filename, int model, int* bootloader_size,int* rb_packedsize,char* errstr,int errstrsize); + +/* load, check and pack a of firmware */ +/* mallocs a buffer for the whole file and one for the packed version */ +/* md5sum should be a char buffer for the md5sum */ +/* model and fw_version will contain info about the loaded file */ +/* firmware size is the size of the unkomnpressed firmare */ +/* of_packed and of packedsize contain the packed file */ +unsigned char* load_offile(char* filename, off_t* bufsize,char* md5sum,int* model,int* fw_version, + int* firmware_size,unsigned char** of_packed,int* of_packedsize,char* errstr,int errstrsize); + + +/* patch the firmware in buffer */ +void patch_firmware(int model,int fw_version,int firmware_size,unsigned char* buf,int len, + unsigned char* of_packed,int of_packedsize, unsigned char* rb_packed, int rb_packedsize); + +#endif Index: Makefile =================================================================== --- Makefile (revision 20848) +++ Makefile (working copy) @@ -56,11 +56,14 @@ $(CC) $(CFLAGS) -c -o md5.o -W -Wall md5.c mkamsboot.o: mkamsboot.c $(BOOTHEADERS) nrv2e_d8.h md5.h - $(CC) $(CFLAGS) -c -o mkamsboot.o -W -Wall mkamsboot.c + $(CC) $(CFLAGS) -DLIB -c -o mkamsboot.o -W -Wall mkamsboot.c mkamsboot: mkamsboot.o $(BOOTIMAGES) nrv2e_d8.o md5.o $(LIBUCL) $(CC) -o mkamsboot mkamsboot.o $(BOOTIMAGES) nrv2e_d8.o md5.o $(LIBUCL) +libmkamsboot.a: mkamsboot.o $(BOOTIMAGES) nrv2e_d8.o md5.o $(LIBUCL) + $(SILENT)$(AR) ruv $@ $+ > /dev/null 2>&1 + # Rules for the ARM code embedded in mkamsboot - assemble, link, then extract # the binary code and finally convert to .c/.h for linking with mkamsboot Index: mkamsboot.c =================================================================== --- mkamsboot.c (revision 20848) +++ mkamsboot.c (working copy) @@ -73,6 +73,7 @@ */ +#include "mkamsboot.h" #include #include @@ -317,16 +318,20 @@ return outbuf; } -static unsigned char* load_file(char* filename, off_t* bufsize) +unsigned char* load_offile(char* filename, off_t* bufsize,char* md5sum,int* model,int* fw_version, + int* firmware_size,unsigned char** of_packed,int* of_packedsize,char* errstr,int errstrsize) { int fd; - unsigned char* buf; + unsigned char* buf =NULL; off_t n; - + unsigned int i=0; + uint32_t checksum; + int model_id; + fd = open(filename, O_RDONLY|O_BINARY); if (fd < 0) { - fprintf(stderr,"[ERR] Could not open %s for reading\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not open %s for reading\n",filename); return NULL; } @@ -334,25 +339,88 @@ buf = malloc(*bufsize); if (buf == NULL) { - fprintf(stderr,"[ERR] Could not allocate memory for %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not allocate memory for %s\n",filename); return NULL; } n = read(fd, buf, *bufsize); if (n != *bufsize) { - fprintf(stderr,"[ERR] Could not read file %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not read file %s\n",filename); + free(buf); return NULL; } + /* check the file */ + + /* Calculate MD5 checksum of OF */ + calc_MD5(buf, *bufsize, md5sum); + + while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0)) + i++; + + if (i < NUM_MD5S) { + *model = sansasums[i].model; + *fw_version = sansasums[i].fw_version; + } else { + fprintf(stderr,"[WARN] ****** Original firmware unknown ******\n"); + if (get_uint32le(&buf[0x204])==0x0000f000) { + *fw_version = 2; + model_id = buf[0x219]; + } else { + *fw_version = 1; + model_id = buf[0x215]; + } + + *model = get_model(model_id); + + if (*model == MODEL_UNKNOWN) { + snprintf(errstr,errstrsize,"[ERR] Unknown firmware - model id 0x%02x\n", + model_id); + free(buf); + return NULL; + } + } + + + /* TODO: Do some more sanity checks on the OF image. Some images (like + m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */ + checksum = get_uint32le(buf + *bufsize - 4); + if (checksum != 0xefbeadde && checksum != calc_checksum(buf, *bufsize - 4)) { + + snprintf(errstr,errstrsize,"[ERR] Whole file checksum failed\n"); + free(buf); + return NULL; + } + + if (bootloaders[*model] == NULL) { + snprintf(errstr,errstrsize,"[ERR] Unsupported model - \"%s\"\n",model_names[*model]); + free(buf); + return NULL; + } + + + /* Get the firmware size */ + *firmware_size = get_uint32le(&buf[0x0c]); + + /* Compress the original firmware image */ + *of_packed = uclpack(buf + 0x400, *firmware_size, of_packedsize); + if (*of_packed == NULL) + { + snprintf(errstr,errstrsize,"[ERR] Could not compress %s\n",filename); + free(buf); + free(*of_packed); + return NULL; + } return buf; } -static unsigned char* load_rockbox_file(char* filename, int model, off_t* bufsize) +unsigned char* load_rockbox_file(char* filename, int model, int* bufsize,int* rb_packedsize,char* errstr,int errstrsize) { int fd; - unsigned char* buf; + unsigned char* buf = NULL; + unsigned char* packed = NULL; unsigned char header[8]; uint32_t sum; off_t n; @@ -361,20 +429,20 @@ fd = open(filename, O_RDONLY|O_BINARY); if (fd < 0) { - fprintf(stderr,"[ERR] Could not open %s for reading\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not open %s for reading\n",filename); return NULL; } /* Read Rockbox header */ n = read(fd, header, sizeof(header)); if (n != sizeof(header)) { - fprintf(stderr,"[ERR] Could not read file %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not read file %s\n",filename); return NULL; } /* Check for correct model string */ if (memcmp(rb_model_names[model],header + 4,4)!=0) { - fprintf(stderr,"[ERR] Model name \"%s\" not found in %s\n", + snprintf(errstr,errstrsize,"[ERR] Model name \"%s\" not found in %s\n", rb_model_names[model],filename); } @@ -382,14 +450,15 @@ buf = malloc(*bufsize); if (buf == NULL) { - fprintf(stderr,"[ERR] Could not allocate memory for %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not allocate memory for %s\n",filename); return NULL; } n = read(fd, buf, *bufsize); if (n != *bufsize) { - fprintf(stderr,"[ERR] Could not read file %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Could not read file %s\n",filename); + free(buf); return NULL; } @@ -401,168 +470,32 @@ } if (sum != get_uint32be(header)) { - fprintf(stderr,"[ERR] Checksum mismatch in %s\n",filename); + snprintf(errstr,errstrsize,"[ERR] Checksum mismatch in %s\n",filename); + free(buf); return NULL; } - return buf; + + packed = uclpack(buf, *bufsize, rb_packedsize); + if(packed == NULL) + { + snprintf(errstr,errstrsize,"[ERR] Could not compress %s\n",filename); + free(buf); + return NULL; + } + free(buf); + return packed; } - -int main(int argc, char* argv[]) +void patch_firmware(int model,int fw_version,int firmware_size,unsigned char* buf,int len, + unsigned char* of_packed,int of_packedsize, unsigned char* rb_packed, int rb_packedsize) { - char *infile, *bootfile, *outfile; - int fdout; - off_t len; - uint32_t n; - unsigned char* buf; - int firmware_size; - off_t bootloader_size; + unsigned char* p; uint32_t sum,filesum; - uint8_t model_id; - int model; - uint32_t i; - unsigned char* of_packed; - int of_packedsize; - unsigned char* rb_unpacked; - unsigned char* rb_packed; - int rb_packedsize; - int fw_version; - int totalsize; - unsigned char* p; - uint32_t checksum; - char md5sum[33]; /* 32 hex digits, plus terminating zero */ - - fprintf(stderr,"mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n"); - fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); - fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); - - if(argc != 4) { - printf("Usage: mkamsboot \n\n"); - return 1; - } - - infile = argv[1]; - bootfile = argv[2]; - outfile = argv[3]; - - /* Load original firmware file */ - buf = load_file(infile, &len); - - if (buf == NULL) { - fprintf(stderr,"[ERR] Could not load %s\n",infile); - return 1; - } - - /* Calculate MD5 checksum of OF */ - calc_MD5(buf, len, md5sum); - - fprintf(stderr,"[INFO] MD5 sum - %s\n",md5sum); - - i = 0; - while ((i < NUM_MD5S) && (strcmp(sansasums[i].md5, md5sum) != 0)) - i++; - - if (i < NUM_MD5S) { - model = sansasums[i].model; - fw_version = sansasums[i].fw_version; - fprintf(stderr,"[INFO] Original firmware MD5 checksum match - %s %s\n", - model_names[model], sansasums[i].version); - } else { - fprintf(stderr,"[WARN] ****** Original firmware unknown ******\n"); - - if (get_uint32le(&buf[0x204])==0x0000f000) { - fw_version = 2; - model_id = buf[0x219]; - } else { - fw_version = 1; - model_id = buf[0x215]; - } - - model = get_model(model_id); - - if (model == MODEL_UNKNOWN) { - fprintf(stderr,"[ERR] Unknown firmware - model id 0x%02x\n", - model_id); - free(buf); - return 1; - } - } + unsigned int i; - - /* TODO: Do some more sanity checks on the OF image. Some images (like - m200v4) dont have a checksum at the end, only padding (0xdeadbeef). */ - checksum = get_uint32le(buf + len - 4); - if (checksum != 0xefbeadde && checksum != calc_checksum(buf, len - 4)) { - - fprintf(stderr,"[ERR] Whole file checksum failed - %s\n",infile); - free(buf); - return 1; - } - - if (bootloaders[model] == NULL) { - fprintf(stderr,"[ERR] Unsupported model - \"%s\"\n",model_names[model]); - free(buf); - return 1; - } - - /* Load bootloader file */ - rb_unpacked = load_rockbox_file(bootfile, model, &bootloader_size); - if (rb_unpacked == NULL) { - fprintf(stderr,"[ERR] Could not load %s\n",bootfile); - free(buf); - return 1; - } - - printf("[INFO] Patching %s firmware\n",model_names[model]); - - /* Get the firmware size */ - firmware_size = get_uint32le(&buf[0x0c]); - - /* Compress the original firmware image */ - of_packed = uclpack(buf + 0x400, firmware_size, &of_packedsize); - if (of_packed == NULL) { - fprintf(stderr,"[ERR] Could not compress original firmware\n"); - free(buf); - free(rb_unpacked); - return 1; - } - - rb_packed = uclpack(rb_unpacked, bootloader_size, &rb_packedsize); - if (rb_packed == NULL) { - fprintf(stderr,"[ERR] Could not compress %s\n",bootfile); - free(buf); - free(rb_unpacked); - free(of_packed); - return 1; - } - - /* We are finished with the unpacked version of the bootloader */ - free(rb_unpacked); - - fprintf(stderr,"[INFO] Original firmware size: %d bytes\n",firmware_size); - fprintf(stderr,"[INFO] Packed OF size: %d bytes\n",of_packedsize); - fprintf(stderr,"[INFO] Bootloader size: %d bytes\n",(int)bootloader_size); - fprintf(stderr,"[INFO] Packed bootloader size: %d bytes\n",rb_packedsize); - fprintf(stderr,"[INFO] Dual-boot function size: %d bytes\n",bootloader_sizes[model]); - fprintf(stderr,"[INFO] UCL unpack function size: %d bytes\n",sizeof(nrv2e_d8)); - - totalsize = bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize + - rb_packedsize; - - fprintf(stderr,"[INFO] Total size of new image: %d bytes\n",totalsize); - - if (totalsize > firmware_size) { - fprintf(stderr,"[ERR] No room to insert bootloader, aborting\n"); - free(buf); - free(rb_unpacked); - free(of_packed); - return 1; - } - /* Zero the original firmware area - not needed, but helps debugging */ memset(buf + 0x400, 0, firmware_size); - /* Insert dual-boot bootloader at offset 0 */ memcpy(buf + 0x400, bootloaders[model], bootloader_sizes[model]); @@ -621,13 +554,94 @@ filesum += get_uint32le(&buf[i]); put_uint32le(buf + len - 4, filesum); +} +int main(int argc, char* argv[]) +{ + char *infile, *bootfile, *outfile; + int fdout; + off_t len; + uint32_t n; + unsigned char* buf; + int firmware_size; + int bootloader_size; + int model; + unsigned char* of_packed; + int of_packedsize; + unsigned char* rb_packed; + int rb_packedsize; + int fw_version; + int totalsize; + char md5sum[33]; /* 32 hex digits, plus terminating zero */ + char errstr[200]; + + fprintf(stderr,"mkamsboot v" VERSION " - (C) Dave Chapman and Rafaël Carré 2008\n"); + fprintf(stderr,"This is free software; see the source for copying conditions. There is NO\n"); + fprintf(stderr,"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"); + if(argc != 4) { + printf("Usage: mkamsboot \n\n"); + return 1; + } + + infile = argv[1]; + bootfile = argv[2]; + outfile = argv[3]; + + /* Load original firmware file */ + buf = load_offile(infile, &len,md5sum,&model,&fw_version,&firmware_size,&of_packed,&of_packedsize, + errstr,sizeof(errstr)); + + if (buf == NULL) { + fprintf(stderr,errstr); + fprintf(stderr,"[ERR] Could not load %s\n",infile); + return 1; + } + + fprintf(stderr,"[INFO] Original firmware MD5 checksum match - %s\n",model_names[model]); + + + /* Load bootloader file */ + rb_packed = load_rockbox_file(bootfile, model, &bootloader_size,&rb_packedsize,errstr,sizeof(errstr)); + if (rb_packed == NULL) { + fprintf(stderr,errstr); + fprintf(stderr,"[ERR] Could not load %s\n",bootfile); + free(buf); + return 1; + } + + printf("[INFO] Patching %s firmware\n",model_names[model]); + + fprintf(stderr,"[INFO] Original firmware size: %d bytes\n",firmware_size); + fprintf(stderr,"[INFO] Packed OF size: %d bytes\n",of_packedsize); + fprintf(stderr,"[INFO] Bootloader size: %d bytes\n",(int)bootloader_size); + fprintf(stderr,"[INFO] Packed bootloader size: %d bytes\n",rb_packedsize); + fprintf(stderr,"[INFO] Dual-boot function size: %d bytes\n",bootloader_sizes[model]); + fprintf(stderr,"[INFO] UCL unpack function size: %d bytes\n",sizeof(nrv2e_d8)); + + totalsize = bootloader_sizes[model] + sizeof(nrv2e_d8) + of_packedsize + + rb_packedsize; + + fprintf(stderr,"[INFO] Total size of new image: %d bytes\n",totalsize); + + if (totalsize > firmware_size) { + fprintf(stderr,"[ERR] No room to insert bootloader, aborting\n"); + free(buf); + free(of_packed); + free(rb_packed); + return 1; + } + + patch_firmware(model,fw_version,firmware_size,buf,len,of_packed,of_packedsize,rb_packed,rb_packedsize); + /* Write the new firmware */ fdout = open(outfile, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); if (fdout < 0) { fprintf(stderr,"[ERR] Could not open %s for writing\n",outfile); + free(buf); + free(of_packed); + free(rb_packed); return 1; } @@ -635,15 +649,22 @@ if (n != (unsigned)len) { fprintf(stderr,"[ERR] Could not write firmware file\n"); + free(buf); + free(of_packed); + free(rb_packed); return 1; } close(fdout); - + free(buf); + free(of_packed); + free(rb_packed); + + fprintf(stderr," *****************************************************************************\n"); fprintf(stderr," *** THIS CODE IS UNTESTED - DO NOT USE IF YOU CAN NOT RECOVER YOUR DEVICE ***\n"); fprintf(stderr," *****************************************************************************\n"); return 0; -} +} \ No newline at end of file