From 955afed1b74793a3f06f98f3c259ec98f26a0b82 Mon Sep 17 00:00:00 2001 From: Dave Chapman Date: Sat, 10 Feb 2007 19:49:43 +0000 Subject: Move ipodpatcher into rbutil directory git-svn-id: svn://svn.rockbox.org/rockbox/trunk@12263 a1c6a512-1295-4272-9138-f99709370657 --- rbutil/ipodpatcher/ipodpatcher.c | 1261 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1261 insertions(+) create mode 100644 rbutil/ipodpatcher/ipodpatcher.c (limited to 'rbutil/ipodpatcher/ipodpatcher.c') diff --git a/rbutil/ipodpatcher/ipodpatcher.c b/rbutil/ipodpatcher/ipodpatcher.c new file mode 100644 index 0000000000..6c60bae38d --- /dev/null +++ b/rbutil/ipodpatcher/ipodpatcher.c @@ -0,0 +1,1261 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006-2007 Dave Chapman + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "parttypes.h" +#include "ipodio.h" +#include "ipodpatcher.h" + +#ifdef WITH_BOOTOBJS +#include "ipod3g.h" +#include "ipod4g.h" +#include "ipodmini.h" +#include "ipodmini2g.h" +#include "ipodcolor.h" +#include "ipodnano.h" +#include "ipodvideo.h" +#endif + +extern int verbose; + +unsigned char* sectorbuf; + +/* The following string appears at the start of the firmware partition */ +static const char *apple_stop_sign = "{{~~ /-----\\ "\ + "{{~~ / \\ "\ + "{{~~| | "\ + "{{~~| S T O P | "\ + "{{~~| | "\ + "{{~~ \\ / "\ + "{{~~ \\-----/ "\ + "Copyright(C) 200"\ + "1 Apple Computer"\ + ", Inc.----------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "----------------"\ + "---------------"; + +/* Windows requires the buffer for disk I/O to be aligned in memory on a + multiple of the disk volume size - so we use a single global variable + and initialise it with ipod_alloc_buf() +*/ + +char* get_parttype(int pt) +{ + int i; + static char unknown[]="Unknown"; + + if (pt == -1) { + return "HFS/HFS+"; + } + + i=0; + while (parttypes[i].name != NULL) { + if (parttypes[i].type == pt) { + return (parttypes[i].name); + } + i++; + } + + return unknown; +} + +off_t filesize(int fd) { + struct stat buf; + + if (fstat(fd,&buf) < 0) { + perror("[ERR] Checking filesize of input file"); + return -1; + } else { + return(buf.st_size); + } +} + +/* Partition table parsing code taken from Rockbox */ + +#define MAX_SECTOR_SIZE 2048 +#define SECTOR_SIZE 512 + +unsigned short static inline le2ushort(unsigned char* buf) +{ + unsigned short res = (buf[1] << 8) | buf[0]; + + return res; +} + +int static inline le2int(unsigned char* buf) +{ + int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + return res; +} + +int static inline be2int(unsigned char* buf) +{ + int32_t res = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + + return res; +} + +int static inline getint16le(char* buf) +{ + int16_t res = (buf[1] << 8) | buf[0]; + + return res; +} + +void static inline short2le(unsigned short val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; +} + +void static inline int2le(unsigned int val, unsigned char* addr) +{ + addr[0] = val & 0xFF; + addr[1] = (val >> 8) & 0xff; + addr[2] = (val >> 16) & 0xff; + addr[3] = (val >> 24) & 0xff; +} + +void int2be(unsigned int val, unsigned char* addr) +{ + addr[0] = (val >> 24) & 0xff; + addr[1] = (val >> 16) & 0xff; + addr[2] = (val >> 8) & 0xff; + addr[3] = val & 0xFF; +} + + +#define BYTES2INT32(array,pos)\ + ((long)array[pos] | ((long)array[pos+1] << 8 ) |\ + ((long)array[pos+2] << 16 ) | ((long)array[pos+3] << 24 )) + +void display_partinfo(struct ipod_t* ipod) +{ + int i; + double sectors_per_MB = (1024.0*1024.0)/ipod->sector_size; + + printf("[INFO] Part Start Sector End Sector Size (MB) Type\n"); + for ( i = 0; i < 4; i++ ) { + if (ipod->pinfo[i].start != 0) { + printf("[INFO] %d %10ld %10ld %10.1f %s (0x%02x)\n", + i, + ipod->pinfo[i].start, + ipod->pinfo[i].start+ipod->pinfo[i].size-1, + ipod->pinfo[i].size/sectors_per_MB, + get_parttype(ipod->pinfo[i].type), + ipod->pinfo[i].type); + } + } +} + +int read_partinfo(struct ipod_t* ipod, int silent) +{ + int i; + unsigned long count; + + count = ipod_read(ipod,sectorbuf, ipod->sector_size); + + if (count <= 0) { + print_error(" Error reading from disk: "); + return -1; + } + + if ((sectorbuf[510] == 0x55) && (sectorbuf[511] == 0xaa)) { + /* DOS partition table */ + if ((memcmp(§orbuf[71],"iPod",4) != 0) && + (memcmp(§orbuf[0x40],"This is your Apple iPod. You probably do not want to boot from it!",66) != 0) ) { + if (!silent) fprintf(stderr,"[ERR] Drive is not an iPod, aborting\n"); + return -1; + } + + ipod->macpod = 0; + /* parse partitions */ + for ( i = 0; i < 4; i++ ) { + unsigned char* ptr = sectorbuf + 0x1be + 16*i; + ipod->pinfo[i].type = ptr[4]; + ipod->pinfo[i].start = BYTES2INT32(ptr, 8); + ipod->pinfo[i].size = BYTES2INT32(ptr, 12); + + /* extended? */ + if ( ipod->pinfo[i].type == 5 ) { + /* not handled yet */ + } + } + } else if ((sectorbuf[0] == 'E') && (sectorbuf[1] == 'R')) { + /* Apple Partition Map */ + + /* APM parsing code based on the check_mac_partitions() function in + ipodloader2 - written by Thomas Tempelmann and released + under the GPL. */ + + int blkNo = 1; + int partBlkCount = 1; + int partBlkSizMul = sectorbuf[2] / 2; + + int pmMapBlkCnt; /* # of blks in partition map */ + int pmPyPartStart; /* physical start blk of partition */ + int pmPartBlkCnt; /* # of blks in this partition */ + int i = 0; + + ipod->macpod = 1; + + memset(ipod->pinfo,0,sizeof(ipod->pinfo)); + + while (blkNo <= partBlkCount) { + if (ipod_seek(ipod, blkNo * partBlkSizMul * 512) < 0) { + fprintf(stderr,"[ERR] Seek failed whilst reading APM\n"); + return -1; + } + + count = ipod_read(ipod, sectorbuf, ipod->sector_size); + + if (count <= 0) { + print_error(" Error reading from disk: "); + return -1; + } + + /* see if it's a partition entry */ + if ((sectorbuf[0] != 'P') || (sectorbuf[1] != 'M')) { + /* end of partition table -> leave the loop */ + break; + } + + /* Extract the interesting entries */ + pmMapBlkCnt = be2int(sectorbuf + 4); + pmPyPartStart = be2int(sectorbuf + 8); + pmPartBlkCnt = be2int(sectorbuf + 12); + + /* update the number of part map blocks */ + partBlkCount = pmMapBlkCnt; + + if (strncmp((char*)(sectorbuf + 48), "Apple_MDFW", 32)==0) { + /* A Firmware partition */ + ipod->pinfo[i].start = pmPyPartStart; + ipod->pinfo[i].size = pmPartBlkCnt; + ipod->pinfo[i].type = 0; + i++; + } else if (strncmp((char*)(sectorbuf + 48), "Apple_HFS", 32)==0) { + /* A HFS partition */ + ipod->pinfo[i].start = pmPyPartStart; + ipod->pinfo[i].size = pmPartBlkCnt; + ipod->pinfo[i].type = -1; + i++; + } + + blkNo++; /* read next partition map entry */ + } + } else { + if (!silent) fprintf(stderr,"[ERR] Bad boot sector signature\n"); + return -1; + } + + ipod->start = ipod->pinfo[0].start*ipod->sector_size; + return 0; +} + +int read_partition(struct ipod_t* ipod, int outfile) +{ + int res; + unsigned long n; + int bytesleft; + int chunksize; + int count = ipod->pinfo[0].size; + + if (ipod_seek(ipod, ipod->start) < 0) { + return -1; + } + + fprintf(stderr,"[INFO] Writing %d sectors to output file\n",count); + + bytesleft = count * ipod->sector_size; + while (bytesleft > 0) { + if (bytesleft > BUFFER_SIZE) { + chunksize = BUFFER_SIZE; + } else { + chunksize = bytesleft; + } + + n = ipod_read(ipod, sectorbuf, chunksize); + + if (n < 0) { + return -1; + } + + if (n < chunksize) { + fprintf(stderr, + "[ERR] Short read in disk_read() - requested %d, got %lu\n", + chunksize,n); + return -1; + } + + bytesleft -= n; + + res = write(outfile,sectorbuf,n); + + if (res < 0) { + perror("[ERR] write in disk_read"); + return -1; + } + + if (res != n) { + fprintf(stderr, + "Short write - requested %lu, received %d - aborting.\n",n,res); + return -1; + } + } + + fprintf(stderr,"[INFO] Done.\n"); + return 0; +} + +int write_partition(struct ipod_t* ipod, int infile) +{ + unsigned long res; + int n; + int bytesread; + int byteswritten = 0; + int eof; + int padding = 0; + + if (ipod_seek(ipod, ipod->start) < 0) { + return -1; + } + + fprintf(stderr,"[INFO] Writing input file to device\n"); + bytesread = 0; + eof = 0; + while (!eof) { + n = read(infile,sectorbuf,BUFFER_SIZE); + + if (n < 0) { + perror("[ERR] read in disk_write"); + return -1; + } + + if (n < BUFFER_SIZE) { + eof = 1; + /* We need to pad the last write to a multiple of SECTOR_SIZE */ + if ((n % ipod->sector_size) != 0) { + padding = (ipod->sector_size-(n % ipod->sector_size)); + n += padding; + } + } + + bytesread += n; + + res = ipod_write(ipod, sectorbuf, n); + + if (res < 0) { + print_error(" Error writing to disk: "); + fprintf(stderr,"Bytes written: %d\n",byteswritten); + return -1; + } + + if (res != n) { + fprintf(stderr,"[ERR] Short write - requested %d, received %lu - aborting.\n",n,res); + return -1; + } + + byteswritten += res; + } + + fprintf(stderr,"[INFO] Wrote %d bytes plus %d bytes padding.\n", + byteswritten-padding,padding); + return 0; +} + +char* ftypename[] = { "OSOS", "RSRC", "AUPD", "HIBE" }; + +int diskmove(struct ipod_t* ipod, int delta) +{ + int src_start; + int src_end; + int bytesleft; + int chunksize; + int i; + int n; + + src_start = ipod->ipod_directory[1].devOffset + ipod->sector_size; + src_end = (ipod->ipod_directory[ipod->nimages-1].devOffset + ipod->sector_size + + ipod->ipod_directory[ipod->nimages-1].len + + (ipod->sector_size-1)) & ~(ipod->sector_size-1); + bytesleft = src_end - src_start; + + if (verbose) { + fprintf(stderr,"[INFO] Need to move images 2-%d forward %08x bytes\n", ipod->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",src_start+delta); + fprintf(stderr,"[VERB] dest_end = %08x\n",src_end+delta); + 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 (absolute %08x to %08x)\n", + chunksize, + src_end-chunksize, + src_end-chunksize+delta, + (unsigned int)(ipod->start+src_end-chunksize), + (unsigned int)(ipod->start+src_end-chunksize+delta)); + } + + + if (ipod_seek(ipod, ipod->start+src_end-chunksize) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(ipod,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(ipod, ipod->start+src_end-chunksize+delta) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,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; + } + + src_end -= chunksize; + bytesleft -= chunksize; + } + + return 0; +} + +int add_bootloader(struct ipod_t* ipod, char* filename, int type) +{ + int length; + int i; + int x; + 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 */ + unsigned char* bootloader_buf; + + /* Calculate the position in the OSOS image where our bootloader will go. */ + if (ipod->ipod_directory[0].entryOffset>0) { + /* Keep the same entryOffset */ + entryOffset = ipod->ipod_directory[0].entryOffset; + } else { + entryOffset = (ipod->ipod_directory[0].len+ipod->sector_size-1)&~(ipod->sector_size-1); + } + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + fprintf(stderr,"[INFO] Using internal bootloader - %d bytes\n",ipod->bootloader_len); + memcpy(sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); + length = ipod->bootloader_len; + paddedlength=(ipod->bootloader_len+ipod->sector_size-1)&~(ipod->sector_size-1); + } + else +#endif + { + infile=open(filename,O_RDONLY); + if (infile < 0) { + fprintf(stderr,"[ERR] Couldn't open input file %s\n",filename); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + /* First check that the input file is the correct type for this ipod. */ + 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, ipod->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], ipod->modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length=filesize(infile)-8; + } else { + length=filesize(infile); + } + paddedlength=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + bootloader_buf = malloc(length); + if (bootloader_buf == NULL) { + fprintf(stderr,"[ERR] Can not allocate memory for bootlaoder\n"); + } + /* Now read our bootloader - we need to check it before modifying the partition*/ + n = read(infile,bootloader_buf,length); + close(infile); + + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + return -1; + } + + if (type==FILETYPE_DOT_IPOD) { + /* Calculate and confirm bootloader checksum */ + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += bootloader_buf[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 (entryOffset+paddedlength > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + return -1; + } + + if (verbose) { + fprintf(stderr,"[VERB] Original firmware begins at 0x%08x\n", ipod->ipod_directory[0].devOffset + ipod->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 (ipod->nimages > 1) { + if ((ipod->ipod_directory[0].devOffset+entryOffset+paddedlength) > + ipod->ipod_directory[1].devOffset) { + fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); + delta = ipod->ipod_directory[0].devOffset + entryOffset+paddedlength + - ipod->ipod_directory[1].devOffset; + + if (diskmove(ipod, delta) < 0) { + 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(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_read(ipod,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; + } + +#ifdef WITH_BOOTOBJS + if (type == FILETYPE_INTERNAL) { + memcpy(sectorbuf+entryOffset,ipod->bootloader,ipod->bootloader_len); + } + else +#endif + { + memcpy(sectorbuf+entryOffset,bootloader_buf,length); + free(bootloader_buf); + } + + /* Calculate new checksum for combined image */ + chksum = 0; + for (i=0;ifwoffset+ipod->ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,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); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Directory read failed\n"); + return -1; + } + + /* Update entries for image 0 */ + int2le(entryOffset+length,sectorbuf+x+16); + int2le(entryOffset,sectorbuf+x+24); + int2le(chksum,sectorbuf+x+28); + int2le(0xffffffff,sectorbuf+x+36); /* loadAddr */ + + /* Update devOffset entries for other images, if we have moved them */ + if (delta > 0) { + for (i=1;inimages;i++) { + int2le(le2int(sectorbuf+x+i*40+12)+delta,sectorbuf+x+i*40+12); + } + } + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to %d failed\n", (int)(ipod->start+ipod->diroffset-x)); + return -1; + } + n=ipod_write(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Directory write failed\n"); + return -1; + } + + return 0; +} + +int delete_bootloader(struct ipod_t* ipod) +{ + int length; + int i; + int x; + 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->ipod_directory[0].entryOffset == 0) { + fprintf(stderr,"[ERR] No bootloader found.\n"); + return -1; + } + + length = ipod->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(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + return -1; + } + + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", + length,i); + + if ((n = ipod_read(ipod,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"); + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update entries for image 0 */ + int2le(length,sectorbuf+x+16); + int2le(0,sectorbuf+x+24); + int2le(chksum,sectorbuf+x+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +int write_firmware(struct ipod_t* ipod, char* filename, int type) +{ + int length; + int i; + int x; + int n; + int infile; + int newsize; + int bytesavailable; + 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; + } + + if (type==FILETYPE_DOT_IPOD) { + 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, ipod->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], ipod->modelname); + close(infile); + return -1; + } + + filechksum = be2int(header); + + length = filesize(infile)-8; + } else { + length = filesize(infile); + } + newsize=(length+ipod->sector_size-1)&~(ipod->sector_size-1); + + fprintf(stderr,"[INFO] Padding input file from 0x%08x to 0x%08x bytes\n", + length,newsize); + + if (newsize > BUFFER_SIZE) { + fprintf(stderr,"[ERR] Input file too big for buffer\n"); + close(infile); + return -1; + } + + /* Check if we have enough space */ + /* TODO: Check the size of the partition. */ + if (ipod->nimages > 1) { + bytesavailable=ipod->ipod_directory[1].devOffset-ipod->ipod_directory[0].devOffset; + if (bytesavailable < newsize) { + fprintf(stderr,"[INFO] Moving images to create room for new firmware...\n"); + + /* TODO: Implement image movement */ + fprintf(stderr,"[ERR] Image movement not yet implemented.\n"); + close(infile); + return -1; + } + } + + fprintf(stderr,"[INFO] Reading input file...\n"); + /* We now know we have enough space, so write it. */ + memset(sectorbuf+length,0,newsize-length); + n = read(infile,sectorbuf,length); + if (n < 0) { + fprintf(stderr,"[ERR] Couldn't read input file\n"); + close(infile); + return -1; + } + close(infile); + + if (type==FILETYPE_DOT_IPOD) { + chksum = ipod->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_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + fprintf(stderr,"[ERR] Seek failed\n"); + return -1; + } + + if ((n = ipod_write(ipod,sectorbuf,newsize)) < 0) { + perror("[ERR] Write failed\n"); + return -1; + } + + if (n < newsize) { + fprintf(stderr,"[ERR] Short write - requested %d bytes, received %d\n" + ,i,n); + return -1; + } + fprintf(stderr,"[INFO] Wrote %d bytes to firmware partition\n",n); + + /* Now we need to update the "len", "entryOffset" and "chksum" fields */ + chksum = 0; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + /* Update entries for image 0 */ + int2le(length,sectorbuf+x+16); + int2le(0,sectorbuf+x+24); + int2le(chksum,sectorbuf+x+28); + + /* Write directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { return -1; } + n=ipod_write(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { return -1; } + + return 0; +} + +int read_firmware(struct ipod_t* ipod, char* filename) +{ + int length; + int i; + int outfile; + int n; + unsigned long chksum=0; /* 32 bit checksum - Rockbox .ipod style*/ + unsigned char header[8]; /* Header for .ipod file */ + + if (ipod->ipod_directory[0].entryOffset != 0) { + /* We have a bootloader... */ + length = ipod->ipod_directory[0].entryOffset; + } else { + length = ipod->ipod_directory[0].len; + } + + fprintf(stderr,"[INFO] Reading firmware (%d bytes)\n",length); + + if (ipod_seek(ipod, ipod->fwoffset+ipod->ipod_directory[0].devOffset) < 0) { + return -1; + } + + i = (length+ipod->sector_size-1) & ~(ipod->sector_size-1); + fprintf(stderr,"[INFO] Padding read from 0x%08x to 0x%08x bytes\n", + length,i); + + if ((n = ipod_read(ipod,sectorbuf,i)) < 0) { + return -1; + } + + if (n < i) { + fprintf(stderr,"[ERR] Short read - requested %d bytes, received %d\n", + i,n); + return -1; + } + + chksum = ipod->modelnum; + for (i = 0; i < length; i++) { + /* add 8 unsigned bits but keep a 32 bit sum */ + chksum += sectorbuf[i]; + } + + int2be(chksum,header); + memcpy(header+4, ipod->modelname,4); + + outfile = open(filename,O_CREAT|O_TRUNC|O_WRONLY|O_BINARY,0666); + if (outfile < 0) { + fprintf(stderr,"[ERR] Couldn't open file %s\n",filename); + return -1; + } + + write(outfile,header,8); + write(outfile,sectorbuf,length); + close(outfile); + + return 0; +} + +int read_directory(struct ipod_t* ipod) +{ + int n; + int x; + unsigned char* p; + unsigned short version; + + ipod->nimages=0; + + /* Read firmware partition header (first 512 bytes of disk - but + let's read a whole sector) */ + + if (ipod_seek(ipod, ipod->start) < 0) { + fprintf(stderr,"[ERR] Seek to 0x%08x in read_directory() failed.\n", + (unsigned int)(ipod->start)); + return -1; + } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] ipod_read(ipod,buf,0x%08x) failed in read_directory()\n", ipod->sector_size); + return -1; + } + + if (memcmp(sectorbuf,apple_stop_sign,sizeof(apple_stop_sign))!=0) { + fprintf(stderr,"[ERR] Firmware partition doesn't contain Apple copyright, aborting.\n"); + return -1; + } + + if (memcmp(sectorbuf+0x100,"]ih[",4)!=0) { + fprintf(stderr,"[ERR] Bad firmware directory\n"); + return -1; + } + + version = le2ushort(sectorbuf+0x10a); + if ((version != 2) && (version != 3)) { + fprintf(stderr,"[ERR] Unknown firmware format version %04x\n", + version); + } + ipod->diroffset=le2int(sectorbuf+0x104) + 0x200; + + /* diroffset may not be sector-aligned */ + x = ipod->diroffset % ipod->sector_size; + + /* Read directory */ + if (ipod_seek(ipod, ipod->start + ipod->diroffset - x) < 0) { + fprintf(stderr,"[ERR] Seek to diroffset (%08x) failed.\n",(unsigned)ipod->diroffset); + return -1; + } + + n=ipod_read(ipod, sectorbuf, ipod->sector_size); + if (n < 0) { + fprintf(stderr,"[ERR] Read of directory failed.\n"); + return -1; + } + + p = sectorbuf + x; + + while ((ipod->nimages < MAX_IMAGES) && (p < (sectorbuf + x + 400)) && + (memcmp(p,"!ATA",4)==0)) { + p+=4; + if (memcmp(p,"soso",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_OSOS; + } else if (memcmp(p,"crsr",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_RSRC; + } else if (memcmp(p,"dpua",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_AUPD; + } else if (memcmp(p,"ebih",4)==0) { + ipod->ipod_directory[ipod->nimages].ftype=FTYPE_HIBE; + } else { + fprintf(stderr,"[ERR] Unknown image type %c%c%c%c\n", + p[0],p[1],p[2],p[3]); + } + p+=4; + ipod->ipod_directory[ipod->nimages].id=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].devOffset=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].len=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].addr=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].entryOffset=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].chksum=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].vers=le2int(p); + p+=4; + ipod->ipod_directory[ipod->nimages].loadAddr=le2int(p); + p+=4; + ipod->nimages++; + } + + if ((ipod->nimages > 1) && (version==2)) { + /* The 3g firmware image doesn't appear to have a version, so + let's make one up... Note that this is never written back to the + ipod, so it's OK to do. */ + + if (ipod->ipod_directory[0].vers == 0) { ipod->ipod_directory[0].vers = 3; } + + ipod->fwoffset = ipod->start; + } else { + ipod->fwoffset = ipod->start + ipod->sector_size; + } + + return 0; +} + +int list_images(struct ipod_t* ipod) +{ + int i; + + if (verbose) { + printf(" Type id devOffset len addr entryOffset chksum vers loadAddr devOffset+len\n"); + for (i = 0 ; i < ipod->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->ipod_directory[i].ftype], + ipod->ipod_directory[i].id, + ipod->ipod_directory[i].devOffset, + ipod->ipod_directory[i].len, + ipod->ipod_directory[i].addr, + ipod->ipod_directory[i].entryOffset, + ipod->ipod_directory[i].chksum, + ipod->ipod_directory[i].vers, + ipod->ipod_directory[i].loadAddr, + ipod->ipod_directory[i].devOffset+((ipod->ipod_directory[i].len+ipod->sector_size-1)&~(ipod->sector_size-1))); + } + } + + printf("\n"); + printf("Listing firmware partition contents:\n"); + printf("\n"); + + for (i = 0 ; i < ipod->nimages; i++) { + printf("Image %d:\n",i+1); + switch(ipod->ipod_directory[i].ftype) { + case FTYPE_OSOS: + if (ipod->ipod_directory[i].entryOffset==0) { + printf(" Main firmware - %d bytes\n", + ipod->ipod_directory[i].len); + } else { + printf(" Main firmware - %d bytes\n", + ipod->ipod_directory[i].entryOffset); + printf(" Third-party bootloader - %d bytes\n", + ipod->ipod_directory[i].len-ipod->ipod_directory[i].entryOffset); + } + break; + default: + printf(" %s - %d bytes\n", + ftypename[ipod->ipod_directory[i].ftype], + ipod->ipod_directory[i].len); + } + } + printf("\n"); + + return 0; +} + +int getmodel(struct ipod_t* ipod, int ipod_version) +{ + switch (ipod_version) { + case 0x02: + ipod->modelstr="3rd Generation"; + ipod->modelnum = 7; + ipod->modelname = "ip3g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipod3g; + ipod->bootloader_len = LEN_ipod3g; +#endif + break; + case 0x40: + ipod->modelstr="1st Generation Mini"; + ipod->modelnum = 9; + ipod->modelname = "mini"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodmini; + ipod->bootloader_len = LEN_ipodmini; +#endif + break; + case 0x50: + ipod->modelstr="4th Generation"; + ipod->modelnum = 8; + ipod->modelname = "ip4g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipod4g; + ipod->bootloader_len = LEN_ipod4g; +#endif + break; + case 0x60: + ipod->modelstr="Photo/Color"; + ipod->modelnum = 3; + ipod->modelname = "ipco"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodcolor; + ipod->bootloader_len = LEN_ipodcolor; +#endif + break; + case 0x70: + ipod->modelstr="2nd Generation Mini"; + ipod->modelnum = 11; + ipod->modelname = "mn2g"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodmini2g; + ipod->bootloader_len = LEN_ipodmini2g; +#endif + break; + case 0xc0: + ipod->modelstr="1st Generation Nano"; + ipod->modelnum = 4; + ipod->modelname = "nano"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodnano; + ipod->bootloader_len = LEN_ipodnano; +#endif + break; + case 0xb0: + ipod->modelstr="Video (aka 5th Generation)"; + ipod->modelnum = 5; + ipod->modelname = "ipvd"; +#ifdef WITH_BOOTOBJS + ipod->bootloader = ipodvideo; + ipod->bootloader_len = LEN_ipodvideo; +#endif + break; + default: + ipod->modelname = NULL; + ipod->modelnum = 0; +#ifdef WITH_BOOTOBJS + ipod->bootloader = NULL; + ipod->bootloader_len = 0; +#endif + return -1; + } + return 0; +} + +int ipod_scan(struct ipod_t* ipod) +{ + int i; + int n = 0; + int ipod_version; + char last_ipod[4096]; + + printf("[INFO] Scanning disk devices...\n"); + + for (i = 0; i <= 25 ; i++) { +#ifdef __WIN32__ + sprintf(ipod->diskname,"\\\\.\\PhysicalDrive%d",i); +#elif defined(linux) || defined (__linux) + sprintf(ipod->diskname,"/dev/sd%c",'a'+i); +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__bsdi__) || defined(__DragonFly__) + sprintf(ipod->diskname,"/dev/da%d",i); +#elif defined(__APPLE__) && defined(__MACH__) + sprintf(ipod->diskname,"/dev/disk%d",i); +#else + #error No disk paths defined for this platform +#endif + if (ipod_open(ipod, 1) < 0) { + continue; + } + + if (read_partinfo(ipod,1) < 0) { + continue; + } + + if ((ipod->pinfo[0].start==0) || (ipod->pinfo[0].type != 0)) { + continue; + } + + if (read_directory(ipod) < 0) { + continue; + } + + ipod_version=(ipod->ipod_directory[0].vers>>8); + if (getmodel(ipod,ipod_version) < 0) { + continue; + } + +#ifdef __WIN32__ + printf("[INFO] Ipod found - %s (\"%s\") - disk device %d\n", + ipod->modelstr,ipod->macpod ? "macpod" : "winpod",i); +#else + printf("[INFO] Ipod found - %s (\"%s\") - %s\n", + ipod->modelstr,ipod->macpod ? "macpod" : "winpod",ipod->diskname); +#endif + n++; + strcpy(last_ipod,ipod->diskname); + ipod_close(ipod); + } + + if (n==1) { + /* Remember the disk name */ + strcpy(ipod->diskname,last_ipod); + } + return n; +} -- cgit v1.2.3