summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/ipodpatcher/ipodpatcher.c466
1 files changed, 414 insertions, 52 deletions
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,
+
+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);