blob: 26152296bb286a85742b639574f4594873d83ad9 [file] [log] [blame]
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""A utility to program Chrome OS devices' firmware using servo.
6
7This utility expects the DUT to be connected to a servo device. This allows us
8to put the DUT into the required state and to actually program the DUT's
9firmware using FTDI, USB and/or serial interfaces provided by servo.
10
11Servo state is preserved across the programming process.
12"""
13
Ricky Liangc101a562014-05-15 10:56:15 +080014import glob
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080015import logging
16import os
Ricky Liangc101a562014-05-15 10:56:15 +080017import re
Ricky Liangc7cc3402014-05-24 00:25:26 +080018import site
Tom Wai-Hong Tama8d4a102015-10-10 10:43:20 +080019import time
Ricky Liangf19ab9f2014-06-11 15:50:44 +080020import xml.etree.ElementTree
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080021
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070022from autotest_lib.client.common_lib import error
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080023from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig
24
25
Dan Shifecdaf42015-07-28 10:17:26 -070026# Number of seconds for program EC/BIOS to time out.
27FIRMWARE_PROGRAM_TIMEOUT_SEC = 600
28
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080029class ProgrammerError(Exception):
30 """Local exception class wrapper."""
31 pass
32
33
34class _BaseProgrammer(object):
35 """Class implementing base programmer services.
36
37 Private attributes:
38 _servo: a servo object controlling the servo device
39 _servo_prog_state: a tuple of strings of "<control>:<value>" pairs,
40 listing servo controls and their required values for
41 programming
Tom Wai-Hong Tama8d4a102015-10-10 10:43:20 +080042 _servo_prog_state_delay: time in second to wait after changing servo
43 controls for programming.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080044 _servo_saved_state: a list of the same elements as _servo_prog_state,
45 those which need to be restored after programming
46 _program_cmd: a string, the shell command to run on the servo host
47 to actually program the firmware. Dependent on
48 firmware/hardware type, set by subclasses.
49 """
50
51 def __init__(self, servo, req_list):
52 """Base constructor.
53 @param servo: a servo object controlling the servo device
54 @param req_list: a list of strings, names of the utilities required
55 to be in the path for the programmer to succeed
56 """
57 self._servo = servo
58 self._servo_prog_state = ()
Tom Wai-Hong Tama8d4a102015-10-10 10:43:20 +080059 self._servo_prog_state_delay = 0
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080060 self._servo_saved_state = []
61 self._program_cmd = ''
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070062 try:
63 self._servo.system('which %s' % ' '.join(req_list))
64 except error.AutoservRunError:
65 # TODO: We turn this exception into a warn since the fw programmer
66 # is not working right now, and some systems do not package the
67 # required utilities its checking for.
68 # We should reinstate this exception once the programmer is working
69 # to indicate the missing utilities earlier in the test cycle.
70 # Bug chromium:371011 filed to track this.
71 logging.warn("Ignoring exception when verify required bins : %s",
72 ' '.join(req_list))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080073
Dan Shia5fef052015-05-18 23:28:47 -070074
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080075 def _set_servo_state(self):
76 """Set servo for programming, while saving the current state."""
77 logging.debug("Setting servo state for programming")
78 for item in self._servo_prog_state:
79 key, value = item.split(':')
Tom Wai-Hong Tama8d4a102015-10-10 10:43:20 +080080 try:
81 present = self._servo.get(key)
82 except error.TestFail:
83 logging.warn('Missing servo control: %s', key)
84 continue
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080085 if present != value:
86 self._servo_saved_state.append('%s:%s' % (key, present))
87 self._servo.set(key, value)
Tom Wai-Hong Tama8d4a102015-10-10 10:43:20 +080088 time.sleep(self._servo_prog_state_delay)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080089
90
91 def _restore_servo_state(self):
92 """Restore previously saved servo state."""
93 logging.debug("Restoring servo state after programming")
94 self._servo_saved_state.reverse() # Do it in the reverse order.
95 for item in self._servo_saved_state:
96 key, value = item.split(':')
97 self._servo.set(key, value)
98
99
100 def program(self):
101 """Program the firmware as configured by a subclass."""
102 self._set_servo_state()
103 try:
104 logging.debug("Programmer command: %s", self._program_cmd)
Dan Shifecdaf42015-07-28 10:17:26 -0700105 self._servo.system(self._program_cmd,
106 timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800107 finally:
108 self._restore_servo_state()
109
110
111class FlashromProgrammer(_BaseProgrammer):
112 """Class for programming AP flashrom."""
113
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800114 def __init__(self, servo, keep_ro=False):
115 """Configure required servo state.
116
117 @param servo: a servo object controlling the servo device
118 @param keep_ro: True to keep the RO portion unchanged
119 """
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800120 super(FlashromProgrammer, self).__init__(servo, ['flashrom',])
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800121 self._keep_ro = keep_ro
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800122 self._fw_path = None
123 self._tmp_path = '/tmp'
124 self._fw_main = os.path.join(self._tmp_path, 'fw_main')
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800125 self._wp_ro = os.path.join(self._tmp_path, 'wp_ro')
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800126 self._ro_vpd = os.path.join(self._tmp_path, 'ro_vpd')
127 self._rw_vpd = os.path.join(self._tmp_path, 'rw_vpd')
128 self._gbb = os.path.join(self._tmp_path, 'gbb')
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800129 self._servo_version = self._servo.get_servo_version()
130 self._servo_serials = self._servo._server.get_servo_serials()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800131
132
133 def program(self):
134 """Program the firmware but preserve VPD and HWID."""
135 assert self._fw_path is not None
136 self._set_servo_state()
137 try:
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800138 wp_ro_section = [('WP_RO', self._wp_ro)]
139 rw_vpd_section = [('RW_VPD', self._rw_vpd)]
140 ro_vpd_section = [('RO_VPD', self._ro_vpd)]
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800141 gbb_section = [('GBB', self._gbb)]
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800142 if self._keep_ro:
143 # Keep the whole RO portion
144 preserved_sections = wp_ro_section + rw_vpd_section
145 else:
146 preserved_sections = ro_vpd_section + rw_vpd_section
147
Dan Shia5fef052015-05-18 23:28:47 -0700148 servo_v2_programmer = 'ft2232_spi:type=servo-v2'
149 servo_v3_programmer = 'linux_spi'
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800150 servo_v4_with_micro_programmer = 'raiden_spi'
151 servo_v4_with_ccd_programmer = 'raiden_debug_spi:target=AP'
152 if self._servo_version == 'servo_v2':
Dan Shia5fef052015-05-18 23:28:47 -0700153 programmer = servo_v2_programmer
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800154 servo_serial = self._servo_serials.get('main')
155 if servo_serial:
156 programmer += ',serial=%s' % servo_serial
157 elif self._servo_version == 'servo_v3':
Dan Shia5fef052015-05-18 23:28:47 -0700158 programmer = servo_v3_programmer
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800159 elif self._servo_version == 'servo_v4':
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700160 # Get the serial of the servo micro if it exists.
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800161 servo_micro_serial = self._servo_serials.get('servo_micro')
162 ccd_serial = self._servo_serials.get('ccd')
163 # When a uServo is connected to a DUT with CCD support, the
164 # firmware programmer will always use the uServo to program.
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700165 if servo_micro_serial:
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800166 programmer = servo_v4_with_micro_programmer
167 programmer += ',serial=%s' % servo_micro_serial
168 elif ccd_serial:
169 programmer = servo_v4_with_ccd_programmer
170 programmer += ',serial=%s' % ccd_serial
Dan Shia5fef052015-05-18 23:28:47 -0700171 else:
172 raise Exception('Servo version %s is not supported.' %
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800173 self._servo_version)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800174 # Save needed sections from current firmware
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800175 for section in preserved_sections + gbb_section:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800176 self._servo.system(' '.join([
Dan Shia5fef052015-05-18 23:28:47 -0700177 'flashrom', '-V', '-p', programmer,
Dan Shifecdaf42015-07-28 10:17:26 -0700178 '-r', self._fw_main, '-i', '%s:%s' % section]),
179 timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800180
181 # Pack the saved VPD into new firmware
182 self._servo.system('cp %s %s' % (self._fw_path, self._fw_main))
183 img_size = self._servo.system_output(
184 "stat -c '%%s' %s" % self._fw_main)
185 pack_cmd = ['flashrom',
186 '-p', 'dummy:image=%s,size=%s,emulate=VARIABLE_SIZE' % (
187 self._fw_main, img_size),
188 '-w', self._fw_main]
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800189 for section in preserved_sections:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800190 pack_cmd.extend(['-i', '%s:%s' % section])
Dan Shifecdaf42015-07-28 10:17:26 -0700191 self._servo.system(' '.join(pack_cmd),
192 timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800193
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800194 # HWID is inside the RO portion. Don't preserve HWID if we keep RO.
195 if not self._keep_ro:
196 # Read original HWID. The output format is:
197 # hardware_id: RAMBI TEST A_A 0128
198 gbb_hwid_output = self._servo.system_output(
199 'gbb_utility -g --hwid %s' % self._gbb)
200 original_hwid = gbb_hwid_output.split(':', 1)[1].strip()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800201
Tom Wai-Hong Tam73f14ed22016-01-08 02:21:43 +0800202 # Write HWID to new firmware
203 self._servo.system("gbb_utility -s --hwid='%s' %s" %
204 (original_hwid, self._fw_main))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800205
206 # Flash the new firmware
207 self._servo.system(' '.join([
Dan Shia5fef052015-05-18 23:28:47 -0700208 'flashrom', '-V', '-p', programmer,
Dan Shifecdaf42015-07-28 10:17:26 -0700209 '-w', self._fw_main]), timeout=FIRMWARE_PROGRAM_TIMEOUT_SEC)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800210 finally:
Tom Wai-Hong Tam97678db2016-03-17 05:36:13 +0800211 self._servo.get_power_state_controller().reset()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800212 self._restore_servo_state()
213
214
215 def prepare_programmer(self, path):
216 """Prepare programmer for programming.
217
218 @param path: a string, name of the file containing the firmware image.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800219 """
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800220 self._fw_path = path
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800221 # CCD takes care holding AP/EC. Don't need the following steps.
222 if not (self._servo_version == 'servo_v4' and
223 'ccd' in self._servo_serials):
224 faft_config = FAFTConfig(self._servo.get_board())
225 self._servo_prog_state_delay = faft_config.servo_prog_state_delay
226 self._servo_prog_state = (
227 'spi2_vref:%s' % faft_config.spi_voltage,
228 'spi2_buf_en:on',
229 'spi2_buf_on_flex_en:on',
230 'spi_hold:off',
231 'cold_reset:on',
232 'usbpd_reset:on',
233 )
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800234
235
236class FlashECProgrammer(_BaseProgrammer):
237 """Class for programming AP flashrom."""
238
239 def __init__(self, servo):
240 """Configure required servo state."""
241 super(FlashECProgrammer, self).__init__(servo, ['flash_ec',])
242 self._servo = servo
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800243 self._servo_serials = self._servo._server.get_servo_serials()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800244
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800245 def prepare_programmer(self, image):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800246 """Prepare programmer for programming.
247
248 @param image: string with the location of the image file
249 """
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700250 port = self._servo._servo_host.servo_port
251 self._program_cmd = ('flash_ec --chip=%s --image=%s --port=%d' %
252 (self._servo.get('ec_chip'), image, port))
Wai-Hong Tam36824cc2016-11-10 15:51:07 -0800253 if 'ccd' in self._servo_serials:
254 self._program_cmd += ' --raiden'
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800255
256
257class ProgrammerV2(object):
Dan Shia5fef052015-05-18 23:28:47 -0700258 """Main programmer class which provides programmer for BIOS and EC with
259 servo V2."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800260
261 def __init__(self, servo):
262 self._servo = servo
Ricky Liangc101a562014-05-15 10:56:15 +0800263 self._valid_boards = self._get_valid_v2_boards()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800264 self._bios_programmer = self._factory_bios(self._servo)
265 self._ec_programmer = self._factory_ec(self._servo)
266
267
Ricky Liangc101a562014-05-15 10:56:15 +0800268 @staticmethod
269 def _get_valid_v2_boards():
270 """Greps servod config files to look for valid v2 boards.
271
272 @return A list of valid board names.
273 """
Ricky Liangc7cc3402014-05-24 00:25:26 +0800274 site_packages_paths = site.getsitepackages()
275 SERVOD_CONFIG_DATA_DIR = None
276 for p in site_packages_paths:
277 servo_data_path = os.path.join(p, 'servo', 'data')
278 if os.path.exists(servo_data_path):
279 SERVOD_CONFIG_DATA_DIR = servo_data_path
280 break
281 if not SERVOD_CONFIG_DATA_DIR:
282 raise ProgrammerError(
283 'Unable to locate data directory of Python servo module')
Ricky Liangc101a562014-05-15 10:56:15 +0800284 SERVOFLEX_V2_R0_P50_CONFIG = 'servoflex_v2_r0_p50.xml'
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800285 SERVO_CONFIG_GLOB = 'servo_*_overlay.xml'
Ricky Liangc101a562014-05-15 10:56:15 +0800286 SERVO_CONFIG_REGEXP = 'servo_(?P<board>.+)_overlay.xml'
287
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800288 def is_v2_compatible_board(board_config_path):
289 """Check if the given board config file is v2-compatible.
290
291 @param board_config_path: Path to a board config XML file.
292
293 @return True if the board is v2-compatible; False otherwise.
294 """
295 configs = []
296 def get_all_includes(config_path):
297 """Get all included XML config names in the given config file.
298
299 @param config_path: Path to a servo config file.
300 """
301 root = xml.etree.ElementTree.parse(config_path).getroot()
302 for element in root.findall('include'):
303 include_name = element.find('name').text
304 configs.append(include_name)
305 get_all_includes(os.path.join(
306 SERVOD_CONFIG_DATA_DIR, include_name))
307
308 get_all_includes(board_config_path)
309 return True if SERVOFLEX_V2_R0_P50_CONFIG in configs else False
310
Ricky Liangc101a562014-05-15 10:56:15 +0800311 result = []
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800312 board_overlays = glob.glob(
313 os.path.join(SERVOD_CONFIG_DATA_DIR, SERVO_CONFIG_GLOB))
314 for overlay_path in board_overlays:
315 if is_v2_compatible_board(overlay_path):
316 result.append(re.search(SERVO_CONFIG_REGEXP,
317 overlay_path).group('board'))
Ricky Liangc101a562014-05-15 10:56:15 +0800318 return result
319
320
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700321 def _get_flashrom_programmer(self, servo):
322 """Gets a proper flashrom programmer.
323
324 @param servo: A servo object.
325
326 @return A programmer for flashrom.
327 """
328 return FlashromProgrammer(servo)
329
330
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800331 def _factory_bios(self, servo):
332 """Instantiates and returns (bios, ec) programmers for the board.
333
334 @param servo: A servo object.
335
336 @return A programmer for ec. If the programmer is not supported
337 for the board, None will be returned.
338 """
339 _bios_prog = None
340 _board = servo.get_board()
341
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800342 logging.debug('Setting up BIOS programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800343 if _board in self._valid_boards:
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700344 _bios_prog = self._get_flashrom_programmer(servo)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800345 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700346 logging.warning('No BIOS programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800347
348 return _bios_prog
349
350
351 def _factory_ec(self, servo):
352 """Instantiates and returns ec programmer for the board.
353
354 @param servo: A servo object.
355
356 @return A programmer for ec. If the programmer is not supported
357 for the board, None will be returned.
358 """
359 _ec_prog = None
360 _board = servo.get_board()
361
362 logging.debug('Setting up EC programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800363 if _board in self._valid_boards:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800364 _ec_prog = FlashECProgrammer(servo)
365 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700366 logging.warning('No EC programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800367
368 return _ec_prog
369
370
371 def program_bios(self, image):
372 """Programs the DUT with provide bios image.
373
374 @param image: (required) location of bios image file.
375
376 """
377 self._bios_programmer.prepare_programmer(image)
378 self._bios_programmer.program()
379
Dan Shia5fef052015-05-18 23:28:47 -0700380
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800381 def program_ec(self, image):
382 """Programs the DUT with provide ec image.
383
384 @param image: (required) location of ec image file.
385
386 """
387 self._ec_programmer.prepare_programmer(image)
388 self._ec_programmer.program()
Dan Shia5fef052015-05-18 23:28:47 -0700389
390
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700391class ProgrammerV2RwOnly(ProgrammerV2):
392 """Main programmer class which provides programmer for only updating the RW
393 portion of BIOS with servo V2.
394
395 It does nothing on EC, as EC software sync on the next boot will
396 automatically overwrite the EC RW portion, using the EC RW image inside
397 the BIOS RW image.
398
399 """
400
401 def _get_flashrom_programmer(self, servo):
402 """Gets a proper flashrom programmer.
403
404 @param servo: A servo object.
405
406 @return A programmer for flashrom.
407 """
408 return FlashromProgrammer(servo, keep_ro=True)
409
410
411 def program_ec(self, image):
412 """Programs the DUT with provide ec image.
413
414 @param image: (required) location of ec image file.
415
416 """
417 # Do nothing. EC software sync will update the EC RW.
418 pass
419
420
Dan Shia5fef052015-05-18 23:28:47 -0700421class ProgrammerV3(object):
422 """Main programmer class which provides programmer for BIOS and EC with
423 servo V3.
424
425 Different from programmer for servo v2, programmer for servo v3 does not
426 try to validate if the board can use servo V3 to update firmware. As long as
427 the servod process running in beagblebone with given board, the program will
428 attempt to flash bios and ec.
429
430 """
431
432 def __init__(self, servo):
433 self._servo = servo
434 self._bios_programmer = FlashromProgrammer(servo)
435 self._ec_programmer = FlashECProgrammer(servo)
436
437
438 def program_bios(self, image):
439 """Programs the DUT with provide bios image.
440
441 @param image: (required) location of bios image file.
442
443 """
444 self._bios_programmer.prepare_programmer(image)
445 self._bios_programmer.program()
446
447
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800448 def program_ec(self, image):
Dan Shia5fef052015-05-18 23:28:47 -0700449 """Programs the DUT with provide ec image.
450
451 @param image: (required) location of ec image file.
Dan Shia5fef052015-05-18 23:28:47 -0700452
453 """
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800454 self._ec_programmer.prepare_programmer(image)
Dan Shia5fef052015-05-18 23:28:47 -0700455 self._ec_programmer.program()
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800456
457
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700458class ProgrammerV3RwOnly(ProgrammerV3):
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800459 """Main programmer class which provides programmer for only updating the RW
460 portion of BIOS with servo V3.
461
462 It does nothing on EC, as EC software sync on the next boot will
463 automatically overwrite the EC RW portion, using the EC RW image inside
464 the BIOS RW image.
465
466 """
467
468 def __init__(self, servo):
469 self._servo = servo
470 self._bios_programmer = FlashromProgrammer(servo, keep_ro=True)
471
472
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800473 def program_ec(self, image):
474 """Programs the DUT with provide ec image.
475
476 @param image: (required) location of ec image file.
477
478 """
479 # Do nothing. EC software sync will update the EC RW.
480 pass