blob: b305c3fbf5848125a13f8c1fb47815740b2fc133 [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 Tam28f86cd2013-03-18 10:59:42 +080018from chromeos_interface import ChromeOSInterfaceError
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080019
20class FvSection(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080021 """An object to hold information about a firmware section.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080022
23 This includes file names for the signature header and the body, and the
24 version number.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080025 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080026
27 def __init__(self, sig_name, body_name):
28 self._sig_name = sig_name
29 self._body_name = body_name
30 self._version = -1 # Is not set on construction.
31 self._flags = 0 # Is not set on construction.
32 self._sha = None # Is not set on construction.
ctchang38ae4922012-09-03 17:01:16 +080033 self._sig_sha = None # Is not set on construction.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080034 self._datakey_version = -1 # Is not set on construction.
35 self._kernel_subkey_version = -1 # Is not set on construction.
36
37 def names(self):
38 return (self._sig_name, self._body_name)
39
40 def get_sig_name(self):
41 return self._sig_name
42
43 def get_body_name(self):
44 return self._body_name
45
46 def get_version(self):
47 return self._version
48
49 def get_flags(self):
50 return self._flags
51
52 def get_sha(self):
53 return self._sha
54
ctchang38ae4922012-09-03 17:01:16 +080055 def get_sig_sha(self):
56 return self._sig_sha
57
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080058 def get_datakey_version(self):
59 return self._datakey_version
60
61 def get_kernel_subkey_version(self):
62 return self._kernel_subkey_version
63
64 def set_version(self, version):
65 self._version = version
66
67 def set_flags(self, flags):
68 self._flags = flags
69
70 def set_sha(self, sha):
71 self._sha = sha
72
ctchang38ae4922012-09-03 17:01:16 +080073 def set_sig_sha(self, sha):
74 self._sig_sha = sha
75
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080076 def set_datakey_version(self, version):
77 self._datakey_version = version
78
79 def set_kernel_subkey_version(self, version):
80 self._kernel_subkey_version = version
81
82class FlashromHandlerError(Exception):
83 pass
84
85
86class FlashromHandler(object):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +080087 """An object to provide logical services for automated flashrom testing."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +080088
89 DELTA = 1 # value to add to a byte to corrupt a section contents
90
91 # File in the state directory to store public root key.
92 PUB_KEY_FILE_NAME = 'root.pubkey'
93 FW_KEYBLOCK_FILE_NAME = 'firmware.keyblock'
94 FW_PRIV_DATA_KEY_FILE_NAME = 'firmware_data_key.vbprivk'
95 KERNEL_SUBKEY_FILE_NAME = 'kernel_subkey.vbpubk'
96
97 def __init__(self):
98 # make sure it does not accidentally overwrite the image.
99 self.fum = None
100 self.chros_if = None
101 self.image = ''
102 self.pub_key_file = ''
103
104 def init(self, flashrom_util_module,
105 chros_if,
106 pub_key_file=None,
107 dev_key_path='./',
108 target='bios'):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800109 """Flashrom handler initializer.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800110
111 Args:
112 flashrom_util_module - a module providing flashrom access utilities.
113 chros_if - a module providing interface to Chromium OS services
114 pub_key_file - a string, name of the file contaning a public key to
115 use for verifying both existing and new firmware.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800116 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800117 if target == 'bios':
118 self.fum = flashrom_util_module.flashrom_util(target_is_ec=False)
119 self.fv_sections = {
120 'a': FvSection('VBOOTA', 'FVMAIN'),
121 'b': FvSection('VBOOTB', 'FVMAINB'),
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800122 'ec_a': FvSection(None, 'ECMAINA'),
123 'ec_b': FvSection(None, 'ECMAINB'),
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800124 }
125 elif target == 'ec':
126 self.fum = flashrom_util_module.flashrom_util(target_is_ec=True)
127 self.fv_sections = {
128 'rw': FvSection(None, 'EC_RW'),
129 }
130 else:
131 raise FlashromHandlerError("Invalid target.")
132 self.chros_if = chros_if
133 self.pub_key_file = pub_key_file
134 self.dev_key_path = dev_key_path
135
136 def new_image(self, image_file=None):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800137 """Parse the full flashrom image and store sections into files.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800138
139 Args:
140 image_file - a string, the name of the file contaning full ChromeOS
141 flashrom image. If not passed in or empty - the actual
142 flashrom is read and its contents are saved into a
143 temporary file which is used instead.
144
145 The input file is parsed and the sections of importance (as defined in
146 self.fv_sections) are saved in separate files in the state directory
147 as defined in the chros_if object.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800148 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800149
150 if image_file:
151 self.image = open(image_file, 'rb').read()
152 self.fum.set_firmware_layout(image_file)
153 else:
154 self.image = self.fum.read_whole()
155
156 for section in self.fv_sections.itervalues():
157 for subsection_name in section.names():
158 if not subsection_name:
159 continue
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800160 blob = self.fum.get_section(self.image, subsection_name)
161 if blob:
162 f = open(self.chros_if.state_dir_file(subsection_name),
163 'wb')
164 f.write(blob)
165 f.close()
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800166
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800167 blob = self.fum.get_section(self.image, section.get_body_name())
168 if blob:
169 s = hashlib.sha1()
170 s.update(blob)
171 section.set_sha(s.hexdigest())
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800172
173 # If there is no "sig" subsection, skip reading version and flags.
174 if not section.get_sig_name():
175 continue
176
177 # Now determine this section's version number.
178 vb_section = self.fum.get_section(
179 self.image, section.get_sig_name())
180
181 section.set_version(self.chros_if.retrieve_body_version(vb_section))
182 section.set_flags(self.chros_if.retrieve_preamble_flags(vb_section))
183 section.set_datakey_version(
184 self.chros_if.retrieve_datakey_version(vb_section))
185 section.set_kernel_subkey_version(
186 self.chros_if.retrieve_kernel_subkey_version(vb_section))
187
ctchang38ae4922012-09-03 17:01:16 +0800188 s = hashlib.sha1()
189 s.update(self.fum.get_section(self.image, section.get_sig_name()))
190 section.set_sig_sha(s.hexdigest())
191
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800192 if not self.pub_key_file:
193 self._retrieve_pub_key()
194
195 def _retrieve_pub_key(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800196 """Retrieve root public key from the firmware GBB section."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800197
198 gbb_header_format = '<4s20s2I'
199 pubk_header_format = '<2Q'
200
201 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
202
203 # do some sanity checks
204 try:
205 sig, _, rootk_offs, rootk_size = struct.unpack_from(
206 gbb_header_format, gbb_section)
207 except struct.error, e:
208 raise FlashromHandlerError(e)
209
210 if sig != '$GBB' or (rootk_offs + rootk_size) > len(gbb_section):
211 raise FlashromHandlerError('Bad gbb header')
212
213 key_body_offset, key_body_size = struct.unpack_from(
214 pubk_header_format, gbb_section, rootk_offs)
215
216 # Generally speaking the offset field can be anything, but in case of
217 # GBB section the key is stored as a standalone entity, so the offset
218 # of the key body is expected to be equal to the key header size of
219 # 0x20.
220 # Should this convention change, the check below would fail, which
221 # would be a good prompt for revisiting this test's behavior and
222 # algorithms.
223 if key_body_offset != 0x20 or key_body_size > rootk_size:
224 raise FlashromHandlerError('Bad public key format')
225
226 # All checks passed, let's store the key in a file.
227 self.pub_key_file = self.chros_if.state_dir_file(self.PUB_KEY_FILE_NAME)
228 keyf = open(self.pub_key_file, 'w')
229 key = gbb_section[
230 rootk_offs:rootk_offs + key_body_offset + key_body_size]
231 keyf.write(key)
232 keyf.close()
233
234 def verify_image(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800235 """Confirm the image's validity.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800236
237 Using the file supplied to init() as the public key container verify
238 the two sections' (FirmwareA and FirmwareB) integrity. The contents of
239 the sections is taken from the files created by new_image()
240
241 In case there is an integrity error raises FlashromHandlerError
242 exception with the appropriate error message text.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800243 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800244
245 for section in self.fv_sections.itervalues():
Tom Wai-Hong Tam8086f962013-03-21 17:15:16 +0800246 if section.get_sig_name():
247 cmd = 'vbutil_firmware --verify %s --signpubkey %s --fv %s' % (
248 self.chros_if.state_dir_file(section.get_sig_name()),
249 self.pub_key_file,
250 self.chros_if.state_dir_file(section.get_body_name()))
251 self.chros_if.run_shell_command(cmd)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800252
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800253 def _modify_section(self, section, delta, body_or_sig=False,
254 corrupt_all=False):
255 """Modify a firmware section inside the image, either body or signature.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800256
257 If corrupt_all is set, the passed in delta is added to all bytes in the
258 section. Otherwise, the delta is added to the value located at 2% offset
259 into the section blob, either body or signature.
260
261 Calling this function again for the same section the complimentary
262 delta value would restore the section contents.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800263 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800264
265 if not self.image:
266 raise FlashromHandlerError(
267 'Attempt at using an uninitialized object')
268 if section not in self.fv_sections:
269 raise FlashromHandlerError('Unknown FW section %s'
270 % section)
271
272 # Get the appropriate section of the image.
273 if body_or_sig:
274 subsection_name = self.fv_sections[section].get_body_name()
275 else:
276 subsection_name = self.fv_sections[section].get_sig_name()
277 blob = self.fum.get_section(self.image, subsection_name)
278
279 # Modify the byte in it within 2% of the section blob.
280 modified_index = len(blob) / 50
281 if corrupt_all:
282 blob_list = [('%c' % ((ord(x) + delta) % 0x100)) for x in blob]
283 else:
284 blob_list = list(blob)
285 blob_list[modified_index] = ('%c' %
286 ((ord(blob[modified_index]) + delta) % 0x100))
287 self.image = self.fum.put_section(self.image,
288 subsection_name, ''.join(blob_list))
289
290 return subsection_name
291
292 def corrupt_section(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800293 """Corrupt a section signature of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800294
295 return self._modify_section(section, self.DELTA, body_or_sig=False,
296 corrupt_all=corrupt_all)
297
298 def corrupt_section_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800299 """Corrupt a section body of the image"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800300
301 return self._modify_section(section, self.DELTA, body_or_sig=True,
302 corrupt_all=corrupt_all)
303
304 def restore_section(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800305 """Restore a previously corrupted section signature of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800306
307 return self._modify_section(section, -self.DELTA, body_or_sig=False,
308 corrupt_all=restore_all)
309
310 def restore_section_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800311 """Restore a previously corrupted section body of the image."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800312
313 return self._modify_section(section, -self.DELTA, body_or_sig=True,
314 corrupt_all=restore_all)
315
316 def corrupt_firmware(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800317 """Corrupt a section signature in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800318
319 subsection_name = self.corrupt_section(section, corrupt_all=corrupt_all)
320 self.fum.write_partial(self.image, (subsection_name, ))
321
322 def corrupt_firmware_body(self, section, corrupt_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800323 """Corrupt a section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800324
325 subsection_name = self.corrupt_section_body(section,
326 corrupt_all=corrupt_all)
327 self.fum.write_partial(self.image, (subsection_name, ))
328
329 def restore_firmware(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800330 """Restore the previously corrupted section sig in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800331
332 subsection_name = self.restore_section(section, restore_all=restore_all)
333 self.fum.write_partial(self.image, (subsection_name, ))
334
335 def restore_firmware_body(self, section, restore_all=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800336 """Restore the previously corrupted section body in the FLASHROM!!!"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800337
338 subsection_name = self.restore_section_body(section,
339 restore_all=False)
340 self.fum.write_partial(self.image, (subsection_name, ))
341
342 def firmware_sections_equal(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800343 """Check if firmware sections A and B are equal.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800344
345 This function presumes that the entire BIOS image integrity has been
346 verified, so different signature sections mean different images and
347 vice versa.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800348 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800349 sig_a = self.fum.get_section(self.image,
350 self.fv_sections['a'].get_sig_name())
351 sig_b = self.fum.get_section(self.image,
352 self.fv_sections['b'].get_sig_name())
353 return sig_a == sig_b
354
355 def copy_from_to(self, src, dst):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800356 """Copy one firmware image section to another.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800357
358 This function copies both signature and body of one firmware section
359 into another. After this function runs both sections are identical.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800360 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800361 src_sect = self.fv_sections[src]
362 dst_sect = self.fv_sections[dst]
363 self.image = self.fum.put_section(
364 self.image,
365 dst_sect.get_body_name(),
366 self.fum.get_section(self.image, src_sect.get_body_name()))
367 self.image = self.fum.put_section(
368 self.image,
369 dst_sect.get_sig_name(),
370 self.fum.get_section(self.image, src_sect.get_sig_name()))
371
372 def write_whole(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800373 """Write the whole image into the flashrom."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800374
375 if not self.image:
376 raise FlashromHandlerError(
377 'Attempt at using an uninitialized object')
378 self.fum.write_whole(self.image)
379
380 def dump_whole(self, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800381 """Write the whole image into a file."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800382
383 if not self.image:
384 raise FlashromHandlerError(
385 'Attempt at using an uninitialized object')
386 open(filename, 'w').write(self.image)
387
388 def dump_partial(self, subsection_name, filename):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800389 """Write the subsection part into a file."""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800390
391 if not self.image:
392 raise FlashromHandlerError(
393 'Attempt at using an uninitialized object')
394 blob = self.fum.get_section(self.image, subsection_name)
395 open(filename, 'w').write(blob)
396
397 def get_gbb_flags(self):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800398 """Retrieve the GBB flags"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800399 gbb_header_format = '<12sL'
400 gbb_section = self.fum.get_section(self.image, 'FV_GBB')
401 try:
402 _, gbb_flags = struct.unpack_from(gbb_header_format, gbb_section)
403 except struct.error, e:
404 raise FlashromHandlerError(e)
405 return gbb_flags
406
Tom Wai-Hong Tam44204b32012-11-20 13:55:40 +0800407 def enable_write_protect(self):
408 """Enable write protect of the flash chip"""
409 self.fum.enable_write_protect()
410
411 def disable_write_protect(self):
412 """Disable write protect of the flash chip"""
413 self.fum.disable_write_protect()
414
ctchang38ae4922012-09-03 17:01:16 +0800415 def get_section_sig_sha(self, section):
416 """Retrieve SHA1 hash of a firmware vblock section"""
417 return self.fv_sections[section].get_sig_sha()
418
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800419 def get_section_sha(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800420 """Retrieve SHA1 hash of a firmware body section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800421 return self.fv_sections[section].get_sha()
422
423 def get_section_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800424 """Retrieve version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800425 return self.fv_sections[section].get_version()
426
427 def get_section_flags(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800428 """Retrieve preamble flags of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800429 return self.fv_sections[section].get_flags()
430
431 def get_section_datakey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800432 """Retrieve data key version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800433 return self.fv_sections[section].get_datakey_version()
434
435 def get_section_kernel_subkey_version(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800436 """Retrieve kernel subkey version number of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800437 return self.fv_sections[section].get_kernel_subkey_version()
438
439 def get_section_body(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800440 """Retrieve body of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800441 subsection_name = self.fv_sections[section].get_body_name()
442 blob = self.fum.get_section(self.image, subsection_name)
443 return blob
444
ctchang38ae4922012-09-03 17:01:16 +0800445 def get_section_sig(self, section):
446 """Retrieve vblock of a firmware section"""
447 subsection_name = self.fv_sections[section].get_sig_name()
448 blob = self.fum.get_section(self.image, subsection_name)
449 return blob
450
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800451 def _find_dtb_offset(self, blob):
452 """Return the offset of DTB blob from the given firmware blob"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800453 # The RW firmware is concatenated from u-boot, dtb, and ecbin.
454 # Search the magic of dtb to locate the dtb bloc.
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800455 return blob.find("\xD0\x0D\xFE\xED\x00")
456
457 def _find_ecbin_offset(self, blob):
458 """Return the offset of EC binary from the given firmware blob"""
459 dtb_offset = self._find_dtb_offset(blob)
Gabe Black23048032013-01-13 07:33:00 -0800460 # If the device tree was found, use it. Otherwise assume an index
461 # structure.
462 if dtb_offset != -1:
463 # The dtb size is a 32-bit integer which follows the magic.
464 _, dtb_size = struct.unpack_from(">2L", blob, dtb_offset)
465 # The ecbin part is aligned to 4-byte.
466 return (dtb_offset + dtb_size + 3) & ~3
467 else:
468 # The index will have a count, an offset and size for the system
469 # firmware, and then an offset and size for the EC binary.
470 return struct.unpack_from("<I", blob, 3 * 4)[0]
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800471
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800472 def get_section_ecbin(self, section):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800473 """Retrieve EC binary of a firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800474 blob = self.get_section_body(section)
475 ecbin_offset = self._find_ecbin_offset(blob)
476 # Remove the pads of ecbin.
477 pad = blob[-1]
478 ecbin = blob[ecbin_offset :].rstrip(pad)
479 return ecbin
480
ctchang38ae4922012-09-03 17:01:16 +0800481 def set_section_body(self, section, blob, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800482 """Put the supplied blob to the body of the firmware section"""
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800483 subsection_name = self.fv_sections[section].get_body_name()
484 self.image = self.fum.put_section(self.image, subsection_name, blob)
485
ctchang38ae4922012-09-03 17:01:16 +0800486 if write_through:
487 self.dump_partial(subsection_name,
488 self.chros_if.state_dir_file(subsection_name))
489 self.fum.write_partial(self.image, (subsection_name, ))
490
491 def set_section_sig(self, section, blob, write_through=False):
492 """Put the supplied blob to the vblock of the firmware section"""
493 subsection_name = self.fv_sections[section].get_sig_name()
494 self.image = self.fum.put_section(self.image, subsection_name, blob)
495
496 if write_through:
497 self.dump_partial(subsection_name,
498 self.chros_if.state_dir_file(subsection_name))
499 self.fum.write_partial(self.image, (subsection_name, ))
500
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800501 def set_section_ecbin(self, section, ecbin, write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800502 """Put the supplied EC binary to the firwmare section.
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800503
504 Note that the updated firmware image is not signed yet. Should call
505 set_section_version() afterward.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800506 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800507 # Remove unncessary padding bytes.
508 pad = '\xff'
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800509 align = 4
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800510 ecbin = ecbin.rstrip(pad)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800511 ecbin += pad * (-len(ecbin) % align)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800512 ecbin_size = len(ecbin)
513
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800514 # Get the original main firmware body.
515 main_blob = self.get_section_body(section)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800516
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800517 # Compute the hash of the ecbin.
518 hasher = hashlib.sha256()
519 hasher.update(ecbin)
520 echash = hasher.digest()
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800521
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800522 # Depthcharge firmware doesn't save the EC size on the DTB.
523 is_depthcharge = (self._find_dtb_offset(main_blob) == -1)
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800524
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800525 if is_depthcharge:
526 # The Depthcharge index header in main firmware section is like:
527 # count = 2,
528 # offset and size of main firmware,
529 # offset and size of EC binary hash.
530 echash_offset = struct.unpack_from('<I', main_blob, 3 * 4)[0]
Tom Wai-Hong Tam7aad3af2013-03-18 10:46:31 +0800531
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800532 # Update the EC binary hash.
533 main_blob = main_blob[0 : echash_offset] + echash
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800534
Tom Wai-Hong Tam28f86cd2013-03-18 10:59:42 +0800535 # Construct the EC firmware section, which is composed of:
536 # count = 1,
537 # offset and size of EC firmware,
538 # EC firmware.
539 ecbin_blob = struct.pack('<III', 1, 3 * 4, ecbin_size) + ecbin
540
541 # Write the EC firmware section back.
542 ec_section = 'ec_' + section
543 self.set_section_body(ec_section, ecbin_blob)
544 if write_through:
545 ec_fmap = self.fv_sections[ec_section].get_body_name()
546 self.dump_partial(ec_fmap,
547 self.chros_if.state_dir_file(ec_fmap))
548 self.fum.write_partial(self.image, (ec_fmap, ))
549 else:
550 # Update the size and the hash of the EC binary on the DTB.
551 dtb_offset = self._find_dtb_offset(main_blob)
552 ecbin_offset = self._find_ecbin_offset(main_blob)
553 dtb_blob = main_blob[dtb_offset : ecbin_offset]
554 dtb_size = ecbin_offset - dtb_offset
555 with tempfile.NamedTemporaryFile() as dtb_file:
556 dtb_file.write(dtb_blob)
557 dtb_file.flush()
558
559 cmd = ["fdtput", "-t lu", dtb_file.name,
560 "/flash/rw-a-boot/ecbin",
561 "reg", str(ecbin_offset), str(ecbin_size)]
562 self.chros_if.run_shell_command(' '.join(cmd))
563
564 # The old version of firmware doesn't save the EC hash in DTB.
565 # So check the EC hash first before updating.
566 try:
567 cmd = ["fdtget", "-t bu", dtb_file.name,
568 "/flash/rw-a-boot/ecbin", "hash"]
569 self.chros_if.run_shell_command(' '.join(cmd))
570 except ChromeOSInterfaceError:
571 self.chros_if.log(
572 "Skip updating EC hash on the old firmware.")
573 else:
574 cmd = ["fdtput", "-t bu", dtb_file.name,
575 "/flash/rw-a-boot/ecbin", "hash"]
576 cmd += [str(ord(c)) for c in echash]
577 self.chros_if.run_shell_command(' '.join(cmd))
578
579 dtb_file.seek(0)
580 dtb_blob = dtb_file.read()
581 dtb_blob += pad * (-len(dtb_blob) % align)
582 assert len(dtb_blob) == dtb_size, (
583 'Updating DTB should not change its size.')
584
585 pad = main_blob[-1]
586 pad_size = len(main_blob) - ecbin_offset - ecbin_size
587 main_blob = (main_blob[0 : dtb_offset] +
588 dtb_blob + ecbin + pad * pad_size)
589
590 # Write the main firmware section back.
591 self.set_section_body(section, main_blob)
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800592 if write_through:
593 subsection_name = self.fv_sections[section].get_body_name()
594 self.dump_partial(subsection_name,
595 self.chros_if.state_dir_file(subsection_name))
596 self.fum.write_partial(self.image, (subsection_name, ))
597
598 def set_section_version(self, section, version, flags,
599 write_through=False):
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800600 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800601 Re-sign the firmware section using the supplied version number and
602 flag.
Tom Wai-Hong Tambe153282012-09-13 13:56:16 +0800603 """
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800604 if (self.get_section_version(section) == version and
605 self.get_section_flags(section) == flags):
606 return # No version or flag change, nothing to do.
607 if version < 0:
608 raise FlashromHandlerError(
609 'Attempt to set version %d on section %s' % (version, section))
610 fv_section = self.fv_sections[section]
611 sig_name = self.chros_if.state_dir_file(fv_section.get_sig_name())
612 sig_size = os.path.getsize(sig_name)
613
614 # Construct the command line
615 args = ['--vblock %s' % sig_name]
616 args.append('--keyblock %s' % os.path.join(
617 self.dev_key_path, self.FW_KEYBLOCK_FILE_NAME))
618 args.append('--fv %s' % self.chros_if.state_dir_file(
619 fv_section.get_body_name()))
620 args.append('--version %d' % version)
621 args.append('--kernelkey %s' % os.path.join(
622 self.dev_key_path, self.KERNEL_SUBKEY_FILE_NAME))
623 args.append('--signprivate %s' % os.path.join(
624 self.dev_key_path, self.FW_PRIV_DATA_KEY_FILE_NAME))
625 args.append('--flags %d' % flags)
626 cmd = 'vbutil_firmware %s' % ' '.join(args)
627 self.chros_if.run_shell_command(cmd)
628
629 # Pad the new signature.
630 new_sig = open(sig_name, 'a')
Tom Wai-Hong Tamdc2d8072012-12-18 10:50:55 +0800631 pad = ('%c' % 0) * (sig_size - os.path.getsize(sig_name))
Tom Wai-Hong Tamc0168912012-09-13 13:24:02 +0800632 new_sig.write(pad)
633 new_sig.close()
634
635 # Inject the new signature block into the image
636 new_sig = open(sig_name, 'r').read()
637 self.image = self.fum.put_section(
638 self.image, fv_section.get_sig_name(), new_sig)
639 if write_through:
640 self.fum.write_partial(self.image, (fv_section.get_sig_name(), ))