fix r26953: use physical address for DMA buffer, also for IRAM
[kugel-rb.git] / utils / AMS / hacking / amsinfo.c
blob433333ecca9f8d97f18b59d449ef88157ae2357d
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; uint16_t unknown_1, 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 /* checksums the firmware (the firmware header contains the verification) */
80 static uint32_t do_checksum(void)
82 uint32_t c = 0;
84 size_t i = 0x400/4;
85 while(i<(0x400+firmware_sz)/4)
86 c += ((uint32_t*)buf)[i++];
88 return c;
91 /* verify the firmware header */
92 static void check(void)
94 uint32_t checksum2;
96 assert(sz >= 0x400 && sz % 0x200 == 0);
98 size_t i;
99 checksum2 = 0;
100 for(i=0;i<sz/4-1;i++)
101 checksum2 += ((uint32_t*)buf)[i];
103 uint32_t last_word = get32le(sz - 4);
105 switch(last_word)
107 case 0: /* no whole file checksum */
108 break;
109 case 0xefbeadde: /* no whole file checksum */
110 break;
111 default: /* verify whole file checksum */
112 assert(last_word == checksum2);
115 idx = get32le(0);
116 unsigned int shift = (get32le(4) == 0x0000f000) ? 4 : 0;
117 checksum = get32le(4 + shift);
118 bs_multiplier = get32le(8 + shift);
119 firmware_sz = get32le(0xc + shift);
120 assert(bs_multiplier << 9 == PAD_TO_BOUNDARY(firmware_sz)); /* 0x200 * bs_multiplier */
122 unknown_4_1 = get32le(0x10 + shift);
123 unknown_1 = get16le(0x14 + shift);
124 unknown_2 = get16le(0x16 + shift);
125 unknown_4_2 = get32le(0x18 + shift);
126 unknown_4_3 = get32le(0x1c + shift);
128 color(GREEN);
129 printf("4 Index %d\n",idx);
130 assert(idx == 0);
131 color(GREEN);
132 printf("4 Firmware Checksum %x",checksum);
133 checksum2=do_checksum();
134 color(GREEN);
135 printf(" (%x)\n",checksum2);
136 assert(checksum == checksum2);
137 color(GREEN);
138 printf("4 Block Size Multiplier %x\n",bs_multiplier);
139 color(GREEN);
140 printf("4 Firmware block size %x (%d)\n",firmware_sz,firmware_sz);
142 color(GREEN);
143 printf("4 Unknown (should be 3) %x\n",unknown_4_1);
144 assert(unknown_4_1 == 3);
146 /* variable */
147 color(GREEN);
148 printf("1 Unknown %x\n",unknown_1);
150 color(GREEN);
151 printf("2 Unknown (should be 0) %x\n",unknown_2);
152 assert(unknown_2 == 0);
154 color(GREEN);
155 printf("4 Unknown (should be 40) %x\n",unknown_4_2);
156 assert(unknown_4_2 == 0x40 );
158 color(GREEN);
159 printf("4 Unknown (should be 1) %x\n",unknown_4_3);
160 assert(unknown_4_3 == 1);
162 /* the 2nd block is identical, except that the 1st byte has been incremented */
163 assert(buf[0x0]==0&&buf[0x200]==1);
164 assert(!memcmp(&buf[1],&buf[0x201],0x1FF - shift));
167 typedef enum
169 #if 0
170 FW_HEADER,
172 #endif
173 LIB,
174 PAD,
175 HEADER,
176 UNKNOWN
177 } type;
179 static unsigned int n_libs = 0, n_pads_ff = 0, n_pads_deadbeef = 0, n_unkn = 0, n_headers = 0;
181 static void show_lib(size_t off)
183 /* first word: char* */
184 uint32_t start = get32le(off+4);
185 uint32_t stop = get32le(off+8);
187 uint32_t size = get32le(off+0xc);
189 #if 0 /* library block hacking */
190 /* assert(stop > start); */
192 /* assert(stop - start == size); */
194 if(stop - start != size)
196 color(RED);
197 printf("STOP - START != SIZE || 0x%.8x - 0x%.8x == 0x%.8x != 0x%.8x\n",
198 stop, start, stop - start, size);
201 color(RED);
202 printf("0x%.8x -> 0x%.8x SIZE 0x%.6x\n", start, stop, size);
204 uint32_t first = get32le(off+0x10); /* ? */
205 printf("? = 0x%.8x , ",first);
206 #endif
208 uint32_t funcs = get32le(off+0x14); /* nmbr of functions */
209 color(YELLOW);
210 printf("\t%d funcs",funcs);
212 unsigned int i;
213 for(i=0;i<funcs;i++)
215 uint32_t fptr = get32le(off+0x18+i*4);
216 if(!fptr)
218 assert(funcs==1); /* if 1 function is exported, it's empty */
220 else
222 assert(fptr - start < 0x0000ffff);
223 /* printf("0x%.4x ",fptr); */
227 color(BLUE);
228 printf("\tBASE 0x%.8x (code + 0x%x) END 0x%.8x : SIZE 0x%.8x\n",start, 0x18 + i*4, stop, stop - start);
230 char name[12+1];
231 memcpy(name,&buf[off+get32le(off)],12);
232 name[12] = '\0';
234 FILE *out = fopen(name,"w");
235 if(!out)
236 bug("library block");
238 if(fwrite(&buf[off],size,1,out)!=1)
239 bug();
241 fclose(out);
244 static int unknown = 0;
245 static int padding = 0;
246 static void print_block(size_t off, type t)
248 /* reset counters if needed */
249 if(t != UNKNOWN && unknown)
250 { /* print only the number of following blocks */
251 color(GREY);
252 printf("%d unknown blocks (0x%.6x bytes)\n",unknown,unknown*0x200);
253 unknown = 0;
255 else if(t != PAD && padding)
256 { /* same */
257 color(GREY);
258 printf("%d padding blocks (0x%.6x bytes)\n",padding,padding*0x200);
259 padding = 0;
262 if(t != UNKNOWN && t != PAD) /* for other block types, always print the offset */
264 color(GREEN);
265 printf("0x%.6x\t", (unsigned int)off);
266 color(OFF);
269 switch(t)
271 size_t s;
272 FILE *f;
273 char filename[8+4]; /* unknown\0 , 10K max */
274 #if 0
275 case FW_HEADER:
276 printf("firmware header 0x%x\n",off);
277 break;
278 case FW:
279 printf("firmware block 0x%x\n",off);
280 break;
281 #endif
282 case LIB:
283 s = get32le(off+12);
284 color(RED);
285 printf("library block 0x%.6x\t->\t0x%.6x\t\"%s\"\n",
286 (unsigned int)s, (unsigned int)(off+s),
287 &buf[off+get32le(off)]);
288 show_lib(off);
289 n_libs++;
290 break;
291 case PAD:
292 if(buf[off] == 0xff)
293 n_pads_ff++;
294 else
295 n_pads_deadbeef++;
296 padding++;
297 break;
298 case UNKNOWN:
299 unknown++;
300 n_unkn++;
301 #if 0 /* do not dump unknown blocks */
302 snprintf(filename, sizeof(filename), "unknown%d", n_unkn);
303 f = fopen(filename, "w");
304 if(f)
306 if( fwrite(buf+off, 0x200, 1, f) != 1 )
307 bugp("unknown block");
308 fclose(f);
310 else
311 bugp("unknown block");
312 #endif
313 break;
314 case HEADER:
315 color(YELLOW);
316 printf("header block 0x%.6x\t->\t0x%.6x\n",
317 PAD_TO_BOUNDARY(get32le(off)),
318 (unsigned int)PAD_TO_BOUNDARY(off+get32le(off)));
319 snprintf(filename, sizeof(filename), "header%d", n_headers++);
320 f = fopen(filename,"w");
321 if(!f)
322 bug("header");
324 if(fwrite(&buf[off],get32le(off),1,f)!=1)
325 bug();
327 fclose(f);
329 break;
330 default:
331 abort();
334 if(t != PAD && t != UNKNOWN)
335 printf("\n");
338 static size_t verify_block(size_t off)
340 assert(!(off%0x200));
341 assert(off+0x200 < sz);
343 size_t s = 0x200;
344 type t = UNKNOWN;
346 size_t offset_str = get32le(off);
347 if(get32le(off) == 0xefbeadde )
349 #if 0 /* some blocks begin with 0xdeadbeef but aren't padded with that value */
350 unsigned int i;
351 for(i=0;i<s;i+=4)
352 assert(get32le(off+i) == 0xefbeadde);
353 #endif
354 t = PAD;
356 else if( *(uint32_t*)(&buf[off]) == 0xffffffff)
358 unsigned int i;
359 for(i=0;i<s;i++)
360 assert(buf[off+i] == 0xff);
361 t = PAD;
363 else if(off+offset_str+12<sz) /* XXX: we should check that the address at which
364 * the string is located is included in this
365 * library block's size, but we only know the
366 * block's size after we confirmed that this is
367 * a library block (by looking at the 11 chars
368 * ASCII string). */
370 short int ok = 1;
371 unsigned int i;
372 for(i=0;i<11;i++)
373 if(buf[off+offset_str+i] >> 7 || !buf[off+offset_str+i])
374 ok = 0;
375 if(buf[off+offset_str+11])
376 ok = 0;
377 if(ok) /* library block */
379 t = LIB;
380 s = get32le(off+12);
382 else
383 t = UNKNOWN;
385 else
386 t = UNKNOWN;
388 if(t==UNKNOWN)
390 if(!strncmp((char*)buf+off+8,"HEADER",6))
392 s = PAD_TO_BOUNDARY(get32le(off)); /* first 4 bytes le are the block size */
393 t = HEADER;
397 print_block(off,t);
399 return PAD_TO_BOUNDARY(s);
402 static void extract(void)
404 FILE *out = fopen("firmware","w");
405 if(!out)
406 bug("firmware");
408 if(fwrite(&buf[0x400],firmware_sz,1,out)!=1)
409 bug("firmare writing");
410 fclose(out);
412 off_t off = PAD_TO_BOUNDARY(0x400 + firmware_sz);
413 unsigned int n = 0;
415 printf("\n");
416 color(RED);
417 printf("Extracting\n\n");
419 while((unsigned int)(off+0x200)<sz)
421 /* look at the next 0x200 bytes if we can recognize a block type */
422 off += verify_block(off); /* then skip its real size */
423 n++; /* and look at the next block ;) */
426 /* statistics */
427 printf("\n");
428 color(RED);
429 printf("TOTAL\t%d\tblocks (%d unknown)\n",n,n_unkn);
430 color(BLUE);
431 printf("\t%d\tlibs\n",n_libs);
432 color(GREY);
433 printf("\t%d\tpads ff\n",n_pads_ff);
434 color(GREY);
435 printf("\t%d\tpads deadbeef\n",n_pads_deadbeef);
436 color(GREEN);
437 printf("\t%d\theaders\n",n_headers);
440 int main(int argc, const char **argv)
442 int fd;
443 struct stat st;
444 if(argc != 2)
445 bug("Usage: %s <firmware>\n",*argv);
447 if( (fd = open(argv[1],O_RDONLY)) == -1 )
448 bugp("opening firmware failed");
450 if(fstat(fd,&st) == -1)
451 bugp("firmware stat() failed");
452 sz = st.st_size;
454 buf=xmalloc(sz);
455 if(read(fd,buf,sz)!=(ssize_t)sz) /* load the whole file into memory */
456 bugp("reading firmware");
458 close(fd);
460 check(); /* verify header and checksums */
461 extract(); /* split in blocks */
463 free(buf);
464 return 0;