blob: 6f792533be645ebc25a0a4b73b4bc3e708d0a992 [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
Ricky Liangf19ab9f2014-06-11 15:50:44 +080019import xml.etree.ElementTree
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080020
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070021from autotest_lib.client.common_lib import error
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080022from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig
23
24
25class ProgrammerError(Exception):
26 """Local exception class wrapper."""
27 pass
28
29
30class _BaseProgrammer(object):
31 """Class implementing base programmer services.
32
33 Private attributes:
34 _servo: a servo object controlling the servo device
35 _servo_prog_state: a tuple of strings of "<control>:<value>" pairs,
36 listing servo controls and their required values for
37 programming
38 _servo_saved_state: a list of the same elements as _servo_prog_state,
39 those which need to be restored after programming
40 _program_cmd: a string, the shell command to run on the servo host
41 to actually program the firmware. Dependent on
42 firmware/hardware type, set by subclasses.
43 """
44
45 def __init__(self, servo, req_list):
46 """Base constructor.
47 @param servo: a servo object controlling the servo device
48 @param req_list: a list of strings, names of the utilities required
49 to be in the path for the programmer to succeed
50 """
51 self._servo = servo
52 self._servo_prog_state = ()
53 self._servo_saved_state = []
54 self._program_cmd = ''
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070055 try:
56 self._servo.system('which %s' % ' '.join(req_list))
57 except error.AutoservRunError:
58 # TODO: We turn this exception into a warn since the fw programmer
59 # is not working right now, and some systems do not package the
60 # required utilities its checking for.
61 # We should reinstate this exception once the programmer is working
62 # to indicate the missing utilities earlier in the test cycle.
63 # Bug chromium:371011 filed to track this.
64 logging.warn("Ignoring exception when verify required bins : %s",
65 ' '.join(req_list))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080066
Dan Shia5fef052015-05-18 23:28:47 -070067
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080068 def _set_servo_state(self):
69 """Set servo for programming, while saving the current state."""
70 logging.debug("Setting servo state for programming")
71 for item in self._servo_prog_state:
72 key, value = item.split(':')
73 present = self._servo.get(key)
74 if present != value:
75 self._servo_saved_state.append('%s:%s' % (key, present))
76 self._servo.set(key, value)
77
78
79 def _restore_servo_state(self):
80 """Restore previously saved servo state."""
81 logging.debug("Restoring servo state after programming")
82 self._servo_saved_state.reverse() # Do it in the reverse order.
83 for item in self._servo_saved_state:
84 key, value = item.split(':')
85 self._servo.set(key, value)
86
87
88 def program(self):
89 """Program the firmware as configured by a subclass."""
90 self._set_servo_state()
91 try:
92 logging.debug("Programmer command: %s", self._program_cmd)
93 self._servo.system(self._program_cmd)
94 finally:
95 self._restore_servo_state()
96
97
98class FlashromProgrammer(_BaseProgrammer):
99 """Class for programming AP flashrom."""
100
101 def __init__(self, servo):
102 """Configure required servo state."""
103 super(FlashromProgrammer, self).__init__(servo, ['flashrom',])
104 self._fw_path = None
105 self._tmp_path = '/tmp'
106 self._fw_main = os.path.join(self._tmp_path, 'fw_main')
107 self._ro_vpd = os.path.join(self._tmp_path, 'ro_vpd')
108 self._rw_vpd = os.path.join(self._tmp_path, 'rw_vpd')
109 self._gbb = os.path.join(self._tmp_path, 'gbb')
110
111
112 def program(self):
113 """Program the firmware but preserve VPD and HWID."""
114 assert self._fw_path is not None
115 self._set_servo_state()
116 try:
117 vpd_sections = [('RW_VPD', self._rw_vpd), ('RO_VPD', self._ro_vpd)]
118 gbb_section = [('GBB', self._gbb)]
Dan Shia5fef052015-05-18 23:28:47 -0700119 servo_version = self._servo.get_servo_version()
120 servo_v2_programmer = 'ft2232_spi:type=servo-v2'
121 servo_v3_programmer = 'linux_spi'
122 if servo_version == 'servo_v2':
123 programmer = servo_v2_programmer
124 if self._servo.servo_serial:
125 programmer += ',serial=%s' % self._servo.servo_serial
126 elif servo_version == 'servo_v3':
127 programmer = servo_v3_programmer
128 else:
129 raise Exception('Servo version %s is not supported.' %
130 servo_version)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800131 # Save needed sections from current firmware
132 for section in vpd_sections + gbb_section:
133 self._servo.system(' '.join([
Dan Shia5fef052015-05-18 23:28:47 -0700134 'flashrom', '-V', '-p', programmer,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800135 '-r', self._fw_main, '-i', '%s:%s' % section]))
136
137 # Pack the saved VPD into new firmware
138 self._servo.system('cp %s %s' % (self._fw_path, self._fw_main))
139 img_size = self._servo.system_output(
140 "stat -c '%%s' %s" % self._fw_main)
141 pack_cmd = ['flashrom',
142 '-p', 'dummy:image=%s,size=%s,emulate=VARIABLE_SIZE' % (
143 self._fw_main, img_size),
144 '-w', self._fw_main]
145 for section in vpd_sections:
146 pack_cmd.extend(['-i', '%s:%s' % section])
147 self._servo.system(' '.join(pack_cmd))
148
149 # Read original HWID. The output format is:
150 # hardware_id: RAMBI TEST A_A 0128
151 gbb_hwid_output = self._servo.system_output(
152 'gbb_utility -g --hwid %s' % self._gbb)
153 original_hwid = gbb_hwid_output.split(':', 1)[1].strip()
154
155 # Write HWID to new firmware
156 self._servo.system("gbb_utility -s --hwid='%s' %s" %
157 (original_hwid, self._fw_main))
158
159 # Flash the new firmware
160 self._servo.system(' '.join([
Dan Shia5fef052015-05-18 23:28:47 -0700161 'flashrom', '-V', '-p', programmer,
162 '-w', self._fw_main]))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800163 finally:
164 self._restore_servo_state()
165
166
167 def prepare_programmer(self, path):
168 """Prepare programmer for programming.
169
170 @param path: a string, name of the file containing the firmware image.
171 @param board: a string, used to find servo voltage setting.
172 """
173 faft_config = FAFTConfig(self._servo.get_board())
174 self._fw_path = path
175 self._servo_prog_state = (
Vic Yang15c2dcb2014-06-02 10:31:54 -0700176 'spi2_vref:%s' % faft_config.spi_voltage,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800177 'spi2_buf_en:on',
178 'spi2_buf_on_flex_en:on',
179 'spi_hold:off',
180 'cold_reset:on',
181 )
182
183
184class FlashECProgrammer(_BaseProgrammer):
185 """Class for programming AP flashrom."""
186
187 def __init__(self, servo):
188 """Configure required servo state."""
189 super(FlashECProgrammer, self).__init__(servo, ['flash_ec',])
190 self._servo = servo
191
192
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800193 def prepare_programmer(self, image):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800194 """Prepare programmer for programming.
195
196 @param image: string with the location of the image file
197 """
198 # TODO: need to not have port be hardcoded
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800199 self._program_cmd = ('flash_ec --chip=%s --image=%s --port=%s' %
200 (self._servo.get('ec_chip'), image, '9999'))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800201
202
203class ProgrammerV2(object):
Dan Shia5fef052015-05-18 23:28:47 -0700204 """Main programmer class which provides programmer for BIOS and EC with
205 servo V2."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800206
207 def __init__(self, servo):
208 self._servo = servo
Ricky Liangc101a562014-05-15 10:56:15 +0800209 self._valid_boards = self._get_valid_v2_boards()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800210 self._bios_programmer = self._factory_bios(self._servo)
211 self._ec_programmer = self._factory_ec(self._servo)
212
213
Ricky Liangc101a562014-05-15 10:56:15 +0800214 @staticmethod
215 def _get_valid_v2_boards():
216 """Greps servod config files to look for valid v2 boards.
217
218 @return A list of valid board names.
219 """
Ricky Liangc7cc3402014-05-24 00:25:26 +0800220 site_packages_paths = site.getsitepackages()
221 SERVOD_CONFIG_DATA_DIR = None
222 for p in site_packages_paths:
223 servo_data_path = os.path.join(p, 'servo', 'data')
224 if os.path.exists(servo_data_path):
225 SERVOD_CONFIG_DATA_DIR = servo_data_path
226 break
227 if not SERVOD_CONFIG_DATA_DIR:
228 raise ProgrammerError(
229 'Unable to locate data directory of Python servo module')
Ricky Liangc101a562014-05-15 10:56:15 +0800230 SERVOFLEX_V2_R0_P50_CONFIG = 'servoflex_v2_r0_p50.xml'
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800231 SERVO_CONFIG_GLOB = 'servo_*_overlay.xml'
Ricky Liangc101a562014-05-15 10:56:15 +0800232 SERVO_CONFIG_REGEXP = 'servo_(?P<board>.+)_overlay.xml'
233
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800234 def is_v2_compatible_board(board_config_path):
235 """Check if the given board config file is v2-compatible.
236
237 @param board_config_path: Path to a board config XML file.
238
239 @return True if the board is v2-compatible; False otherwise.
240 """
241 configs = []
242 def get_all_includes(config_path):
243 """Get all included XML config names in the given config file.
244
245 @param config_path: Path to a servo config file.
246 """
247 root = xml.etree.ElementTree.parse(config_path).getroot()
248 for element in root.findall('include'):
249 include_name = element.find('name').text
250 configs.append(include_name)
251 get_all_includes(os.path.join(
252 SERVOD_CONFIG_DATA_DIR, include_name))
253
254 get_all_includes(board_config_path)
255 return True if SERVOFLEX_V2_R0_P50_CONFIG in configs else False
256
Ricky Liangc101a562014-05-15 10:56:15 +0800257 result = []
Ricky Liangf19ab9f2014-06-11 15:50:44 +0800258 board_overlays = glob.glob(
259 os.path.join(SERVOD_CONFIG_DATA_DIR, SERVO_CONFIG_GLOB))
260 for overlay_path in board_overlays:
261 if is_v2_compatible_board(overlay_path):
262 result.append(re.search(SERVO_CONFIG_REGEXP,
263 overlay_path).group('board'))
Ricky Liangc101a562014-05-15 10:56:15 +0800264 return result
265
266
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800267 def _factory_bios(self, servo):
268 """Instantiates and returns (bios, ec) programmers for the board.
269
270 @param servo: A servo object.
271
272 @return A programmer for ec. If the programmer is not supported
273 for the board, None will be returned.
274 """
275 _bios_prog = None
276 _board = servo.get_board()
277
278 servo_prog_state = [
279 'spi2_buf_en:on',
280 'spi2_buf_on_flex_en:on',
281 'spi_hold:off',
282 'cold_reset:on',
283 ]
284
285 logging.debug('Setting up BIOS programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800286 if _board in self._valid_boards:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800287 _bios_prog = FlashromProgrammer(servo)
288 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700289 logging.warning('No BIOS programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800290
291 return _bios_prog
292
293
294 def _factory_ec(self, servo):
295 """Instantiates and returns ec programmer for the board.
296
297 @param servo: A servo object.
298
299 @return A programmer for ec. If the programmer is not supported
300 for the board, None will be returned.
301 """
302 _ec_prog = None
303 _board = servo.get_board()
304
305 logging.debug('Setting up EC programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800306 if _board in self._valid_boards:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800307 _ec_prog = FlashECProgrammer(servo)
308 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700309 logging.warning('No EC programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800310
311 return _ec_prog
312
313
314 def program_bios(self, image):
315 """Programs the DUT with provide bios image.
316
317 @param image: (required) location of bios image file.
318
319 """
320 self._bios_programmer.prepare_programmer(image)
321 self._bios_programmer.program()
322
Dan Shia5fef052015-05-18 23:28:47 -0700323
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800324 def program_ec(self, image):
325 """Programs the DUT with provide ec image.
326
327 @param image: (required) location of ec image file.
328
329 """
330 self._ec_programmer.prepare_programmer(image)
331 self._ec_programmer.program()
Dan Shia5fef052015-05-18 23:28:47 -0700332
333
334class ProgrammerV3(object):
335 """Main programmer class which provides programmer for BIOS and EC with
336 servo V3.
337
338 Different from programmer for servo v2, programmer for servo v3 does not
339 try to validate if the board can use servo V3 to update firmware. As long as
340 the servod process running in beagblebone with given board, the program will
341 attempt to flash bios and ec.
342
343 """
344
345 def __init__(self, servo):
346 self._servo = servo
347 self._bios_programmer = FlashromProgrammer(servo)
348 self._ec_programmer = FlashECProgrammer(servo)
349
350
351 def program_bios(self, image):
352 """Programs the DUT with provide bios image.
353
354 @param image: (required) location of bios image file.
355
356 """
357 self._bios_programmer.prepare_programmer(image)
358 self._bios_programmer.program()
359
360
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800361 def program_ec(self, image):
Dan Shia5fef052015-05-18 23:28:47 -0700362 """Programs the DUT with provide ec image.
363
364 @param image: (required) location of ec image file.
Dan Shia5fef052015-05-18 23:28:47 -0700365
366 """
Tom Wai-Hong Tam7b7eeac2015-07-23 01:53:52 +0800367 self._ec_programmer.prepare_programmer(image)
Dan Shia5fef052015-05-18 23:28:47 -0700368 self._ec_programmer.program()