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

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: Address code review 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..8077290c526c9f1e5392e5cc8543f6ac0da13659
--- /dev/null
+++ b/utility/pack_image
@@ -0,0 +1,266 @@
+#!/usr/bin/env python2.6
+
+# Copyright (c) 2011 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 os
+import re
+import struct
+import subprocess
+import sys
+import tempfile
+
+# TODO(clchiou): Rewrite this part after official flashmap implementation is
+# pulled into Chromium OS code base
+
+# constants imported from lib/fmap.h
+FMAP_SIGNATURE = "__FMAP__"
+FMAP_VER_MAJOR = 1
+FMAP_VER_MINOR = 0
+FMAP_STRLEN = 32
+
+FMAP_AREA_STATIC = 1 << 0
+FMAP_AREA_COMPRESSED = 1 << 1
+FMAP_AREA_RO = 1 << 2
+
+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+)=(.*)$')
+
+VERBOSE = False
+
+
+class ConfigError(Exception):
+ pass
+
+
+class PackError(Exception):
+ pass
+
+
+class Entry(dict):
+
+ @staticmethod
+ def _CheckFields(kwargs, fields):
+ for f in fields:
+ if f not in kwargs:
+ raise ConfigError('Entry: missing required field: %s' % f)
+
+ def __init__(self, **kwargs):
+ Entry._CheckFields(kwargs, ('offset', 'length', 'name'))
+ super(Entry, self).__init__(kwargs)
+
+ def __getattr__(self, name):
+ return self[name]
+
+ def IsOverlapped(self, entry):
+ return (entry.offset <= self.offset < entry.offset + entry.length or
+ self.offset <= entry.offset < self.offset + self.length)
Hung-Te 2011/01/05 10:21:01 please re-indent this line to match parentheses lo
Che-Liang Chiou 2011/01/06 03:01:50 Done.
+
+ def Pack(self, firmware_image, entries):
+ raise PackError('class Entry does not implement Pack()')
+
+
+class EntryFmap(Entry):
+
+ def __init__(self, **kwargs):
+ Entry._CheckFields(kwargs, ('ver_major', 'ver_minor', 'base', 'size'))
+ super(EntryFmap, self).__init__(**kwargs)
+
+ def Pack(self, firmware_image, entries):
+ # prepare header areas
+ areas = []
+ for e in entries:
+ if isinstance(e, EntryFmapArea):
+ areas.append(dict((name, e[name] if name != 'size' else e['length'])
+ for name in FMAP_AREA_NAMES))
Hung-Te 2011/01/05 10:21:01 please re-indent this line to match parentheses lo
Che-Liang Chiou 2011/01/06 03:01:50 Done.
+
+ # prepare header
+ obj = {'areas':areas}
+ for name in FMAP_HEADER_NAMES:
+ if name == 'nareas':
+ v = len(areas)
+ elif name == 'signature':
+ v = FMAP_SIGNATURE
+ else:
+ v = self[name]
+ obj[name] = v
+
+ blob = fmap_encode(obj)
+
+ if len(blob) > self.length:
+ raise PackError('fmap too large: %d > %d' % (len(blob), self.length))
+
+ firmware_image.seek(self.offset)
+ firmware_image.write(blob)
+
+
+class EntryFmapArea(Entry):
+
+ def __init__(self, **kwargs):
+ Entry._CheckFields(kwargs, ('flags',))
+ super(EntryFmapArea, self).__init__(**kwargs)
+
+
+class EntryBlob(EntryFmapArea):
+
+ def __init__(self, **kwargs):
+ Entry._CheckFields(kwargs, ('path',))
+ super(EntryBlob, self).__init__(**kwargs)
+
+ def Pack(self, firmware_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, 'rb') as blob_image:
+ firmware_image.seek(self.offset)
+ firmware_image.write(blob_image.read(size))
+
+
+class EntryKeyBlock(EntryFmapArea):
+
+ stdout = None
+ stderr = None
+
+ def __init__(self, **kwargs):
+ Entry._CheckFields(kwargs,
+ ('keyblock', 'signprivate', 'version', 'fv', 'kernelkey'))
+ super(EntryKeyBlock, self).__init__(**kwargs)
+ if VERBOSE:
+ EntryKeyBlock.stdout = sys.stdout
+ EntryKeyBlock.stderr = sys.stderr
+
+ def Pack(self, firmware_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,
+ ]
+ _Info('run: %s' % ' '.join(args))
+ proc = subprocess.Popen(args,
+ stdout=EntryKeyBlock.stdout, stderr=EntryKeyBlock.stderr)
+ proc.wait()
+ if proc.returncode != 0:
+ raise PackError('cannot make key block: vbutil_firmware returns %d' %
+ proc.returncode)
+
+ size = os.stat(path).st_size
+ if size > self.length:
+ raise PackError('key block too large: %d > %d' % (size, self.length))
+
+ with open(path, 'rb') as keyblock_image:
+ firmware_image.seek(self.offset)
+ firmware_image.write(keyblock_image.read())
+ finally:
+ os.unlink(path)
+
+
+# TODO(clchiou): Keep fmap_encode interface compatible with official's flashmap
+# implementation, and remove it after it is pulled in.
+def fmap_encode(obj):
gauravsh 2011/01/06 02:06:45 nit: use CamelCase for method names
Che-Liang Chiou 2011/01/06 03:01:50 fmap_encode() is named for begin compatible with o
+ def _format_blob(format, names, obj):
gauravsh 2011/01/06 02:06:45 same here
Che-Liang Chiou 2011/01/06 03:01:50 Done.
+ return struct.pack(format, *(obj[name] for name in names))
+ obj['nareas'] = len(obj['areas'])
+ blob = _format_blob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, obj)
+ for area in obj['areas']:
+ blob = blob + _format_blob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
+ return blob
+
+
+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.startswith('"') and expr.endswith('"')) or
+ (expr.startswith("'") and expr.endswith("'"))):
+ return expr[1:-1] # if it is quoted, always interpreted as string literals
+ try:
+ return int(expr, 0)
+ except ValueError:
+ return expr # if not a number, interpret as string literals
+
+
+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.IsOverlapped(e2):
+ raise PackError('overlapped entries: [%08x:%08x], [%08x:%08x]' %
+ (e1.offset, e1.offset + e1.length, e2.offset, e2.offset + e2.length))
+
+ with open(output_path, 'wb') as firmware_image:
+ # resize firmware image file
+ firmware_image.seek(0)
+ firmware_image.write('\0' * image_size)
+
+ for entry in entries:
+ entry.Pack(firmware_image, entries)
+
+
+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