diff options
| author | Dave Chapman <dave@dchapman.com> | 2006-12-14 18:41:03 +0000 |
|---|---|---|
| committer | Dave Chapman <dave@dchapman.com> | 2006-12-14 18:41:03 +0000 |
| commit | 75a11124f0446823ef2aa48910d92f02b1a7d1bb (patch) | |
| tree | 6b0957c70e1f83132bc97a25b702d903c4c1d799 | |
| parent | da945c0873adaca6a824981b59eb080e0b7bdcdd (diff) | |
| download | rockbox-75a11124f0446823ef2aa48910d92f02b1a7d1bb.zip rockbox-75a11124f0446823ef2aa48910d92f02b1a7d1bb.tar.gz rockbox-75a11124f0446823ef2aa48910d92f02b1a7d1bb.tar.bz2 rockbox-75a11124f0446823ef2aa48910d92f02b1a7d1bb.tar.xz | |
Implement the add/delete bootloader functionality.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11765 a1c6a512-1295-4272-9138-f99709370657
| -rw-r--r-- | tools/ipodpatcher/ipodpatcher.c | 466 |
1 files changed, 414 insertions, 52 deletions
diff --git a/tools/ipodpatcher/ipodpatcher.c b/tools/ipodpatcher/ipodpatcher.c index 6fc1ce4..87b181b 100644 --- a/tools/ipodpatcher/ipodpatcher.c +++ b/tools/ipodpatcher/ipodpatcher.c @@ -31,7 +31,7 @@ #define VERSION "0.5" -//#define DEBUG +int verbose = 0; /* The following string appears at the start of the firmware partition */ static const char *apple_stop_sign = "{{~~ /-----\\ "\ @@ -337,8 +337,8 @@ void print_usage(void) { fprintf(stderr," -l, --list\n"); fprintf(stderr," -r, --read-partition bootpartition.bin\n"); fprintf(stderr," -w, --write-partition bootpartition.bin\n"); - fprintf(stderr," -ef, --extract-firmware filename.ipod\n"); - fprintf(stderr," -rf, --replace-firmware filename.ipod\n"); + fprintf(stderr," -rf, --read-firmware filename.ipod\n"); + fprintf(stderr," -wf, --write-firmware filename.ipod\n"); fprintf(stderr," -a, --add-bootloader filename.ipod\n"); fprintf(stderr," -d, --delete-bootloader\n"); fprintf(stderr,"\n"); @@ -360,10 +360,10 @@ enum { NONE, SHOW_INFO, LIST_IMAGES, - REMOVE_BOOTLOADER, - INSERT_BOOTLOADER, - EXTRACT_FIRMWARE, - REPLACE_FIRMWARE, + DELETE_BOOTLOADER, + ADD_BOOTLOADER, + READ_FIRMWARE, + WRITE_FIRMWARE, READ_PARTITION, WRITE_PARTITION }; @@ -389,16 +389,344 @@ struct ipod_directory_t { uint32_t loadAddr; }; -int remove_bootloader(HANDLE dh, int start, int sector_size, + +int diskmove(HANDLE dh, int start, int nimages, struct ipod_directory_t* ipod_directory, + int sector_size,int delta) +{ + int src_start; + int src_end; + int dest_start; + int dest_end; + int bytesleft; + int chunksize; + int i; + int n; + + src_start = start + ipod_directory[1].devOffset + sector_size; + src_end = (start + ipod_directory[nimages-1].devOffset + sector_size + ipod_directory[nimages-1].len + (sector_size-1)) & ~(sector_size-1); + bytesleft = src_end - src_start; + dest_start = start + src_start + delta; + dest_end = start + src_end + delta; + + if (verbose) { + fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n",nimages,delta); + fprintf(stderr,"[VERB] src_start = %08x\n",src_start); + fprintf(stderr,"[VERB] src_end = %08x\n",src_end); + fprintf(stderr,"[VERB] dest_start = %08x\n",dest_start); + fprintf(stderr,"[VERB] dest_end = %08x\n",dest_end); + fprintf(stderr,"[VERB] bytes to copy = %08x\n",bytesleft); + } + + while (bytesleft > 0) { + if (bytesleft <= BUFFER_SIZE) { + chunksize = bytesleft; + } else { + chunksize = BUFFER_SIZE; + } + + if (verbose) { + fprintf(stderr,"[VERB] Copying %08x bytes from %08x to %08x\n", + chunksize, + dest_end-chunksize, + dest_end-chunksize+delta); + } + + + if (ipod_seek(dh,dest_end-chunksize) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(dh,sectorbuf,chunksize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < chunksize) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + if (ipod_seek(dh,dest_end-chunksize+delta) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(dh,sectorbuf,chunksize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < chunksize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,i,n); + return -1; + } + + dest_end -= chunksize; + bytesleft -= chunksize; + } + + return 0; +} + +int add_bootloader(HANDLE dh, char* filename, int start, int sector_size, + int nimages, struct ipod_directory_t* ipod_directory, + off_t diroffset, int modelnum, char* modelname) +{ + int length; + int i; + int n; + int infile; + int paddedlength; + int entryOffset; + int delta = 0; + unsigned long chksum=0; + unsigned long filechksum=0; + unsigned char header[8]; /* Header for .ipod file */ + + /* First check that the input file is the correct type for this ipod. */ + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + n = read(infile,header,8); + if (n < 8) { + fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); + close(infile); + return -1; + } + + if (memcmp(header+4,modelname,4)!=0) { + fprintf(stderr,"[ERR] Model name in input file (%c%c%c%c) doesn't match ipod model (%s)\n", + header[4],header[5],header[6],header[7],modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length=filesize(infile)-8; + paddedlength=(length+sector_size-1)&~(sector_size-1); + + /* Now read our bootloader - we need to check it before modifying the partition*/ + n = read(infile,sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + + /* Calculate and confirm bootloader checksum */ + chksum = modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + if (chksum == filechksum) { + fprintf(stderr,"[INFO] Checksum OK in %s\n",filename); + } else { + fprintf(stderr,"[ERR] Checksum in %s failed check\n",filename); + return -1; + } + + if (ipod_directory[0].entryOffset>0) { + /* Keep the same entryOffset */ + entryOffset = ipod_directory[0].entryOffset; + } else { + entryOffset = (ipod_directory[0].len+sector_size-1)&~(sector_size-1); + } + + if (entryOffset+paddedlength > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + close(infile); + return -1; + } + + if (verbose) { + fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n",ipod_directory[0].devOffset + sector_size); + fprintf(stderr,"[VERB] New entryOffset will be 0x%08x\n",entryOffset); + fprintf(stderr,"[VERB] End of bootloader will be at 0x%08x\n",entryOffset+paddedlength); + } + + /* Check if we have enough space */ + /* TODO: Check the size of the partition. */ + if (nimages > 1) { + if ((entryOffset+paddedlength) >= ipod_directory[1].devOffset) { + fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); + delta = entryOffset+paddedlength-ipod_directory[1].devOffset; + + if (diskmove(dh,start,nimages,ipod_directory,sector_size,delta) < 0) { + close(infile); + fprintf(stderr,"[ERR] Image movement failed.\n"); + return -1; + } + } + } + + + /* We have moved the partitions, now we can write our bootloader */ + + /* Firstly read the original firmware into sectorbuf */ + fprintf(stderr,"[INFO] Reading original firmware...\n"); + + if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(dh,sectorbuf,entryOffset)) < 0) { + perror("[ERR] Read failed\n"); + return -1; + } + + if (n < entryOffset) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + ,i,n); + return -1; + } + + /* Now read our bootloader - we need to seek back to 8 bytes from start */ + lseek(infile,8,SEEK_SET); + n = read(infile,sectorbuf+entryOffset,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + + /* Calculate new checksum for combined image */ + chksum = 0; + for (i=0;i<entryOffset + length; i++) { + chksum += sectorbuf[i]; + } + + /* Now write the combined firmware image to the disk */ + + if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(dh,sectorbuf,entryOffset+paddedlength)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < (entryOffset+paddedlength)) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n" + ,i,n); + return -1; + } + + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",entryOffset+paddedlength); + + + /* Read directory */ + if (ipod_seek(dh,start + diroffset) < 0) { return -1; } + + n=ipod_read(dh, sectorbuf, sector_size); + if (n < 0) { return -1; } + + /* Update entries for image 0 */ + int2le(entryOffset+length,sectorbuf+16); + int2le(entryOffset,sectorbuf+24); + int2le(chksum,sectorbuf+28); + + /* Update devOffset entries for other images, if we have moved them */ + if (delta > 0) { + for (i=1;i<nimages;i++) { + int2le(le2int(sectorbuf+i*40+12)+delta,sectorbuf+i*40+12); + } + } + + /* Write directory */ + if (ipod_seek(dh,start + diroffset) < 0) { return -1; } + n=ipod_write(dh, sectorbuf, sector_size); + if (n < 0) { return -1; } + + return 0; +} + +int delete_bootloader(HANDLE dh, int start, int sector_size, off_t diroffset, struct ipod_directory_t* ipod_directory) { - fprintf(stderr,"[ERR] Sorry, not yet implemented.\n"); - return -1; + int length; + int i; + int n; + unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ + + /* Removing the bootloader involves adjusting the "length", + "chksum" and "entryOffset" values in the osos image's directory + entry. */ + + /* Firstly check we have a bootloader... */ + + if (ipod_directory[0].entryOffset == 0) { + fprintf(stderr,"[ERR] No bootloader found.\n"); + return -1; + } + + length = ipod_directory[0].entryOffset; + + /* Read the firmware so we can calculate the checksum */ + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + if (ipod_seek(dh,start+sector_size+ipod_directory[0].devOffset) < 0) { + return -1; + } + + i = (length+sector_size-1) & ~(sector_size-1); + fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", + length,i); + + if ((n = ipod_read(dh,sectorbuf,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + chksum = 0; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + /* Now write back the updated directory entry */ + + fprintf(stderr,"[INFO] Updating firmware checksum\n"); + + /* Read directory */ + if (ipod_seek(dh,start + diroffset) < 0) { return -1; } + + n=ipod_read(dh, sectorbuf, sector_size); + if (n < 0) { return -1; } + + /* Update entries for image 0 */ + int2le(length,sectorbuf+16); + int2le(0,sectorbuf+24); + int2le(chksum,sectorbuf+28); + + /* Write directory */ + if (ipod_seek(dh,start + diroffset) < 0) { return -1; } + n=ipod_write(dh, sectorbuf, sector_size); + if (n < 0) { return -1; } + + return 0; } -int replace_firmware(HANDLE dh, char* filename, int start, int sector_size, - int nimages, struct ipod_directory_t* ipod_directory, - off_t diroffset, int modelnum, char* modelname) +int write_firmware(HANDLE dh, char* filename, int start, int sector_size, + int nimages, struct ipod_directory_t* ipod_directory, + off_t diroffset, int modelnum, char* modelname) { int length; int i; @@ -414,11 +742,14 @@ int replace_firmware(HANDLE dh, char* filename, int start, int sector_size, infile=open(filename,O_RDONLY); if (infile < 0) { fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; } n = read(infile,header,8); if (n < 8) { fprintf(stderr,"[ERR] Failed to read header from %s\n",filename); + close(infile); + return -1; } if (memcmp(header+4,modelname,4)!=0) { @@ -523,7 +854,7 @@ int replace_firmware(HANDLE dh, char* filename, int start, int sector_size, return 0; } -int extract_firmware(HANDLE dh, char* filename, int start, int sector_size, +int read_firmware(HANDLE dh, char* filename, int start, int sector_size, struct ipod_directory_t* ipod_directory, int modelnum, char* modelname) { @@ -661,22 +992,22 @@ int list_images(int nimages, struct ipod_directory_t* ipod_directory, { int i; -#ifdef DEBUG - printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); - for (i = 0 ; i < nimages; i++) { - printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, - ftypename[ipod_directory[i].ftype], - ipod_directory[i].id, - ipod_directory[i].devOffset, - ipod_directory[i].len, - ipod_directory[i].addr, - ipod_directory[i].entryOffset, - ipod_directory[i].chksum, - ipod_directory[i].vers, - ipod_directory[i].loadAddr, - ipod_directory[i].devOffset+sector_size+((ipod_directory[i].len+sector_size-1)&~(sector_size-1))); + if (verbose) { + printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); + for (i = 0 ; i < nimages; i++) { + printf("%d - %s 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",i, + ftypename[ipod_directory[i].ftype], + ipod_directory[i].id, + ipod_directory[i].devOffset, + ipod_directory[i].len, + ipod_directory[i].addr, + ipod_directory[i].entryOffset, + ipod_directory[i].chksum, + ipod_directory[i].vers, + ipod_directory[i].loadAddr, + ipod_directory[i].devOffset+sector_size+((ipod_directory[i].len+sector_size-1)&~(sector_size-1))); + } } -#endif printf("\n"); printf("Listing firmware partition contents:\n"); @@ -732,7 +1063,8 @@ int main(int argc, char* argv[]) 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 < 2) { + if ((argc < 2) || (strcmp(argv[1],"-h")==0) || + (strcmp(argv[1],"--help")==0)) { print_usage(); return 1; } @@ -751,19 +1083,27 @@ int main(int argc, char* argv[]) if ((strcmp(argv[i],"-l")==0) || (strcmp(argv[i],"--list")==0)) { action = LIST_IMAGES; i++; - } else if (strcmp(argv[i],"--remove-bootloader")==0) { - action = REMOVE_BOOTLOADER; + } else if ((strcmp(argv[i],"-d")==0) || + (strcmp(argv[i],"--delete-bootloader")==0)) { + action = DELETE_BOOTLOADER; i++; - } else if ((strcmp(argv[i],"-ef")==0) || - (strcmp(argv[i],"--extract-firmware")==0)) { - action = EXTRACT_FIRMWARE; + } else if ((strcmp(argv[i],"-a")==0) || + (strcmp(argv[i],"--add-bootloader")==0)) { + action = ADD_BOOTLOADER; i++; if (i == argc) { print_usage(); return 1; } filename=argv[i]; i++; } else if ((strcmp(argv[i],"-rf")==0) || - (strcmp(argv[i],"--replace-firmware")==0)) { - action = REPLACE_FIRMWARE; + (strcmp(argv[i],"--read-firmware")==0)) { + action = READ_FIRMWARE; + i++; + if (i == argc) { print_usage(); return 1; } + filename=argv[i]; + i++; + } else if ((strcmp(argv[i],"-wf")==0) || + (strcmp(argv[i],"--write-firmware")==0)) { + action = WRITE_FIRMWARE; i++; if (i == argc) { print_usage(); return 1; } filename=argv[i]; @@ -782,6 +1122,10 @@ int main(int argc, char* argv[]) if (i == argc) { print_usage(); return 1; } filename=argv[i]; i++; + } else if ((strcmp(argv[i],"-v")==0) || + (strcmp(argv[i],"--verbose")==0)) { + verbose++; + i++; } else { print_usage(); return 1; } @@ -868,34 +1212,52 @@ int main(int argc, char* argv[]) if (action==LIST_IMAGES) { list_images(nimages,ipod_directory,sector_size); - } else if (action==REMOVE_BOOTLOADER) { + } else if (action==DELETE_BOOTLOADER) { + if (ipod_reopen_rw(&dh, devicename) < 0) { + return 5; + } + if (ipod_directory[0].entryOffset==0) { fprintf(stderr,"[ERR] No bootloader detected.\n"); } else { - if (remove_bootloader(dh, pinfo[0].start*sector_size, sector_size, - ipod_directory)==0) { + if (delete_bootloader(dh, pinfo[0].start*sector_size, sector_size, + diroffset, ipod_directory)==0) { fprintf(stderr,"[INFO] Bootloader removed.\n"); + } else { + fprintf(stderr,"[ERR] --delete-bootloader failed.\n"); } } - } else if (action==REPLACE_FIRMWARE) { + } else if (action==ADD_BOOTLOADER) { + if (ipod_reopen_rw(&dh, devicename) < 0) { + return 5; + } + + if (add_bootloader(dh, filename,pinfo[0].start*sector_size, + sector_size, nimages, ipod_directory, diroffset, + modelnum, modelname)==0) { + fprintf(stderr,"[INFO] Bootloader %s written to device.\n",filename); + } else { + fprintf(stderr,"[ERR] --add-bootloader failed.\n"); + } + } else if (action==WRITE_FIRMWARE) { if (ipod_reopen_rw(&dh, devicename) < 0) { return 5; } - if (replace_firmware(dh, filename,pinfo[0].start*sector_size, - sector_size, nimages, ipod_directory, diroffset, - modelnum, modelname)==0) { - fprintf(stderr,"[INFO] Firmware replaced with %s.\n",filename); + if (write_firmware(dh, filename,pinfo[0].start*sector_size, + sector_size, nimages, ipod_directory, diroffset, + modelnum, modelname)==0) { + fprintf(stderr,"[INFO] Firmware %s written to device.\n",filename); } else { - fprintf(stderr,"[ERR] --replace-firmware failed.\n"); + fprintf(stderr,"[ERR] --write-firmware failed.\n"); } - } else if (action==EXTRACT_FIRMWARE) { - if (extract_firmware(dh, filename,pinfo[0].start*sector_size, - sector_size, ipod_directory, modelnum, modelname - )==0) { - fprintf(stderr,"[INFO] Firmware extracted to %s.\n",filename); + } else if (action==READ_FIRMWARE) { + if (read_firmware(dh, filename,pinfo[0].start*sector_size, + sector_size, ipod_directory, modelnum, modelname + )==0) { + fprintf(stderr,"[INFO] Firmware read to file %s.\n",filename); } else { - fprintf(stderr,"[ERR] --extract-firmware failed.\n"); + fprintf(stderr,"[ERR] --read-firmware failed.\n"); } } else if (action==READ_PARTITION) { outfile = open(filename,O_CREAT|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE); |