1
0
Fork 0
forked from len0rd/rockbox

Implement the add/delete bootloader functionality.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11765 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Dave Chapman 2006-12-14 18:41:03 +00:00
parent da945c0873
commit 75a11124f0

View file

@ -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<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)
{
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 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) {
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) {
fprintf(stderr,"[INFO] Bootloader removed.\n");
}
}
} else if (action==REPLACE_FIRMWARE) {
} else if (action==DELETE_BOOTLOADER) {
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 (ipod_directory[0].entryOffset==0) {
fprintf(stderr,"[ERR] No bootloader detected.\n");
} else {
fprintf(stderr,"[ERR] --replace-firmware failed.\n");
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==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==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] --extract-firmware failed.\n");
fprintf(stderr,"[ERR] --add-bootloader failed.\n");
}
} else if (action==WRITE_FIRMWARE) {
if (ipod_reopen_rw(&dh, devicename) < 0) {
return 5;
}
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] --write-firmware failed.\n");
}
} 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] --read-firmware failed.\n");
}
} else if (action==READ_PARTITION) {
outfile = open(filename,O_CREAT|O_WRONLY|O_BINARY,S_IREAD|S_IWRITE);