Index: utils/AMS/hacking/test.S =================================================================== --- utils/AMS/hacking/test.S (révision 18463) +++ utils/AMS/hacking/test.S (copie de travail) @@ -1,11 +0,0 @@ - -/* This value is filled in by mkamsboot */ -originalentry: .word 0 - - /* A delay loop - just to prove we're running */ - mov r1, #0x500000 /* Approximately 5 seconds */ -loop: subs r1, r1, #1 - bne loop - - /* Now branch back to the original firmware's entry point */ - ldr pc, originalentry Index: utils/AMS/hacking/stage1.S =================================================================== --- utils/AMS/hacking/stage1.S (révision 0) +++ utils/AMS/hacking/stage1.S (révision 0) @@ -0,0 +1,69 @@ +/* soc */ +.equ GPIOA, 0xC80B0000 +.equ CGU_PERI, 0xC80F0014 +.equ CCU_BASE, 0xC8100000 + +/* DO NOT MODIFY AT THE RISK TO BREAK THE SOFTWARE RECOVERY MODE */ +/* WARNING : THE GPIO MAPPING CORRESPONDS TO CLIP */ + +originalentry: +.word 0 /* This value is filled in by mkamsboot */ + +/* 2nd stage, filled by mkamsboot */ +stage2_offset: +.word 0 +stage2_size: +.word 0 +stage2_val: +.word 0 + + /* enable gpio clock */ + ldr r1, =CGU_PERI + ldr r2, [r1] + mov r0, #0x10000 /* gpio : bit 16 */ + orr r2, r2, r0 + str r2, [r1] + + /* copy the OF from ROM into RAM */ + mov r0, #0x20000 /* OF size */ + mov r1, #0x81000000 /* RAM */ + mov r2, #0x0 /* ROM */ + +loop_load: + subs r0, r0, #4 /* word copy */ + ldr r3, [r2, r0] + str r3, [r1, r0] + bne loop_load + /* XXX : DO NOT TOUCH r0 WE ASSUME IT IS #0 */ + + ldr r1, =CCU_BASE + mov r2, #1 /* ram */ + str r2, [r1, #8] /* ccu_memmap */ + + /* now the RAM is mapped at 0x0 and we continue execution in RAM */ + + /* check hold button / usb connection */ + ldr r1, =GPIOA + strb r0, [r1, #0x400] /* gpioa_dir (r0 == 0 after loop_load) */ + ldrb r0, [r1, #0x120] /* pin 3 : hold, 6 : usb */ + + cmp r0, #0 + bne resume /* resume OF if set */ + + adr lr, resume /* stage2 can use bx lr to resume OF */ + ldr pc, stage2_offset /* branch to stage2 */ + + /* back from stage 2 */ +resume: + ldr r0, stage2_offset + ldr r1, stage2_size + ldr r2, stage2_val + + /* restore OF state */ +clean_loop: + subs r1, r1, #4 /* word copy */ + str r2, [r0, r1] + bne clean_loop + + /* branch to OF */ + ldr pc, originalentry Index: utils/AMS/hacking/stage2.S =================================================================== --- utils/AMS/hacking/stage2.S (révision 0) +++ utils/AMS/hacking/stage2.S (révision 0) @@ -0,0 +1,56 @@ +/* soc */ +.equ GPIOD, 0xC80E0000 + + mov sp, #0x30000 /* 64kB stack */ + stmed sp!,{lr} /* keep return address to 1st code block in stack */ + + bl blink + + b resume + + + +/* store our functions here */ + +/* blink : switches the light on/off */ +/* void blink(void) */ +blink: + stmed sp!, {r0, r1, r2, r3, lr} + + ldr r2, =GPIOD + ldrb r0, [r2, #0x400] /* gpiod_dir */ + orr r0, r0, #0x80 /* sets pin 7 as output */ + strb r0, [r2, #0x400] + + mov r3, #0 /* Start by toggling off */ + mov r1, #0x8 /* Let's toggle 8 times (4 on/off cycles) */ + +toggle_led: + strb r3, [r2, #0x200] /* switch pin 7 : led on/off */ + /* and prepare for next cycle */ + eor r3, r3, #0xFF /* toggle value we write into pin 7 */ + + mov r0, #0x20000 /* approx 1/8 second delay */ + bl sleep + + /* Let's do 4 on/off cycles */ + subs r1, r1, #1 + bne toggle_led + + ldmed sp!, {r0, r1, r2, r3, lr} + bx lr +/* end of function blink */ + + + +/* void sleep(r0) : r0 = 0x10000 ~= 1 second */ +sleep: + subs r0, r0, #1 + bne sleep + bx lr +/* end of function sleep */ + + +resume: + ldmed sp!,{lr} /* get the return address to the 1st code block */ + bx lr /* return 1st code block -> OF */ Index: utils/AMS/hacking/Makefile =================================================================== --- utils/AMS/hacking/Makefile (révision 18463) +++ utils/AMS/hacking/Makefile (copie de travail) @@ -10,25 +10,33 @@ all: amsinfo $(OUTFILE) amsinfo: amsinfo.c - gcc -o amsinfo -W -Wall amsinfo.c + gcc -o amsinfo -W -Wall -Werror amsinfo.c mkamsboot: mkamsboot.c - gcc -o mkamsboot -W -Wall mkamsboot.c + gcc -o mkamsboot -W -Wall -Werror mkamsboot.c # Rules for our test ARM application - assemble, link, then extract # the binary code -test.o: test.S - arm-elf-as -o test.o test.S +stage1.o: stage1.S + arm-elf-as -o stage1.o stage1.S -test.elf: test.o - arm-elf-ld -e 0 -o test.elf test.o +stage1.elf: stage1.o + arm-elf-ld -e 0 -o stage1.elf stage1.o -test.bin: test.elf - arm-elf-objcopy -O binary test.elf test.bin +stage1.bin: stage1.elf + arm-elf-objcopy -O binary stage1.elf stage1.bin -$(OUTFILE): mkamsboot test.bin $(INFILE) - ./mkamsboot $(INFILE) test.bin $(OUTFILE) +stage2.o: stage2.S + arm-elf-as -o stage2.o stage2.S +find-offset: find-offset.c + gcc -o find-offset -W -Wall -Werror find-offset.c + +$(OUTFILE): mkamsboot stage1.bin stage2.o $(INFILE) find-offset + arm-elf-ld -e `./find-offset $(INFILE)` -o stage2.elf stage2.o + arm-elf-objcopy -O binary stage2.elf stage2.bin + ./mkamsboot $(INFILE) stage1.bin stage2.bin `./find-offset $(INFILE)` $(OUTFILE) + clean: - rm -fr amsinfo mkamsboot test.bin test.o test.elf $(OUTFILE) *~ + rm -fr amsinfo mkamsboot stage{1,2}.bin stage{1,2}.o stage{1,2}.elf $(OUTFILE) find-offset *~ Index: utils/AMS/hacking/mkamsboot.c =================================================================== --- utils/AMS/hacking/mkamsboot.c (révision 18463) +++ utils/AMS/hacking/mkamsboot.c (copie de travail) @@ -4,6 +4,7 @@ (AMS) firmware file Copyright (C) Dave Chapman 2008 +Copyright (C) Rafaël Carré 2008 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -32,21 +33,39 @@ where [pc, #xxx] contains the entry point of the firmware - e.g. 0x00000138 -mkamsboot appends the Rockbox bootloader to the end of the original -firmware block in the firmware file and shifts the remaining contents of the firmware file to make space for it. +mkamsboot appends the 1st stage of the Rockbox bootloader to the end of the +original firmware block, while verifying it will not overflow the last 0x200 +bytes block. It also replaces the contents of [pc, #xxx] with the entry point of -our bootloader - i.e. the length of the original firmware block plus 4 +our bootloader - i.e. the length of the original firmware block plus 16 bytes. It then stores the original entry point from [pc, #xxx] in the first -four bytes of the Rockbox bootloader image, which is used by the -bootloader to dual-boot. +four bytes of the 1st stage Rockbox bootloader, which is used by the bootloader +to dual-boot. +A separate tool is run on the file to find the longest block padded with the +same 32bits value, and is used to link the 2nd stage bootloader with the entry +point being this offset. + +mkamsboot takes this offset in argument and will write the starting offset, the +length of this block, and the 32bits value it is filled with, in the 12 +following bytes of the 1st stage. +The 1st stage use these values to restore the OF in its original form before +booting the OF. + +Next step is writing the 2nd stage bootloader at this offset, while verifying +that it is small enough to fit in. + Finally, mkamsboot corrects the length and checksum in the main firmware headers (both copies), creating a new legal firmware file -which can be installed on the device. +which can be installed on the device, and which implements a recovery mode in +software in case the 2nd stage is gone bad. +WARNING : pay special attention to modifications to the 1st stage, because the +software recovery mode could be broken, and you may end up bricking your device. + */ @@ -113,8 +132,8 @@ int main(int argc, char* argv[]) { - char *infile, *bootfile, *outfile; - int fdin, fdboot,fdout; + char *infile, *bootfile, *boot2file, *outfile; + int fdin, fdboot,fdboot2,fdout; off_t len; uint32_t n; unsigned char* buf; @@ -123,18 +142,22 @@ uint32_t firmware_size; uint32_t firmware_paddedsize; uint32_t bootloader_size; + uint32_t stage2_size; uint32_t new_paddedsize; uint32_t sum,filesum; uint32_t new_length; + uint32_t stage2_offset; uint32_t i; - if(argc != 4) { + if(argc != 6) { usage(); } infile = argv[1]; bootfile = argv[2]; - outfile = argv[3]; + boot2file = argv[3]; + stage2_offset = atoi(argv[4]); + outfile = argv[5]; /* Open the bootloader file */ fdboot = open(bootfile, O_RDONLY|O_BINARY); @@ -147,6 +170,16 @@ bootloader_size = filesize(fdboot); + /* Open the 2nd stage bootloader file */ + fdboot2 = open(boot2file, O_RDONLY|O_BINARY); + if (fdboot2 < 0) + { + fprintf(stderr,"[ERR] Could not open %s for reading\n",boot2file); + return 1; + } + + stage2_size = filesize(fdboot2); + /* Open the firmware file */ fdin = open(infile,O_RDONLY|O_BINARY); @@ -195,17 +228,8 @@ fprintf(stderr,"New total size of firmware - 0x%08x\n",new_length); if (firmware_paddedsize != new_paddedsize) { - /* Move everything after the firmare block "bootloader_size" - bytes forward to make room for the bootloader */ - - fprintf(stderr,"Calling memmove(buf + 0x%08x,buf + 0x%08x,0x%08x)\n", - 0x400 + new_paddedsize, - 0x400 + firmware_paddedsize, - (int)len - firmware_paddedsize); - - memmove(buf + 0x400 + new_paddedsize, - buf + 0x400 + firmware_paddedsize, - len - firmware_paddedsize); + fprintf(stderr,"[ERR] %d words too much - ABORTING\n", (firmware_size + bootloader_size - firmware_paddedsize) / 4); + return 1; } ldr = get_uint32le(&buf[0x400]); @@ -217,7 +241,7 @@ origoffset = (ldr&0xfff) + 8; printf("original firmware entry point: 0x%08x\n",get_uint32le(buf + 0x400 + origoffset)); - printf("New entry point: 0x%08x\n", firmware_size + 4); + printf("New entry point: 0x%08x\n", firmware_size + 16); #if 0 /* Replace the "Product: Express" string with "Rockbox" */ @@ -235,10 +259,46 @@ } #endif + /* + * STAGE 2 + */ + + if(stage2_offset >= firmware_size) + { + fprintf(stderr,"[ERR] Invalid stage2 offset\n"); + return 1; + } + + uint32_t stage2_max; + uint32_t stage2_val = get_uint32le(buf + 0x400 + stage2_offset); + for(stage2_max=0; stage2_max< firmware_size - stage2_offset; stage2_max+=4) + if(stage2_val != get_uint32le(buf + 0x400 + stage2_offset + stage2_max)) + break; + + if(stage2_size > stage2_max) + { + fprintf(stderr,"[ERR] stage2 too big by %d words\n", + (stage2_size-stage2_max)/4); + return 1; + } + + /* read stage2 */ + unsigned char stage2[stage2_size]; + n = read(fdboot2, stage2, stage2_size); + if (n != stage2_size) { + fprintf(stderr,"[ERR] Could not read stage2 file\n"); + return 1; + } + close(fdboot2); + /* and copy it */ + memcpy(buf+0x400+stage2_offset,stage2,stage2_size); + + printf("Stage2 entry point: 0x%08x\n", stage2_offset); + n = read(fdboot, buf + 0x400 + firmware_size, bootloader_size); if (n != bootloader_size) { - fprintf(stderr,"[ERR] Could not bootloader file\n"); + fprintf(stderr,"[ERR] Could not read stage2 file\n"); return 1; } close(fdboot); @@ -246,10 +306,14 @@ /* Replace first word of the bootloader with the original entry point */ put_uint32le(buf + 0x400 + firmware_size, get_uint32le(buf + 0x400 + origoffset)); -#if 1 - put_uint32le(buf + 0x400 + origoffset, firmware_size + 4); -#endif + put_uint32le(buf + 0x400 + origoffset, firmware_size + 16); + /* Replace 3 next words of the bootloader with stage2 offset, size, and + * value */ + put_uint32le(buf + 0x400 + firmware_size + 4, stage2_offset); + put_uint32le(buf + 0x400 + firmware_size + 8, stage2_max); + put_uint32le(buf + 0x400 + firmware_size + 12,stage2_val); + /* Update checksum */ sum = calc_checksum(buf + 0x400,firmware_size + bootloader_size); Index: utils/AMS/hacking/find-offset.c =================================================================== --- utils/AMS/hacking/find-offset.c (révision 0) +++ utils/AMS/hacking/find-offset.c (révision 0) @@ -0,0 +1,119 @@ +/* find-offset.c - a tool to find the biggset block filled with the same + 32 bits word in a SansaV2 firmware + + Copyright (C) 2008 Rafaël Carré + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* find the biggest area containing the same word */ + +int main(int argc, const char **argv) +{ + if(argc != 2) + { + fprintf(stderr, "Usage: %s \n", *argv); + return 1; + } + + struct stat st; + if( stat(argv[1], &st) == -1 ) + { + perror(argv[1]); + return 2; + } + + if(st.st_size <= 0x400 || st.st_size % 0x200) + { + fprintf(stderr, "%s doesn't look like a sansa firmware\n", argv[1]); + return 3; + } + + FILE *f = fopen(argv[1], "r"); + if(!f) + { + perror(argv[1]); + return 4; + } + + unsigned char header[0x400]; + if( fread(header, 0x400, 1, f) != 1 ) + { + perror(argv[1]); + fclose(f); + return 5; + } + + int firmware_size = ((header[9] << 8) | header[8]) * 0x200; + uint32_t *buf = malloc(firmware_size); + if(!buf) + { + fprintf(stderr,"%zd is too big\n", firmware_size); + fclose(f); + return 6; + } + + if(fread(buf, firmware_size, 1, f) != 1) + { + perror(argv[1]); + fclose(f); + free(buf); + return 7; + } + fclose(f); + + uint32_t offset, offset_max; + uint32_t val; /* word tracked */ + int n = 0; /* number of values found */ + int n_max = 0; /* highest block size found */ + int w = 0, words = firmware_size/4; + + while(w < words) + { + if(n == 0) + { + offset = w * 4; + val = buf[w]; /* start tracking another word */ + } + + if(buf[w] == val) /* increase the counter */ + n++; + else /* reset */ + { + if(n > n_max) /* store score? */ + { + n_max = n; + offset_max = offset; + } + n = 0; + } + + w++; /* next word */ + } + + printf("%d\n",offset_max); + + free(buf); + return 0; +} +