#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static char *file_start; #pragma pack(1) typedef signed char s8; typedef signed short s16; typedef signed int s32; typedef signed long long s64; typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; #if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) #define le32_to_cpu(x) (\ (((x)>>24)&0xff)\ |\ (((x)>>8)&0xff00)\ |\ (((x)<<8)&0xff0000)\ |\ (((x)<<24)&0xff000000)\ ) #define le16_to_cpu(x) ( (((x)>>8)&0xff) | (((x)<<8)&0xff00) ) #else #define le32_to_cpu(x) (x) #define le16_to_cpu(x) (x) #endif #define cpu_to_le32(x) le32_to_cpu(x) #define cpu_to_le16(x) le16_to_cpu(x) ////////////////// static void hexdump(char *prefix, char *data, int size){ if(size > 640) size = 640; while(size){ int amt = (size>16) ? 16 : size; char samp[20]; char hex[80]; samp[amt]='\0'; hex[0] = '\0'; memcpy(samp,data,amt); int i = 0; char *hp = hex; while(i\n",prefix,hex,samp); data += amt; size -= amt; } } ///////////////////////////////////// static unsigned crctable[256]; #define POLYNOMIAL 0x04C11DB7 static void init_crc(void){ unsigned dividend = 0; do{ unsigned remainder = dividend << 24; unsigned bit = 0; do{ if(remainder & 0x80000000){ remainder <<= 1; remainder ^= POLYNOMIAL; }else{ remainder <<= 1; } }while(++bit<8); crctable[dividend++] = remainder; }while(dividend<256); } // for Marvell, pass 0 as the initial remainder static unsigned do_crc(unsigned remainder, const unsigned char *p, unsigned n){ unsigned i = 0; while(i < n){ unsigned char data = *p ^ (remainder >> 24); remainder = crctable[data] ^ (remainder << 8); p++; i++; } return remainder; } /////////////////////////////////////// #define need(size,msg)({ \ if(mapsize<(int)(size)){ \ /*fprintf(stdout,"not enough bytes, have %d of %d for %s\n",(int)mapsize,(int)(size),msg);*/ \ return orig; \ } \ }) //////////////////////////////////// /** fwheader */ struct fwheader { u32 dnldcmd; // 1, except 4 for the 0-sized block at the end u32 baseaddr; // within 64 k following 0 or 0xc0000000 u32 datalength; // The driver accepts 0 to 600. We see 512, plus short blocks at the end of a segment/section. u32 CRC; }; // for sending to the chip, an extra u32 seq num goes here (before data) #define FW_HAS_DATA_TO_RECV 0x00000001 #define FW_HAS_LAST_BLOCK 0x00000004 /////////////////////////////////////// static char *spew(char *map, ssize_t mapsize){ char *orig = map; for(;;){ if(!mapsize){ fprintf(stdout,"ran out at file offset %zd 0x%08zx\n",map-file_start,map-file_start); return orig; } if(mapsize<0){ fprintf(stdout,"INTERNAL ERROR NEGATIVE REMAINDER %zd 0x%08zx\n",map-file_start,map-file_start); return orig; } struct fwheader fwheader; need(sizeof fwheader, "fwheader"); memcpy(&fwheader,map,sizeof fwheader); if(map==orig){ if(le32_to_cpu(fwheader.dnldcmd)!=FW_HAS_DATA_TO_RECV || le32_to_cpu(fwheader.datalength)>600 || le32_to_cpu(fwheader.datalength)<42 || le32_to_cpu(fwheader.baseaddr)>0xc0027fff) return orig; } map += sizeof fwheader; mapsize -= sizeof fwheader; fprintf(stdout, "%08x cmd:%08x addr:%08x len:%08x crc:%08x\n", (unsigned)(orig-file_start), le32_to_cpu(fwheader.dnldcmd), le32_to_cpu(fwheader.baseaddr), le32_to_cpu(fwheader.datalength), le32_to_cpu(fwheader.CRC) ); if(do_crc(0,(unsigned char*)&fwheader,16)){ fprintf(stdout,"bad header CRC\n"); return orig; } if(le32_to_cpu(fwheader.dnldcmd)==FW_HAS_LAST_BLOCK && fwheader.datalength==0){ fprintf(stdout,"Got one! Size 0x%08x.\n", (unsigned)(map-orig)); return map; } if(le32_to_cpu(fwheader.dnldcmd)!=FW_HAS_DATA_TO_RECV || le32_to_cpu(fwheader.datalength) > 600){ fprintf(stdout,"oh crap\n"); hexdump("x ",orig,mapsize+sizeof fwheader); return orig; } need(le32_to_cpu(fwheader.datalength), "data"); int len = le32_to_cpu(fwheader.datalength) - 4; int addr = le32_to_cpu(fwheader.baseaddr) & 0x0003ffff; int past = addr + len; if(past>0x40000) return orig; if(do_crc(0,(unsigned char*)map,len+4)){ fprintf(stdout,"bad body CRC\n"); return orig; } switch(le32_to_cpu(fwheader.baseaddr) & 0xfffc0000u){ case 0xc0000000: case 0x00000000: case 0x04000000: break; default: fprintf(stdout,"le32_to_cpu(fwheader.baseaddr) is 0x%08x\n",le32_to_cpu(fwheader.baseaddr)); return orig; } // hexdump("data ",map,le32_to_cpu(fwheader.datalength)); map += le32_to_cpu(fwheader.datalength); mapsize -= le32_to_cpu(fwheader.datalength); // hexdump("junk ",map,mapsize); } } static void mapit(const char *restrict const name){ int fd = open(name, O_RDONLY); if(fd<3){ perror("open"); exit(88); } struct stat sbuf; fstat(fd,&sbuf); char *map; map = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); file_start = map; if(!map || map==(char*)-1){ fprintf(stdout,"%d is a bad size or mapping failed\n",(int)sbuf.st_size); exit(3); } int mapsize = sbuf.st_size; // fprintf(stdout,"mapped %6d bytes\n",mapsize); while(mapsize >= 24){ char *end = spew(map,mapsize); unsigned sz = end-map; if(sz >= 24){ char path[64]; snprintf(path,sizeof path,"%.11s-%08x.bin",name,(unsigned)(map-file_start)); FILE *fp = fopen(path,"w"); fwrite(map,sz,1,fp); fclose(fp); }else{ sz = 1; } map += sz; mapsize -= sz; } } int main(int argc, char *argv[]){ init_crc(); // fprintf(stdout,"filename %s\n",argv[1]); mapit(argv[1]); return 0; }