Home | History | Annotate | Download | only in l-loader
      1 #!/usr/bin/env python
      2 
      3 import os
      4 import os.path
      5 import sys, getopt
      6 import binascii
      7 import struct
      8 import string
      9 
     10 class generator(object):
     11     #
     12     # struct l_loader_head {
     13     #      unsigned int	first_instr;
     14     #      unsigned char	magic[16];	@ BOOTMAGICNUMBER!
     15     #      unsigned int	l_loader_start;
     16     #      unsigned int	l_loader_end;
     17     # };
     18     file_header = [0, 0, 0, 0, 0, 0, 0]
     19 
     20     #
     21     # struct entry_head {
     22     #       unsigned char   magic[8];           @ ENTY
     23     #       unsigned char   name[8];            @ loader/bl1
     24     #       unsigned int    start_lba;
     25     #       unsigned int    count_lba;
     26     #       unsigned int    flag;               @ boot partition or not
     27     # };
     28 
     29     s1_entry_name = ['loader', 'bl1']
     30     s2_entry_name = ['primary', 'second']
     31 
     32     block_size = 512
     33 
     34     stage = 0
     35 
     36     # set in self.add()
     37     idx = 0
     38 
     39     # set in self.parse()
     40     ptable_lba = 0
     41     stable_lba = 0
     42 
     43     # file pointer
     44     p_entry = 0
     45     p_file = 0
     46 
     47     def __init__(self, out_img):
     48         try:
     49             self.fp = open(out_img, "wb+")
     50         except IOError, e:
     51             print "*** file open error:", e
     52             sys.exit(3)
     53         else:
     54             self.entry_hd = [[0 for col in range(7)] for row in range(5)]
     55 
     56     def __del__(self):
     57         self.fp.close()
     58 
     59     # parse partition from the primary ptable
     60     def parse(self, fname):
     61         try:
     62             fptable = open(fname, "rb")
     63         except IOError, e:
     64             print "*** file open error:", e
     65             sys.exit(3)
     66         else:
     67             # skip the first block in primary partition table
     68             # that is MBR protection information
     69             fptable.read(self.block_size)
     70             # check whether it's a primary paritition table
     71             data = struct.unpack("8s", fptable.read(8))
     72             efi_magic = 'EFI PART'
     73             if cmp("EFI PART", data[0]):
     74                 print "It's not partition table image."
     75                 fptable.close()
     76                 sys.exit(4)
     77             # skip 16 bytes
     78             fptable.read(16)
     79             # get lba of both primary partition table and secondary partition table
     80             data = struct.unpack("QQQQ", fptable.read(32))
     81             self.ptable_lba = data[0] - 1
     82             self.stable_lba = data[3] + 1
     83             # skip 24 bytes
     84             fptable.read(24)
     85             data = struct.unpack("i", fptable.read(4))
     86             pentries = data[0]
     87             # skip the reset in this block
     88             fptable.read(self.block_size - 84)
     89 
     90             for i in range(1, pentries):
     91                 # name is encoded as UTF-16
     92                 d0,lba,d2,name = struct.unpack("32sQ16s72s", fptable.read(128))
     93                 plainname = unicode(name, "utf-16")
     94                 if (not cmp(plainname[0:7], 'l-loader'[0:7])):
     95                     print 'bl1_lba: ', lba
     96                     self.bl1_lba = lba
     97                     sys.exit(1)
     98 
     99             fptable.close()
    100 
    101     def add(self, lba, fname):
    102         try:
    103             fsize = os.path.getsize(fname)
    104         except IOError, e:
    105             print "*** file open error:", e
    106             sys.exit(4)
    107         else:
    108             blocks = (fsize + self.block_size - 1) / self.block_size
    109             if (self.stage == 1):
    110                 # Boot Area1 in eMMC
    111                 bootp = 1
    112                 if self.idx == 0:
    113                     self.p_entry = 28
    114             elif (self.stage == 2):
    115                 # User Data Area in eMMC
    116                 bootp = 0
    117                 # create an empty block only for stage2
    118                 # This empty block is used to store entry head
    119                 print 'p_file: ', self.p_file, 'p_entry: ', self.p_entry
    120                 if self.idx == 0:
    121                     self.fp.seek(self.p_file)
    122                     for i in range (0, self.block_size):
    123                         zero = struct.pack('x')
    124                         self.fp.write(zero)
    125                     self.p_file += self.block_size
    126                     self.p_entry = 0
    127             else:
    128                 print "wrong stage ", stage, "is specified"
    129                 sys.exit(4)
    130             # Maybe the file size isn't aligned. So pad it.
    131             if (self.idx == 0) and (self.stage == 1):
    132                 if fsize > 2048:
    133                     print 'loader size exceeds 2KB. file size: ', fsize
    134                     sys.exit(4)
    135                 else:
    136                     left_bytes = 2048 - fsize
    137             else:
    138                 left_bytes = fsize % self.block_size
    139                 if left_bytes:
    140                     left_bytes = self.block_size - left_bytes
    141             print 'lba: ', lba, 'blocks: ', blocks, 'bootp: ', bootp, 'fname: ', fname
    142             # write images
    143             fimg = open(fname, "rb")
    144             for i in range (0, blocks):
    145                 buf = fimg.read(self.block_size)
    146                 self.fp.seek(self.p_file)
    147                 self.fp.write(buf)
    148                 # p_file is the file pointer of the new binary file
    149                 # At last, it means the total block size of the new binary file
    150                 self.p_file += self.block_size
    151 
    152             if (self.idx == 0) and (self.stage == 1):
    153                 self.p_file = 2048
    154             print 'p_file: ', self.p_file, 'last block is ', fsize % self.block_size, 'bytes', '  tell: ', self.fp.tell(), 'left_bytes: ', left_bytes
    155             if left_bytes:
    156                 for i in range (0, left_bytes):
    157                     zero = struct.pack('x')
    158                     self.fp.write(zero)
    159                 print 'p_file: ', self.p_file, '  pad to: ', self.fp.tell()
    160 
    161             # write entry information at the header
    162             if self.stage == 1:
    163                 byte = struct.pack('8s8siii', 'ENTRYHDR', self.s1_entry_name[self.idx], lba, blocks, bootp)
    164             elif self.stage == 2:
    165                 byte = struct.pack('8s8siii', 'ENTRYHDR', self.s2_entry_name[self.idx], lba, blocks, bootp)
    166             self.fp.seek(self.p_entry)
    167             self.fp.write(byte)
    168             self.p_entry += 28
    169             self.idx += 1
    170 
    171             fimg.close()
    172 
    173     def hex2(self, data):
    174         return data > 0 and hex(data) or hex(data & 0xffffffff)
    175 
    176     def end(self):
    177         if self.stage == 1:
    178             self.fp.seek(20)
    179             start,end = struct.unpack("ii", self.fp.read(8))
    180             print "start: ", self.hex2(start), 'end: ', self.hex2(end)
    181             end = start + self.p_file
    182             print "start: ", self.hex2(start), 'end: ', self.hex2(end)
    183             self.fp.seek(24)
    184             byte = struct.pack('i', end)
    185             self.fp.write(byte)
    186         self.fp.close()
    187 
    188     def create_stage1(self, img_loader, img_bl1, output_img):
    189         print '+-----------------------------------------------------------+'
    190         print ' Input Images:'
    191         print '     loader:                       ', img_loader
    192         print '     bl1:                          ', img_bl1
    193         print ' Ouput Image:                      ', output_img
    194         print '+-----------------------------------------------------------+\n'
    195 
    196         self.stage = 1
    197 
    198         # The first 2KB is reserved
    199         # The next 2KB is for loader image
    200         self.add(4, img_loader)    # img_loader doesn't exist in partition table
    201         print 'self.idx: ', self.idx
    202         # bl1.bin starts from 4KB
    203         self.add(8, img_bl1)      # img_bl1 doesn't exist in partition table
    204 
    205     def create_stage2(self, img_prm_ptable, img_sec_ptable, output_img):
    206         print '+-----------------------------------------------------------+'
    207         print ' Input Images:'
    208         print '     primary partition table:      ', img_prm_ptable
    209         print '     secondary partition table:    ', img_sec_ptable
    210         print ' Ouput Image:                      ', output_img
    211         print '+-----------------------------------------------------------+\n'
    212 
    213         self.stage = 2
    214         self.parse(img_prm_ptable)
    215         self.add(self.ptable_lba, img_prm_ptable)
    216         if (cmp(img_sec_ptable, 'secondary partition table')):
    217             # Doesn't match. It means that secondary ptable is specified.
    218             self.add(self.stable_lba, img_sec_ptable)
    219         else:
    220             print 'Don\'t need secondary partition table'
    221 
    222 def main(argv):
    223     stage1 = 0
    224     stage2 = 0
    225     img_prm_ptable = "primary partition table"
    226     img_sec_ptable = "secondary partition table"
    227     try:
    228         opts, args = getopt.getopt(argv,"ho:",["img_loader=","img_bl1=","img_prm_ptable=","img_sec_ptable="])
    229     except getopt.GetoptError:
    230         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>'
    231         sys.exit(2)
    232     for opt, arg in opts:
    233         if opt == '-h':
    234             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>'
    235             sys.exit(1)
    236         elif opt == '-o':
    237             output_img = arg
    238         elif opt in ("--img_loader"):
    239             img_loader = arg
    240             stage1 = 1
    241         elif opt in ("--img_bl1"):
    242             img_bl1 = arg
    243             stage1 = 1
    244         elif opt in ("--img_prm_ptable"):
    245             img_prm_ptable = arg
    246             stage2 = 1
    247         elif opt in ("--img_sec_ptable"):
    248             img_sec_ptable = arg
    249 
    250     loader = generator(output_img)
    251     loader.idx = 0
    252 
    253     if (stage1 == 1) and (stage2 == 1):
    254         print 'There are only loader & BL1 in stage1.'
    255         print 'And there are primary partition table, secondary partition table and FIP in stage2.'
    256         sys.exit(1)
    257     elif (stage1 == 0) and (stage2 == 0):
    258         print 'No input images are specified.'
    259         sys.exit(1)
    260     elif stage1 == 1:
    261         loader.create_stage1(img_loader, img_bl1, output_img)
    262     elif stage2 == 1:
    263         loader.create_stage2(img_prm_ptable, img_sec_ptable, output_img)
    264 
    265     loader.end()
    266 
    267 if __name__ == "__main__":
    268     main(sys.argv[1:])
    269