Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(629)

Unified Diff: utility/pack_image

Issue 5985009: Add firmware image packing tool (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/vboot_reference.git@master
Patch Set: Created 9 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « utility/Makefile ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: utility/pack_image
diff --git a/utility/pack_image b/utility/pack_image
new file mode 100755
index 0000000000000000000000000000000000000000..2c9d6ce4cf2862dcaf8a43b92686addd1144d730
--- /dev/null
+++ b/utility/pack_image
@@ -0,0 +1,243 @@
+#!/usr/bin/env python2.6
+
+# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import mmap
Hung-Te 2011/01/04 08:39:03 Any reason to use mmap? I think this script does n
Che-Liang Chiou 2011/01/04 09:38:25 Because it's supposedly faster. Do you have stron
Hung-Te 2011/01/04 10:05:23 I think performance is not really critical for thi
Che-Liang Chiou 2011/01/05 08:01:55 Done.
+import os
+import re
+import struct
+import subprocess
+import sys
+import tempfile
+
+FMAP_SIGNATURE = "__FMAP__"
Hung-Te 2011/01/04 08:39:03 The fmap has an python implementation, supporting
Che-Liang Chiou 2011/01/04 09:38:25 I agree, but it should be done in a separate CL be
Hung-Te 2011/01/04 10:05:23 We already have that in autotest common lib folder
Che-Liang Chiou 2011/01/05 08:01:55 Done.
+
+FMAP_AREA_STATIC = 1 << 0
+FMAP_AREA_COMPRESSED = 1 << 1
+FMAP_AREA_RO = 1 << 2
+
+FMAP_STRLEN = 32
+FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % FMAP_STRLEN
+FMAP_AREA_FORMAT = "<II%dsH" % FMAP_STRLEN
+
+FMAP_HEADER_NAMES = (
+ 'signature',
+ 'ver_major',
+ 'ver_minor',
+ 'base',
+ 'size',
+ 'name',
+ 'nareas',
+)
+
+FMAP_AREA_NAMES = (
+ 'offset',
+ 'size',
+ 'name',
+ 'flags',
+)
+
+RE_ASSIGNMENT = re.compile(r'^(\w+)=(.*)$')
+RE_HEXADECIMAL = re.compile(r'^0x[0-9A-Fa-f]+$')
+RE_DECIMAL = re.compile(r'^[1-9][0-9]*$')
Hung-Te 2011/01/04 08:39:03 int(a, 0) prevents the use of RE_HEXADECIMAL and R
Che-Liang Chiou 2011/01/04 09:38:25 Done, but this would add one more try-catch block
+
+VERBOSE = False
+
Hung-Te 2011/01/04 08:39:03 Please make 2 blank lines for top level definition
Che-Liang Chiou 2011/01/04 09:38:25 Done.
+class ConfigError(Exception):
+ pass
+
+class PackError(Exception):
+ pass
+
+class Entry(dict):
+ @staticmethod
+ def _check_fields(kwargs, fields):
+ for f in fields:
+ if f not in kwargs:
+ raise ConfigError('Entry: missing required field: %s' % f)
+
+ def __init__(self, **kwargs):
+ Entry._check_fields(kwargs, ('offset', 'length', 'name'))
+ super(Entry, self).__init__(kwargs)
+
+ def __getattr__(self, name):
+ return self[name]
+
+ def is_overlapped(self, entry):
+ return entry.offset <= self.offset < entry.offset + entry.length or \
+ self.offset <= entry.offset < self.offset + self.length
+
+ def pack(self, fw_image, entries):
+ raise PackError('class Entry does not implement pack()')
+
+class EntryFmap(Entry):
+ def __init__(self, **kwargs):
+ Entry._check_fields(kwargs, ('ver_major', 'ver_minor', 'base', 'size'))
+ super(EntryFmap, self).__init__(**kwargs)
+
+ def pack(self, fw_image, entries):
+ areas = [e for e in entries
+ if isinstance(e, EntryBlob) or isinstance(e, EntryKeyBlock)]
+ areas.sort(key=lambda a: a.offset)
+
+ offset = 0
+
+ # pack header
+ values = []
+ for name in FMAP_HEADER_NAMES:
+ if name == 'nareas':
+ v = len(areas)
+ elif name == 'signature':
+ v = FMAP_SIGNATURE
+ else:
+ v = self[name]
+ values.append(v)
+ offset = self._append(fw_image, offset,
+ struct.pack(FMAP_HEADER_FORMAT, *values))
+
+ # pack areas
+ for a in areas:
+ values = [a.length if name == 'size' else a[name]
+ for name in FMAP_AREA_NAMES]
+ offset = self._append(fw_image, offset,
+ struct.pack(FMAP_AREA_FORMAT, *values))
+
+ if offset > self.length:
+ raise PackError('fmap too large: %d > %d' % (offset, self.length))
+
+ def _append(self, fw_image, offset, blob):
+ size = len(blob)
+ fw_image[self.offset+offset:self.offset+offset+size] = blob
+ return offset + size
+
+class EntryBlob(Entry):
+ def __init__(self, **kwargs):
+ Entry._check_fields(kwargs, ('flags', 'path'))
+ super(EntryBlob, self).__init__(**kwargs)
+
+ def pack(self, fw_image, entries):
+ size = os.stat(self.path).st_size
+ if size > 0:
+ size = min(size, self.length)
+ else:
+ size = self.length
+ with open(self.path, 'r+b') as f:
+ fvimg = mmap.mmap(f.fileno(), size, mmap.MAP_PRIVATE, mmap.PROT_READ)
+ fw_image.seek(self.offset)
+ fw_image.write(fvimg[0:size])
+ fvimg.close()
+
+class EntryKeyBlock(Entry):
+ def __init__(self, **kwargs):
+ Entry._check_fields(kwargs,
+ ('flags', 'keyblock', 'signprivate', 'version', 'fv', 'kernelkey'))
+ super(EntryKeyBlock, self).__init__(**kwargs)
+
+ def pack(self, fw_image, entries):
+ fd, path = tempfile.mkstemp()
+ try:
+ args = [
+ 'vbutil_firmware',
+ '--vblock', path,
+ '--keyblock', self.keyblock,
+ '--signprivate', self.signprivate,
+ '--version', '%d' % self.version,
+ '--fv', self.fv,
+ '--kernelkey', self.kernelkey,
+ ]
+ if VERBOSE:
+ stdout = sys.stdout
+ stderr = sys.stderr
+ else:
+ stdout = None
+ stderr = None
+ info('run: %s' % ' '.join(args))
+ proc = subprocess.Popen(args, stdout=stdout, stderr=stderr)
+ proc.wait()
+ if proc.returncode != 0:
+ raise PackError('cannot make key block: vbutil_firmware returns %d' %
+ proc.returncode)
+
+ img = mmap.mmap(fd, 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
+ size = img.size()
+ if size > self.length:
+ img.close()
+ raise PackError('key block too large: %d > %d' % (size, self.length))
+ fw_image.seek(self.offset)
+ fw_image.write(img)
+ img.close()
+ finally:
+ os.unlink(path)
+
+def parse_assignment(stmt):
+ m = RE_ASSIGNMENT.match(stmt)
+ if m is None:
+ raise ConfigError('illegal statement: %s' % repr(stmt))
+ return (m.group(1), parse_value(m.group(2)))
+
+def parse_value(expr):
+ if expr[0] == expr[-1] == '"' or expr[0] == expr[-1] == "'":
Hung-Te 2011/01/04 08:39:03 To prevent confusion and help readbility, please a
Che-Liang Chiou 2011/01/04 09:38:25 Done.
+ expr = expr[1:-1]
+ if RE_HEXADECIMAL.match(expr):
+ return int(expr, 16)
+ elif RE_DECIMAL.match(expr):
+ return int(expr)
+ else:
+ return expr # if not a number, interpret as string literals
Hung-Te 2011/01/04 08:39:03 I'd prefer a more strict syntax, ex: every string
Che-Liang Chiou 2011/01/04 09:38:25 That's not a good idea. shell will unquote comman
Hung-Te 2011/01/04 10:05:23 If you have only 3 parameters, maybe we should con
+
+def pack_image(entries, output_path, image_size):
+ entries = sorted(entries, key=lambda e: e.offset)
+ for e1, e2 in zip(entries, entries[1:]):
+ if e1.is_overlapped(e2):
+ raise PackError('overlapped entries: [%08x:%08x], [%08x:%08x]' %
+ (e1.offset, e1.offset + e1.length, e2.offset, e2.offset + e2.length))
+
+ # create image so that mmap will succeed
Hung-Te 2011/01/04 08:39:03 If you use simply buffer processing without mmap,
Che-Liang Chiou 2011/01/05 08:01:55 Done.
+ with open(output_path, 'w+b') as f:
+ f.write('\0')
+
+ with open(output_path, 'rw+b') as f:
+ fw_image = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)
+ fw_image.resize(image_size)
+ for entry in entries:
+ entry.pack(fw_image, entries)
+ fw_image.flush()
+ fw_image.close()
+
+def info(msg):
+ if VERBOSE:
+ print >>sys.stderr, 'INFO: %s' % msg
+
+def main():
+ global VERBOSE
+
+ if len(sys.argv) < 2:
+ print 'Usage: %s [-v] CONFIG_FILE [NAME=VALUE...]' % sys.argv[0]
+ sys.exit(1)
+
+ if sys.argv[1] == '-v':
+ VERBOSE = True
+ argv = sys.argv[0:1] + sys.argv[2:]
+ else:
+ argv = sys.argv
+
+ if len(argv) > 2:
+ env = dict(parse_assignment(stmt) for stmt in argv[2:])
+ else:
+ env = {}
+
+ execfile(argv[1], globals(), env)
+
+ for varname in ('ENTRIES', 'OUTPUT', 'SIZE'):
+ if varname not in env:
+ raise ConfigError('undefined variable: %s' % varname)
+ info('%s = %s' % (varname, repr(env[varname])))
+
+ pack_image(env['ENTRIES'], env['OUTPUT'], env['SIZE'])
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
« no previous file with comments | « utility/Makefile ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698