blob: 457e3a51e9b1ec83c8736b73ce8554c6fa4e8d7d [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
14import logging
15import os
16
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070017from autotest_lib.client.common_lib import error
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080018from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig
19
20
21class ProgrammerError(Exception):
22 """Local exception class wrapper."""
23 pass
24
25
26class _BaseProgrammer(object):
27 """Class implementing base programmer services.
28
29 Private attributes:
30 _servo: a servo object controlling the servo device
31 _servo_prog_state: a tuple of strings of "<control>:<value>" pairs,
32 listing servo controls and their required values for
33 programming
34 _servo_saved_state: a list of the same elements as _servo_prog_state,
35 those which need to be restored after programming
36 _program_cmd: a string, the shell command to run on the servo host
37 to actually program the firmware. Dependent on
38 firmware/hardware type, set by subclasses.
39 """
40
41 def __init__(self, servo, req_list):
42 """Base constructor.
43 @param servo: a servo object controlling the servo device
44 @param req_list: a list of strings, names of the utilities required
45 to be in the path for the programmer to succeed
46 """
47 self._servo = servo
48 self._servo_prog_state = ()
49 self._servo_saved_state = []
50 self._program_cmd = ''
Yusuf Mohsinally8c4631d2014-05-05 18:20:54 -070051 try:
52 self._servo.system('which %s' % ' '.join(req_list))
53 except error.AutoservRunError:
54 # TODO: We turn this exception into a warn since the fw programmer
55 # is not working right now, and some systems do not package the
56 # required utilities its checking for.
57 # We should reinstate this exception once the programmer is working
58 # to indicate the missing utilities earlier in the test cycle.
59 # Bug chromium:371011 filed to track this.
60 logging.warn("Ignoring exception when verify required bins : %s",
61 ' '.join(req_list))
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080062
63 def _set_servo_state(self):
64 """Set servo for programming, while saving the current state."""
65 logging.debug("Setting servo state for programming")
66 for item in self._servo_prog_state:
67 key, value = item.split(':')
68 present = self._servo.get(key)
69 if present != value:
70 self._servo_saved_state.append('%s:%s' % (key, present))
71 self._servo.set(key, value)
72
73
74 def _restore_servo_state(self):
75 """Restore previously saved servo state."""
76 logging.debug("Restoring servo state after programming")
77 self._servo_saved_state.reverse() # Do it in the reverse order.
78 for item in self._servo_saved_state:
79 key, value = item.split(':')
80 self._servo.set(key, value)
81
82
83 def program(self):
84 """Program the firmware as configured by a subclass."""
85 self._set_servo_state()
86 try:
87 logging.debug("Programmer command: %s", self._program_cmd)
88 self._servo.system(self._program_cmd)
89 finally:
90 self._restore_servo_state()
91
92
93class FlashromProgrammer(_BaseProgrammer):
94 """Class for programming AP flashrom."""
95
96 def __init__(self, servo):
97 """Configure required servo state."""
98 super(FlashromProgrammer, self).__init__(servo, ['flashrom',])
99 self._fw_path = None
100 self._tmp_path = '/tmp'
101 self._fw_main = os.path.join(self._tmp_path, 'fw_main')
102 self._ro_vpd = os.path.join(self._tmp_path, 'ro_vpd')
103 self._rw_vpd = os.path.join(self._tmp_path, 'rw_vpd')
104 self._gbb = os.path.join(self._tmp_path, 'gbb')
105
106
107 def program(self):
108 """Program the firmware but preserve VPD and HWID."""
109 assert self._fw_path is not None
110 self._set_servo_state()
111 try:
112 vpd_sections = [('RW_VPD', self._rw_vpd), ('RO_VPD', self._ro_vpd)]
113 gbb_section = [('GBB', self._gbb)]
Ricky Liang0dd379c2014-04-23 16:29:08 +0800114 ft2232_programmer = 'ft2232_spi:type=servo-v2'
115 if self._servo.servo_serial:
116 ft2232_programmer += ',serial=%s' % self._servo.servo_serial
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800117
118 # Save needed sections from current firmware
119 for section in vpd_sections + gbb_section:
120 self._servo.system(' '.join([
Ricky Liang0dd379c2014-04-23 16:29:08 +0800121 'flashrom', '-V', '-p', ft2232_programmer,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800122 '-r', self._fw_main, '-i', '%s:%s' % section]))
123
124 # Pack the saved VPD into new firmware
125 self._servo.system('cp %s %s' % (self._fw_path, self._fw_main))
126 img_size = self._servo.system_output(
127 "stat -c '%%s' %s" % self._fw_main)
128 pack_cmd = ['flashrom',
129 '-p', 'dummy:image=%s,size=%s,emulate=VARIABLE_SIZE' % (
130 self._fw_main, img_size),
131 '-w', self._fw_main]
132 for section in vpd_sections:
133 pack_cmd.extend(['-i', '%s:%s' % section])
134 self._servo.system(' '.join(pack_cmd))
135
136 # Read original HWID. The output format is:
137 # hardware_id: RAMBI TEST A_A 0128
138 gbb_hwid_output = self._servo.system_output(
139 'gbb_utility -g --hwid %s' % self._gbb)
140 original_hwid = gbb_hwid_output.split(':', 1)[1].strip()
141
142 # Write HWID to new firmware
143 self._servo.system("gbb_utility -s --hwid='%s' %s" %
144 (original_hwid, self._fw_main))
145
146 # Flash the new firmware
147 self._servo.system(' '.join([
Ricky Liang0dd379c2014-04-23 16:29:08 +0800148 'flashrom', '-V', '-p', ft2232_programmer,
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800149 '-w', self._fw_main]))
150 finally:
151 self._restore_servo_state()
152
153
154 def prepare_programmer(self, path):
155 """Prepare programmer for programming.
156
157 @param path: a string, name of the file containing the firmware image.
158 @param board: a string, used to find servo voltage setting.
159 """
160 faft_config = FAFTConfig(self._servo.get_board())
161 self._fw_path = path
162 self._servo_prog_state = (
163 'spi2_vref:%s' % faft_config.wp_voltage,
164 'spi2_buf_en:on',
165 'spi2_buf_on_flex_en:on',
166 'spi_hold:off',
167 'cold_reset:on',
168 )
169
170
171class FlashECProgrammer(_BaseProgrammer):
172 """Class for programming AP flashrom."""
173
174 def __init__(self, servo):
175 """Configure required servo state."""
176 super(FlashECProgrammer, self).__init__(servo, ['flash_ec',])
177 self._servo = servo
178
179
180 def prepare_programmer(self, image):
181 """Prepare programmer for programming.
182
183 @param image: string with the location of the image file
184 """
185 # TODO: need to not have port be hardcoded
186 self._program_cmd = 'flash_ec --board=%s --image=%s --port=%s' % (
187 self._servo.get_board(), image, '9999')
188
189
190class ProgrammerV2(object):
191 """Main programmer class which provides programmer for BIOS and EC."""
192
193 def __init__(self, servo):
194 self._servo = servo
195 self._bios_programmer = self._factory_bios(self._servo)
196 self._ec_programmer = self._factory_ec(self._servo)
197
198
199 def _factory_bios(self, servo):
200 """Instantiates and returns (bios, ec) programmers for the board.
201
202 @param servo: A servo object.
203
204 @return A programmer for ec. If the programmer is not supported
205 for the board, None will be returned.
206 """
207 _bios_prog = None
208 _board = servo.get_board()
209
210 servo_prog_state = [
211 'spi2_buf_en:on',
212 'spi2_buf_on_flex_en:on',
213 'spi_hold:off',
214 'cold_reset:on',
215 ]
216
217 logging.debug('Setting up BIOS programmer for board: %s', _board)
218 if _board in ('daisy_spring', 'rambi', 'pit', 'spring',
219 'snow', 'daisy', 'monroe', 'panther', 'beltino',
220 'bolt', 'slippy', 'falco', 'link', 'stumpy',
221 'lumpy', 'parrot', 'stout', 'butterfly', 'alex',
Ricky Liangc0003472014-04-24 16:11:44 +0800222 'zgb', 'mario', 'squawks'):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800223 _bios_prog = FlashromProgrammer(servo)
224 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700225 logging.warning('No BIOS programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800226
227 return _bios_prog
228
229
230 def _factory_ec(self, servo):
231 """Instantiates and returns ec programmer for the board.
232
233 @param servo: A servo object.
234
235 @return A programmer for ec. If the programmer is not supported
236 for the board, None will be returned.
237 """
238 _ec_prog = None
239 _board = servo.get_board()
240
241 logging.debug('Setting up EC programmer for board: %s', _board)
242 if _board in ('daisy', 'kirby', 'pit', 'puppy', 'snow',
243 'spring', 'discovery', 'nyan', 'bolt', 'samus',
Ricky Liangc0003472014-04-24 16:11:44 +0800244 'falco', 'peppy', 'rambi', 'slippy', 'link',
245 'squawks'):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800246 _ec_prog = FlashECProgrammer(servo)
247 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700248 logging.warning('No EC programmer found for board: %s', _board)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800249
250 return _ec_prog
251
252
253 def program_bios(self, image):
254 """Programs the DUT with provide bios image.
255
256 @param image: (required) location of bios image file.
257
258 """
259 self._bios_programmer.prepare_programmer(image)
260 self._bios_programmer.program()
261
262 def program_ec(self, image):
263 """Programs the DUT with provide ec image.
264
265 @param image: (required) location of ec image file.
266
267 """
268 self._ec_programmer.prepare_programmer(image)
269 self._ec_programmer.program()