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

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: 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) 2010 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 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.
8 import os
9 import re
10 import struct
11 import subprocess
12 import sys
13 import tempfile
14
15 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.
16
17 FMAP_AREA_STATIC = 1 << 0
18 FMAP_AREA_COMPRESSED = 1 << 1
19 FMAP_AREA_RO = 1 << 2
20
21 FMAP_STRLEN = 32
22 FMAP_HEADER_FORMAT = "<8sBBQI%dsH" % FMAP_STRLEN
23 FMAP_AREA_FORMAT = "<II%dsH" % FMAP_STRLEN
24
25 FMAP_HEADER_NAMES = (
26 'signature',
27 'ver_major',
28 'ver_minor',
29 'base',
30 'size',
31 'name',
32 'nareas',
33 )
34
35 FMAP_AREA_NAMES = (
36 'offset',
37 'size',
38 'name',
39 'flags',
40 )
41
42 RE_ASSIGNMENT = re.compile(r'^(\w+)=(.*)$')
43 RE_HEXADECIMAL = re.compile(r'^0x[0-9A-Fa-f]+$')
44 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
45
46 VERBOSE = False
47
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.
48 class ConfigError(Exception):
49 pass
50
51 class PackError(Exception):
52 pass
53
54 class Entry(dict):
55 @staticmethod
56 def _check_fields(kwargs, fields):
57 for f in fields:
58 if f not in kwargs:
59 raise ConfigError('Entry: missing required field: %s' % f)
60
61 def __init__(self, **kwargs):
62 Entry._check_fields(kwargs, ('offset', 'length', 'name'))
63 super(Entry, self).__init__(kwargs)
64
65 def __getattr__(self, name):
66 return self[name]
67
68 def is_overlapped(self, entry):
69 return entry.offset <= self.offset < entry.offset + entry.length or \
70 self.offset <= entry.offset < self.offset + self.length
71
72 def pack(self, fw_image, entries):
73 raise PackError('class Entry does not implement pack()')
74
75 class EntryFmap(Entry):
76 def __init__(self, **kwargs):
77 Entry._check_fields(kwargs, ('ver_major', 'ver_minor', 'base', 'size'))
78 super(EntryFmap, self).__init__(**kwargs)
79
80 def pack(self, fw_image, entries):
81 areas = [e for e in entries
82 if isinstance(e, EntryBlob) or isinstance(e, EntryKeyBlock)]
83 areas.sort(key=lambda a: a.offset)
84
85 offset = 0
86
87 # pack header
88 values = []
89 for name in FMAP_HEADER_NAMES:
90 if name == 'nareas':
91 v = len(areas)
92 elif name == 'signature':
93 v = FMAP_SIGNATURE
94 else:
95 v = self[name]
96 values.append(v)
97 offset = self._append(fw_image, offset,
98 struct.pack(FMAP_HEADER_FORMAT, *values))
99
100 # pack areas
101 for a in areas:
102 values = [a.length if name == 'size' else a[name]
103 for name in FMAP_AREA_NAMES]
104 offset = self._append(fw_image, offset,
105 struct.pack(FMAP_AREA_FORMAT, *values))
106
107 if offset > self.length:
108 raise PackError('fmap too large: %d > %d' % (offset, self.length))
109
110 def _append(self, fw_image, offset, blob):
111 size = len(blob)
112 fw_image[self.offset+offset:self.offset+offset+size] = blob
113 return offset + size
114
115 class EntryBlob(Entry):
116 def __init__(self, **kwargs):
117 Entry._check_fields(kwargs, ('flags', 'path'))
118 super(EntryBlob, self).__init__(**kwargs)
119
120 def pack(self, fw_image, entries):
121 size = os.stat(self.path).st_size
122 if size > 0:
123 size = min(size, self.length)
124 else:
125 size = self.length
126 with open(self.path, 'r+b') as f:
127 fvimg = mmap.mmap(f.fileno(), size, mmap.MAP_PRIVATE, mmap.PROT_READ)
128 fw_image.seek(self.offset)
129 fw_image.write(fvimg[0:size])
130 fvimg.close()
131
132 class EntryKeyBlock(Entry):
133 def __init__(self, **kwargs):
134 Entry._check_fields(kwargs,
135 ('flags', 'keyblock', 'signprivate', 'version', 'fv', 'kernelkey'))
136 super(EntryKeyBlock, self).__init__(**kwargs)
137
138 def pack(self, fw_image, entries):
139 fd, path = tempfile.mkstemp()
140 try:
141 args = [
142 'vbutil_firmware',
143 '--vblock', path,
144 '--keyblock', self.keyblock,
145 '--signprivate', self.signprivate,
146 '--version', '%d' % self.version,
147 '--fv', self.fv,
148 '--kernelkey', self.kernelkey,
149 ]
150 if VERBOSE:
151 stdout = sys.stdout
152 stderr = sys.stderr
153 else:
154 stdout = None
155 stderr = None
156 info('run: %s' % ' '.join(args))
157 proc = subprocess.Popen(args, stdout=stdout, stderr=stderr)
158 proc.wait()
159 if proc.returncode != 0:
160 raise PackError('cannot make key block: vbutil_firmware returns %d' %
161 proc.returncode)
162
163 img = mmap.mmap(fd, 0, mmap.MAP_PRIVATE, mmap.PROT_READ)
164 size = img.size()
165 if size > self.length:
166 img.close()
167 raise PackError('key block too large: %d > %d' % (size, self.length))
168 fw_image.seek(self.offset)
169 fw_image.write(img)
170 img.close()
171 finally:
172 os.unlink(path)
173
174 def parse_assignment(stmt):
175 m = RE_ASSIGNMENT.match(stmt)
176 if m is None:
177 raise ConfigError('illegal statement: %s' % repr(stmt))
178 return (m.group(1), parse_value(m.group(2)))
179
180 def parse_value(expr):
181 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.
182 expr = expr[1:-1]
183 if RE_HEXADECIMAL.match(expr):
184 return int(expr, 16)
185 elif RE_DECIMAL.match(expr):
186 return int(expr)
187 else:
188 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
189
190 def pack_image(entries, output_path, image_size):
191 entries = sorted(entries, key=lambda e: e.offset)
192 for e1, e2 in zip(entries, entries[1:]):
193 if e1.is_overlapped(e2):
194 raise PackError('overlapped entries: [%08x:%08x], [%08x:%08x]' %
195 (e1.offset, e1.offset + e1.length, e2.offset, e2.offset + e2.length))
196
197 # 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.
198 with open(output_path, 'w+b') as f:
199 f.write('\0')
200
201 with open(output_path, 'rw+b') as f:
202 fw_image = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)
203 fw_image.resize(image_size)
204 for entry in entries:
205 entry.pack(fw_image, entries)
206 fw_image.flush()
207 fw_image.close()
208
209 def info(msg):
210 if VERBOSE:
211 print >>sys.stderr, 'INFO: %s' % msg
212
213 def main():
214 global VERBOSE
215
216 if len(sys.argv) < 2:
217 print 'Usage: %s [-v] CONFIG_FILE [NAME=VALUE...]' % sys.argv[0]
218 sys.exit(1)
219
220 if sys.argv[1] == '-v':
221 VERBOSE = True
222 argv = sys.argv[0:1] + sys.argv[2:]
223 else:
224 argv = sys.argv
225
226 if len(argv) > 2:
227 env = dict(parse_assignment(stmt) for stmt in argv[2:])
228 else:
229 env = {}
230
231 execfile(argv[1], globals(), env)
232
233 for varname in ('ENTRIES', 'OUTPUT', 'SIZE'):
234 if varname not in env:
235 raise ConfigError('undefined variable: %s' % varname)
236 info('%s = %s' % (varname, repr(env[varname])))
237
238 pack_image(env['ENTRIES'], env['OUTPUT'], env['SIZE'])
239
240 sys.exit(0)
241
242 if __name__ == '__main__':
243 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