diff --git a/tools/ipodpatcher/ipodpatcher.c b/tools/ipodpatcher/ipodpatcher.c index 6fc1ce427d..87b181b322 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, - struct ipod_directory_t* ipod_directory) + +int diskmove(HANDLE dh, int start, int nimages, struct ipod_directory_t* ipod_directory, + int sector_size,int delta) { - fprintf(stderr,"[ERR] Sorry, not yet implemented.\n"); - return -1; + 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 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 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 0) { + for (i=1;i