blob: da67980375d843507abb0ec2b2538975ffc24a16 [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'),
121 }
122 elif target == 'ec':
123 self.fum = flashrom_util_module.flashrom_util(target_is_ec=True)
124 self.fv_sections = {
125 'rw': FvSection(None, 'EC_RW'),
126 }
127 else:
128 raise FlashromHandlerError("Invalid target.")
129 self.chros_if = chros_if
130 self.pub_key_file = pub_key_file
131 self.dev_key_path = dev_key_path
132
133 def new_image(self, image_file=None):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800134 """Parse the full flashrom image and store sections into files.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800135
136 Args:
137 image_file - a string, the name of the file contaning full ChromeOS
138 flashrom image. If not passed in or empty - the actual
139 flashrom is read and its contents are saved into a
140 temporary file which is used instead.
141
142 The input file is parsed and the sections of importance (as defined in
143 self.fv_sections) are saved in separate files in the state directory
144 as defined in the chros_if object.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800145 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800146
147 if image_file:
148 self.image = open(image_file, 'rb').read()
149 self.fum.set_firmware_layout(image_file)
150 else:
151 self.image = self.fum.read_whole()
152
153 for section in self.fv_sections.itervalues():
154 for subsection_name in section.names():
155 if not subsection_name:
156 continue
157 f = open(self.chros_if.state_dir_file(subsection_name), 'wb')
158 f.write(self.fum.get_section(self.image, subsection_name))
159 f.close()
160
161 s = hashlib.sha1()
162 s.update(self.fum.get_section(self.image, section.get_body_name()))
163 section.set_sha(s.hexdigest())
164
165 # If there is no "sig" subsection, skip reading version and flags.
166 if not section.get_sig_name():
167 continue
168
169 # Now determine this section's version number.
170 vb_section = self.fum.get_section(
171 self.image, section.get_sig_name())
172
173 section.set_version(self.chros_if.retrieve_body_version(vb_section))
174 section.set_flags(self.chros_if.retrieve_preamble_flags(vb_section))
175 section.set_datakey_version(
176 self.chros_if.retrieve_datakey_version(vb_section))
177 section.set_kernel_subkey_version(
178 self.chros_if.retrieve_kernel_subkey_version(vb_section))
179
ctchang38ae4922012-09-03 17:01:16 +0800180 s = hashlib.sha1()
181 s.update(self.fum.get_section(self.image, section.get_sig_name()))
182 section.set_sig_sha(s.hexdigest())
183
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800184 if not self.pub_key_file:
185 self._retrieve_pub_key()
186
187 def _retrieve_pub_key(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800188 """Retrieve root public key from the firmware GBB section."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800189
190 gbb_header_format = '<4s20s2I'
191 pubk_header_format = '<2Q'
192
193 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
194
195 # do some sanity checks
196 try:
197 sig, _, rootk_offs, rootk_size = struct.unpack_from(
198 gbb_header_format, gbb_section)
199 except struct.error, e:
200 raise FlashromHandlerError(e)
201
202 if sig != '$GBB' or (rootk_offs + rootk_size) > len(gbb_section):
203 raise FlashromHandlerError('Bad gbb header')
204
205 key_body_offset, key_body_size = struct.unpack_from(
206 pubk_header_format, gbb_section, rootk_offs)
207
208 # Generally speaking the offset field can be anything, but in case of
209 # GBB section the key is stored as a standalone entity, so the offset
210 # of the key body is expected to be equal to the key header size of
211 # 0x20.
212 # Should this convention change, the check below would fail, which
213 # would be a good prompt for revisiting this test's behavior and
214 # algorithms.
215 if key_body_offset != 0x20 or key_body_size > rootk_size:
216 raise FlashromHandlerError('Bad public key format')
217
218 # All checks passed, let's store the key in a file.
219 self.pub_key_file = self.chros_if.state_dir_file(self.PUB_KEY_FILE_NAME)
220 keyf = open(self.pub_key_file, 'w')
221 key = gbb_section[
222 rootk_offs:rootk_offs + key_body_offset + key_body_size]
223 keyf.write(key)
224 keyf.close()
225
226 def verify_image(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800227 """Confirm the image's validity.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800228
229 Using the file supplied to init() as the public key container verify
230 the two sections' (FirmwareA and FirmwareB) integrity. The contents of
231 the sections is taken from the files created by new_image()
232
233 In case there is an integrity error raises FlashromHandlerError
234 exception with the appropriate error message text.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800235 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800236
237 for section in self.fv_sections.itervalues():
238 cmd = 'vbutil_firmware --verify %s --signpubkey %s --fv %s' % (
239 self.chros_if.state_dir_file(section.get_sig_name()),
240 self.pub_key_file,
241 self.chros_if.state_dir_file(section.get_body_name()))
242 self.chros_if.run_shell_command(cmd)
243
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800244 def _modify_section(self, section, delta, body_or_sig=False,
245 corrupt_all=False):
246 """Modify a firmware section inside the image, either body or signature.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800247
248 If corrupt_all is set, the passed in delta is added to all bytes in the
249 section. Otherwise, the delta is added to the value located at 2% offset
250 into the section blob, either body or signature.
251
252 Calling this function again for the same section the complimentary
253 delta value would restore the section contents.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800254 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800255
256 if not self.image:
257 raise FlashromHandlerError(
258 'Attempt at using an uninitialized object')
259 if section not in self.fv_sections:
260 raise FlashromHandlerError('Unknown FW section %s'
261 % section)
262
263 # Get the appropriate section of the image.
264 if body_or_sig:
265 subsection_name = self.fv_sections[section].get_body_name()
266 else:
267 subsection_name = self.fv_sections[section].get_sig_name()
268 blob = self.fum.get_section(self.image, subsection_name)
269
270 # Modify the byte in it within 2% of the section blob.
271 modified_index = len(blob) / 50
272 if corrupt_all:
273 blob_list = [('%c' % ((ord(x) + delta) % 0x100)) for x in blob]
274 else:
275 blob_list = list(blob)
276 blob_list[modified_index] = ('%c' %
277 ((ord(blob[modified_index]) + delta) % 0x100))
278 self.image = self.fum.put_section(self.image,
279 subsection_name, ''.join(blob_list))
280
281 return subsection_name
282
283 def corrupt_section(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800284 """Corrupt a section signature of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800285
286 return self._modify_section(section, self.DELTA, body_or_sig=False,
287 corrupt_all=corrupt_all)
288
289 def corrupt_section_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800290 """Corrupt a section body of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800291
292 return self._modify_section(section, self.DELTA, body_or_sig=True,
293 corrupt_all=corrupt_all)
294
295 def restore_section(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800296 """Restore a previously corrupted section signature of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800297
298 return self._modify_section(section, -self.DELTA, body_or_sig=False,
299 corrupt_all=restore_all)
300
301 def restore_section_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800302 """Restore a previously corrupted section body of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800303
304 return self._modify_section(section, -self.DELTA, body_or_sig=True,
305 corrupt_all=restore_all)
306
307 def corrupt_firmware(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800308 """Corrupt a section signature in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800309
310 subsection_name = self.corrupt_section(section, corrupt_all=corrupt_all)
311 self.fum.write_partial(self.image, (subsection_name, ))
312
313 def corrupt_firmware_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800314 """Corrupt a section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800315
316 subsection_name = self.corrupt_section_body(section,
317 corrupt_all=corrupt_all)
318 self.fum.write_partial(self.image, (subsection_name, ))
319
320 def restore_firmware(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800321 """Restore the previously corrupted section sig in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800322
323 subsection_name = self.restore_section(section, restore_all=restore_all)
324 self.fum.write_partial(self.image, (subsection_name, ))
325
326 def restore_firmware_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800327 """Restore the previously corrupted section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800328
329 subsection_name = self.restore_section_body(section,
330 restore_all=False)
331 self.fum.write_partial(self.image, (subsection_name, ))
332
333 def firmware_sections_equal(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800334 """Check if firmware sections A and B are equal.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800335
336 This function presumes that the entire BIOS image integrity has been
337 verified, so different signature sections mean different images and
338 vice versa.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800339 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800340 sig_a = self.fum.get_section(self.image,
341 self.fv_sections['a'].get_sig_name())
342 sig_b = self.fum.get_section(self.image,
343 self.fv_sections['b'].get_sig_name())
344 return sig_a == sig_b
345
346 def copy_from_to(self, src, dst):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800347 """Copy one firmware image section to another.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800348
349 This function copies both signature and body of one firmware section
350 into another. After this function runs both sections are identical.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800351 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800352 src_sect = self.fv_sections[src]
353 dst_sect = self.fv_sections[dst]
354 self.image = self.fum.put_section(
355 self.image,
356 dst_sect.get_body_name(),
357 self.fum.get_section(self.image, src_sect.get_body_name()))
358 self.image = self.fum.put_section(
359 self.image,
360 dst_sect.get_sig_name(),
361 self.fum.get_section(self.image, src_sect.get_sig_name()))
362
363 def write_whole(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800364 """Write the whole image into the flashrom."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800365
366 if not self.image:
367 raise FlashromHandlerError(
368 'Attempt at using an uninitialized object')
369 self.fum.write_whole(self.image)
370
371 def dump_whole(self, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800372 """Write the whole image into a file."""
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 open(filename, 'w').write(self.image)
378
379 def dump_partial(self, subsection_name, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800380 """Write the subsection part 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 blob = self.fum.get_section(self.image, subsection_name)
386 open(filename, 'w').write(blob)
387
388 def get_gbb_flags(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800389 """Retrieve the GBB flags"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800390 gbb_header_format = '<12sL'
391 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
392 try:
393 _, gbb_flags = struct.unpack_from(gbb_header_format, gbb_section)
394 except struct.error, e:
395 raise FlashromHandlerError(e)
396 return gbb_flags
397
Tom Wai-Hong Tam44204b32012-11-20 13:55:40 +0800398 def enable_write_protect(self):
399 """Enable write protect of the flash chip"""
400 self.fum.enable_write_protect()
401
402 def disable_write_protect(self):
403 """Disable write protect of the flash chip"""
404 self.fum.disable_write_protect()
405
ctchang38ae4922012-09-03 17:01:16 +0800406 def get_section_sig_sha(self, section):
407 """Retrieve SHA1 hash of a firmware vblock section"""
408 return self.fv_sections[section].get_sig_sha()
409
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800410 def get_section_sha(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800411 """Retrieve SHA1 hash of a firmware body section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800412 return self.fv_sections[section].get_sha()
413
414 def get_section_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800415 """Retrieve version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800416 return self.fv_sections[section].get_version()
417
418 def get_section_flags(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800419 """Retrieve preamble flags of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800420 return self.fv_sections[section].get_flags()
421
422 def get_section_datakey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800423 """Retrieve data key version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800424 return self.fv_sections[section].get_datakey_version()
425
426 def get_section_kernel_subkey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800427 """Retrieve kernel subkey version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800428 return self.fv_sections[section].get_kernel_subkey_version()
429
430 def get_section_body(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800431 """Retrieve body of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800432 subsection_name = self.fv_sections[section].get_body_name()
433 blob = self.fum.get_section(self.image, subsection_name)
434 return blob
435
ctchang38ae4922012-09-03 17:01:16 +0800436 def get_section_sig(self, section):
437 """Retrieve vblock of a firmware section"""
438 subsection_name = self.fv_sections[section].get_sig_name()
439 blob = self.fum.get_section(self.image, subsection_name)
440 return blob
441
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800442 def _find_dtb_offset(self, blob):
443 """Return the offset of DTB blob from the given firmware blob"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800444 # The RW firmware is concatenated from u-boot, dtb, and ecbin.
445 # Search the magic of dtb to locate the dtb bloc.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800446 return blob.find("\xD0\x0D\xFE\xED\x00")
447
448 def _find_ecbin_offset(self, blob):
449 """Return the offset of EC binary from the given firmware blob"""
450 dtb_offset = self._find_dtb_offset(blob)
Gabe Black23048032013-01-13 07:33:00 -0800451 # If the device tree was found, use it. Otherwise assume an index
452 # structure.
453 if dtb_offset != -1:
454 # The dtb size is a 32-bit integer which follows the magic.
455 _, dtb_size = struct.unpack_from(">2L", blob, dtb_offset)
456 # The ecbin part is aligned to 4-byte.
457 return (dtb_offset + dtb_size + 3) & ~3
458 else:
459 # The index will have a count, an offset and size for the system
460 # firmware, and then an offset and size for the EC binary.
461 return struct.unpack_from("<I", blob, 3 * 4)[0]
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800462
463 def _find_ecbin_size_offset_on_dtb(self, blob):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800464 """Return the offset of EC binary size on the DTB blob"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800465 # We now temporarily use this hack to find the offset.
466 # TODO(waihong@chromium.org): Should use fdtget to get the field and
467 # fdtput to change it.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800468 dtb_offset = self._find_dtb_offset(blob)
Gabe Black23048032013-01-13 07:33:00 -0800469 # If the device tree wasn't found, give up and return an error.
470 if dtb_offset == -1:
471 return -1
Tom Wai-Hong Tam4c1c53b2013-01-15 19:20:27 +0800472 # Search the patterns "blob boot,dtb,ecbin" / "blob boot,dtb-rwa,ecbin".
473 prop_offset = blob.index("blob boot,dtb", dtb_offset) + 0x18
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800474 ecbin_size_offset = blob.index("ecbin", prop_offset) + 0x18
475 return ecbin_size_offset
476
477 def get_section_ecbin(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800478 """Retrieve EC binary of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800479 blob = self.get_section_body(section)
480 ecbin_offset = self._find_ecbin_offset(blob)
481 # Remove the pads of ecbin.
482 pad = blob[-1]
483 ecbin = blob[ecbin_offset :].rstrip(pad)
484 return ecbin
485
ctchang38ae4922012-09-03 17:01:16 +0800486 def set_section_body(self, section, blob, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800487 """Put the supplied blob to the body of the firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800488 subsection_name = self.fv_sections[section].get_body_name()
489 self.image = self.fum.put_section(self.image, subsection_name, blob)
490
ctchang38ae4922012-09-03 17:01:16 +0800491 if write_through:
492 self.dump_partial(subsection_name,
493 self.chros_if.state_dir_file(subsection_name))
494 self.fum.write_partial(self.image, (subsection_name, ))
495
496 def set_section_sig(self, section, blob, write_through=False):
497 """Put the supplied blob to the vblock of the firmware section"""
498 subsection_name = self.fv_sections[section].get_sig_name()
499 self.image = self.fum.put_section(self.image, subsection_name, blob)
500
501 if write_through:
502 self.dump_partial(subsection_name,
503 self.chros_if.state_dir_file(subsection_name))
504 self.fum.write_partial(self.image, (subsection_name, ))
505
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800506 def set_section_ecbin(self, section, ecbin, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800507 """Put the supplied EC binary to the firwmare section.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800508
509 Note that the updated firmware image is not signed yet. Should call
510 set_section_version() afterward.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800511 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800512 # Remove unncessary padding bytes.
513 pad = '\xff'
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800514 align = 4
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800515 ecbin = ecbin.rstrip(pad)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800516 ecbin += pad * (-len(ecbin) % align)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800517 ecbin_size = len(ecbin)
518
519 # Put the ecbin into the firmware body.
520 old_blob = self.get_section_body(section)
521 ecbin_offset = self._find_ecbin_offset(old_blob)
522 pad = old_blob[-1]
523 pad_size = len(old_blob) - ecbin_offset - ecbin_size
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800524
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800525 # Update the size of the EC binary on the DTB.
526 dtb_offset = self._find_dtb_offset(old_blob)
527 dtb_blob = old_blob[dtb_offset : ecbin_offset]
528 dtb_size = ecbin_offset - dtb_offset
529 with tempfile.NamedTemporaryFile() as dtb_file:
530 dtb_file.write(dtb_blob)
531 dtb_file.flush()
532 cmd = ["fdtput", "-t lu", dtb_file.name,
533 "/flash/rw-a-boot/ecbin",
534 "reg", str(ecbin_offset), str(ecbin_size)]
535 self.chros_if.run_shell_command(' '.join(cmd))
536
537 dtb_file.seek(0)
538 dtb_blob = dtb_file.read()
539 dtb_blob += pad * (-len(dtb_blob) % align)
540 assert len(dtb_blob) == dtb_size, (
541 'Updating DTB should not change its size.')
542
543 # Update the new DTB.
544 new_blob = old_blob[0 : dtb_offset] + dtb_blob + ecbin + pad * pad_size
545
546 # Also modify the EC binary size in the new format.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800547 size_offset = self._find_ecbin_size_offset_on_dtb(new_blob)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800548 if size_offset == -1:
Gabe Black23048032013-01-13 07:33:00 -0800549 # The index will have a count, an offset and size for the system
550 # firmware, and then an offset and size for the EC binary.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800551 new_blob = (new_blob[0 : 4 * 4] + struct.pack('<I', ecbin_size) +
Gabe Black23048032013-01-13 07:33:00 -0800552 new_blob[5 * 4 :])
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800553
554 self.set_section_body(section, new_blob)
555 if write_through:
556 subsection_name = self.fv_sections[section].get_body_name()
557 self.dump_partial(subsection_name,
558 self.chros_if.state_dir_file(subsection_name))
559 self.fum.write_partial(self.image, (subsection_name, ))
560
561 def set_section_version(self, section, version, flags,
562 write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800563 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800564 Re-sign the firmware section using the supplied version number and
565 flag.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800566 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800567 if (self.get_section_version(section) == version and
568 self.get_section_flags(section) == flags):
569 return # No version or flag change, nothing to do.
570 if version < 0:
571 raise FlashromHandlerError(
572 'Attempt to set version %d on section %s' % (version, section))
573 fv_section = self.fv_sections[section]
574 sig_name = self.chros_if.state_dir_file(fv_section.get_sig_name())
575 sig_size = os.path.getsize(sig_name)
576
577 # Construct the command line
578 args = ['--vblock %s' % sig_name]
579 args.append('--keyblock %s' % os.path.join(
580 self.dev_key_path, self.FW_KEYBLOCK_FILE_NAME))
581 args.append('--fv %s' % self.chros_if.state_dir_file(
582 fv_section.get_body_name()))
583 args.append('--version %d' % version)
584 args.append('--kernelkey %s' % os.path.join(
585 self.dev_key_path, self.KERNEL_SUBKEY_FILE_NAME))
586 args.append('--signprivate %s' % os.path.join(
587 self.dev_key_path, self.FW_PRIV_DATA_KEY_FILE_NAME))
588 args.append('--flags %d' % flags)
589 cmd = 'vbutil_firmware %s' % ' '.join(args)
590 self.chros_if.run_shell_command(cmd)
591
592 # Pad the new signature.
593 new_sig = open(sig_name, 'a')
Tom Wai-Hong Tamdc2d8072012-12-18 10:50:55 +0800594 pad = ('%c' % 0) * (sig_size - os.path.getsize(sig_name))
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800595 new_sig.write(pad)
596 new_sig.close()
597
598 # Inject the new signature block into the image
599 new_sig = open(sig_name, 'r').read()
600 self.image = self.fum.put_section(
601 self.image, fv_section.get_sig_name(), new_sig)
602 if write_through:
603 self.fum.write_partial(self.image, (fv_section.get_sig_name(), ))