blob: 687edbe6209f3c62d8ef691e407f5a95737c2fe0 [file] [log] [blame]
ctchang38ae4922012-09-03 17:01:16 +08001#!/usr/bin/python
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +08002# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +08006"""A module to support automated testing of ChromeOS firmware.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +08007
8Utilizes services provided by saft_flashrom_util.py read/write the
9flashrom chip and to parse the flash rom image.
10
11See docstring for FlashromHandler class below.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080012"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080013
14import hashlib
15import os
16import struct
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +080017import tempfile
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080018
19class FvSection(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080020 """An object to hold information about a firmware section.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080021
22 This includes file names for the signature header and the body, and the
23 version number.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080024 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080025
26 def __init__(self, sig_name, body_name):
27 self._sig_name = sig_name
28 self._body_name = body_name
29 self._version = -1 # Is not set on construction.
30 self._flags = 0 # Is not set on construction.
31 self._sha = None # Is not set on construction.
ctchang38ae4922012-09-03 17:01:16 +080032 self._sig_sha = None # Is not set on construction.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080033 self._datakey_version = -1 # Is not set on construction.
34 self._kernel_subkey_version = -1 # Is not set on construction.
35
36 def names(self):
37 return (self._sig_name, self._body_name)
38
39 def get_sig_name(self):
40 return self._sig_name
41
42 def get_body_name(self):
43 return self._body_name
44
45 def get_version(self):
46 return self._version
47
48 def get_flags(self):
49 return self._flags
50
51 def get_sha(self):
52 return self._sha
53
ctchang38ae4922012-09-03 17:01:16 +080054 def get_sig_sha(self):
55 return self._sig_sha
56
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080057 def get_datakey_version(self):
58 return self._datakey_version
59
60 def get_kernel_subkey_version(self):
61 return self._kernel_subkey_version
62
63 def set_version(self, version):
64 self._version = version
65
66 def set_flags(self, flags):
67 self._flags = flags
68
69 def set_sha(self, sha):
70 self._sha = sha
71
ctchang38ae4922012-09-03 17:01:16 +080072 def set_sig_sha(self, sha):
73 self._sig_sha = sha
74
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080075 def set_datakey_version(self, version):
76 self._datakey_version = version
77
78 def set_kernel_subkey_version(self, version):
79 self._kernel_subkey_version = version
80
81class FlashromHandlerError(Exception):
82 pass
83
84
85class FlashromHandler(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080086 """An object to provide logical services for automated flashrom testing."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080087
88 DELTA = 1 # value to add to a byte to corrupt a section contents
89
90 # File in the state directory to store public root key.
91 PUB_KEY_FILE_NAME = 'root.pubkey'
92 FW_KEYBLOCK_FILE_NAME = 'firmware.keyblock'
93 FW_PRIV_DATA_KEY_FILE_NAME = 'firmware_data_key.vbprivk'
94 KERNEL_SUBKEY_FILE_NAME = 'kernel_subkey.vbpubk'
95
96 def __init__(self):
97 # make sure it does not accidentally overwrite the image.
98 self.fum = None
99 self.chros_if = None
100 self.image = ''
101 self.pub_key_file = ''
102
103 def init(self, flashrom_util_module,
104 chros_if,
105 pub_key_file=None,
106 dev_key_path='./',
107 target='bios'):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800108 """Flashrom handler initializer.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800109
110 Args:
111 flashrom_util_module - a module providing flashrom access utilities.
112 chros_if - a module providing interface to Chromium OS services
113 pub_key_file - a string, name of the file contaning a public key to
114 use for verifying both existing and new firmware.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800115 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800116 if target == 'bios':
117 self.fum = flashrom_util_module.flashrom_util(target_is_ec=False)
118 self.fv_sections = {
119 'a': FvSection('VBOOTA', 'FVMAIN'),
120 'b': FvSection('VBOOTB', 'FVMAINB'),
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800121 'ec_a': FvSection(None, 'ECMAINA'),
122 'ec_b': FvSection(None, 'ECMAINB'),
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800123 }
124 elif target == 'ec':
125 self.fum = flashrom_util_module.flashrom_util(target_is_ec=True)
126 self.fv_sections = {
127 'rw': FvSection(None, 'EC_RW'),
128 }
129 else:
130 raise FlashromHandlerError("Invalid target.")
131 self.chros_if = chros_if
132 self.pub_key_file = pub_key_file
133 self.dev_key_path = dev_key_path
134
135 def new_image(self, image_file=None):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800136 """Parse the full flashrom image and store sections into files.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800137
138 Args:
139 image_file - a string, the name of the file contaning full ChromeOS
140 flashrom image. If not passed in or empty - the actual
141 flashrom is read and its contents are saved into a
142 temporary file which is used instead.
143
144 The input file is parsed and the sections of importance (as defined in
145 self.fv_sections) are saved in separate files in the state directory
146 as defined in the chros_if object.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800147 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800148
149 if image_file:
150 self.image = open(image_file, 'rb').read()
151 self.fum.set_firmware_layout(image_file)
152 else:
153 self.image = self.fum.read_whole()
154
155 for section in self.fv_sections.itervalues():
156 for subsection_name in section.names():
157 if not subsection_name:
158 continue
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800159 blob = self.fum.get_section(self.image, subsection_name)
160 if blob:
161 f = open(self.chros_if.state_dir_file(subsection_name),
162 'wb')
163 f.write(blob)
164 f.close()
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800165
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800166 blob = self.fum.get_section(self.image, section.get_body_name())
167 if blob:
168 s = hashlib.sha1()
169 s.update(blob)
170 section.set_sha(s.hexdigest())
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800171
172 # If there is no "sig" subsection, skip reading version and flags.
173 if not section.get_sig_name():
174 continue
175
176 # Now determine this section's version number.
177 vb_section = self.fum.get_section(
178 self.image, section.get_sig_name())
179
180 section.set_version(self.chros_if.retrieve_body_version(vb_section))
181 section.set_flags(self.chros_if.retrieve_preamble_flags(vb_section))
182 section.set_datakey_version(
183 self.chros_if.retrieve_datakey_version(vb_section))
184 section.set_kernel_subkey_version(
185 self.chros_if.retrieve_kernel_subkey_version(vb_section))
186
ctchang38ae4922012-09-03 17:01:16 +0800187 s = hashlib.sha1()
188 s.update(self.fum.get_section(self.image, section.get_sig_name()))
189 section.set_sig_sha(s.hexdigest())
190
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800191 if not self.pub_key_file:
192 self._retrieve_pub_key()
193
194 def _retrieve_pub_key(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800195 """Retrieve root public key from the firmware GBB section."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800196
197 gbb_header_format = '<4s20s2I'
198 pubk_header_format = '<2Q'
199
200 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
201
202 # do some sanity checks
203 try:
204 sig, _, rootk_offs, rootk_size = struct.unpack_from(
205 gbb_header_format, gbb_section)
206 except struct.error, e:
207 raise FlashromHandlerError(e)
208
209 if sig != '$GBB' or (rootk_offs + rootk_size) > len(gbb_section):
210 raise FlashromHandlerError('Bad gbb header')
211
212 key_body_offset, key_body_size = struct.unpack_from(
213 pubk_header_format, gbb_section, rootk_offs)
214
215 # Generally speaking the offset field can be anything, but in case of
216 # GBB section the key is stored as a standalone entity, so the offset
217 # of the key body is expected to be equal to the key header size of
218 # 0x20.
219 # Should this convention change, the check below would fail, which
220 # would be a good prompt for revisiting this test's behavior and
221 # algorithms.
222 if key_body_offset != 0x20 or key_body_size > rootk_size:
223 raise FlashromHandlerError('Bad public key format')
224
225 # All checks passed, let's store the key in a file.
226 self.pub_key_file = self.chros_if.state_dir_file(self.PUB_KEY_FILE_NAME)
227 keyf = open(self.pub_key_file, 'w')
228 key = gbb_section[
229 rootk_offs:rootk_offs + key_body_offset + key_body_size]
230 keyf.write(key)
231 keyf.close()
232
233 def verify_image(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800234 """Confirm the image's validity.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800235
236 Using the file supplied to init() as the public key container verify
237 the two sections' (FirmwareA and FirmwareB) integrity. The contents of
238 the sections is taken from the files created by new_image()
239
240 In case there is an integrity error raises FlashromHandlerError
241 exception with the appropriate error message text.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800242 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800243
244 for section in self.fv_sections.itervalues():
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800245 if section.get_sig_name():
246 cmd = 'vbutil_firmware --verify %s --signpubkey %s --fv %s' % (
247 self.chros_if.state_dir_file(section.get_sig_name()),
248 self.pub_key_file,
249 self.chros_if.state_dir_file(section.get_body_name()))
250 self.chros_if.run_shell_command(cmd)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800251
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800252 def _modify_section(self, section, delta, body_or_sig=False,
253 corrupt_all=False):
254 """Modify a firmware section inside the image, either body or signature.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800255
256 If corrupt_all is set, the passed in delta is added to all bytes in the
257 section. Otherwise, the delta is added to the value located at 2% offset
258 into the section blob, either body or signature.
259
260 Calling this function again for the same section the complimentary
261 delta value would restore the section contents.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800262 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800263
264 if not self.image:
265 raise FlashromHandlerError(
266 'Attempt at using an uninitialized object')
267 if section not in self.fv_sections:
268 raise FlashromHandlerError('Unknown FW section %s'
269 % section)
270
271 # Get the appropriate section of the image.
272 if body_or_sig:
273 subsection_name = self.fv_sections[section].get_body_name()
274 else:
275 subsection_name = self.fv_sections[section].get_sig_name()
276 blob = self.fum.get_section(self.image, subsection_name)
277
278 # Modify the byte in it within 2% of the section blob.
279 modified_index = len(blob) / 50
280 if corrupt_all:
281 blob_list = [('%c' % ((ord(x) + delta) % 0x100)) for x in blob]
282 else:
283 blob_list = list(blob)
284 blob_list[modified_index] = ('%c' %
285 ((ord(blob[modified_index]) + delta) % 0x100))
286 self.image = self.fum.put_section(self.image,
287 subsection_name, ''.join(blob_list))
288
289 return subsection_name
290
291 def corrupt_section(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800292 """Corrupt a section signature of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800293
294 return self._modify_section(section, self.DELTA, body_or_sig=False,
295 corrupt_all=corrupt_all)
296
297 def corrupt_section_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800298 """Corrupt a section body of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800299
300 return self._modify_section(section, self.DELTA, body_or_sig=True,
301 corrupt_all=corrupt_all)
302
303 def restore_section(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800304 """Restore a previously corrupted section signature of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800305
306 return self._modify_section(section, -self.DELTA, body_or_sig=False,
307 corrupt_all=restore_all)
308
309 def restore_section_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800310 """Restore a previously corrupted section body of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800311
312 return self._modify_section(section, -self.DELTA, body_or_sig=True,
313 corrupt_all=restore_all)
314
315 def corrupt_firmware(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800316 """Corrupt a section signature in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800317
318 subsection_name = self.corrupt_section(section, corrupt_all=corrupt_all)
319 self.fum.write_partial(self.image, (subsection_name, ))
320
321 def corrupt_firmware_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800322 """Corrupt a section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800323
324 subsection_name = self.corrupt_section_body(section,
325 corrupt_all=corrupt_all)
326 self.fum.write_partial(self.image, (subsection_name, ))
327
328 def restore_firmware(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800329 """Restore the previously corrupted section sig in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800330
331 subsection_name = self.restore_section(section, restore_all=restore_all)
332 self.fum.write_partial(self.image, (subsection_name, ))
333
334 def restore_firmware_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800335 """Restore the previously corrupted section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800336
337 subsection_name = self.restore_section_body(section,
338 restore_all=False)
339 self.fum.write_partial(self.image, (subsection_name, ))
340
341 def firmware_sections_equal(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800342 """Check if firmware sections A and B are equal.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800343
344 This function presumes that the entire BIOS image integrity has been
345 verified, so different signature sections mean different images and
346 vice versa.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800347 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800348 sig_a = self.fum.get_section(self.image,
349 self.fv_sections['a'].get_sig_name())
350 sig_b = self.fum.get_section(self.image,
351 self.fv_sections['b'].get_sig_name())
352 return sig_a == sig_b
353
354 def copy_from_to(self, src, dst):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800355 """Copy one firmware image section to another.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800356
357 This function copies both signature and body of one firmware section
358 into another. After this function runs both sections are identical.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800359 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800360 src_sect = self.fv_sections[src]
361 dst_sect = self.fv_sections[dst]
362 self.image = self.fum.put_section(
363 self.image,
364 dst_sect.get_body_name(),
365 self.fum.get_section(self.image, src_sect.get_body_name()))
366 self.image = self.fum.put_section(
367 self.image,
368 dst_sect.get_sig_name(),
369 self.fum.get_section(self.image, src_sect.get_sig_name()))
370
371 def write_whole(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800372 """Write the whole image into the flashrom."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800373
374 if not self.image:
375 raise FlashromHandlerError(
376 'Attempt at using an uninitialized object')
377 self.fum.write_whole(self.image)
378
379 def dump_whole(self, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800380 """Write the whole image into a file."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800381
382 if not self.image:
383 raise FlashromHandlerError(
384 'Attempt at using an uninitialized object')
385 open(filename, 'w').write(self.image)
386
387 def dump_partial(self, subsection_name, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800388 """Write the subsection part into a file."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800389
390 if not self.image:
391 raise FlashromHandlerError(
392 'Attempt at using an uninitialized object')
393 blob = self.fum.get_section(self.image, subsection_name)
394 open(filename, 'w').write(blob)
395
396 def get_gbb_flags(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800397 """Retrieve the GBB flags"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800398 gbb_header_format = '<12sL'
399 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
400 try:
401 _, gbb_flags = struct.unpack_from(gbb_header_format, gbb_section)
402 except struct.error, e:
403 raise FlashromHandlerError(e)
404 return gbb_flags
405
Tom Wai-Hong Tam44204b32012-11-20 13:55:40 +0800406 def enable_write_protect(self):
407 """Enable write protect of the flash chip"""
408 self.fum.enable_write_protect()
409
410 def disable_write_protect(self):
411 """Disable write protect of the flash chip"""
412 self.fum.disable_write_protect()
413
ctchang38ae4922012-09-03 17:01:16 +0800414 def get_section_sig_sha(self, section):
415 """Retrieve SHA1 hash of a firmware vblock section"""
416 return self.fv_sections[section].get_sig_sha()
417
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800418 def get_section_sha(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800419 """Retrieve SHA1 hash of a firmware body section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800420 return self.fv_sections[section].get_sha()
421
422 def get_section_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800423 """Retrieve version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800424 return self.fv_sections[section].get_version()
425
426 def get_section_flags(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800427 """Retrieve preamble flags of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800428 return self.fv_sections[section].get_flags()
429
430 def get_section_datakey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800431 """Retrieve data key version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800432 return self.fv_sections[section].get_datakey_version()
433
434 def get_section_kernel_subkey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800435 """Retrieve kernel subkey version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800436 return self.fv_sections[section].get_kernel_subkey_version()
437
438 def get_section_body(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800439 """Retrieve body of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800440 subsection_name = self.fv_sections[section].get_body_name()
441 blob = self.fum.get_section(self.image, subsection_name)
442 return blob
443
ctchang38ae4922012-09-03 17:01:16 +0800444 def get_section_sig(self, section):
445 """Retrieve vblock of a firmware section"""
446 subsection_name = self.fv_sections[section].get_sig_name()
447 blob = self.fum.get_section(self.image, subsection_name)
448 return blob
449
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800450 def _find_dtb_offset(self, blob):
451 """Return the offset of DTB blob from the given firmware blob"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800452 # The RW firmware is concatenated from u-boot, dtb, and ecbin.
453 # Search the magic of dtb to locate the dtb bloc.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800454 return blob.find("\xD0\x0D\xFE\xED\x00")
455
456 def _find_ecbin_offset(self, blob):
457 """Return the offset of EC binary from the given firmware blob"""
458 dtb_offset = self._find_dtb_offset(blob)
Gabe Black23048032013-01-13 07:33:00 -0800459 # If the device tree was found, use it. Otherwise assume an index
460 # structure.
461 if dtb_offset != -1:
462 # The dtb size is a 32-bit integer which follows the magic.
463 _, dtb_size = struct.unpack_from(">2L", blob, dtb_offset)
464 # The ecbin part is aligned to 4-byte.
465 return (dtb_offset + dtb_size + 3) & ~3
466 else:
467 # The index will have a count, an offset and size for the system
468 # firmware, and then an offset and size for the EC binary.
469 return struct.unpack_from("<I", blob, 3 * 4)[0]
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800470
471 def _find_ecbin_size_offset_on_dtb(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800472 """Return the offset of EC binary size on the DTB blob"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800473 # We now temporarily use this hack to find the offset.
474 # TODO(waihong@chromium.org): Should use fdtget to get the field and
475 # fdtput to change it.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800476 dtb_offset = self._find_dtb_offset(blob)
Gabe Black23048032013-01-13 07:33:00 -0800477 # If the device tree wasn't found, give up and return an error.
478 if dtb_offset == -1:
479 return -1
Tom Wai-Hong Tam4c1c53b2013-01-15 19:20:27 +0800480 # Search the patterns "blob boot,dtb,ecbin" / "blob boot,dtb-rwa,ecbin".
481 prop_offset = blob.index("blob boot,dtb", dtb_offset) + 0x18
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800482 ecbin_size_offset = blob.index("ecbin", prop_offset) + 0x18
483 return ecbin_size_offset
484
485 def get_section_ecbin(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800486 """Retrieve EC binary of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800487 blob = self.get_section_body(section)
488 ecbin_offset = self._find_ecbin_offset(blob)
489 # Remove the pads of ecbin.
490 pad = blob[-1]
491 ecbin = blob[ecbin_offset :].rstrip(pad)
492 return ecbin
493
ctchang38ae4922012-09-03 17:01:16 +0800494 def set_section_body(self, section, blob, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800495 """Put the supplied blob to the body of the firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800496 subsection_name = self.fv_sections[section].get_body_name()
497 self.image = self.fum.put_section(self.image, subsection_name, blob)
498
ctchang38ae4922012-09-03 17:01:16 +0800499 if write_through:
500 self.dump_partial(subsection_name,
501 self.chros_if.state_dir_file(subsection_name))
502 self.fum.write_partial(self.image, (subsection_name, ))
503
504 def set_section_sig(self, section, blob, write_through=False):
505 """Put the supplied blob to the vblock of the firmware section"""
506 subsection_name = self.fv_sections[section].get_sig_name()
507 self.image = self.fum.put_section(self.image, subsection_name, blob)
508
509 if write_through:
510 self.dump_partial(subsection_name,
511 self.chros_if.state_dir_file(subsection_name))
512 self.fum.write_partial(self.image, (subsection_name, ))
513
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800514 def set_section_ecbin(self, section, ecbin, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800515 """Put the supplied EC binary to the firwmare section.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800516
517 Note that the updated firmware image is not signed yet. Should call
518 set_section_version() afterward.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800519 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800520 # Remove unncessary padding bytes.
521 pad = '\xff'
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800522 align = 4
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800523 ecbin = ecbin.rstrip(pad)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800524 ecbin += pad * (-len(ecbin) % align)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800525 ecbin_size = len(ecbin)
526
527 # Put the ecbin into the firmware body.
528 old_blob = self.get_section_body(section)
529 ecbin_offset = self._find_ecbin_offset(old_blob)
530 pad = old_blob[-1]
531 pad_size = len(old_blob) - ecbin_offset - ecbin_size
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800532
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800533 # Update the size of the EC binary on the DTB.
534 dtb_offset = self._find_dtb_offset(old_blob)
535 dtb_blob = old_blob[dtb_offset : ecbin_offset]
536 dtb_size = ecbin_offset - dtb_offset
537 with tempfile.NamedTemporaryFile() as dtb_file:
538 dtb_file.write(dtb_blob)
539 dtb_file.flush()
540 cmd = ["fdtput", "-t lu", dtb_file.name,
541 "/flash/rw-a-boot/ecbin",
542 "reg", str(ecbin_offset), str(ecbin_size)]
543 self.chros_if.run_shell_command(' '.join(cmd))
544
545 dtb_file.seek(0)
546 dtb_blob = dtb_file.read()
547 dtb_blob += pad * (-len(dtb_blob) % align)
548 assert len(dtb_blob) == dtb_size, (
549 'Updating DTB should not change its size.')
550
551 # Update the new DTB.
552 new_blob = old_blob[0 : dtb_offset] + dtb_blob + ecbin + pad * pad_size
553
554 # Also modify the EC binary size in the new format.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800555 size_offset = self._find_ecbin_size_offset_on_dtb(new_blob)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800556 if size_offset == -1:
Gabe Black23048032013-01-13 07:33:00 -0800557 # The index will have a count, an offset and size for the system
558 # firmware, and then an offset and size for the EC binary.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800559 new_blob = (new_blob[0 : 4 * 4] + struct.pack('<I', ecbin_size) +
Gabe Black23048032013-01-13 07:33:00 -0800560 new_blob[5 * 4 :])
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800561
562 self.set_section_body(section, new_blob)
563 if write_through:
564 subsection_name = self.fv_sections[section].get_body_name()
565 self.dump_partial(subsection_name,
566 self.chros_if.state_dir_file(subsection_name))
567 self.fum.write_partial(self.image, (subsection_name, ))
568
569 def set_section_version(self, section, version, flags,
570 write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800571 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800572 Re-sign the firmware section using the supplied version number and
573 flag.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800574 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800575 if (self.get_section_version(section) == version and
576 self.get_section_flags(section) == flags):
577 return # No version or flag change, nothing to do.
578 if version < 0:
579 raise FlashromHandlerError(
580 'Attempt to set version %d on section %s' % (version, section))
581 fv_section = self.fv_sections[section]
582 sig_name = self.chros_if.state_dir_file(fv_section.get_sig_name())
583 sig_size = os.path.getsize(sig_name)
584
585 # Construct the command line
586 args = ['--vblock %s' % sig_name]
587 args.append('--keyblock %s' % os.path.join(
588 self.dev_key_path, self.FW_KEYBLOCK_FILE_NAME))
589 args.append('--fv %s' % self.chros_if.state_dir_file(
590 fv_section.get_body_name()))
591 args.append('--version %d' % version)
592 args.append('--kernelkey %s' % os.path.join(
593 self.dev_key_path, self.KERNEL_SUBKEY_FILE_NAME))
594 args.append('--signprivate %s' % os.path.join(
595 self.dev_key_path, self.FW_PRIV_DATA_KEY_FILE_NAME))
596 args.append('--flags %d' % flags)
597 cmd = 'vbutil_firmware %s' % ' '.join(args)
598 self.chros_if.run_shell_command(cmd)
599
600 # Pad the new signature.
601 new_sig = open(sig_name, 'a')
Tom Wai-Hong Tamdc2d8072012-12-18 10:50:55 +0800602 pad = ('%c' % 0) * (sig_size - os.path.getsize(sig_name))
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800603 new_sig.write(pad)
604 new_sig.close()
605
606 # Inject the new signature block into the image
607 new_sig = open(sig_name, 'r').read()
608 self.image = self.fum.put_section(
609 self.image, fv_section.get_sig_name(), new_sig)
610 if write_through:
611 self.fum.write_partial(self.image, (fv_section.get_sig_name(), ))