ff6b0425e4
The field we thought was representative of the model is not, it has changed in the past for fuzev1 and fuzev2. For example the value 0x23 is found in 2 old fuzev1 OF versions, and in the c200v2 OF The only reliable way to detect the model of a given OF is by using the built-in list of md5sums. Modify mkamsboot and rbutilqt to load the rockbox bootloader first, and then check if the model in the bootloader corresponds to the model of the known md5sum of the given OF. That way we can continue to present the user with a list of known OF versions in case the OF is unknown to mkamsboot Also explicit the dependency of main.c on mkamsboot.h in case the prototypes change Correct the header's description not updated in r21648 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26248 a1c6a512-1295-4272-9138-f99709370657
465 lines
11 KiB
C
465 lines
11 KiB
C
/*
|
|
* Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#define _ISOC99_SOURCE /* snprintf() */
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
|
|
#if 1 /* ANSI colors */
|
|
|
|
# define color(a) printf("%s",a)
|
|
char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
|
|
|
|
char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
|
|
char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
|
|
char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
|
|
char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
|
|
char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
|
|
|
|
#else
|
|
/* disable colors */
|
|
# define color(a)
|
|
#endif
|
|
|
|
#define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
|
|
#define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
|
|
|
|
/* byte swapping */
|
|
#define get32le(a) ((uint32_t) \
|
|
( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] ))
|
|
#define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
|
|
|
|
/* all blocks are sized as a multiple of 0x1ff */
|
|
#define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
|
|
|
|
/* If you find a firmware that breaks the known format ^^ */
|
|
#define assert(a) do { if(!(a)) { fprintf(stderr,"Assertion \"%s\" failed in %s() line %d!\n\nPlease send us your firmware!\n",#a,__func__,__LINE__); exit(1); } } while(0)
|
|
|
|
/* globals */
|
|
|
|
size_t sz; /* file size */
|
|
uint8_t *buf; /* file content */
|
|
|
|
|
|
/* 1st block description */
|
|
uint32_t idx,checksum,bs_multiplier,firmware_sz;
|
|
uint32_t unknown_4_1; uint16_t unknown_1, unknown_2;
|
|
uint32_t unknown_4_2,unknown_4_3;
|
|
|
|
static void *xmalloc(size_t s) /* malloc helper */
|
|
{
|
|
void * r = malloc(s);
|
|
if(!r) bugp("malloc");
|
|
return r;
|
|
}
|
|
|
|
/* checksums the firmware (the firmware header contains the verification) */
|
|
static uint32_t do_checksum(void)
|
|
{
|
|
uint32_t c = 0;
|
|
|
|
size_t i = 0x400/4;
|
|
while(i<(0x400+firmware_sz)/4)
|
|
c += ((uint32_t*)buf)[i++];
|
|
|
|
return c;
|
|
}
|
|
|
|
/* verify the firmware header */
|
|
static void check(void)
|
|
{
|
|
uint32_t checksum2;
|
|
|
|
assert(sz >= 0x400 && sz % 0x200 == 0);
|
|
|
|
size_t i;
|
|
checksum2 = 0;
|
|
for(i=0;i<sz/4-1;i++)
|
|
checksum2 += ((uint32_t*)buf)[i];
|
|
|
|
uint32_t last_word = get32le(sz - 4);
|
|
|
|
switch(last_word)
|
|
{
|
|
case 0: /* no whole file checksum */
|
|
break;
|
|
case 0xefbeadde: /* no whole file checksum */
|
|
break;
|
|
default: /* verify whole file checksum */
|
|
assert(last_word == checksum2);
|
|
}
|
|
|
|
idx = get32le(0);
|
|
unsigned int shift = (get32le(4) == 0x0000f000) ? 4 : 0;
|
|
checksum = get32le(4 + shift);
|
|
bs_multiplier = get32le(8 + shift);
|
|
firmware_sz = get32le(0xc + shift);
|
|
assert(bs_multiplier << 9 == PAD_TO_BOUNDARY(firmware_sz)); /* 0x200 * bs_multiplier */
|
|
|
|
unknown_4_1 = get32le(0x10 + shift);
|
|
unknown_1 = get16le(0x14 + shift);
|
|
unknown_2 = get16le(0x16 + shift);
|
|
unknown_4_2 = get32le(0x18 + shift);
|
|
unknown_4_3 = get32le(0x1c + shift);
|
|
|
|
color(GREEN);
|
|
printf("4 Index %d\n",idx);
|
|
assert(idx == 0);
|
|
color(GREEN);
|
|
printf("4 Firmware Checksum %x",checksum);
|
|
checksum2=do_checksum();
|
|
color(GREEN);
|
|
printf(" (%x)\n",checksum2);
|
|
assert(checksum == checksum2);
|
|
color(GREEN);
|
|
printf("4 Block Size Multiplier %x\n",bs_multiplier);
|
|
color(GREEN);
|
|
printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz);
|
|
|
|
color(GREEN);
|
|
printf("4 Unknown (should be 3) %x\n",unknown_4_1);
|
|
assert(unknown_4_1 == 3);
|
|
|
|
/* variable */
|
|
color(GREEN);
|
|
printf("1 Unknown %x\n",unknown_1);
|
|
|
|
color(GREEN);
|
|
printf("2 Unknown (should be 0) %x\n",unknown_2);
|
|
assert(unknown_2 == 0);
|
|
|
|
color(GREEN);
|
|
printf("4 Unknown (should be 40) %x\n",unknown_4_2);
|
|
assert(unknown_4_2 == 0x40 );
|
|
|
|
color(GREEN);
|
|
printf("4 Unknown (should be 1) %x\n",unknown_4_3);
|
|
assert(unknown_4_3 == 1);
|
|
|
|
/* the 2nd block is identical, except that the 1st byte has been incremented */
|
|
assert(buf[0x0]==0&&buf[0x200]==1);
|
|
assert(!memcmp(&buf[1],&buf[0x201],0x1FF - shift));
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
#if 0
|
|
FW_HEADER,
|
|
FW,
|
|
#endif
|
|
LIB,
|
|
PAD,
|
|
HEADER,
|
|
UNKNOWN
|
|
} type;
|
|
|
|
static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0;
|
|
|
|
static void show_lib(size_t off)
|
|
{
|
|
/* first word: char* */
|
|
uint32_t start = get32le(off+4);
|
|
uint32_t stop = get32le(off+8);
|
|
|
|
uint32_t size = get32le(off+0xc);
|
|
|
|
#if 0 /* library block hacking */
|
|
/* assert(stop > start); */
|
|
|
|
/* assert(stop - start == size); */
|
|
|
|
if(stop - start != size)
|
|
{
|
|
color(RED);
|
|
printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
|
|
stop, start, stop - start, size);
|
|
}
|
|
|
|
color(RED);
|
|
printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size);
|
|
|
|
uint32_t first = get32le(off+0x10); /* ? */
|
|
printf("? = 0x%.8x , ",first);
|
|
#endif
|
|
|
|
uint32_t funcs = get32le(off+0x14); /* nmbr of functions */
|
|
color(YELLOW);
|
|
printf("\t%d funcs",funcs);
|
|
|
|
unsigned int i;
|
|
for(i=0;i<funcs;i++)
|
|
{
|
|
uint32_t fptr = get32le(off+0x18+i*4);
|
|
if(!fptr)
|
|
{
|
|
assert(funcs==1); /* if 1 function is exported, it's empty */
|
|
}
|
|
else
|
|
{
|
|
assert(fptr - start < 0x0000ffff);
|
|
/* printf("0x%.4x ",fptr); */
|
|
}
|
|
}
|
|
|
|
color(BLUE);
|
|
printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start);
|
|
|
|
char name[12+1];
|
|
memcpy(name,&buf[off+get32le(off)],12);
|
|
name[12] = '\0';
|
|
|
|
FILE *out = fopen(name,"w");
|
|
if(!out)
|
|
bug("library block");
|
|
|
|
if(fwrite(&buf[off],size,1,out)!=1)
|
|
bug();
|
|
|
|
fclose(out);
|
|
}
|
|
|
|
static int unknown = 0;
|
|
static int padding = 0;
|
|
static void print_block(size_t off, type t)
|
|
{
|
|
/* reset counters if needed */
|
|
if(t != UNKNOWN && unknown)
|
|
{ /* print only the number of following blocks */
|
|
color(GREY);
|
|
printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200);
|
|
unknown = 0;
|
|
}
|
|
else if(t != PAD && padding)
|
|
{ /* same */
|
|
color(GREY);
|
|
printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200);
|
|
padding = 0;
|
|
}
|
|
|
|
if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */
|
|
{
|
|
color(GREEN);
|
|
printf("0x%.6x\t", (unsigned int)off);
|
|
color(OFF);
|
|
}
|
|
|
|
switch(t)
|
|
{
|
|
size_t s;
|
|
FILE *f;
|
|
char filename[8+4]; /* unknown\0 , 10K max */
|
|
#if 0
|
|
case FW_HEADER:
|
|
printf("firmware header 0x%x\n",off);
|
|
break;
|
|
case FW:
|
|
printf("firmware block 0x%x\n",off);
|
|
break;
|
|
#endif
|
|
case LIB:
|
|
s = get32le(off+12);
|
|
color(RED);
|
|
printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
|
|
(unsigned int)s, (unsigned int)(off+s),
|
|
&buf[off+get32le(off)]);
|
|
show_lib(off);
|
|
n_libs++;
|
|
break;
|
|
case PAD:
|
|
if(buf[off] == 0xff)
|
|
n_pads_ff++;
|
|
else
|
|
n_pads_deadbeef++;
|
|
padding++;
|
|
break;
|
|
case UNKNOWN:
|
|
unknown++;
|
|
n_unkn++;
|
|
#if 0 /* do not dump unknown blocks */
|
|
snprintf(filename, sizeof(filename), "unknown%d", n_unkn);
|
|
f = fopen(filename, "w");
|
|
if(f)
|
|
{
|
|
if( fwrite(buf+off, 0x200, 1, f) != 1 )
|
|
bugp("unknown block");
|
|
fclose(f);
|
|
}
|
|
else
|
|
bugp("unknown block");
|
|
#endif
|
|
break;
|
|
case HEADER:
|
|
color(YELLOW);
|
|
printf("header block 0x%.6x\t->\t0x%.6x\n",
|
|
PAD_TO_BOUNDARY(get32le(off)),
|
|
(unsigned int)PAD_TO_BOUNDARY(off+get32le(off)));
|
|
snprintf(filename, sizeof(filename), "header%d", n_headers++);
|
|
f = fopen(filename,"w");
|
|
if(!f)
|
|
bug("header");
|
|
|
|
if(fwrite(&buf[off],get32le(off),1,f)!=1)
|
|
bug();
|
|
|
|
fclose(f);
|
|
|
|
break;
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
if(t != PAD && t != UNKNOWN)
|
|
printf("\n");
|
|
}
|
|
|
|
static size_t verify_block(size_t off)
|
|
{
|
|
assert(!(off%0x200));
|
|
assert(off+0x200 < sz);
|
|
|
|
size_t s = 0x200;
|
|
type t = UNKNOWN;
|
|
|
|
size_t offset_str = get32le(off);
|
|
if(get32le(off) == 0xefbeadde )
|
|
{
|
|
#if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
|
|
unsigned int i;
|
|
for(i=0;i<s;i+=4)
|
|
assert(get32le(off+i) == 0xefbeadde);
|
|
#endif
|
|
t = PAD;
|
|
}
|
|
else if( *(uint32_t*)(&buf[off]) == 0xffffffff)
|
|
{
|
|
unsigned int i;
|
|
for(i=0;i<s;i++)
|
|
assert(buf[off+i] == 0xff);
|
|
t = PAD;
|
|
}
|
|
else if(off+offset_str+12<sz) /* XXX: we should check that the address at which
|
|
* the string is located is included in this
|
|
* library block's size, but we only know the
|
|
* block's size after we confirmed that this is
|
|
* a library block (by looking at the 11 chars
|
|
* ASCII string). */
|
|
{
|
|
short int ok = 1;
|
|
unsigned int i;
|
|
for(i=0;i<11;i++)
|
|
if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i])
|
|
ok = 0;
|
|
if(buf[off+offset_str+11])
|
|
ok = 0;
|
|
if(ok) /* library block */
|
|
{
|
|
t = LIB;
|
|
s = get32le(off+12);
|
|
}
|
|
else
|
|
t = UNKNOWN;
|
|
}
|
|
else
|
|
t = UNKNOWN;
|
|
|
|
if(t==UNKNOWN)
|
|
{
|
|
if(!strncmp((char*)buf+off+8,"HEADER",6))
|
|
{
|
|
s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */
|
|
t = HEADER;
|
|
}
|
|
}
|
|
|
|
print_block(off,t);
|
|
|
|
return PAD_TO_BOUNDARY(s);
|
|
}
|
|
|
|
static void extract(void)
|
|
{
|
|
FILE *out = fopen("firmware","w");
|
|
if(!out)
|
|
bug("firmware");
|
|
|
|
if(fwrite(&buf[0x400],firmware_sz,1,out)!=1)
|
|
bug("firmare writing");
|
|
fclose(out);
|
|
|
|
off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz);
|
|
unsigned int n = 0;
|
|
|
|
printf("\n");
|
|
color(RED);
|
|
printf("Extracting\n\n");
|
|
|
|
while((unsigned int)(off+0x200)<sz)
|
|
{
|
|
/* look at the next 0x200 bytes if we can recognize a block type */
|
|
off += verify_block(off); /* then skip its real size */
|
|
n++; /* and look at the next block ;) */
|
|
}
|
|
|
|
/* statistics */
|
|
printf("\n");
|
|
color(RED);
|
|
printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn);
|
|
color(BLUE);
|
|
printf("\t%d\tlibs\n",n_libs);
|
|
color(GREY);
|
|
printf("\t%d\tpads ff\n",n_pads_ff);
|
|
color(GREY);
|
|
printf("\t%d\tpads deadbeef\n",n_pads_deadbeef);
|
|
color(GREEN);
|
|
printf("\t%d\theaders\n",n_headers);
|
|
}
|
|
|
|
int main(int argc, const char **argv)
|
|
{
|
|
int fd;
|
|
struct stat st;
|
|
if(argc != 2)
|
|
bug("Usage: %s <firmware>\n",*argv);
|
|
|
|
if( (fd = open(argv[1],O_RDONLY)) == -1 )
|
|
bugp("opening firmware failed");
|
|
|
|
if(fstat(fd,&st) == -1)
|
|
bugp("firmware stat() failed");
|
|
sz = st.st_size;
|
|
|
|
buf=xmalloc(sz);
|
|
if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
|
|
bugp("reading firmware");
|
|
|
|
close(fd);
|
|
|
|
check(); /* verify header and checksums */
|
|
extract(); /* split in blocks */
|
|
|
|
free(buf);
|
|
return 0;
|
|
}
|