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

Side by Side 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, 11 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « utility/Makefile ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python2.6
2
3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file.
6
7 import os
8 import re
9 import struct
10 import subprocess
11 import sys
12 import tempfile
13
14 # TODO(clchiou): Rewrite this part after official flashmap implementation is
15 # pulled into Chromium OS code base
16
17 # constants imported from lib/fmap.h
18 FMAP_SIGNATURE = "__FMAP__"
19 FMAP_VER_MAJOR = 1
20 FMAP_VER_MINOR = 0
21 FMAP_STRLEN = 32
22
23 FMAP_AREA_STATIC = 1 << 0
24 FMAP_AREA_COMPRESSED = 1 << 1
25 FMAP_AREA_RO = 1 << 2
26
27 FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % (FMAP_STRLEN)
28 FMAP_AREA_FORMAT = "<II%dsH" % (FMAP_STRLEN)
29
30 FMAP_HEADER_NAMES = (
31 'signature',
32 'ver_major',
33 'ver_minor',
34 'base',
35 'size',
36 'name',
37 'nareas',
38 )
39
40 FMAP_AREA_NAMES = (
41 'offset',
42 'size',
43 'name',
44 'flags',
45 )
46
47 RE_ASSIGNMENT = re.compile(r'^(\w+)=(.*)$')
48
49 VERBOSE = False
50
51
52 class ConfigError(Exception):
53 pass
54
55
56 class PackError(Exception):
57 pass
58
59
60 class Entry(dict):
61
62 @staticmethod
63 def _CheckFields(kwargs, fields):
64 for f in fields:
65 if f not in kwargs:
66 raise ConfigError('Entry: missing required field: %s' % f)
67
68 def __init__(self, **kwargs):
69 Entry._CheckFields(kwargs, ('offset', 'length', 'name'))
70 super(Entry, self).__init__(kwargs)
71
72 def __getattr__(self, name):
73 return self[name]
74
75 def IsOverlapped(self, entry):
76 return (entry.offset <= self.offset < entry.offset + entry.length or
77 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.
78
79 def Pack(self, firmware_image, entries):
80 raise PackError('class Entry does not implement Pack()')
81
82
83 class EntryFmap(Entry):
84
85 def __init__(self, **kwargs):
86 Entry._CheckFields(kwargs, ('ver_major', 'ver_minor', 'base', 'size'))
87 super(EntryFmap, self).__init__(**kwargs)
88
89 def Pack(self, firmware_image, entries):
90 # prepare header areas
91 areas = []
92 for e in entries:
93 if isinstance(e, EntryFmapArea):
94 areas.append(dict((name, e[name] if name != 'size' else e['length'])
95 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.
96
97 # prepare header
98 obj = {'areas':areas}
99 for name in FMAP_HEADER_NAMES:
100 if name == 'nareas':
101 v = len(areas)
102 elif name == 'signature':
103 v = FMAP_SIGNATURE
104 else:
105 v = self[name]
106 obj[name] = v
107
108 blob = fmap_encode(obj)
109
110 if len(blob) > self.length:
111 raise PackError('fmap too large: %d > %d' % (len(blob), self.length))
112
113 firmware_image.seek(self.offset)
114 firmware_image.write(blob)
115
116
117 class EntryFmapArea(Entry):
118
119 def __init__(self, **kwargs):
120 Entry._CheckFields(kwargs, ('flags',))
121 super(EntryFmapArea, self).__init__(**kwargs)
122
123
124 class EntryBlob(EntryFmapArea):
125
126 def __init__(self, **kwargs):
127 Entry._CheckFields(kwargs, ('path',))
128 super(EntryBlob, self).__init__(**kwargs)
129
130 def Pack(self, firmware_image, entries):
131 size = os.stat(self.path).st_size
132 if size > 0:
133 size = min(size, self.length)
134 else:
135 size = self.length
136 with open(self.path, 'rb') as blob_image:
137 firmware_image.seek(self.offset)
138 firmware_image.write(blob_image.read(size))
139
140
141 class EntryKeyBlock(EntryFmapArea):
142
143 stdout = None
144 stderr = None
145
146 def __init__(self, **kwargs):
147 Entry._CheckFields(kwargs,
148 ('keyblock', 'signprivate', 'version', 'fv', 'kernelkey'))
149 super(EntryKeyBlock, self).__init__(**kwargs)
150 if VERBOSE:
151 EntryKeyBlock.stdout = sys.stdout
152 EntryKeyBlock.stderr = sys.stderr
153
154 def Pack(self, firmware_image, entries):
155 fd, path = tempfile.mkstemp()
156 try:
157 args = [
158 'vbutil_firmware',
159 '--vblock', path,
160 '--keyblock', self.keyblock,
161 '--signprivate', self.signprivate,
162 '--version', '%d' % self.version,
163 '--fv', self.fv,
164 '--kernelkey', self.kernelkey,
165 ]
166 _Info('run: %s' % ' '.join(args))
167 proc = subprocess.Popen(args,
168 stdout=EntryKeyBlock.stdout, stderr=EntryKeyBlock.stderr)
169 proc.wait()
170 if proc.returncode != 0:
171 raise PackError('cannot make key block: vbutil_firmware returns %d' %
172 proc.returncode)
173
174 size = os.stat(path).st_size
175 if size > self.length:
176 raise PackError('key block too large: %d > %d' % (size, self.length))
177
178 with open(path, 'rb') as keyblock_image:
179 firmware_image.seek(self.offset)
180 firmware_image.write(keyblock_image.read())
181 finally:
182 os.unlink(path)
183
184
185 # TODO(clchiou): Keep fmap_encode interface compatible with official's flashmap
186 # implementation, and remove it after it is pulled in.
187 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
188 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.
189 return struct.pack(format, *(obj[name] for name in names))
190 obj['nareas'] = len(obj['areas'])
191 blob = _format_blob(FMAP_HEADER_FORMAT, FMAP_HEADER_NAMES, obj)
192 for area in obj['areas']:
193 blob = blob + _format_blob(FMAP_AREA_FORMAT, FMAP_AREA_NAMES, area)
194 return blob
195
196
197 def parse_assignment(stmt):
198 m = RE_ASSIGNMENT.match(stmt)
199 if m is None:
200 raise ConfigError('illegal statement: %s' % repr(stmt))
201 return (m.group(1), parse_value(m.group(2)))
202
203
204 def parse_value(expr):
205 if ((expr.startswith('"') and expr.endswith('"')) or
206 (expr.startswith("'") and expr.endswith("'"))):
207 return expr[1:-1] # if it is quoted, always interpreted as string literals
208 try:
209 return int(expr, 0)
210 except ValueError:
211 return expr # if not a number, interpret as string literals
212
213
214 def pack_image(entries, output_path, image_size):
215 entries = sorted(entries, key=lambda e: e.offset)
216 for e1, e2 in zip(entries, entries[1:]):
217 if e1.IsOverlapped(e2):
218 raise PackError('overlapped entries: [%08x:%08x], [%08x:%08x]' %
219 (e1.offset, e1.offset + e1.length, e2.offset, e2.offset + e2.length))
220
221 with open(output_path, 'wb') as firmware_image:
222 # resize firmware image file
223 firmware_image.seek(0)
224 firmware_image.write('\0' * image_size)
225
226 for entry in entries:
227 entry.Pack(firmware_image, entries)
228
229
230 def _Info(msg):
231 if VERBOSE:
232 print >>sys.stderr, 'INFO: %s' % msg
233
234
235 def main():
236 global VERBOSE
237
238 if len(sys.argv) < 2:
239 print 'Usage: %s [-v] CONFIG_FILE [NAME=VALUE...]' % sys.argv[0]
240 sys.exit(1)
241
242 if sys.argv[1] == '-v':
243 VERBOSE = True
244 argv = sys.argv[0:1] + sys.argv[2:]
245 else:
246 argv = sys.argv
247
248 if len(argv) > 2:
249 env = dict(parse_assignment(stmt) for stmt in argv[2:])
250 else:
251 env = {}
252
253 execfile(argv[1], globals(), env)
254
255 for varname in ('ENTRIES', 'OUTPUT', 'SIZE'):
256 if varname not in env:
257 raise ConfigError('undefined variable: %s' % varname)
258 _Info('%s = %s' % (varname, repr(env[varname])))
259
260 pack_image(env['ENTRIES'], env['OUTPUT'], env['SIZE'])
261
262 sys.exit(0)
263
264
265 if __name__ == '__main__':
266 main()
OLDNEW
« 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