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