#!/usr/bin/env python import os import os.path import sys, getopt import binascii import struct import string class generator(object): # # struct l_loader_head { # unsigned int first_instr; # unsigned char magic[16]; @ BOOTMAGICNUMBER! # unsigned int l_loader_start; # unsigned int l_loader_end; # }; file_header = [0, 0, 0, 0, 0, 0, 0] # # struct entry_head { # unsigned char magic[8]; @ ENTY # unsigned char name[8]; @ loader/bl1 # unsigned int start_lba; # unsigned int count_lba; # unsigned int flag; @ boot partition or not # }; s1_entry_name = ['loader', 'bl1'] s2_entry_name = ['primary', 'second'] block_size = 512 stage = 0 # set in self.add() idx = 0 # set in self.parse() ptable_lba = 0 stable_lba = 0 # file pointer p_entry = 0 p_file = 0 def __init__(self, out_img): try: self.fp = open(out_img, "wb+") except IOError, e: print "*** file open error:", e sys.exit(3) else: self.entry_hd = [[0 for col in range(7)] for row in range(5)] def __del__(self): self.fp.close() # parse partition from the primary ptable def parse(self, fname): try: fptable = open(fname, "rb") except IOError, e: print "*** file open error:", e sys.exit(3) else: # skip the first block in primary partition table # that is MBR protection information fptable.read(self.block_size) # check whether it's a primary paritition table data = struct.unpack("8s", fptable.read(8)) efi_magic = 'EFI PART' if cmp("EFI PART", data[0]): print "It's not partition table image." fptable.close() sys.exit(4) # skip 16 bytes fptable.read(16) # get lba of both primary partition table and secondary partition table data = struct.unpack("QQQQ", fptable.read(32)) self.ptable_lba = data[0] - 1 self.stable_lba = data[3] + 1 # skip 24 bytes fptable.read(24) data = struct.unpack("i", fptable.read(4)) pentries = data[0] # skip the reset in this block fptable.read(self.block_size - 84) for i in range(1, pentries): # name is encoded as UTF-16 d0,lba,d2,name = struct.unpack("32sQ16s72s", fptable.read(128)) plainname = unicode(name, "utf-16") if (not cmp(plainname[0:7], 'l-loader'[0:7])): print 'bl1_lba: ', lba self.bl1_lba = lba sys.exit(1) fptable.close() def add(self, lba, fname): try: fsize = os.path.getsize(fname) except IOError, e: print "*** file open error:", e sys.exit(4) else: blocks = (fsize + self.block_size - 1) / self.block_size if (self.stage == 1): # Boot Area1 in eMMC bootp = 1 if self.idx == 0: self.p_entry = 28 elif (self.stage == 2): # User Data Area in eMMC bootp = 0 # create an empty block only for stage2 # This empty block is used to store entry head print 'p_file: ', self.p_file, 'p_entry: ', self.p_entry if self.idx == 0: self.fp.seek(self.p_file) for i in range (0, self.block_size): zero = struct.pack('x') self.fp.write(zero) self.p_file += self.block_size self.p_entry = 0 else: print "wrong stage ", stage, "is specified" sys.exit(4) # Maybe the file size isn't aligned. So pad it. if (self.idx == 0) and (self.stage == 1): if fsize > 2048: print 'loader size exceeds 2KB. file size: ', fsize sys.exit(4) else: left_bytes = 2048 - fsize else: left_bytes = fsize % self.block_size if left_bytes: left_bytes = self.block_size - left_bytes print 'lba: ', lba, 'blocks: ', blocks, 'bootp: ', bootp, 'fname: ', fname # write images fimg = open(fname, "rb") for i in range (0, blocks): buf = fimg.read(self.block_size) self.fp.seek(self.p_file) self.fp.write(buf) # p_file is the file pointer of the new binary file # At last, it means the total block size of the new binary file self.p_file += self.block_size if (self.idx == 0) and (self.stage == 1): self.p_file = 2048 print 'p_file: ', self.p_file, 'last block is ', fsize % self.block_size, 'bytes', ' tell: ', self.fp.tell(), 'left_bytes: ', left_bytes if left_bytes: for i in range (0, left_bytes): zero = struct.pack('x') self.fp.write(zero) print 'p_file: ', self.p_file, ' pad to: ', self.fp.tell() # write entry information at the header if self.stage == 1: byte = struct.pack('8s8siii', 'ENTRYHDR', self.s1_entry_name[self.idx], lba, blocks, bootp) elif self.stage == 2: byte = struct.pack('8s8siii', 'ENTRYHDR', self.s2_entry_name[self.idx], lba, blocks, bootp) self.fp.seek(self.p_entry) self.fp.write(byte) self.p_entry += 28 self.idx += 1 fimg.close() def hex2(self, data): return data > 0 and hex(data) or hex(data & 0xffffffff) def end(self): if self.stage == 1: self.fp.seek(20) start,end = struct.unpack("ii", self.fp.read(8)) print "start: ", self.hex2(start), 'end: ', self.hex2(end) end = start + self.p_file print "start: ", self.hex2(start), 'end: ', self.hex2(end) self.fp.seek(24) byte = struct.pack('i', end) self.fp.write(byte) self.fp.close() def create_stage1(self, img_loader, img_bl1, output_img): print '+-----------------------------------------------------------+' print ' Input Images:' print ' loader: ', img_loader print ' bl1: ', img_bl1 print ' Ouput Image: ', output_img print '+-----------------------------------------------------------+\n' self.stage = 1 # The first 2KB is reserved # The next 2KB is for loader image self.add(4, img_loader) # img_loader doesn't exist in partition table print 'self.idx: ', self.idx # bl1.bin starts from 4KB self.add(8, img_bl1) # img_bl1 doesn't exist in partition table def create_stage2(self, img_prm_ptable, img_sec_ptable, output_img): print '+-----------------------------------------------------------+' print ' Input Images:' print ' primary partition table: ', img_prm_ptable print ' secondary partition table: ', img_sec_ptable print ' Ouput Image: ', output_img print '+-----------------------------------------------------------+\n' self.stage = 2 self.parse(img_prm_ptable) self.add(self.ptable_lba, img_prm_ptable) if (cmp(img_sec_ptable, 'secondary partition table')): # Doesn't match. It means that secondary ptable is specified. self.add(self.stable_lba, img_sec_ptable) else: print 'Don\'t need secondary partition table' def main(argv): stage1 = 0 stage2 = 0 img_prm_ptable = "primary partition table" img_sec_ptable = "secondary partition table" try: opts, args = getopt.getopt(argv,"ho:",["img_loader=","img_bl1=","img_prm_ptable=","img_sec_ptable="]) except getopt.GetoptError: print 'gen_loader.py -o <l-loader.bin> --img_loader <l-loader> --img_bl1 <bl1.bin> --img_prm_ptable <prm_ptable.img> --img_sec_ptable <sec_ptable.img>' sys.exit(2) for opt, arg in opts: if opt == '-h': print 'gen_loader.py -o <l-loader.bin> --img_loader <l-loader> --img_bl1 <bl1.bin> --img_prm_ptable <prm_ptable.img> --img_sec_ptable <sec_ptable.img>' sys.exit(1) elif opt == '-o': output_img = arg elif opt in ("--img_loader"): img_loader = arg stage1 = 1 elif opt in ("--img_bl1"): img_bl1 = arg stage1 = 1 elif opt in ("--img_prm_ptable"): img_prm_ptable = arg stage2 = 1 elif opt in ("--img_sec_ptable"): img_sec_ptable = arg loader = generator(output_img) loader.idx = 0 if (stage1 == 1) and (stage2 == 1): print 'There are only loader & BL1 in stage1.' print 'And there are primary partition table, secondary partition table and FIP in stage2.' sys.exit(1) elif (stage1 == 0) and (stage2 == 0): print 'No input images are specified.' sys.exit(1) elif stage1 == 1: loader.create_stage1(img_loader, img_bl1, output_img) elif stage2 == 1: loader.create_stage2(img_prm_ptable, img_sec_ptable, output_img) loader.end() if __name__ == "__main__": main(sys.argv[1:])