Vishal Bhoj | 9a67d91 | 2016-06-09 10:02:07 +0100 | [diff] [blame^] | 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:]) |