#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; #define swap32(x) ({\ unsigned _x = (x); \ (((_x)>>24)&0xff)\ |\ (((_x)>>8)&0xff00)\ |\ (((_x)<<8)&0xff0000)\ |\ (((_x)<<24)&0xff000000);\ }) #define swap16(x) ({unsigned _x = (x); (((_x)>>8)&0xff) | (((_x)<<8)&0xff00); }) #if defined(__BIG_ENDIAN__) || defined(_BIG_ENDIAN) #define le32_to_cpu(x) swap32(x) #define le16_to_cpu(x) swap16(x) #define be32_to_cpu(x) (x) #define be16_to_cpu(x) (x) #else #define le32_to_cpu(x) (x) #define le16_to_cpu(x) (x) #define be32_to_cpu(x) swap32(x) #define be16_to_cpu(x) swap16(x) #endif #define cpu_to_le32(x) le32_to_cpu(x) #define cpu_to_le16(x) le16_to_cpu(x) #define cpu_to_be32(x) be32_to_cpu(x) #define cpu_to_be16(x) be16_to_cpu(x) ///////////////////////////////////// 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(stderr,"not enough bytes, have %d of %d for %s\n",(int)mapsize,(int)(size),msg); \ return; \ } \ }) //////////////////////////////////// /** 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 //////////////////////////////////// #define HALF_NUM_BLOCKS ((64*1024+508)/508) /* max num blocks for 64 K */ static char bindata[2*HALF_NUM_BLOCKS*(16+508+4)+16]; static char *pastdata = bindata; static unsigned char *seg0; static unsigned char *segc; static unsigned seg0sz; static unsigned segcsz; static unsigned c_start; #define ARRAYSIZE(x) (sizeof(x)/sizeof((x)[0])) static void spew(unsigned char *p, unsigned n, unsigned addr){ if(!p || !n) return; char *where = pastdata; for(;;){ unsigned amt = n; if(amt>508) amt = 508; if(!amt) break; struct fwheader fwheader; fwheader.dnldcmd = cpu_to_le32(FW_HAS_DATA_TO_RECV); fwheader.baseaddr = cpu_to_le32(addr); fwheader.datalength = cpu_to_le32(amt+4); fwheader.CRC = 0; fwheader.CRC = cpu_to_be32(do_crc(0,(unsigned char*)&fwheader,12)); memcpy(where,&fwheader,16); where += 16; memcpy(where,p,amt); // we rely on static zero initialization of 4 bytes on the end unsigned crc = cpu_to_be32(do_crc(0,(unsigned char*)where,amt)); where += amt; p += amt; n -= amt; addr += amt; memcpy(where,&crc,4); where += 4; } pastdata = where; } static unsigned entrypoint; static int mkbin(void){ if(segcsz&3 || seg0sz&3){ fprintf(stderr,"data size causes misalignment 0x%08x 0x%08x\n",segcsz,seg0sz); exit(45); } spew(segc,segcsz,c_start); spew(seg0,seg0sz,0x00000000u); struct fwheader fwheader = {cpu_to_le32(FW_HAS_LAST_BLOCK),entrypoint,0,0}; fwheader.CRC = cpu_to_be32(do_crc(0,(unsigned char*)&fwheader,12)); memcpy(pastdata,&fwheader,16); pastdata += 16; return pastdata-bindata; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void parse_elf(char *map, ssize_t mapsize){ int i; Elf32_Ehdr ehdr; need(sizeof ehdr,"ELF header"); memcpy(&ehdr,map,sizeof ehdr); if(map[0]!='\177' || map[1]!='E' || map[2]!='L' || map[3]!='F' || map[4]!=ELFCLASS32 || map[5]!=ELFDATA2LSB || map[6]!=EV_CURRENT){ fprintf(stderr,"no ELF magic\n"); exit(84); } #define X16(x) (ehdr.x = le16_to_cpu(ehdr.x)) #define X32(x) (ehdr.x = le32_to_cpu(ehdr.x)) X16(e_type); X16(e_machine); X32(e_version); X32(e_entry); X32(e_phoff); X32(e_shoff); X32(e_flags); X16(e_ehsize); X16(e_phentsize); X16(e_phnum); X16(e_shentsize); X16(e_shnum); X16(e_shstrndx); #undef X16 #undef X32 if(ehdr.e_machine != EM_ARM){ fprintf(stderr,"arch 0x%04hx is not ARM\n",ehdr.e_machine); exit(44); } if(ehdr.e_version != EV_CURRENT){ fprintf(stderr,"ver 0x%08x is not current\n",ehdr.e_version); exit(50); } entrypoint = ehdr.e_entry; if(ehdr.e_ehsize != sizeof ehdr || ehdr.e_phentsize != sizeof(Elf32_Phdr) || ehdr.e_shentsize != sizeof(Elf32_Shdr)){ fprintf(stderr,"bad header size\n"); exit(52); } if(ehdr.e_phnum<2 && ehdr.e_shnum<2){ fprintf(stderr,"forget your data?\n"); exit(48); } if(ehdr.e_phoff || ehdr.e_phnum){ if(ehdr.e_phoff < sizeof ehdr || ehdr.e_phoff >= (unsigned)mapsize || ehdr.e_phnum > (mapsize-ehdr.e_phoff)/sizeof(Elf32_Phdr)){ fprintf(stderr,"program headers don't fit right\n"); exit(14); } } if(ehdr.e_shoff || ehdr.e_shnum){ if(ehdr.e_shoff < sizeof ehdr || ehdr.e_shoff >= (unsigned)mapsize || ehdr.e_shnum > (mapsize-ehdr.e_shoff)/sizeof(Elf32_Shdr)){ fprintf(stderr,"section headers don't fit right\n"); exit(15); } } /////// Look in the program headers unsigned phdr_seg0sz = 0; unsigned phdr_seg0 = 0; unsigned phdr_segcsz = 0; unsigned phdr_segc = 0; unsigned phdr_c_count = 0; unsigned phdr_c_start = 0; i = ehdr.e_phnum; while(i--){ Elf32_Phdr phdr; memcpy(&phdr, map+ehdr.e_phoff+i*sizeof(Elf32_Phdr), sizeof(Elf32_Phdr)); #define X16(x) (phdr.x = le16_to_cpu(phdr.x)) #define X32(x) (phdr.x = le32_to_cpu(phdr.x)) X32(p_type); X32(p_offset); X32(p_vaddr); X32(p_paddr); X32(p_filesz); X32(p_memsz); X32(p_flags); X32(p_align); #undef X16 #undef X32 // fprintf(stderr,"phdr %08x %08x %08x %08x %08x %08x %08x %08x\n",phdr.p_type, phdr.p_offset, phdr.p_vaddr, phdr.p_paddr, phdr.p_filesz,phdr.p_memsz, phdr.p_flags, phdr.p_align); if(phdr.p_type==PT_NULL || phdr.p_type==PT_NOTE) continue; if(phdr.p_type!=PT_LOAD){ fprintf(stderr,"bad phdr.p_type 0x%08x\n",phdr.p_type); exit(53); } if(phdr.p_offset=(unsigned)mapsize || phdr.p_filesz>=(unsigned)mapsize || phdr.p_memsz>=(unsigned)mapsize || phdr.p_filesz+phdr.p_offset>=(unsigned)mapsize || phdr.p_memsz0x00028000u || phdr.p_filesz>0x00028000u){ fprintf(stderr,"p_offset 0x%08x p_filesz 0x%08x p_memsz 0x%08x\n", phdr.p_offset,phdr.p_filesz,phdr.p_memsz); exit(16); } if(phdr.p_align>0x00010000u){ fprintf(stderr,"p_align 0x%08x\n",phdr.p_align); exit(17); } if(phdr.p_flags != (PF_R|PF_X) && phdr.p_flags != (PF_R|PF_W) && phdr.p_flags != (PF_R|PF_W|PF_X)){ fprintf(stderr,"p_flags 0x%08x\n",phdr.p_flags); exit(18); } if(phdr.p_paddr && phdr.p_paddr!=phdr.p_vaddr){ fprintf(stderr,"addr %08x != %08x\n",phdr.p_paddr,phdr.p_vaddr); exit(20); } if(phdr.p_vaddr && (phdr.p_vaddr<0xc0000000u || phdr.p_vaddr>0xc0027fffu)){ fprintf(stderr,"addr 0x%08x unknown\n",phdr.p_vaddr); exit(54); } switch(phdr.p_vaddr){ default: fprintf(stderr,"addr 0x%08x unknown\n",phdr.p_vaddr); exit(21); case 0x00000000u: if(phdr.p_memsz>0x10000 || phdr.p_flags!=(PF_R|PF_X) || phdr.p_filesz<4){ fprintf(stderr,"bad phdr for 0x00000000\n"); exit(19); } phdr_seg0 = phdr.p_offset; phdr_seg0sz = phdr.p_filesz; break; case 0x04000000u: if(phdr.p_memsz>0x2000 || phdr.p_flags!=(PF_R|PF_W) || phdr.p_filesz){ fprintf(stderr,"bad phdr for 0x04000000\n"); exit(24); } break; case 0x80000000u: if(phdr.p_memsz>0x10000 || phdr.p_flags!=(PF_R|PF_W) || phdr.p_filesz){ fprintf(stderr,"bad phdr for 0x80000000\n"); exit(25); } break; case 0x90000000u: if(phdr.p_memsz>0x10000 || phdr.p_flags!=(PF_R|PF_W) || phdr.p_filesz){ fprintf(stderr,"bad phdr for 0x90000000\n"); exit(55); } break; case 0xc0000000u ... 0xc0027fffu: if(phdr.p_memsz>=0x28000 || phdr.p_vaddr+phdr.p_memsz>=0xc0028000 || (phdr.p_flags&PF_R)!=PF_R){ fprintf(stderr,"bad phdr for 0x%08x\n", phdr.p_vaddr); exit(22); } if(phdr.p_filesz){ phdr_segc = phdr.p_offset; phdr_segcsz = phdr.p_filesz; phdr_c_count++; phdr_c_start = phdr.p_vaddr; } break; case 0xfffe0000u ... 0xffff0000u: if(phdr.p_memsz>0x20000 || phdr.p_flags!=(PF_R|PF_X) || phdr.p_filesz){ fprintf(stderr,"bad phdr for ROM area\n"); exit(56); } break; } } /////// Look in the section headers unsigned shdr_seg0sz = 0; unsigned shdr_seg0 = 0; unsigned shdr_segcsz = 0; unsigned shdr_segc = 0; unsigned shdr_c_count = 0; unsigned shdr_c_start = 0; i = ehdr.e_shnum; while(i--){ Elf32_Shdr shdr; memcpy(&shdr, map+ehdr.e_shoff+i*sizeof(Elf32_Shdr), sizeof(Elf32_Shdr)); #define X16(x) (shdr.x = le16_to_cpu(shdr.x)) #define X32(x) (shdr.x = le32_to_cpu(shdr.x)) X32(sh_name); X32(sh_type); X32(sh_flags); X32(sh_addr); X32(sh_offset); X32(sh_size); X32(sh_link); X32(sh_info); X32(sh_addralign); X32(sh_entsize); #undef X16 #undef X32 // fprintf(stderr,"shdr %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x\n", // shdr.sh_name, shdr.sh_type, shdr.sh_flags, shdr.sh_addr, shdr.sh_offset, // shdr.sh_size, shdr.sh_link, shdr.sh_info, shdr.sh_addralign, shdr.sh_entsize // ); switch(shdr.sh_type){ default: continue; case SHT_RELA: case SHT_REL: fprintf(stderr,"can not relocate!\n"); exit(26); case SHT_DYNAMIC: case SHT_SHLIB: case SHT_DYNSYM: fprintf(stderr,"can not dynamicly link!\n"); exit(27); case SHT_PROGBITS: case SHT_NOBITS: break; } if(shdr.sh_entsize || shdr.sh_info || shdr.sh_link || shdr.sh_addralign>0x10000){ fprintf(stderr,"bad shdr stuff\n"); exit(28); } if(shdr.sh_offset > (unsigned)mapsize){ fprintf(stderr,"bad sh_offset 0x%08x\n",shdr.sh_offset); exit(38); } switch(shdr.sh_addr){ default: fprintf(stderr,"addr 0x%08x unknown\n",shdr.sh_addr); exit(29); case 0x00000000u: if(!(shdr.sh_flags&SHF_ALLOC)) break; /* probably debug info and similar */ if(shdr.sh_type!=SHT_PROGBITS || shdr.sh_size>0x10000 || shdr.sh_size<4){ fprintf(stderr,"bad shdr for 0x00000000\n"); exit(30); } if((shdr.sh_flags&(SHF_ALLOC|SHF_EXECINSTR))!=(SHF_ALLOC|SHF_EXECINSTR)){ fprintf( stderr, "bad shdr for 0x00000000 SHF_ALLOC=%d SHF_EXECINSTR=%d SHF_WRITE=%d\n", !!(shdr.sh_flags&SHF_ALLOC), !!(shdr.sh_flags&SHF_EXECINSTR), !!(shdr.sh_flags&SHF_WRITE) ); exit(34); } if(shdr.sh_size + shdr.sh_offset > (unsigned)mapsize){ fprintf(stderr,"bad shdr for 0x00000000\n"); exit(37); } shdr_seg0 = shdr.sh_offset; shdr_seg0sz = shdr.sh_size; break; case 0x04000000u: if(shdr.sh_type!=SHT_NOBITS || shdr.sh_flags!=(SHF_ALLOC|SHF_WRITE) || shdr.sh_size!=0x2000){ fprintf(stderr,"bad shdr for 0x04000000\n"); exit(31); } break; case 0x80000000u: if(shdr.sh_type!=SHT_NOBITS || shdr.sh_size>0x10000 || shdr.sh_flags!=(SHF_ALLOC|SHF_WRITE)){ fprintf(stderr,"bad shdr for 0x80000000\n"); exit(32); } break; case 0x90000000u: if(shdr.sh_type!=SHT_NOBITS || shdr.sh_size>0x10000 || shdr.sh_flags!=(SHF_ALLOC|SHF_WRITE)){ fprintf(stderr,"bad shdr for 0x90000000\n"); exit(33); } break; case 0xc0000000u ... 0xc0027fffu: if(shdr.sh_size>0x28000 || shdr.sh_addr+shdr.sh_size>0xc0028000u){ fprintf(stderr,"bad shdr for 0xc00??000\n"); exit(35); } if(shdr.sh_type==SHT_NOBITS && shdr.sh_flags==(SHF_ALLOC|SHF_WRITE)) break; if(shdr.sh_type!=SHT_PROGBITS || (shdr.sh_flags&SHF_ALLOC)!=SHF_ALLOC){ fprintf(stderr,"bad shdr for 0xc00??000\n"); exit(34); } if(shdr.sh_size){ shdr_segc = shdr.sh_offset; shdr_segcsz = shdr.sh_size; shdr_c_count++; shdr_c_start = shdr.sh_addr; } break; case 0xfffe0000u ... 0xffff0000u: if(shdr.sh_type!=SHT_NOBITS || shdr.sh_size>0x20000 || shdr.sh_flags!=(SHF_ALLOC|SHF_EXECINSTR)){ fprintf(stderr,"bad shdr for ROM area\n"); exit(36); } break; } } ////// If we got both types of header, ensure they match if(shdr_seg0sz && phdr_seg0sz && shdr_seg0sz!=phdr_seg0sz){ fprintf(stderr,"seg0sz mismatch %08x %08x\n",shdr_seg0sz,phdr_seg0sz); exit(39); } if(shdr_segcsz && phdr_segcsz && shdr_segcsz!=phdr_segcsz){ fprintf(stderr,"segcsz mismatch %08x %08x\n",shdr_segcsz,phdr_segcsz); exit(40); } if(shdr_seg0 && phdr_seg0 && shdr_seg0!=phdr_seg0){ fprintf(stderr,"seg0 mismatch %08x %08x\n",shdr_seg0,phdr_seg0); exit(41); } if(shdr_segc && phdr_segc && shdr_segc!=phdr_segc){ fprintf(stderr,"segc mismatch %08x %08x\n",shdr_segc,phdr_segc); exit(42); } if(shdr_c_start && phdr_c_start && shdr_c_start!=phdr_c_start){ fprintf(stderr,"c_start mismatch %08x %08x\n",shdr_c_start,phdr_c_start); exit(43); } if(shdr_c_count>1 || phdr_c_count>1){ fprintf(stderr,"c_count too high %08x %08x\n",shdr_c_count,phdr_c_count); exit(44); } seg0 = (unsigned char*)(map+shdr_seg0); segc = (unsigned char*)(map+shdr_segc); seg0sz = shdr_seg0sz; segcsz = shdr_segcsz; c_start = shdr_c_start; if(phdr_seg0) seg0 = (unsigned char*)(map+phdr_seg0); if(phdr_segc) segc = (unsigned char*)(map+phdr_segc); if(phdr_seg0sz) seg0sz = phdr_seg0sz; if(phdr_segcsz) segcsz = phdr_segcsz; if(phdr_c_start) c_start = phdr_c_start; } //////////////////////////////////////////////////////////////////////////////////////////////// static void mapit(char *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(stderr,"%d is a bad size or mapping failed\n",(int)sbuf.st_size); exit(3); } int mapsize = sbuf.st_size; parse_elf(map,mapsize); fwrite(bindata,mkbin(),1,stdout); } //////////////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]){ (void)argc; init_crc(); // fprintf(stderr,"filename %s\n",argv[1]); mapit(argv[1]); return 0; } /////////////////////////////////////////////////////////////////////////////////////////