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

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: reply 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
gauravsh 2011/01/04 21:14:39 make this version independent
Hung-Te 2011/01/05 03:33:54 That reminds me something. Che-Liang used "with" w
Che-Liang Chiou 2011/01/05 08:01:55 The coding style asks for the shebang line with a
gauravsh 2011/01/06 02:06:45 Chromium OS follows PEP-8, not Google python style
2
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
gauravsh 2011/01/04 21:14:39 2011
Che-Liang Chiou 2011/01/05 08:01:55 Done.
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 mmap
8 import os
9 import re
10 import struct
11 import subprocess
12 import sys
13 import tempfile
14
15 # TODO(clchiou): Rewrite this part by importing fmap after David's fmap
Hung-Te 2011/01/04 10:05:23 Please use "official" instead of "David's"
Che-Liang Chiou 2011/01/05 08:01:55 Done.
16 # implementation is pulled into Chromium OS code base
17
18 FMAP_SIGNATURE = "__FMAP__"
19
20 FMAP_AREA_STATIC = 1 << 0
21 FMAP_AREA_COMPRESSED = 1 << 1
22 FMAP_AREA_RO = 1 << 2
23
24 FMAP_STRLEN = 32
25 FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % FMAP_STRLEN
26 FMAP_AREA_FORMAT = "<II%dsH" % FMAP_STRLEN
27
28 FMAP_HEADER_NAMES = (
29 'signature',
30 'ver_major',
31 'ver_minor',
32 'base',
33 'size',
34 'name',
35 'nareas',
36 )
37
38 FMAP_AREA_NAMES = (
39 'offset',
40 'size',
41 'name',
42 'flags',
43 )
44
45 RE_ASSIGNMENT = re.compile(r'^(\w+)=(.*)$')
46
47 VERBOSE = False
48
49
50 class ConfigError(Exception):
51 pass
52
53
54 class PackError(Exception):
55 pass
56
57
58 class Entry(dict):
gauravsh 2011/01/04 21:14:39 general comment our python style - Chromium OS fol
Che-Liang Chiou 2011/01/05 08:01:55 Done.
59
60 @staticmethod
61 def _check_fields(kwargs, fields):
62 for f in fields:
63 if f not in kwargs:
64 raise ConfigError('Entry: missing required field: %s' % f)
65
66 def __init__(self, **kwargs):
67 Entry._check_fields(kwargs, ('offset', 'length', 'name'))
68 super(Entry, self).__init__(kwargs)
69
70 def __getattr__(self, name):
71 return self[name]
72
73 def is_overlapped(self, entry):
74 return entry.offset <= self.offset < entry.offset + entry.length or \
gauravsh 2011/01/04 21:14:39 you can get rid of the \ and use implicit line bre
Che-Liang Chiou 2011/01/05 08:01:55 Done.
75 self.offset <= entry.offset < self.offset + self.length
76
77 def pack(self, fw_image, entries):
78 raise PackError('class Entry does not implement pack()')
79
80
81 class EntryFmap(Entry):
82
83 def __init__(self, **kwargs):
84 Entry._check_fields(kwargs, ('ver_major', 'ver_minor', 'base', 'size'))
85 super(EntryFmap, self).__init__(**kwargs)
86
87 def pack(self, fw_image, entries):
88 areas = [e for e in entries
89 if isinstance(e, EntryBlob) or isinstance(e, EntryKeyBlock)]
90 areas.sort(key=lambda a: a.offset)
91
92 offset = 0
93
94 # pack header
95 values = []
96 for name in FMAP_HEADER_NAMES:
97 if name == 'nareas':
98 v = len(areas)
99 elif name == 'signature':
100 v = FMAP_SIGNATURE
101 else:
102 v = self[name]
103 values.append(v)
104 offset = self._append(fw_image, offset,
105 struct.pack(FMAP_HEADER_FORMAT, *values))
106
107 # pack areas
108 for a in areas:
109 values = [a.length if name == 'size' else a[name]
110 for name in FMAP_AREA_NAMES]
111 offset = self._append(fw_image, offset,
112 struct.pack(FMAP_AREA_FORMAT, *values))
113
114 if offset > self.length:
115 raise PackError('fmap too large: %d > %d' % (offset, self.length))
116
117 def _append(self, fw_image, offset, blob):
118 size = len(blob)
119 fw_image[self.offset+offset:self.offset+offset+size] = blob
120 return offset + size
121
122
123 class EntryBlob(Entry):
124
125 def __init__(self, **kwargs):
126 Entry._check_fields(kwargs, ('flags', 'path'))
127 super(EntryBlob, self).__init__(**kwargs)
128
129 def pack(self, fw_image, entries):
130 size = os.stat(self.path).st_size
131 if size > 0:
132 size = min(size, self.length)
133 else:
134 size = self.length
135 with open(self.path, 'r+b') as f:
136 fvimg = mmap.mmap(f.fileno(), size, mmap.MAP_PRIVATE, mmap.PROT_READ)
gauravsh 2011/01/04 21:14:39 i am not sure the performance improvement from mma
Che-Liang Chiou 2011/01/05 08:01:55 Done.
137 fw_image.seek(self.offset)
138 fw_image.write(fvimg[0:size])
139 fvimg.close()
140
141
142 class EntryKeyBlock(Entry):
143
144 def __init__(self, **kwargs):
145 Entry._check_fields(kwargs,
146 ('flags', 'keyblock', 'signprivate', 'version', 'fv', 'kernelkey'))
147 super(EntryKeyBlock, self).__init__(**kwargs)
148
149 def pack(self, fw_image, entries):
150 fd, path = tempfile.mkstemp()
151 try:
152 args = [
153 'vbutil_firmware',
154 '--vblock', path,
155 '--keyblock', self.keyblock,
156 '--signprivate', self.signprivate,
157 '--version', '%d' % self.version,
158 '--fv', self.fv,
159 '--kernelkey', self.kernelkey,
160 ]
161 if VERBOSE:
162 stdout = sys.stdout
163 stderr = sys.stderr
164 else:
165 stdout = None
gauravsh 2011/01/04 21:14:39 this could be declared as a class variable right a
Che-Liang Chiou 2011/01/05 08:01:55 Done.
166 stderr = None
167 info('run: %s' % ' '.join(args))
168 proc = subprocess.Popen(args, stdout=stdout, stderr=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 img = mmap.mmap(fd, 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
gauravsh 2011/01/04 21:14:39 in general, Google style recommends not using shor
Che-Liang Chiou 2011/01/05 08:01:55 Done.
175 size = img.size()
176 if size > self.length:
177 img.close()
178 raise PackError('key block too large: %d > %d' % (size, self.length))
179 fw_image.seek(self.offset)
180 fw_image.write(img)
181 img.close()
182 finally:
183 os.unlink(path)
184
185
186 def parse_assignment(stmt):
187 m = RE_ASSIGNMENT.match(stmt)
188 if m is None:
189 raise ConfigError('illegal statement: %s' % repr(stmt))
190 return (m.group(1), parse_value(m.group(2)))
191
192
193 def parse_value(expr):
194 if (expr.startswith('"') and expr.endswith('"')) or \
Hung-Te 2011/01/04 10:05:23 You can use implicit multi-line expression, like
Che-Liang Chiou 2011/01/05 08:01:55 Done.
195 (expr.startswith("'") and expr.endswith("'")):
196 expr = expr[1:-1]
Hung-Te 2011/01/04 10:05:23 return expr[1:-1] ? Otherwise it'll be converted t
Che-Liang Chiou 2011/01/05 08:01:55 Done.
197 try:
198 return int(expr, 0)
199 except ValueError:
200 return expr # if not a number, interpret as string literals
201
202
203 def pack_image(entries, output_path, image_size):
204 entries = sorted(entries, key=lambda e: e.offset)
205 for e1, e2 in zip(entries, entries[1:]):
206 if e1.is_overlapped(e2):
207 raise PackError('overlapped entries: [%08x:%08x], [%08x:%08x]' %
208 (e1.offset, e1.offset + e1.length, e2.offset, e2.offset + e2.length))
209
210 # create image so that mmap will succeed
211 with open(output_path, 'w+b') as f:
gauravsh 2011/01/04 21:14:39 again don't use 'f' here. Use 'file'. The only pla
Hung-Te 2011/01/05 03:33:54 P.S: 'file' is a special keyword in python -- some
Che-Liang Chiou 2011/01/05 08:01:55 Done; avoid use 'file'.
212 f.write('\0')
213
214 with open(output_path, 'rw+b') as f:
215 fw_image = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)
216 fw_image.resize(image_size)
217 for entry in entries:
218 entry.pack(fw_image, entries)
219 fw_image.flush()
220 fw_image.close()
221
222
223 def info(msg):
224 if VERBOSE:
225 print >>sys.stderr, 'INFO: %s' % msg
226
227
228 def main():
229 global VERBOSE
230
231 if len(sys.argv) < 2:
232 print 'Usage: %s [-v] CONFIG_FILE [NAME=VALUE...]' % sys.argv[0]
233 sys.exit(1)
234
235 if sys.argv[1] == '-v':
236 VERBOSE = True
237 argv = sys.argv[0:1] + sys.argv[2:]
238 else:
239 argv = sys.argv
240
241 if len(argv) > 2:
242 env = dict(parse_assignment(stmt) for stmt in argv[2:])
243 else:
244 env = {}
245
246 execfile(argv[1], globals(), env)
247
248 for varname in ('ENTRIES', 'OUTPUT', 'SIZE'):
249 if varname not in env:
250 raise ConfigError('undefined variable: %s' % varname)
251 info('%s = %s' % (varname, repr(env[varname])))
252
253 pack_image(env['ENTRIES'], env['OUTPUT'], env['SIZE'])
254
255 sys.exit(0)
256
257
258 if __name__ == '__main__':
259 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