blob: 0b205093e5e39ecb108760e3556f7f38723443a5 [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
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080019
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070020from autotest_lib.client.common_lib import error
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080021from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig
22
23
24class ProgrammerError(Exception):
25 """Local exception class wrapper."""
26 pass
27
28
29class _BaseProgrammer(object):
30 """Class implementing base programmer services.
31
32 Private attributes:
33 _servo: a servo object controlling the servo device
34 _servo_prog_state: a tuple of strings of "<control>:<value>" pairs,
35 listing servo controls and their required values for
36 programming
37 _servo_saved_state: a list of the same elements as _servo_prog_state,
38 those which need to be restored after programming
39 _program_cmd: a string, the shell command to run on the servo host
40 to actually program the firmware. Dependent on
41 firmware/hardware type, set by subclasses.
42 """
43
44 def __init__(self, servo, req_list):
45 """Base constructor.
46 @param servo: a servo object controlling the servo device
47 @param req_list: a list of strings, names of the utilities required
48 to be in the path for the programmer to succeed
49 """
50 self._servo = servo
51 self._servo_prog_state = ()
52 self._servo_saved_state = []
53 self._program_cmd = ''
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070054 try:
55 self._servo.system('which %s' % ' '.join(req_list))
56 except error.AutoservRunError:
57 # TODO: We turn this exception into a warn since the fw programmer
58 # is not working right now, and some systems do not package the
59 # required utilities its checking for.
60 # We should reinstate this exception once the programmer is working
61 # to indicate the missing utilities earlier in the test cycle.
62 # Bug chromium:371011 filed to track this.
63 logging.warn("Ignoring exception when verify required bins : %s",
64 ' '.join(req_list))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080065
66 def _set_servo_state(self):
67 """Set servo for programming, while saving the current state."""
68 logging.debug("Setting servo state for programming")
69 for item in self._servo_prog_state:
70 key, value = item.split(':')
71 present = self._servo.get(key)
72 if present != value:
73 self._servo_saved_state.append('%s:%s' % (key, present))
74 self._servo.set(key, value)
75
76
77 def _restore_servo_state(self):
78 """Restore previously saved servo state."""
79 logging.debug("Restoring servo state after programming")
80 self._servo_saved_state.reverse() # Do it in the reverse order.
81 for item in self._servo_saved_state:
82 key, value = item.split(':')
83 self._servo.set(key, value)
84
85
86 def program(self):
87 """Program the firmware as configured by a subclass."""
88 self._set_servo_state()
89 try:
90 logging.debug("Programmer command: %s", self._program_cmd)
91 self._servo.system(self._program_cmd)
92 finally:
93 self._restore_servo_state()
94
95
96class FlashromProgrammer(_BaseProgrammer):
97 """Class for programming AP flashrom."""
98
99 def __init__(self, servo):
100 """Configure required servo state."""
101 super(FlashromProgrammer, self).__init__(servo, ['flashrom',])
102 self._fw_path = None
103 self._tmp_path = '/tmp'
104 self._fw_main = os.path.join(self._tmp_path, 'fw_main')
105 self._ro_vpd = os.path.join(self._tmp_path, 'ro_vpd')
106 self._rw_vpd = os.path.join(self._tmp_path, 'rw_vpd')
107 self._gbb = os.path.join(self._tmp_path, 'gbb')
108
109
110 def program(self):
111 """Program the firmware but preserve VPD and HWID."""
112 assert self._fw_path is not None
113 self._set_servo_state()
114 try:
115 vpd_sections = [('RW_VPD', self._rw_vpd), ('RO_VPD', self._ro_vpd)]
116 gbb_section = [('GBB', self._gbb)]
Ricky Liang0dd379c2014-04-23 16:29:08 +0800117 ft2232_programmer = 'ft2232_spi:type=servo-v2'
118 if self._servo.servo_serial:
119 ft2232_programmer += ',serial=%s' % self._servo.servo_serial
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800120
121 # Save needed sections from current firmware
122 for section in vpd_sections + gbb_section:
123 self._servo.system(' '.join([
Ricky Liang0dd379c2014-04-23 16:29:08 +0800124 'flashrom', '-V', '-p', ft2232_programmer,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800125 '-r', self._fw_main, '-i', '%s:%s' % section]))
126
127 # Pack the saved VPD into new firmware
128 self._servo.system('cp %s %s' % (self._fw_path, self._fw_main))
129 img_size = self._servo.system_output(
130 "stat -c '%%s' %s" % self._fw_main)
131 pack_cmd = ['flashrom',
132 '-p', 'dummy:image=%s,size=%s,emulate=VARIABLE_SIZE' % (
133 self._fw_main, img_size),
134 '-w', self._fw_main]
135 for section in vpd_sections:
136 pack_cmd.extend(['-i', '%s:%s' % section])
137 self._servo.system(' '.join(pack_cmd))
138
139 # Read original HWID. The output format is:
140 # hardware_id: RAMBI TEST A_A 0128
141 gbb_hwid_output = self._servo.system_output(
142 'gbb_utility -g --hwid %s' % self._gbb)
143 original_hwid = gbb_hwid_output.split(':', 1)[1].strip()
144
145 # Write HWID to new firmware
146 self._servo.system("gbb_utility -s --hwid='%s' %s" %
147 (original_hwid, self._fw_main))
148
149 # Flash the new firmware
150 self._servo.system(' '.join([
Ricky Liang0dd379c2014-04-23 16:29:08 +0800151 'flashrom', '-V', '-p', ft2232_programmer,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800152 '-w', self._fw_main]))
153 finally:
154 self._restore_servo_state()
155
156
157 def prepare_programmer(self, path):
158 """Prepare programmer for programming.
159
160 @param path: a string, name of the file containing the firmware image.
161 @param board: a string, used to find servo voltage setting.
162 """
163 faft_config = FAFTConfig(self._servo.get_board())
164 self._fw_path = path
165 self._servo_prog_state = (
166 'spi2_vref:%s' % faft_config.wp_voltage,
167 'spi2_buf_en:on',
168 'spi2_buf_on_flex_en:on',
169 'spi_hold:off',
170 'cold_reset:on',
171 )
172
173
174class FlashECProgrammer(_BaseProgrammer):
175 """Class for programming AP flashrom."""
176
177 def __init__(self, servo):
178 """Configure required servo state."""
179 super(FlashECProgrammer, self).__init__(servo, ['flash_ec',])
180 self._servo = servo
181
182
183 def prepare_programmer(self, image):
184 """Prepare programmer for programming.
185
186 @param image: string with the location of the image file
187 """
188 # TODO: need to not have port be hardcoded
189 self._program_cmd = 'flash_ec --board=%s --image=%s --port=%s' % (
190 self._servo.get_board(), image, '9999')
191
192
193class ProgrammerV2(object):
194 """Main programmer class which provides programmer for BIOS and EC."""
195
196 def __init__(self, servo):
197 self._servo = servo
Ricky Liangc101a562014-05-15 10:56:15 +0800198 self._valid_boards = self._get_valid_v2_boards()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800199 self._bios_programmer = self._factory_bios(self._servo)
200 self._ec_programmer = self._factory_ec(self._servo)
201
202
Ricky Liangc101a562014-05-15 10:56:15 +0800203 @staticmethod
204 def _get_valid_v2_boards():
205 """Greps servod config files to look for valid v2 boards.
206
207 @return A list of valid board names.
208 """
Ricky Liangc7cc3402014-05-24 00:25:26 +0800209 site_packages_paths = site.getsitepackages()
210 SERVOD_CONFIG_DATA_DIR = None
211 for p in site_packages_paths:
212 servo_data_path = os.path.join(p, 'servo', 'data')
213 if os.path.exists(servo_data_path):
214 SERVOD_CONFIG_DATA_DIR = servo_data_path
215 break
216 if not SERVOD_CONFIG_DATA_DIR:
217 raise ProgrammerError(
218 'Unable to locate data directory of Python servo module')
Ricky Liangc101a562014-05-15 10:56:15 +0800219 SERVOFLEX_V2_R0_P50_CONFIG = 'servoflex_v2_r0_p50.xml'
220 SERVO_CONFIG_REGEXP = 'servo_(?P<board>.+)_overlay.xml'
221
222 result = []
223 config_glob = glob.glob(os.path.join(SERVOD_CONFIG_DATA_DIR,
224 SERVO_CONFIG_REGEXP.replace(
225 '(?P<board>.+)', '*')))
226 for config in config_glob:
227 with open(config) as f:
228 if SERVOFLEX_V2_R0_P50_CONFIG in f.read():
229 result.append(
230 re.search(SERVO_CONFIG_REGEXP,
231 config).group('board'))
232 return result
233
234
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800235 def _factory_bios(self, servo):
236 """Instantiates and returns (bios, ec) programmers for the board.
237
238 @param servo: A servo object.
239
240 @return A programmer for ec. If the programmer is not supported
241 for the board, None will be returned.
242 """
243 _bios_prog = None
244 _board = servo.get_board()
245
246 servo_prog_state = [
247 'spi2_buf_en:on',
248 'spi2_buf_on_flex_en:on',
249 'spi_hold:off',
250 'cold_reset:on',
251 ]
252
253 logging.debug('Setting up BIOS programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800254 if _board in self._valid_boards:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800255 _bios_prog = FlashromProgrammer(servo)
256 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700257 logging.warning('No BIOS programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800258
259 return _bios_prog
260
261
262 def _factory_ec(self, servo):
263 """Instantiates and returns ec programmer for the board.
264
265 @param servo: A servo object.
266
267 @return A programmer for ec. If the programmer is not supported
268 for the board, None will be returned.
269 """
270 _ec_prog = None
271 _board = servo.get_board()
272
273 logging.debug('Setting up EC programmer for board: %s', _board)
Ricky Liangc101a562014-05-15 10:56:15 +0800274 if _board in self._valid_boards:
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800275 _ec_prog = FlashECProgrammer(servo)
276 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700277 logging.warning('No EC programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800278
279 return _ec_prog
280
281
282 def program_bios(self, image):
283 """Programs the DUT with provide bios image.
284
285 @param image: (required) location of bios image file.
286
287 """
288 self._bios_programmer.prepare_programmer(image)
289 self._bios_programmer.program()
290
291 def program_ec(self, image):
292 """Programs the DUT with provide ec image.
293
294 @param image: (required) location of ec image file.
295
296 """
297 self._ec_programmer.prepare_programmer(image)
298 self._ec_programmer.program()