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