fuzev2: prevent button light flickering when accessing µSD
[kugel-rb.git] / utils / AMS / hacking / amsinfo.c
blobff92175e643a24a92d9492ab7c992bb8bfd433c4
1 /*
2 * Copyright © 2008 Rafaël Carré <rafael.carre@gmail.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
20 #define _ISOC99_SOURCE /* snprintf() */
21 #include <stdio.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <stdlib.h>
28 #include <inttypes.h>
29 #include <string.h>
31 #if 1 /* ANSI colors */
33 # define color(a) printf("%s",a)
34 char OFF[] = { 0x1b, 0x5b, 0x31, 0x3b, '0', '0', 0x6d, '\0' };
36 char GREY[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '0', 0x6d, '\0' };
37 char RED[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '1', 0x6d, '\0' };
38 char GREEN[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '2', 0x6d, '\0' };
39 char YELLOW[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '3', 0x6d, '\0' };
40 char BLUE[] = { 0x1b, 0x5b, 0x31, 0x3b, '3', '4', 0x6d, '\0' };
42 #else
43 /* disable colors */
44 # define color(a)
45 #endif
47 #define bug(...) do { fprintf(stderr,"ERROR: "__VA_ARGS__); exit(1); } while(0)
48 #define bugp(a) do { perror("ERROR: "a); exit(1); } while(0)
50 /* byte swapping */
51 #define get32le(a) ((uint32_t) \
52 ( buf[a+3] << 24 | buf[a+2] << 16 | buf[a+1] << 8 | buf[a] ))
53 #define get16le(a) ((uint16_t)( buf[a+1] << 8 | buf[a] ))
55 /* all blocks are sized as a multiple of 0x1ff */
56 #define PAD_TO_BOUNDARY(x) (((x) + 0x1ff) & ~0x1ff)
58 /* If you find a firmware that breaks the known format ^^ */
59 #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)
61 /* globals */
63 size_t sz; /* file size */
64 uint8_t *buf; /* file content */
67 /* 1st block description */
68 uint32_t idx,checksum,bs_multiplier,firmware_sz;
69 uint32_t unknown_4_1; uint8_t unknown_1,id; uint16_t unknown_2;
70 uint32_t unknown_4_2,unknown_4_3;
72 static void *xmalloc(size_t s) /* malloc helper */
74 void * r = malloc(s);
75 if(!r) bugp("malloc");
76 return r;
79 /* known models */
80 static const char * model(uint8_t id)
82 switch(id)
84 case 0x1E: return "FUZE"; break;
85 case 0x22: return "CLIP"; break;
86 case 0x23: return "C200"; break;
87 case 0x24: return "E200"; break;
88 case 0x25: return "M200"; break;
89 case 0x27: return "CLV2"; break;
90 case 0x28: return "CLI+"; break;
91 case 0x70:
92 case 0x6d: return "FUZ2"; break;
93 default:
94 printf("Unknown ID 0x%x\n", id);
96 assert(id == 0x1E || (id >= 0x22 && id <= 0x28));
97 return "UNKNOWN!";
101 /* checksums the firmware (the firmware header contains the verification) */
102 static uint32_t do_checksum(void)
104 uint32_t c = 0;
106 size_t i = 0x400/4;
107 while(i<(0x400+firmware_sz)/4)
108 c += ((uint32_t*)buf)[i++];
110 return c;
113 /* verify the firmware header */
114 static void check(void)
116 uint32_t checksum2;
118 assert(sz >= 0x400 && sz % 0x200 == 0);
120 size_t i;
121 checksum2 = 0;
122 for(i=0;i<sz/4-1;i++)
123 checksum2 += ((uint32_t*)buf)[i];
125 uint32_t last_word = get32le(sz - 4);
127 switch(last_word)
129 case 0: /* no whole file checksum */
130 break;
131 case 0xefbeadde: /* no whole file checksum */
132 break;
133 default: /* verify whole file checksum */
134 assert(last_word == checksum2);
137 idx = get32le(0);
138 unsigned int shift = (get32le(4) == 0x0000f000) ? 4 : 0;
139 checksum = get32le(4 + shift);
140 bs_multiplier = get32le(8 + shift);
141 firmware_sz = get32le(0xc + shift);
142 assert(bs_multiplier << 9 == PAD_TO_BOUNDARY(firmware_sz)); /* 0x200 * bs_multiplier */
144 unknown_4_1 = get32le(0x10 + shift);
145 unknown_1 = buf[0x14 + shift];
146 id = buf[0x15 + shift];
147 unknown_2 = get16le(0x16 + shift);
148 unknown_4_2 = get32le(0x18 + shift);
149 unknown_4_3 = get32le(0x1c + shift);
151 color(GREEN);
152 printf("4 Index %d\n",idx);
153 assert(idx == 0);
154 color(GREEN);
155 printf("4 Firmware Checksum %x",checksum);
156 checksum2=do_checksum();
157 color(GREEN);
158 printf(" (%x)\n",checksum2);
159 assert(checksum == checksum2);
160 color(GREEN);
161 printf("4 Block Size Multiplier %x\n",bs_multiplier);
162 color(GREEN);
163 printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz);
165 color(GREEN);
166 printf("4 Unknown (should be 3) %x\n",unknown_4_1);
167 assert(unknown_4_1 == 3);
169 /* variable */
170 color(GREEN);
171 printf("1 Unknown %x\n",unknown_1);
173 color(GREEN);
174 printf("1 Model ID %x (%s)\n",id,model(id));
176 color(GREEN);
177 printf("2 Unknown (should be 0) %x\n",unknown_2);
178 assert(unknown_2 == 0);
180 color(GREEN);
181 printf("4 Unknown (should be 40) %x\n",unknown_4_2);
182 assert(unknown_4_2 == 0x40 );
184 color(GREEN);
185 printf("4 Unknown (should be 1) %x\n",unknown_4_3);
186 assert(unknown_4_3 == 1);
188 /* rest of the block is padded with 0xff */
189 for(i=0x20 + shift;i<0x200 - shift;i++)
190 assert(buf[i]==0xff /* normal case */ ||
191 ((id==0x1e||id==0x24) && ( /* Fuze or E200 */
192 (i>=0x3c && i<=0x3f && get32le(0x3c)==0x00005000)
193 )));
195 /* the 2nd block is identical, except that the 1st byte has been incremented */
196 assert(buf[0x0]==0&&buf[0x200]==1);
197 assert(!memcmp(&buf[1],&buf[0x201],0x1FF - shift));
200 typedef enum
202 #if 0
203 FW_HEADER,
205 #endif
206 LIB,
207 PAD,
208 HEADER,
209 UNKNOWN
210 } type;
212 static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0;
214 static void show_lib(size_t off)
216 /* first word: char* */
217 uint32_t start = get32le(off+4);
218 uint32_t stop = get32le(off+8);
220 uint32_t size = get32le(off+0xc);
222 #if 0 /* library block hacking */
223 /* assert(stop > start); */
225 /* assert(stop - start == size); */
227 if(stop - start != size)
229 color(RED);
230 printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
231 stop, start, stop - start, size);
234 color(RED);
235 printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size);
237 uint32_t first = get32le(off+0x10); /* ? */
238 printf("? = 0x%.8x , ",first);
239 #endif
241 uint32_t funcs = get32le(off+0x14); /* nmbr of functions */
242 color(YELLOW);
243 printf("\t%d funcs",funcs);
245 unsigned int i;
246 for(i=0;i<funcs;i++)
248 uint32_t fptr = get32le(off+0x18+i*4);
249 if(!fptr)
251 assert(funcs==1); /* if 1 function is exported, it's empty */
253 else
255 assert(fptr - start < 0x0000ffff);
256 /* printf("0x%.4x ",fptr); */
260 color(BLUE);
261 printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start);
263 char name[12+1];
264 memcpy(name,&buf[off+get32le(off)],12);
265 name[12] = '\0';
267 FILE *out = fopen(name,"w");
268 if(!out)
269 bug("library block");
271 if(fwrite(&buf[off],size,1,out)!=1)
272 bug();
274 fclose(out);
277 static int unknown = 0;
278 static int padding = 0;
279 static void print_block(size_t off, type t)
281 /* reset counters if needed */
282 if(t != UNKNOWN && unknown)
283 { /* print only the number of following blocks */
284 color(GREY);
285 printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200);
286 unknown = 0;
288 else if(t != PAD && padding)
289 { /* same */
290 color(GREY);
291 printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200);
292 padding = 0;
295 if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */
297 color(GREEN);
298 printf("0x%.6x\t", (unsigned int)off);
299 color(OFF);
302 switch(t)
304 size_t s;
305 FILE *f;
306 char filename[8+4]; /* unknown\0 , 10K max */
307 #if 0
308 case FW_HEADER:
309 printf("firmware header 0x%x\n",off);
310 break;
311 case FW:
312 printf("firmware block 0x%x\n",off);
313 break;
314 #endif
315 case LIB:
316 s = get32le(off+12);
317 color(RED);
318 printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
319 (unsigned int)s, (unsigned int)(off+s),
320 &buf[off+get32le(off)]);
321 show_lib(off);
322 n_libs++;
323 break;
324 case PAD:
325 if(buf[off] == 0xff)
326 n_pads_ff++;
327 else
328 n_pads_deadbeef++;
329 padding++;
330 break;
331 case UNKNOWN:
332 unknown++;
333 n_unkn++;
334 #if 0 /* do not dump unknown blocks */
335 snprintf(filename, sizeof(filename), "unknown%d", n_unkn);
336 f = fopen(filename, "w");
337 if(f)
339 if( fwrite(buf+off, 0x200, 1, f) != 1 )
340 bugp("unknown block");
341 fclose(f);
343 else
344 bugp("unknown block");
345 #endif
346 break;
347 case HEADER:
348 color(YELLOW);
349 printf("header block 0x%.6x\t->\t0x%.6x\n",
350 PAD_TO_BOUNDARY(get32le(off)),
351 (unsigned int)PAD_TO_BOUNDARY(off+get32le(off)));
352 snprintf(filename, sizeof(filename), "header%d", n_headers++);
353 f = fopen(filename,"w");
354 if(!f)
355 bug("header");
357 if(fwrite(&buf[off],get32le(off),1,f)!=1)
358 bug();
360 fclose(f);
362 break;
363 default:
364 abort();
367 if(t != PAD && t != UNKNOWN)
368 printf("\n");
371 static size_t verify_block(size_t off)
373 assert(!(off%0x200));
374 assert(off+0x200 < sz);
376 size_t s = 0x200;
377 type t = UNKNOWN;
379 size_t offset_str = get32le(off);
380 if(get32le(off) == 0xefbeadde )
382 #if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
383 unsigned int i;
384 for(i=0;i<s;i+=4)
385 assert(get32le(off+i) == 0xefbeadde);
386 #endif
387 t = PAD;
389 else if( *(uint32_t*)(&buf[off]) == 0xffffffff)
391 unsigned int i;
392 for(i=0;i<s;i++)
393 assert(buf[off+i] == 0xff);
394 t = PAD;
396 else if(off+offset_str+12<sz) /* XXX: we should check that the address at which
397 * the string is located is included in this
398 * library block's size, but we only know the
399 * block's size after we confirmed that this is
400 * a library block (by looking at the 11 chars
401 * ASCII string). */
403 short int ok = 1;
404 unsigned int i;
405 for(i=0;i<11;i++)
406 if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i])
407 ok = 0;
408 if(buf[off+offset_str+11])
409 ok = 0;
410 if(ok) /* library block */
412 t = LIB;
413 s = get32le(off+12);
415 else
416 t = UNKNOWN;
418 else
419 t = UNKNOWN;
421 if(t==UNKNOWN)
423 if(!strncmp((char*)buf+off+8,"HEADER",6))
425 s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */
426 t = HEADER;
430 print_block(off,t);
432 return PAD_TO_BOUNDARY(s);
435 static void extract(void)
437 FILE *out = fopen("firmware","w");
438 if(!out)
439 bug("firmware");
441 if(fwrite(&buf[0x400],firmware_sz,1,out)!=1)
442 bug("firmare writing");
443 fclose(out);
445 off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz);
446 unsigned int n = 0;
448 printf("\n");
449 color(RED);
450 printf("Extracting\n\n");
452 while((unsigned int)(off+0x200)<sz)
454 /* look at the next 0x200 bytes if we can recognize a block type */
455 off += verify_block(off); /* then skip its real size */
456 n++; /* and look at the next block ;) */
459 /* statistics */
460 printf("\n");
461 color(RED);
462 printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn);
463 color(BLUE);
464 printf("\t%d\tlibs\n",n_libs);
465 color(GREY);
466 printf("\t%d\tpads ff\n",n_pads_ff);
467 color(GREY);
468 printf("\t%d\tpads deadbeef\n",n_pads_deadbeef);
469 color(GREEN);
470 printf("\t%d\theaders\n",n_headers);
473 int main(int argc, const char **argv)
475 int fd;
476 struct stat st;
477 if(argc != 2)
478 bug("Usage: %s <firmware>\n",*argv);
480 if( (fd = open(argv[1],O_RDONLY)) == -1 )
481 bugp("opening firmware failed");
483 if(fstat(fd,&st) == -1)
484 bugp("firmware stat() failed");
485 sz = st.st_size;
487 buf=xmalloc(sz);
488 if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
489 bugp("reading firmware");
491 close(fd);
493 check(); /* verify header and checksums */
494 extract(); /* split in blocks */
496 free(buf);
497 return 0;