blob: ccb269aa34e91941a9fc208b9815306f998a29d0 [file] [log] [blame]
Aditya Kumar Patra S12f0be02016-10-25 18:41:15 +05301#!/usr/bin/env python
2#
3# Copyright (c) 2013-2015 The Linux Foundation. All rights reserved.
4#
5"""
6Script to create a U-Boot flashable multi-image blob.
7
8This script creates a multi-image blob, from a bunch of images, and
9adds a U-Boot shell script to the blob, that can flash the images from
10within U-Boot. The procedure to use this script is listed below.
11
12 1. Create an images folder. Ex: my-pack
13
14 2. Copy all the images to be flashed into the folder.
15
16 3. Copy the partition MBN file into the folder. The file should be
17 named 'partition.mbn'. This is used to determine the offsets for
18 each of the named partitions.
19
20 4. Create a flash configuration file, specifying the images be
21 flashed, and the partition in which the images is to be
22 flashed. The flash configuration file can be specified using the
23 -f option, default is flash.conf.
24
25 5. Invoke 'pack' with the folder name as argument, pass flash
26 parameters as arguments if required. A single image file will
27 be created, out side the images folder, with .img suffix. Ex:
28 my-pack.img
29
30 6. Transfer the file into a valid SDRAM address and invoke the
31 following U-Boot command to flash the images. Replace 0x41000000,
32 with address location where the image has been loaded. The script
33 expects the variable 'imgaddr' to be set.
34
35 u-boot> imgaddr=0x41000000 source $imgaddr:script
36
37Host-side Pre-req
38
39 * Python >= 2.6
40 * ordereddict >= 1.1 (for Python 2.6)
41 * mkimage >= 2012.07
42 * dtc >= 1.2.0
43
44Target-side Pre-req
45
46The following U-Boot config macros should be enabled, for the
47generated flashing script to work.
48
49 * CONFIG_FIT -- FIT image format support
50 * CONFIG_SYS_HUSH_PARSER -- bash style scripting support
51 * CONFIG_SYS_NULLDEV -- redirecting command output support
52 * CONFIG_CMD_XIMG -- extracting sub-images support
53 * CONFIG_CMD_NAND -- NAND Flash commands support
54 * CONFIG_CMD_NAND_YAFFS -- NAND YAFFS2 write support
55 * CONFIG_CMD_SF -- SPI Flash commands support
56"""
57
58from ConfigParser import ConfigParser
59from ConfigParser import Error as ConfigParserError
60from os.path import getsize
61from getopt import getopt
62from getopt import GetoptError
63from collections import namedtuple
64from string import Template
65from unittest import TestCase
66from tempfile import mkdtemp
67from shutil import rmtree
68
69import os
70import sys
71import os.path
72import subprocess
73import struct
74import re
75import hashlib
76
77version = "1.1"
78
79#
80# Python 2.6 and earlier did not have OrderedDict use the backport
81# from ordereddict package. If that is not available report error.
82#
83try:
84 from collections import OrderedDict
85except ImportError:
86 try:
87 from ordereddict import OrderedDict
88 except ImportError:
89 print "error: this script requires the 'ordereddict' class."
90 print "Try 'pip install --user ordereddict'"
91 print "Or 'easy_install --user ordereddict'"
92 sys.exit(1)
93
94__all__ = []
95
96KB = 1024
97MB = 1024 * KB
98
99def error(msg, ex=None):
100 """Print an error message and exit.
101
102 msg -- string, the message to print
103 ex -- exception, the associate exception, if any
104 """
105
106 sys.stderr.write("pack: %s" % msg)
107 if ex != None: sys.stderr.write(": %s" % str(ex))
108 sys.stderr.write("\n")
109 sys.exit(1)
110
111FlashInfo = namedtuple("FlashInfo", "type pagesize blocksize chipsize")
112ImageInfo = namedtuple("ProgInfo", "name filename type")
113PartInfo = namedtuple("PartInfo", "name offset length")
114
115def roundup(value, roundto):
116 """Return the next largest multiple of 'roundto'."""
117
118 return ((value + roundto - 1) // roundto) * roundto
119
120class GPT(object):
121 GPTheader = namedtuple("GPTheader", "signature revision header_size"
122 " crc32 current_lba backup_lba first_usable_lba"
123 " last_usable_lba disk_guid start_lba_part_entry"
124 " num_part_entry part_entry_size part_crc32")
125 GPT_SIGNATURE = 'EFI PART'
126 GPT_REVISION = '\x00\x00\x01\x00'
127 GPT_HEADER_SIZE = 0x5C
128 GPT_HEADER_FMT = "<8s4sLL4xQQQQ16sQLLL"
129
130 GPTtable = namedtuple("GPTtable", "part_type unique_guid first_lba"
131 " last_lba attribute_flag part_name")
132 GPT_TABLE_FMT = "<16s16sQQQ72s"
133
134 def __init__(self, filename, pagesize, blocksize, chipsize):
135 self.filename = filename
136 self.pagesize = pagesize
137 self.blocksize = blocksize
138 self.chipsize = chipsize
139 self.__partitions = OrderedDict()
140
141 def __validate_and_read_parts(self, part_fp):
142 """Validate the GPT and read the partition"""
143 part_fp.seek(self.blocksize, os.SEEK_SET)
144 gptheader_str = part_fp.read(struct.calcsize(GPT.GPT_HEADER_FMT))
145 gptheader = struct.unpack(GPT.GPT_HEADER_FMT, gptheader_str)
146 gptheader = GPT.GPTheader._make(gptheader)
147
148 if gptheader.signature != GPT.GPT_SIGNATURE:
149 error("Invalid signature")
150
151 if gptheader.revision != GPT.GPT_REVISION:
152 error("Unsupported GPT Revision")
153
154 if gptheader.header_size != GPT.GPT_HEADER_SIZE:
155 error("Invalid Header size")
156
157 # Adding GPT partition info. This has to be flashed first.
158 # GPT Header starts at LBA1 so (current_lba -1) will give the
159 # starting of primary GPT.
160 # blocksize will equal to gptheader.first_usuable_lba - current_lba + 1
161
162 name = "0:GPT"
163 block_start = gptheader.current_lba - 1
164 block_count = gptheader.first_usable_lba - gptheader.current_lba + 1
165 part_info = PartInfo(name, block_start, block_count)
166 self.__partitions[name] = part_info
167
168 part_fp.seek(2 * self.blocksize, os.SEEK_SET)
169
170 for i in range(gptheader.num_part_entry):
171 gpt_table_str = part_fp.read(struct.calcsize(GPT.GPT_TABLE_FMT))
172 gpt_table = struct.unpack(GPT.GPT_TABLE_FMT, gpt_table_str)
173 gpt_table = GPT.GPTtable._make(gpt_table)
174
175 block_start = gpt_table.first_lba
176 block_count = gpt_table.last_lba - gpt_table.first_lba + 1
177
178 part_name = gpt_table.part_name.strip(chr(0))
179 name = part_name.replace('\0','')
180 part_info = PartInfo(name, block_start, block_count)
181 self.__partitions[name] = part_info
182
183 # Adding the GPT Backup partition.
184 # GPT header backup_lba gives block number where the GPT backup header will be.
185 # GPT Backup header will start from offset of 32 blocks before
186 # the GPTheader.backup_lba. Backup GPT size is 33 blocks.
187 name = "0:GPTBACKUP"
188 block_start = gptheader.backup_lba - 32
189 block_count = 33
190 part_info = PartInfo(name, block_start, block_count)
191 self.__partitions[name] = part_info
192
193 def get_parts(self):
194 """Returns a list of partitions present in the GPT."""
195
196 try:
197 with open(self.filename, "r") as part_fp:
198 self.__validate_and_read_parts(part_fp)
199 except IOError, e:
200 error("error opening %s" % self.filename, e)
201
202 return self.__partitions
203
204class MIBIB(object):
205 Header = namedtuple("Header", "magic1 magic2 version age")
206 HEADER_FMT = "<LLLL"
207 HEADER_MAGIC1 = 0xFE569FAC
208 HEADER_MAGIC2 = 0xCD7F127A
209 HEADER_VERSION = 4
210
211 Table = namedtuple("Table", "magic1 magic2 version numparts")
212 TABLE_FMT = "<LLLL"
213 TABLE_MAGIC1 = 0x55EE73AA
214 TABLE_MAGIC2 = 0xE35EBDDB
215 TABLE_VERSION = 3
216
217 Entry = namedtuple("Entry", "name offset length"
218 " attr1 attr2 attr3 which_flash")
219 ENTRY_FMT = "<16sLLBBBB"
220
221 def __init__(self, filename, pagesize, blocksize, chipsize):
222 self.filename = filename
223 self.pagesize = pagesize
224 self.blocksize = blocksize
225 self.chipsize = chipsize
226 self.__partitions = OrderedDict()
227
228 def __validate(self, part_fp):
229 """Validate the MIBIB by checking for magic bytes."""
230
231 mheader_str = part_fp.read(struct.calcsize(MIBIB.HEADER_FMT))
232 mheader = struct.unpack(MIBIB.HEADER_FMT, mheader_str)
233 mheader = MIBIB.Header._make(mheader)
234
235 if (mheader.magic1 != MIBIB.HEADER_MAGIC1
236 or mheader.magic2 != MIBIB.HEADER_MAGIC2):
237 error("invalid partition table, magic byte not present")
238
239 if mheader.version != MIBIB.HEADER_VERSION:
240 error("unsupport mibib version")
241
242 def __read_parts(self, part_fp):
243 """Read the partitions from the MIBIB."""
244
245 part_fp.seek(self.pagesize, os.SEEK_SET)
246 mtable_str = part_fp.read(struct.calcsize(MIBIB.TABLE_FMT))
247 mtable = struct.unpack(MIBIB.TABLE_FMT, mtable_str)
248 mtable = MIBIB.Table._make(mtable)
249
250 if (mtable.magic1 != MIBIB.TABLE_MAGIC1
251 or mtable.magic2 != MIBIB.TABLE_MAGIC2):
252 error("invalid sys part. table, magic byte not present")
253
254 if mtable.version != MIBIB.TABLE_VERSION:
255 error("unsupported partition table version")
256
257 for i in range(mtable.numparts):
258 mentry_str = part_fp.read(struct.calcsize(MIBIB.ENTRY_FMT))
259 mentry = struct.unpack(MIBIB.ENTRY_FMT, mentry_str)
260 mentry = MIBIB.Entry._make(mentry)
261
262 byte_offset = mentry.offset * self.blocksize
263
264 if mentry.length == 0xFFFFFFFF:
265 byte_length = self.chipsize - byte_offset
266 else:
267 byte_length = mentry.length * self.blocksize
268
269 part_name = mentry.name.strip(chr(0))
270 part_info = PartInfo(part_name, byte_offset, byte_length)
271 self.__partitions[part_name] = part_info
272
273 def __write_header(self, part_fp):
274 """Write the MIBIB header, used for unit testing purposes."""
275
276 header = MIBIB.Header(magic1=MIBIB.HEADER_MAGIC1,
277 magic2=MIBIB.HEADER_MAGIC2,
278 version=MIBIB.HEADER_VERSION,
279 age=1)
280 header_str = struct.pack(MIBIB.HEADER_FMT, *header)
281 part_fp.write(header_str)
282
283 def __write_parts(self, part_fp):
284 """Write the MIBIB partitions, used for unit testing purposes."""
285
286 part_fp.seek(self.pagesize, os.SEEK_SET)
287 table = MIBIB.Table(magic1=MIBIB.TABLE_MAGIC1,
288 magic2=MIBIB.TABLE_MAGIC2,
289 version=MIBIB.TABLE_VERSION,
290 numparts=len(self.__partitions))
291 table_str = struct.pack(MIBIB.TABLE_FMT, *table)
292 part_fp.write(table_str)
293 for name, offset, length in self.__partitions.itervalues():
294 block_offset = offset / self.blocksize
295
296 if length == None:
297 block_length = 0xFFFFFFFF
298 else:
299 block_length = length / self.blocksize
300
301 entry = MIBIB.Entry(name, block_offset, block_length,
302 attr1=0, attr2=0, attr3=0, which_flash=0)
303 entry_str = struct.pack(MIBIB.ENTRY_FMT, *entry)
304 part_fp.write(entry_str)
305
306 def get_parts(self):
307 """Returns a list of partitions present in the MIBIB."""
308
309 try:
310 with open(self.filename, "r") as part_fp:
311 self.__validate(part_fp)
312 self.__read_parts(part_fp)
313 except IOError, e:
314 error("error opening %s" % self.filename, e)
315
316 return self.__partitions
317
318 def add_part(self, part_info):
319 """Add a partition for writing to MIBIB."""
320
321 self.__partitions[part_info.name] = part_info
322
323 def write(self):
324 """Write the MIBIB to file, for unit testing purposes."""
325
326 try:
327 with open(self.filename, "w") as part_fp:
328 self.__write_header(part_fp)
329 self.__write_parts(part_fp)
330 except IOError, e:
331 error("error opening %s" % self.filename, e)
332
333class FlashScript(object):
334 """Base class for creating flash scripts."""
335
336 def __init__(self, flinfo):
337 self.pagesize = flinfo.pagesize
338 self.blocksize = flinfo.blocksize
339 self.script = []
340 self.parts = []
341 self.curr_stdout = "serial"
342 self.activity = None
343
344 self.script.append('if test "x$verbose" = "x"; then\n')
345 self.script.append("failedmsg='[failed]'\n")
346 self.script.append('else\n')
347 self.script.append("failedmsg='%s Failed'\n" % ("#" * 40))
348 self.script.append('fi\n')
349
350 def append(self, cmd, fatal=True):
351 """Add a command to the script.
352
353 Add additional code, to terminate on error. This can be
354 supressed by passing 'fatal' as False.
355 """
356
357 if fatal:
358 self.script.append(cmd
359 + ' || setenv stdout serial'
360 + ' && echo "$failedmsg"'
361 + ' && exit 1\n')
362 else:
363 self.script.append(cmd + "\n")
364
365 def check_isset(self, var):
366 """Check if a variable is set."""
367 self.append('test "x$%s" != "x"' % var)
368
369 def dumps(self):
370 """Return the created script as a string."""
371 return "".join(self.script)
372
373 def redirect(self, dev):
374 """Generate code, to redirect command output to a device."""
375
376 if self.curr_stdout == dev:
377 return
378
379 self.append("setenv stdout %s" % dev, fatal=False)
380 self.curr_stdout = dev
381
382 def start_activity(self, activity):
383 """Generate code, to indicate start of an activity."""
384
385 self.script.append('if test "x$verbose" = "x"; then\n')
386 self.echo("'%-40.40s'" % activity, nl=False)
387 self.script.append('else\n')
388 self.echo("'%s %s Started'" % ("#" * 40, activity), verbose=True)
389 self.script.append('fi\n')
390 self.activity = activity
391
392 def finish_activity(self):
393 """Generate code, to indicate end of an activity."""
394
395 self.script.append('if test "x$verbose" = "x"; then\n')
396 self.echo("'[ done ]'")
397 self.redirect("serial")
398 self.script.append('else\n')
399 self.echo("'%s %s Done'" % ("#" * 40, self.activity), verbose=True)
400 self.script.append('fi\n')
401
402 def imxtract(self, part):
403 """Generate code, to extract image location, from a multi-image blob.
404
405 part -- string, name of the sub-image
406
407 Sets the $fileaddr environment variable, to point to the
408 location of the sub-image.
409 """
410
411 self.append("imxtract $imgaddr %s" % part)
412
413 def echo(self, msg, nl=True, verbose=False):
414 """Generate code, to print a message.
415
416 nl -- bool, indicates whether newline is to be printed
417 verbose -- bool, indicates whether printing in verbose mode
418 """
419
420 if not verbose:
421 self.redirect("serial")
422
423 if nl:
424 self.append("echo %s" % msg, fatal=False)
425 else:
426 self.append("echo %s%s" % (r"\\c", msg), fatal=False)
427
428 if not verbose:
429 self.redirect("nulldev")
430
431 def end(self):
432 """Generate code, to indicate successful completion of script."""
433
434 self.append("exit 0\n", fatal=False)
435
436 def start_if(self, var, value):
437 """Generate code, to check an environment variable.
438
439 var -- string, variable to check
440 value -- string, the value to compare with
441 """
442
443 self.append('if test "$%s" = "%s"; then\n' % (var, value),
444 fatal=False)
445
446 def end_if(self):
447 """Generate code, to end if statement."""
448
449 self.append('fi\n', fatal=False)
450
451class NandScript(FlashScript):
452 """Class for creating NAND flash scripts."""
453
454 def __init__(self, flinfo, ipq_nand):
455 FlashScript.__init__(self, flinfo)
456 self.ipq_nand = ipq_nand
457
458 def erase(self, offset, size):
459 """Generate code, to erase the specified partition."""
460
461 size = roundup(size, self.blocksize)
462 self.append("nand erase 0x%08x 0x%08x" % (offset, size))
463
464 def write(self, offset, size, yaffs):
465 """Generate code, to write to a partition."""
466
467 if size > 0:
468 if yaffs:
469 self.append("nand write.yaffs $fileaddr 0x%08x 0x%08x"
470 % (offset, size))
471 else:
472 size = roundup(size, self.pagesize)
473 self.append("nand write $fileaddr 0x%08x 0x%08x" % (offset, size))
474
475 def switch_layout(self, layout):
476 """Generate code, to switch between sbl/linux layouts."""
477
478 self.append("ipq_nand %s" % layout)
479
480class NorScript(FlashScript):
481 """Class for creating NAND flash scripts."""
482
483 def __init__(self, flinfo):
484 FlashScript.__init__(self, flinfo)
485
486 def erase(self, offset, size):
487 """Generate code, to erase the specified partition."""
488
489 size = roundup(size, self.blocksize)
490 self.append("sf erase 0x%08x +0x%08x" % (offset, size))
491
492 def write(self, offset, size, yaffs):
493 """Generate code, to write to a partition."""
494
495 if size > 0:
496 self.append("sf write $fileaddr 0x%08x 0x%08x" % (offset, size))
497
498 def nand_write(self, offset, part_size, img_size):
499 """Handle the NOR + NAND case
500 All binaries upto HLOS will go to NOR and Root FS will go to NAND
501 Assumed all nand page sizes are less than are equal to 8KB
502 """
503 self.append("nand device 0 && nand erase 0x%08x 0x%08x" % (offset, part_size))
504 if img_size > 0:
505 self.append("nand write $fileaddr 0x%08x 0x%08x" % (offset, img_size))
506
507 def switch_layout(self, layout):
508 pass
509
510class EmmcScript(FlashScript):
511 """Class for creating EMMC scripts."""
512
513 def __init__(self, flinfo):
514 FlashScript.__init__(self, flinfo)
515
516 def erase(self, offset, size):
517 """Generate code, to erase the specified partition."""
518
519 self.append("mmc erase 0x%08x %x" % (offset, size))
520
521 def write(self, offset, size, yaffs):
522 """Generate code, to write to a partition."""
523 if size > 0:
524 size = roundup(size, self.blocksize)
525 blk_cnt = size / self.blocksize
526 self.append("mmc write $fileaddr 0x%08x %x" % (offset, blk_cnt))
527
528 def switch_layout(self, layout):
529 pass
530
531its_tmpl = Template("""
532/dts-v1/;
533
534/ {
535 description = "${desc}";
536 images {
537${images}
538 };
539};
540""")
541
542its_image_tmpl = Template("""
543 ${name} {
544 description = "${desc}";
545 data = /incbin/("./${fname}");
546 type = "${imtype}";
547 arch = "arm";
548 compression = "none";
549 hash@1 { algo = "crc32"; };
550 };
551""")
552
553
554def sha1(message):
555 """Returns SHA1 digest in hex format of the message."""
556
557 m = hashlib.sha1()
558 m.update(message)
559 return m.hexdigest()
560
561
562class Pack(object):
563 """Class to create a flashable, multi-image blob.
564
565 Combine multiple images present in a directory, and generate a
566 U-Boot script to flash the images.
567 """
568 # The maximum rootfs size is 64MB
569 norplusnand_rootfs_img_size = (64 * 1024 * 1024)
570
571 def __init__(self):
572 self.flinfo = None
573 self.images_dname = None
574 self.ipq_nand = None
575 self.partitions = {}
576
577 self.fconf_fname = None
578 self.scr_fname = None
579 self.its_fname = None
580 self.img_fname = None
581
582 def __get_yaffs(self, info, section):
583 """Get the yaffs flag for a section.
584
585 info -- ConfigParser object, containing image flashing info
586 section -- section to check if yaffs flag is set
587 """
588 try:
589 yaffs = info.get(section, "yaffs")
590 if yaffs.lower() in ["0", "no"]:
591 yaffs = False
592 elif yaffs.lower() in ["1", "yes"]:
593 yaffs = True
594 else:
595 error("invalid value for 'yaffs' in '%s'" % section)
596 except ConfigParserError, e:
597 yaffs = False
598
599 if self.flinfo.type == "nor" and yaffs == True:
600 error("yaffs cannot be used with NOR flash type")
601
602 return yaffs
603
604 def __get_layout(self, info, section):
605 """Get the layout for a section.
606
607 info -- ConfigParser object, containing image flashing info
608 section - section to retreive the layout from
609 """
610 try:
611 layout = info.get(section, "layout")
612 except ConfigParserError, e:
613 layout = None
614
615 if self.ipq_nand and layout == None:
616 error("layout not specified for IPQ device")
617
618 if not self.ipq_nand and layout != None:
619 error("layout specified for a non IPQ device")
620
621 if layout not in ("sbl", "linux", None):
622 error("invalid layout in '%s'" % section)
623
624 return layout
625
626 def __get_machid(self, info, section):
627 """Get the machid for a section.
628
629 info -- ConfigParser object, containing image flashing info
630 section -- section to retreive the machid from
631 """
632 try:
633 machid = info.get(section, "if_machid")
634 machid = int(machid, 0)
635 machid = "%x" % machid
636 except ConfigParserError, e:
637 machid = None
638 except ValueError, e:
639 error("invalid value for machid, should be integer")
640
641 return machid
642
643 def __get_img_size(self, filename):
644 """Get the size of the image to be flashed
645
646 filaneme -- string, filename of the image to be flashed
647 """
648
649 if filename.lower() == "none":
650 return 0
651 try:
652 return getsize(os.path.join(self.images_dname, filename))
653 except OSError, e:
654 error("error getting image size '%s'" % filename, e)
655
656 def __get_part_info(self, partition):
657 """Return partition info for the specified partition.
658
659 partition -- string, partition name
660 """
661 try:
662 return self.partitions[partition]
663 except KeyError, e:
664 return None
665
666 def __gen_flash_script(self, info, script, flinfo):
667 """Generate the script to flash the images.
668
669 info -- ConfigParser object, containing image flashing info
670 script -- Script object, to append commands to
671 """
672 count = 0
673
674 for section in info.sections():
675 try:
676 filename = info.get(section, "filename")
677 partition = info.get(section, "partition")
678 include = info.get(section, "include")
679 except ConfigParserError, e:
680 error("error getting image info in section '%s'" % section, e)
681
682 if include.lower() in ["0", "no"]:
683 continue
684
685 machid = self.__get_machid(info, section)
686 layout = self.__get_layout(info, section)
687 yaffs = self.__get_yaffs(info, section)
688
689 img_size = self.__get_img_size(filename)
690 part_info = self.__get_part_info(partition)
691
692 if self.flinfo.type == 'nand':
693 size = roundup(img_size, flinfo.pagesize)
694 tr = ' | tr \"\\000\" \"\\377\"'
695
696 if self.flinfo.type == 'emmc':
697 size = roundup(img_size, flinfo.blocksize)
698 tr = ''
699
700 if ((self.flinfo.type == 'nand' or self.flinfo.type == 'emmc') and (size != img_size)):
701 pad_size = size - img_size
702 filename_abs = os.path.join(self.images_dname, filename)
703 filename_abs_pad = filename_abs + ".padded"
704 cmd = 'cat %s > %s' % (filename_abs, filename_abs_pad)
705 ret = subprocess.call(cmd, shell=True)
706 if ret != 0:
707 error("failed to copy image")
708 cmd = 'dd if=/dev/zero count=1 bs=%s %s >> %s' % (pad_size, tr, filename_abs_pad)
709 cmd = '(' + cmd + ') 1>/dev/null 2>/dev/null'
710 ret = subprocess.call(cmd, shell=True)
711 if ret != 0:
712 error("failed to create padded image from script")
713
714 if self.flinfo.type != "emmc":
715 if part_info == None:
716 if self.flinfo.type == 'norplusnand':
717 if count > 2:
718 error("More than 2 NAND images for NOR+NAND is not allowed")
719 elif img_size > part_info.length:
720 error("img size is larger than part. len in '%s'" % section)
721 else:
722 if part_info != None:
723 if (img_size > 0):
724 if img_size > (part_info.length * self.flinfo.blocksize):
725 error("img size is larger than part. len in '%s'" % section)
726
727 if part_info == None and self.flinfo.type != 'norplusnand':
728 continue
729
730 if machid:
731 script.start_if("machid", machid)
732
733 script.start_activity("Flashing %s:" % section)
734 if self.ipq_nand: script.switch_layout(layout)
735 if img_size > 0:
736 if ((self.flinfo.type == 'nand' or self.flinfo.type == 'emmc') and (size != img_size)):
737 filename_pad = filename + ".padded"
738 script.imxtract(section + "-" + sha1(filename_pad))
739 else:
740 script.imxtract(section + "-" + sha1(filename))
741
742 if part_info == None:
743 if self.flinfo.type == 'norplusnand':
744 offset = count * Pack.norplusnand_rootfs_img_size
745 part_size = Pack.norplusnand_rootfs_img_size
746 script.nand_write(offset, part_size, img_size)
747 count = count + 1
748 else:
749 offset = part_info.offset
750 script.erase(offset, part_info.length)
751 script.write(offset, img_size, yaffs)
752 script.finish_activity()
753
754 if machid:
755 script.end_if()
756
757 if part_info == None and self.flinfo.type != 'norplusnand':
758 print "Flash type is norplusemmc"
759 else:
760 script.end()
761
762 def __gen_script(self, script_fp, info_fp, script, images, flinfo):
763 """Generate the script to flash the multi-image blob.
764
765 script_fp -- file object, to write script to
766 info_fp -- file object, to read flashing information from
767 script -- Script object, to append the commands to
768 images -- list of ImageInfo, appended to, based on images in config
769 """
770 try:
771 info = ConfigParser({"include": "yes"})
772 info.readfp(info_fp)
773 except ConfigParserError, e:
774 error("error parsing info file '%s'" % self.fconf_fname, e)
775
776 self.__gen_flash_script(info, script, flinfo)
777
778 for section in info.sections():
779 if info.get(section, "include").lower() in ["0", "no"]:
780 continue
781
782 partition = info.get(section, "partition")
783 part_info = self.__get_part_info(partition)
784
785 if part_info == None and self.flinfo.type != 'norplusnand':
786 continue
787
788 filename = info.get(section, "filename")
789 if self.flinfo.type == 'nand':
790 img_size = self.__get_img_size(filename)
791 size = roundup(img_size, flinfo.pagesize)
792 if ( size != img_size ):
793 filename = filename + ".padded"
794 if self.flinfo.type == 'emmc':
795 img_size = self.__get_img_size(filename)
796 size = roundup(img_size, flinfo.blocksize)
797 if ( size != img_size ):
798 filename = filename + ".padded"
799 image_info = ImageInfo(section + "-" + sha1(filename),
800 filename, "firmware")
801 if filename.lower() != "none":
802 if image_info not in images:
803 images.append(image_info)
804
805 def __its_escape(self, string):
806 """Return string with ITS special characters escaped.
807
808 string -- string to be escape.
809
810 String in ITS files, consider 'slash' as special
811 character. Escape them by prefixing them with a slash.
812 """
813 return string.replace("\\", "\\\\")
814
815 def __mkimage(self, images):
816 """Create the multi-image blob.
817
818 images -- list of ImageInfo, containing images to be part of the blob
819 """
820 try:
821 its_fp = open(self.its_fname, "wb")
822 except IOError, e:
823 error("error opening its file '%s'" % self.its_fname, e)
824
825 desc = "Flashing %s %x %x"
826 desc = desc % (self.flinfo.type, self.flinfo.pagesize,
827 self.flinfo.blocksize)
828
829 image_data = []
830 for (section, fname, imtype) in images:
831 fname = self.__its_escape(fname)
832 subs = dict(name=section, desc=fname, fname=fname, imtype=imtype)
833 image_data.append(its_image_tmpl.substitute(subs))
834
835 image_data = "".join(image_data)
836 its_data = its_tmpl.substitute(desc=desc, images=image_data)
837
838 its_fp.write(its_data)
839 its_fp.close()
840
841 try:
842 cmd = ["mkimage", "-f", self.its_fname, self.img_fname]
843 ret = subprocess.call(cmd)
844 if ret != 0:
845 error("failed to create u-boot image from script")
846 except OSError, e:
847 error("error executing mkimage", e)
848
849 def __create_fnames(self):
850 """Populate the filenames."""
851
852 self.scr_fname = os.path.join(self.images_dname, "flash.scr")
853 self.its_fname = os.path.join(self.images_dname, "flash.its")
854
855 def __gen_board_script(self, board_section, machid, flinfo,
856 part_fname, fconf_fname, images):
857 """Generate the flashing script for one board.
858
859 board_section -- string, board section in board config file
860 machid -- string, board machine ID in hex format
861 flinfo -- FlashInfo object, contains board specific flash params
862 part_fname -- string, partition file specific to the board
863 fconf_fname -- string, flash config file specific to the board
864 images -- list of ImageInfo, append images used by the board here
865 """
866 script_fp = open(self.scr_fname, "a")
867
868 try:
869 fconf_fp = open(fconf_fname)
870 except IOError, e:
871 error("error opening flash config file '%s'" % fconf_fname, e)
872
873 if flinfo.type != "emmc":
874 mibib = MIBIB(part_fname, flinfo.pagesize, flinfo.blocksize,
875 flinfo.chipsize)
876 self.partitions = mibib.get_parts()
877 else:
878 gpt = GPT(part_fname, flinfo.pagesize, flinfo.blocksize, flinfo.chipsize)
879 self.partitions = gpt.get_parts()
880
881 self.flinfo = flinfo
882 if flinfo.type == "nand":
883 self.ipq_nand = True
884 script = NandScript(flinfo, self.ipq_nand)
885 elif flinfo.type == "nor" or flinfo.type == "norplusnand":
886 self.ipq_nand = False
887 script = NorScript(flinfo)
888 elif flinfo.type == "emmc":
889 self.ipq_nand = False
890 script = EmmcScript(flinfo)
891 else:
892 error("error, flash type unspecified.")
893
894 script.start_if("machid", machid)
895 self.__gen_script(script_fp, fconf_fp, script, images, flinfo)
896 script.end_if()
897
898 try:
899 script_fp.write(script.dumps())
900 except IOError, e:
901 error("error writing to script '%s'" % script_fp.name, e)
902
903 script_fp.close()
904
905 def __process_board_flash_emmc(self, ftype, board_section, machid, images):
906 """Extract board info from config and generate the flash script.
907
908 ftype -- string, flash type 'emmc'
909 board_section -- string, board section in config file
910 machid -- string, board machine ID in hex format
911 images -- list of ImageInfo, append images used by the board here
912 """
913
914 pagesize_param = "%s_pagesize" % ftype
915 blocksize_param = "%s_blocksize" % "emmc"
916 blocks_per_chip_param = "%s_total_blocks" % "emmc"
917 part_fname_param = "%s_partition_mbn" % ftype
918 fconf_fname_param = "%s_flash_conf" % ftype
919
920 if ftype == "norplusemmc":
921 part_fname_param = "%s_gpt_bin" % ftype
922
923 try:
924 pagesize = int(self.bconf.get(board_section, pagesize_param))
925 blocksize = int(self.bconf.get(board_section, blocksize_param))
926 chipsize = int(self.bconf.get(board_section,
927 blocks_per_chip_param))
928 except ConfigParserError, e:
929 error("missing flash info in section '%s'" % board_section, e)
930 except ValueError, e:
931 error("invalid flash info in section '%s'" % board_section, e)
932
933 if ftype == "norplusemmc":
934 ftype = "emmc"
935
936 flinfo = FlashInfo(ftype, pagesize, blocksize, chipsize)
937
938 try:
939 part_fname = self.bconf.get(board_section, part_fname_param)
940 if not os.path.isabs(part_fname):
941 part_fname = os.path.join(self.images_dname, part_fname)
942 except ConfigParserError, e:
943 error("missing partition file in section '%s'" % board_section, e)
944
945 try:
946 fconf_fname = self.bconf.get(board_section, fconf_fname_param)
947 if not os.path.isabs(fconf_fname):
948 fconf_fname = os.path.join(self.images_dname, fconf_fname)
949 except ConfigParserError, e:
950 error("missing NAND config in section '%s'" % board_section, e)
951
952 self.__gen_board_script(board_section, machid, flinfo,
953 part_fname, fconf_fname, images)
954
955 def __process_board_flash(self, ftype, board_section, machid, images):
956 """Extract board info from config and generate the flash script.
957
958 ftype -- string, flash type 'nand' or 'nor' or 'emmc' or 'norplusnand'
959 board_section -- string, board section in config file
960 machid -- string, board machine ID in hex format
961 images -- list of ImageInfo, append images used by the board here
962 """
963
964 pagesize_param = "%s_pagesize" % ftype
965 pages_per_block_param = "%s_pages_per_block" % ftype
966 blocks_per_chip_param = "%s_total_blocks" % ftype
967 part_fname_param = "%s_partition_mbn" % ftype
968 fconf_fname_param = "%s_flash_conf" % ftype
969
970 if ftype == "norplusnand" or ftype == "norplusemmc":
971 pagesize_param = "%s_pagesize" % "nor"
972 pages_per_block_param = "%s_pages_per_block" % "nor"
973 blocks_per_chip_param = "%s_total_blocks" % "nor"
974
975 try:
976 pagesize = int(self.bconf.get(board_section, pagesize_param))
977 pages_per_block = int(self.bconf.get(board_section,
978 pages_per_block_param))
979 blocks_per_chip = int(self.bconf.get(board_section,
980 blocks_per_chip_param))
981 except ConfigParserError, e:
982 error("missing flash info in section '%s'" % board_section, e)
983 except ValueError, e:
984 error("invalid flash info in section '%s'" % board_section, e)
985
986 blocksize = pages_per_block * pagesize
987 chipsize = blocks_per_chip * blocksize
988 if ftype == "norplusemmc":
989 ftype = "nor"
990
991 flinfo = FlashInfo(ftype, pagesize, blocksize, chipsize)
992
993 try:
994 part_fname = self.bconf.get(board_section, part_fname_param)
995 if not os.path.isabs(part_fname):
996 part_fname = os.path.join(self.images_dname, part_fname)
997 except ConfigParserError, e:
998 error("missing partition file in section '%s'" % board_section, e)
999
1000 try:
1001 fconf_fname = self.bconf.get(board_section, fconf_fname_param)
1002 if not os.path.isabs(fconf_fname):
1003 fconf_fname = os.path.join(self.images_dname, fconf_fname)
1004 except ConfigParserError, e:
1005 error("missing NAND config in section '%s'" % board_section, e)
1006
1007 self.__gen_board_script(board_section, machid, flinfo,
1008 part_fname, fconf_fname, images)
1009
1010 def __process_board(self, board_section, images):
1011 try:
1012 machid = int(self.bconf.get(board_section, "machid"), 0)
1013 machid = "%x" % machid
1014 except ConfigParserError, e:
1015 error("missing machid in section '%s'" % board_section, e)
1016 except ValueError, e:
1017 error("invalid machid in section '%s'" % board_section, e)
1018
1019 try:
1020 available = self.bconf.get(board_section, "nand_available")
1021 if available == "true" and self.flash_type == "nand":
1022 self.__process_board_flash("nand", board_section, machid, images)
1023 except ConfigParserError, e:
1024 error("error getting board info in section '%s'" % board_section, e)
1025
1026 try:
1027 available = self.bconf.get(board_section, "nor_available")
1028 if available == "true" and self.flash_type == "nor":
1029 self.__process_board_flash("nor", board_section, machid, images)
1030 except ConfigParserError, e:
1031 error("error getting board info in section '%s'" % board_section, e)
1032
1033 try:
1034 available = self.bconf.get(board_section, "emmc_available")
1035 if available == "true" and self.flash_type == "emmc":
1036 self.__process_board_flash_emmc("emmc", board_section, machid, images)
1037 except ConfigParserError, e:
1038 error("error getting board info in section '%s'" % board_section, e)
1039
1040 try:
1041 available = self.bconf.get(board_section, "norplusnand_available")
1042 if available == "true" and self.flash_type == "norplusnand":
1043 self.__process_board_flash("norplusnand", board_section, machid, images)
1044 except ConfigParserError, e:
1045 error("error getting board info in section '%s'" % board_section, e)
1046
1047 try:
1048 available = self.bconf.get(board_section, "norplusemmc_available")
1049 if available == "true" and self.flash_type == "norplusemmc":
1050 self.__process_board_flash("norplusemmc", board_section, machid, images)
1051 self.__process_board_flash_emmc("norplusemmc", board_section, machid, images)
1052 except ConfigParserError, e:
1053 error("error getting board info in section '%s'" % board_section, e)
1054
1055
1056
1057 def main_bconf(self, flash_type, images_dname, out_fname, brdconfig):
1058 """Start the packing process, using board config.
1059
1060 flash_type -- string, indicates flash type, 'nand' or 'nor' or 'emmc' or 'norplusnand'
1061 images_dname -- string, name of images directory
1062 out_fname -- string, output file path
1063 """
1064 self.flash_type = flash_type
1065 self.images_dname = images_dname
1066 self.img_fname = out_fname
1067
1068 self.__create_fnames()
1069
1070 try:
1071 os.unlink(self.scr_fname)
1072 except OSError, e:
1073 pass
1074
1075 try:
1076 bconf_fname = os.path.join(images_dname, brdconfig)
1077 bconf_fp = open(bconf_fname)
1078 self.bconf = ConfigParser()
1079 self.bconf.readfp(bconf_fp)
1080 bconf_fp.close()
1081 except IOError, e:
1082 error("error reading board configuration file", e)
1083 except ConfigParserError, e:
1084 error("error parsing board configuration file", e)
1085
1086 images = []
1087 for section in self.bconf.sections():
1088 self.__process_board(section, images)
1089
1090 images.insert(0, ImageInfo("script", "flash.scr", "script"))
1091 self.__mkimage(images)
1092
1093 def main(self, flinfo, images_dname, out_fname, part_fname, fconf_fname, ipq_nand):
1094 """Start the packing process.
1095
1096 flinfo -- FlashInfo object, containing flash parameters
1097 images_dname -- string, name of images directory
1098 out_fname -- string, output file path
1099 part_fname -- string, partition file path
1100 fconf_fname -- string, flash confing file path
1101 ipq_nand -- bool, indicates whether this is an IPQ device
1102 """
1103 self.flinfo = flinfo
1104 self.images_dname = images_dname
1105 self.img_fname = out_fname
1106 self.ipq_nand = ipq_nand
1107
1108 self.__create_fnames()
1109
1110 if self.flinfo.type == "nand":
1111 script = NandScript(self.flinfo, self.ipq_nand)
1112 elif self.flinfo.type == "nor" or self.flinfo.type == "norplusnand":
1113 script = NorScript(self.flinfo)
1114 elif self.flinfo.type == "emmc":
1115 script = EmmcScript(self.flinfo)
1116 else:
1117 error("Invalid flash type specified. It should be 'nand' or 'nor' or 'norplusnand'")
1118
1119 if not os.path.isabs(part_fname):
1120 part_fname = os.path.join(self.images_dname, part_fname)
1121
1122 if not os.path.isabs(fconf_fname):
1123 self.fconf_fname = os.path.join(self.images_dname, fconf_fname)
1124
1125 mibib = MIBIB(part_fname, self.flinfo.pagesize, self.flinfo.blocksize,
1126 self.flinfo.chipsize)
1127 self.partitions = mibib.get_parts()
1128
1129 script.echo("", verbose=True)
1130
1131 script.start_activity("Check environment:")
1132 script.check_isset("imgaddr")
1133 script.check_isset("machid")
1134 script.finish_activity()
1135
1136 try:
1137 info_fp = open(self.fconf_fname)
1138 except IOError, e:
1139 error("error opening info file '%s'" % self.fconf_fname, e)
1140
1141 try:
1142 scr_fp = open(self.scr_fname, "wb")
1143 except IOError, e:
1144 error("error opening script file '%s'" % self.scr_fname, e)
1145
1146 images = []
1147 self.__gen_script(scr_fp, info_fp, script, images)
1148 try:
1149 scr_fp.write(script.dumps())
1150 except IOError, e:
1151 error("error writing to script '%s'" % script_fp.name, e)
1152 scr_fp.close() # Flush out all written commands
1153
1154 images.insert(0, ImageInfo("script", "flash.scr", "script"))
1155 self.__mkimage(images)
1156
1157class UsageError(Exception):
1158 """Indicates error in command arguments."""
1159 pass
1160
1161class ArgParser(object):
1162 """Class to parse command-line arguments."""
1163
1164 DEFAULT_PAGESIZE = 4096
1165 DEFAULT_PAGES_PER_BLOCK = 64
1166 DEFAULT_BLOCKS_PER_CHIP = 1024
1167 DEFAULT_TYPE = "nand"
1168 DEFAULT_PART_FNAME = "partition.mbn"
1169 DEFAULT_FCONF_FNAME = "flash.conf"
1170
1171 def __init__(self):
1172 self.__pagesize = None
1173 self.__pages_per_block = None
1174 self.__blocksize = None
1175 self.__chipsize = None
1176 self.__flash_type = None
1177
1178 self.flash_info = None
1179 self.images_dname = None
1180 self.ipq_nand = False
1181 self.bconf = False
1182 self.bconf_fname = "boardconfig"
1183 self.part_fname = None
1184 self.fconf_fname = None
1185 self.genitb_fname = None
1186
1187 def __init_pagesize(self, pagesize):
1188 """Set the pagesize, from the command line argument.
1189
1190 pagesize -- string, flash page size
1191
1192 Raise UsageError, if pagesize is invalid
1193 """
1194 if pagesize == None:
1195 self.__pagesize = ArgParser.DEFAULT_PAGESIZE
1196 else:
1197 try:
1198 self.__pagesize = int(pagesize)
1199 except ValueError:
1200 raise UsageError("invalid page size '%s'" % pagesize)
1201
1202 def __init_blocksize(self, pages_per_block):
1203 """Set the blocksize, from the command line argument.
1204
1205 pages_per_block -- string, no. of pages in a flash block
1206
1207 Raise UsageError, if pages_per_block is invalid
1208 """
1209 if pages_per_block == None:
1210 self.__blocksize = (self.__pagesize
1211 * ArgParser.DEFAULT_PAGES_PER_BLOCK)
1212 else:
1213 try:
1214 self.__blocksize = self.__pagesize * int(pages_per_block)
1215 except ValueError:
1216 raise UsageError("invalid block size '%s'" % self.__blocksize)
1217
1218 def __init_chipsize(self, blocks_per_chip):
1219 """Set the chipsize, from the command line argument.
1220
1221 blocks_per_chip -- string, no. of blocks in a flash chip
1222
1223 Raise UsageError, if chips_per_block is invalid
1224 """
1225 if blocks_per_chip == None:
1226 self.__chipsize = (self.__blocksize
1227 * ArgParser.DEFAULT_BLOCKS_PER_CHIP)
1228 else:
1229 try:
1230 self.__chipsize = self.__blocksize * int(blocks_per_chip)
1231 except ValueError:
1232 raise UsageError("invalid chip size '%s'" % self.__chipsize)
1233
1234 def __init_flash_info(self):
1235 """Set flash_info from the parsed flash paramaters."""
1236
1237 self.flash_info = FlashInfo(self.__flash_type,
1238 self.__pagesize,
1239 self.__blocksize,
1240 self.__chipsize)
1241
1242 def __init_flash_type(self, flash_type):
1243 """Set the flash_type, from the command line argument.
1244
1245 flash_type -- string, nand or nor
1246
1247 Raise UsageError, if flash_type is invalid
1248 """
1249
1250 if flash_type == None:
1251 self.__flash_type = ArgParser.DEFAULT_TYPE
1252 elif flash_type in [ "nand", "nor", "emmc", "norplusnand", "norplusemmc" ]:
1253 self.__flash_type = flash_type
1254 else:
1255 raise UsageError("invalid flash type '%s'" % flash_type)
1256
1257 def __init_part_fname(self, part_fname):
1258 """Set the partition filename from command line argument
1259
1260 part_fname -- string, the partition filename
1261 """
1262
1263 if part_fname == None:
1264 self.part_fname = ArgParser.DEFAULT_PART_FNAME
1265 else:
1266 self.part_fname = part_fname
1267
1268 def __init_fconf_fname(self, fconf_fname):
1269 """Set the flash configuration filename from command line argument
1270
1271 fconf_fname -- string, the flash configuration filename
1272 """
1273
1274 if fconf_fname == None:
1275 self.fconf_fname = ArgParser.DEFAULT_FCONF_FNAME
1276 else:
1277 self.fconf_fname = fconf_fname
1278
1279 def __init_out_fname(self, out_fname, images_dname, flash_type,
1280 pagesize, pages_per_block, blocks_per_chip,
1281 bconf):
1282 """Set the out_fname from the command line argument.
1283
1284 out_fname -- string, the output filename
1285 images_dname -- string, the images dirname
1286 flash_type -- string, the flash type
1287 pagesize -- int, the flash page size in bytes
1288 pages_per_block -- int, the flash pages per block
1289 blocks_per_chip -- int, the flash blocks per chip
1290 bconf -- bool, whether is board config mode
1291 """
1292
1293 if out_fname == None:
1294 images_dname_norm = os.path.abspath(images_dname)
1295 if bconf:
1296 fmt = "%s-%s%simg"
1297 self.out_fname = fmt % (images_dname_norm, flash_type,
1298 os.path.extsep)
1299 else:
1300 fmt = "%s-%s-%d-%d-%d%simg"
1301 self.out_fname = fmt % (images_dname_norm, flash_type,
1302 pagesize, pages_per_block,
1303 blocks_per_chip, os.path.extsep)
1304 else:
1305 if os.path.isabs(out_fname):
1306 self.out_fname = out_fname
1307 else:
1308 images_dname_parent = os.path.dirname(images_dname)
1309 self.out_fname = os.path.join(images_dname_parent, out_fname)
1310
1311 def __init_images_dname(self, args):
1312 """Set the images_dname from the command line argument.
1313
1314 args -- list of string, command line args after stripping options
1315 """
1316 self.images_dname = args[0]
1317
1318 def parse(self, argv):
1319 """Start the parsing process, and populate members with parsed value.
1320
1321 argv -- list of string, the command line arguments
1322 """
1323 flash_type = None
1324 pagesize = None
1325 pages_per_block = None
1326 blocks_per_chip = None
1327 ipq_nand = False
1328 out_fname = None
1329 part_fname = None
1330 fconf_fname = None
1331 bconf = False
1332 bconf_fname = None
1333 genitb_fname = None
1334
1335 try:
1336 opts, args = getopt(argv[1:], "Bib:hp:t:o:c:m:f:F:M:")
1337 except GetoptError, e:
1338 raise UsageError(e.msg)
1339
1340 for option, value in opts:
1341 if option == "-t":
1342 flash_type = value
1343 elif option == "-i":
1344 ipq_nand = True
1345 elif option == "-p":
1346 pagesize = value
1347 elif option == "-b":
1348 pages_per_block = value
1349 elif option == '-c':
1350 blocks_per_chip = value
1351 elif option == "-o":
1352 out_fname = value
1353 elif option == "-m":
1354 part_fname = value
1355 elif option == "-f":
1356 fconf_fname = value
1357 elif option == "-B":
1358 bconf = True
1359 elif option == "-F":
1360 bconf_fname = value
1361 elif option == "-M":
1362 genitb_fname= value
1363
1364 if len(args) != 1:
1365 raise UsageError("insufficient arguments")
1366
1367 self.__init_flash_type(flash_type)
1368 self.__init_pagesize(pagesize)
1369 self.__init_blocksize(pages_per_block)
1370 self.__init_chipsize(blocks_per_chip)
1371 self.__init_flash_info()
1372 self.__init_images_dname(args)
1373 self.__init_out_fname(out_fname, self.images_dname,
1374 self.__flash_type, self.__pagesize,
1375 self.__blocksize / self.__pagesize,
1376 self.__chipsize / self.__blocksize,
1377 bconf)
1378 self.__init_part_fname(part_fname)
1379 self.__init_fconf_fname(fconf_fname)
1380
1381 self.ipq_nand = ipq_nand
1382 self.bconf = bconf
1383 if bconf_fname != None:
1384 self.bconf_fname = bconf_fname
1385
1386 self.genitb_fname = genitb_fname
1387
1388 def usage(self, msg):
1389 """Print error message and command usage information.
1390
1391 msg -- string, the error message
1392 """
1393 print "pack: %s" % msg
1394 print
1395 print "Usage: pack [options] IDIR"
1396 print
1397 print "where IDIR is the path containing the images."
1398 print
1399 print " -t TYPE specifies partition type, 'nand' or 'nor',"
1400 print " default is '%s'." % ArgParser.DEFAULT_TYPE
1401 print " -p SIZE specifies the page size in bytes,"
1402 print " default is %d." % ArgParser.DEFAULT_PAGESIZE
1403 print " -b COUNT specifies the pages per block,"
1404 print " default is %d." % ArgParser.DEFAULT_PAGES_PER_BLOCK
1405 print " -c COUNT specifies the no. of blocks per chip"
1406 print " default is %d." % ArgParser.DEFAULT_BLOCKS_PER_CHIP
1407 print " -i specifies IPQ processor specific NAND layout"
1408 print " switch, default disabled."
1409 print " -m FILE specifies the partition filename"
1410 print " default is '%s'." % ArgParser.DEFAULT_PART_FNAME
1411 print " -f FILE specifies the flash configuration filename"
1412 print " default is '%s'." % ArgParser.DEFAULT_FCONF_FNAME
1413 print " -o FILE specifies the output filename"
1414 print " default is IDIR-TYPE-SIZE-COUNT.img"
1415 print " if the filename is relative, it is relative"
1416 print " to the parent of IDIR."
1417 print " -M specifies script name to build single ITB with multiple dtb's,"
1418 print " default disabled."
1419 print
1420 print "NOTE: The above invocation method of pack is deprecated."
1421 print "The new pack invocation uses a board config file to retrieve"
1422 print "the board specific parameters. The new invocation is provided"
1423 print "below."
1424 print
1425 print "Usage: pack [options] IDIR"
1426 print
1427 print "where IDIR is the path containing the images."
1428 print
1429 print " -t TYPE specifies partition type, 'nand' or 'nor', or 'emmc'"
1430 print " default is '%s'." % ArgParser.DEFAULT_TYPE
1431 print " -B specifies board config should be used, for"
1432 print " flash parameters."
1433 print " -F specifies board config file name should be used, for"
1434 print " flash parameters. Defaults to 'boardconfig'"
1435 print " -o FILE specifies the output filename"
1436 print " default is IDIR-TYPE.img"
1437 print " if the filename is relative, it is relative"
1438 print " to the parent of IDIR."
1439 print
1440 print "Pack Version: %s" % version
1441
1442
1443def main():
1444 """Main script entry point.
1445
1446 Created to avoid polluting the global namespace.
1447 """
1448 try:
1449 parser = ArgParser()
1450 parser.parse(sys.argv)
1451 except UsageError, e:
1452 parser.usage(e.args[0])
1453 sys.exit(1)
1454
1455 if parser.genitb_fname:
1456 prc = subprocess.Popen(['sh', parser.genitb_fname])
1457 prc.wait()
1458
1459 if prc.returncode != 0:
1460 print 'ERROR: unable to create ITB'
1461 return prc.returncode
1462 else:
1463 print '...ITB binary created'
1464
1465
1466 pack = Pack()
1467 if parser.bconf:
1468 pack.main_bconf(parser.flash_info.type, parser.images_dname,
1469 parser.out_fname, parser.bconf_fname)
1470 else:
1471 pack.main(parser.flash_info, parser.images_dname,
1472 parser.out_fname, parser.part_fname,
1473 parser.fconf_fname, parser.ipq_nand)
1474
1475class ArgParserTestCase(TestCase):
1476 def setUp(self):
1477 self.parser = ArgParser()
1478
1479 def test_defaults(self):
1480 self.parser.parse(["pack.py", "itest"])
1481 self.assertEqual(self.parser.images_dname, "itest")
1482
1483 fmt = "itest-%s-%d-%d-%d.img"
1484 expected_fname = fmt % (ArgParser.DEFAULT_TYPE,
1485 ArgParser.DEFAULT_PAGESIZE,
1486 ArgParser.DEFAULT_PAGES_PER_BLOCK,
1487 ArgParser.DEFAULT_BLOCKS_PER_CHIP)
1488 self.assertEqual(self.parser.out_fname, expected_fname)
1489 self.assertEqual(self.parser.ipq_nand, False)
1490 self.assertEqual(self.parser.flash_info.type,
1491 ArgParser.DEFAULT_TYPE)
1492 self.assertEqual(self.parser.flash_info.pagesize,
1493 ArgParser.DEFAULT_PAGESIZE)
1494 self.assertEqual(self.parser.flash_info.blocksize,
1495 ArgParser.DEFAULT_PAGES_PER_BLOCK
1496 * ArgParser.DEFAULT_PAGESIZE)
1497 self.assertEqual(self.parser.flash_info.chipsize,
1498 ArgParser.DEFAULT_BLOCKS_PER_CHIP
1499 * ArgParser.DEFAULT_PAGES_PER_BLOCK
1500 * ArgParser.DEFAULT_PAGESIZE)
1501
1502 def test_ipq_flag(self):
1503 self.parser.parse(["pack.py", "-i", "itest"])
1504 self.assertEqual(self.parser.ipq_nand, True)
1505
1506 def test_invalid_flag(self):
1507 self.assertRaises(UsageError, self.parser.parse,
1508 ["pack.py", "-x", "itest"])
1509
1510 def test_type_option(self):
1511 self.parser.parse(["pack.py", "-t", "nor", "itest"])
1512 self.assertEqual(self.parser.flash_info.type, "nor")
1513
1514 def test_invalid_type_option(self):
1515 self.assertRaises(UsageError, self.parser.parse,
1516 ["pack.py", "-t", "abcd", "itest"])
1517
1518 def test_pagesize_option(self):
1519 self.parser.parse(["pack.py", "-p", "2048", "itest"])
1520 self.assertEqual(self.parser.flash_info.pagesize, 2048)
1521
1522 def test_invalid_pagesize_option(self):
1523 self.assertRaises(UsageError, self.parser.parse,
1524 ["pack.py", "-p", "abcd", "itest"])
1525
1526 def test_pages_per_block_option(self):
1527 self.parser.parse(["pack.py", "-b", "32", "itest"])
1528 self.assertEqual(self.parser.flash_info.blocksize,
1529 ArgParser.DEFAULT_PAGESIZE * 32)
1530
1531 def test_invalid_pages_per_block_option(self):
1532 self.assertRaises(UsageError, self.parser.parse,
1533 ["pack.py", "-b", "abcd", "itest"])
1534
1535 def test_blocks_per_chip_option(self):
1536 self.parser.parse(["pack.py", "-c", "512", "itest"])
1537 self.assertEqual(self.parser.flash_info.chipsize,
1538 ArgParser.DEFAULT_PAGESIZE
1539 * ArgParser.DEFAULT_PAGES_PER_BLOCK
1540 * 512)
1541
1542 def test_out_fname_rel_option(self):
1543 self.parser.parse(["pack.py", "-o", "abcd", "/tmp/test/itest"])
1544 self.assertEqual(self.parser.out_fname, "/tmp/test/abcd")
1545
1546 def test_out_fname_abs_option(self):
1547 self.parser.parse(["pack.py", "-o", "/tmp/abcd", "/tmp/test/itest"])
1548 self.assertEqual(self.parser.out_fname, "/tmp/abcd")
1549
1550 def test_partition_option(self):
1551 self.parser.parse(["pack.py", "-m", "abcd", "/tmp/test/itest"])
1552 self.assertEqual(self.parser.part_fname, "abcd")
1553
1554 def test_flash_conf_option(self):
1555 self.parser.parse(["pack.py", "-f", "abcd", "/tmp/test/itest"])
1556 self.assertEqual(self.parser.fconf_fname, "abcd")
1557
1558class PackTestCase(TestCase):
1559 def setUp(self):
1560 self.pack = Pack()
1561 blocksize = (ArgParser.DEFAULT_PAGESIZE
1562 * ArgParser.DEFAULT_PAGES_PER_BLOCK)
1563 chipsize = blocksize * ArgParser.DEFAULT_BLOCKS_PER_CHIP
1564
1565 self.flinfo = FlashInfo(ArgParser.DEFAULT_TYPE,
1566 ArgParser.DEFAULT_PAGESIZE,
1567 blocksize, chipsize)
1568 self.img_dname = mkdtemp()
1569 self.img_fname = self.img_dname + ".img"
1570 self.part_fname = "partition.mbn"
1571 self.fconf_fname = "flash.conf"
1572
1573 sbl1_fp = open(os.path.join(self.img_dname, "sbl1.mbn"), "w")
1574 sbl1_fp.write("#" * blocksize * 2)
1575 sbl1_fp.close()
1576
1577 self.__create_partition_mbn(blocksize, chipsize)
1578
1579 def __create_partition_mbn(self, blocksize, chipsize):
1580 part_fname = os.path.join(self.img_dname, self.part_fname)
1581
1582 mibib = MIBIB(part_fname, ArgParser.DEFAULT_PAGESIZE, blocksize,
1583 chipsize)
1584
1585 offset = 0
1586 part_size = 2 * blocksize
1587 mibib.add_part(PartInfo("0:SBL1", offset, part_size))
1588
1589 offset += part_size
1590 part_size = 2 * blocksize
1591 mibib.add_part(PartInfo("0:MIBIB", offset, part_size))
1592
1593 offset += part_size
1594 part_size = 1 * blocksize
1595 mibib.add_part(PartInfo("0:SBL2", offset, part_size))
1596
1597 offset += part_size
1598 part_size = None
1599 mibib.add_part(PartInfo("0:FS", offset, part_size))
1600
1601 mibib.write()
1602
1603 def __mkconf(self, conf_str):
1604 conf_fname = os.path.join(self.img_dname, self.fconf_fname)
1605 conf_fp = open(conf_fname, "w")
1606 conf_fp.write(conf_str)
1607 conf_fp.close()
1608
1609 def tearDown(self):
1610 rmtree(self.img_dname)
1611 try:
1612 os.remove(self.img_fname)
1613 except OSError:
1614 pass
1615
1616 def test_simple(self):
1617 self.__mkconf("""
1618[sbl1]
1619partition = 0:SBL1
1620filename = sbl1.mbn
1621""")
1622
1623 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1624 self.part_fname, self.fconf_fname, ipq_nand=False)
1625 self.assertEqual(os.path.exists(self.img_fname), True)
1626
1627 def test_missing_conf(self):
1628 self.assertRaises(SystemExit,
1629 self.pack.main,
1630 self.flinfo,
1631 self.img_dname,
1632 self.img_fname,
1633 self.part_fname,
1634 self.fconf_fname,
1635 ipq_nand=False)
1636
1637 def test_nand_layout(self):
1638 self.__mkconf("""
1639[sbl1]
1640partition = 0:SBL1
1641filename = sbl1.mbn
1642layout = sbl
1643""")
1644 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1645 self.part_fname, self.fconf_fname, ipq_nand=True)
1646 self.assertEqual(os.path.exists(self.img_fname), True)
1647
1648 def test_invalid_layout(self):
1649 self.__mkconf("""
1650[sbl1]
1651partition = 0:SBL1
1652filename = sbl1.mbn
1653layout = abcd
1654""")
1655 self.assertRaises(SystemExit,
1656 self.pack.main,
1657 self.flinfo,
1658 self.img_dname,
1659 self.img_fname,
1660 self.part_fname,
1661 self.fconf_fname,
1662 ipq_nand=True)
1663
1664 def test_inconsistent_layout(self):
1665 self.__mkconf("""
1666[sbl1]
1667partition = 0:SBL1
1668filename = sbl1.mbn
1669layout = sbl
1670""")
1671 self.assertRaises(SystemExit,
1672 self.pack.main,
1673 self.flinfo,
1674 self.img_dname,
1675 self.img_fname,
1676 self.part_fname,
1677 self.fconf_fname,
1678 ipq_nand=False)
1679
1680 def test_invalid_filename(self):
1681 self.__mkconf("""
1682[sbl1]
1683partition = 0:SBL1
1684filename = sbl10.mbn
1685""")
1686 self.assertRaises(SystemExit,
1687 self.pack.main,
1688 self.flinfo,
1689 self.img_dname,
1690 self.img_fname,
1691 self.part_fname,
1692 self.fconf_fname,
1693 ipq_nand=False)
1694
1695 def test_special_chars_in_filename(self):
1696 self.__mkconf("""
1697[slash]
1698partition = 0:SBL1
1699filename = sb\\l1.mbn
1700""")
1701
1702 sbl1_fp = open(os.path.join(self.img_dname, "sb\\l1.mbn"), "w")
1703 sbl1_fp.write("abcdef")
1704 sbl1_fp.close()
1705
1706 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1707 self.part_fname, self.fconf_fname, ipq_nand=False)
1708 self.assertEqual(os.path.exists(self.img_fname), True)
1709
1710 def __get_images(self):
1711 mkimage_output = subprocess.check_output(["mkimage", "-l", self.img_fname])
1712 return re.findall(r"Image \d+ \((.*)\)", mkimage_output)
1713
1714 def test_multi_image(self):
1715 self.__mkconf("""
1716[sbl1]
1717partition = 0:SBL1
1718filename = sbl1.mbn
1719
1720[sbl2]
1721partition = 0:MIBIB
1722filename = partition.mbn
1723""")
1724
1725 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1726 self.part_fname, self.fconf_fname, ipq_nand=False)
1727 count = len(self.__get_images())
1728 self.assertEqual(count, 3)
1729
1730 def test_include(self):
1731 self.__mkconf("""
1732[sbl1]
1733partition = 0:SBL1
1734filename = sbl1.mbn
1735include = no
1736
1737[sbl2]
1738partition = 0:MIBIB
1739filename = partition.mbn
1740""")
1741
1742 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1743 self.part_fname, self.fconf_fname, ipq_nand=False)
1744 images = self.__get_images()
1745 print images
1746 self.assertTrue("sbl2" in images)
1747 self.assertTrue("sbl1" not in images)
1748
1749 def test_yaffs_yes(self):
1750 self.__mkconf("""
1751[sbl1]
1752partition = 0:SBL1
1753filename = sbl1.mbn
1754yaffs = yes
1755""")
1756 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1757 self.part_fname, self.fconf_fname, ipq_nand=False)
1758
1759 def test_yaffs_no(self):
1760 self.__mkconf("""
1761[sbl1]
1762partition = 0:SBL1
1763filename = sbl1.mbn
1764yaffs = no
1765""")
1766 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1767 self.part_fname, self.fconf_fname, ipq_nand=False)
1768
1769 def test_yaffs_invalid(self):
1770 self.__mkconf("""
1771[sbl1]
1772partition = 0:SBL1
1773filename = sbl1.mbn
1774yaffs = abcd
1775""")
1776 self.assertRaises(SystemExit,
1777 self.pack.main,
1778 self.flinfo,
1779 self.img_dname,
1780 self.img_fname,
1781 self.part_fname,
1782 self.fconf_fname,
1783 ipq_nand=False)
1784
1785 def test_invalid_partition(self):
1786 self.__mkconf("""
1787[sbl1]
1788partition = 0:SBL5
1789filename = sbl1.mbn
1790""")
1791 self.assertRaises(SystemExit,
1792 self.pack.main,
1793 self.flinfo,
1794 self.img_dname,
1795 self.img_fname,
1796 self.part_fname,
1797 self.fconf_fname,
1798 ipq_nand=False)
1799
1800 def test_img_larger_than_partition(self):
1801 self.__mkconf("""
1802[sbl2]
1803partition = 0:SBL2
1804filename = sbl1.mbn
1805""")
1806 self.assertRaises(SystemExit,
1807 self.pack.main,
1808 self.flinfo,
1809 self.img_dname,
1810 self.img_fname,
1811 self.part_fname,
1812 self.fconf_fname,
1813 ipq_nand=False)
1814
1815 def test_machid_in_hex(self):
1816 self.__mkconf("""
1817[sbl1]
1818partition = 0:SBL1
1819filename = sbl1.mbn
1820if_machid = 0x152
1821""")
1822 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1823 self.part_fname, self.fconf_fname, ipq_nand=False)
1824 self.assertEqual(os.path.exists(self.img_fname), True)
1825
1826 def test_machid_in_dec(self):
1827 self.__mkconf("""
1828[sbl1]
1829partition = 0:SBL1
1830filename = sbl1.mbn
1831if_machid = 256
1832""")
1833 self.pack.main(self.flinfo, self.img_dname, self.img_fname,
1834 self.part_fname, self.fconf_fname, ipq_nand=False)
1835 self.assertEqual(os.path.exists(self.img_fname), True)
1836
1837 def test_machid_invalid(self):
1838 self.__mkconf("""
1839[sbl1]
1840partition = 0:SBL1
1841filename = sbl1.mbn
1842if_machid = xyz
1843""")
1844 self.assertRaises(SystemExit,
1845 self.pack.main,
1846 self.flinfo,
1847 self.img_dname,
1848 self.img_fname,
1849 self.part_fname,
1850 self.fconf_fname,
1851 ipq_nand=False)
1852
1853if __name__ == "__main__":
1854 main()