blob: 107e3a6dbf9ca75109ad07f02ba05f750b4b7488 [file] [log] [blame]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001# Copyright (c) 2011 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
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +08005import ctypes
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08006import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08007import os
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08008import re
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +08009import subprocess
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080010import sys
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080011import time
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080012
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080013from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080014from autotest_lib.client.common_lib import error
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +080015from autotest_lib.server.cros import vboot_constants as vboot
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +080016from autotest_lib.server.cros.chrome_ec import ChromeEC
Vic Yangebd6de62012-06-26 14:25:57 +080017from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +080018from autotest_lib.server.cros.faft_delay_constants import FAFTDelayConstants
Tom Wai-Hong Tam22b77302011-11-03 13:03:48 +080019from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080020from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +080021from autotest_lib.site_utils.chromeos_test.common_util import ChromeOSTestError
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080022
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080023dirname = os.path.dirname(sys.modules[__name__].__file__)
24autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
25cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080026
27class FAFTSequence(ServoTest):
28 """
29 The base class of Fully Automated Firmware Test Sequence.
30
31 Many firmware tests require several reboot cycles and verify the resulted
32 system states. To do that, an Autotest test case should detailly handle
33 every action on each step. It makes the test case hard to read and many
34 duplicated code. The base class FAFTSequence is to solve this problem.
35
36 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
37 There are four functions in the FAFT_STEP dict:
38 state_checker: a function to check the current is valid or not,
39 returning True if valid, otherwise, False to break the whole
40 test sequence.
41 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080042 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080043 firmware_action: a function to describe the action ran after reboot.
44
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080045 And configurations:
46 install_deps_after_boot: if True, install the Autotest dependency after
47 boot; otherwise, do nothing. It is for the cases of recovery mode
48 test. The test boots a USB/SD image instead of an internal image.
49 The previous installed Autotest dependency on the internal image
50 is lost. So need to install it again.
51
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080052 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080053 userspace_action and firmware_action. Its reboot_action is a hardware
54 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080055 self.register_faft_template(FAFT_STEP).
56
57 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
58 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
59 fall back to default.
60
61 In the run_once(), it should register and run FAFT_SEQUENCE like:
62 def run_once(self):
63 self.register_faft_sequence(FAFT_SEQUENCE)
64 self.run_faft_sequnce()
65
66 Note that in the last step, we only run state_checker. The
67 userspace_action, reboot_action, and firmware_action are not executed.
68
69 Attributes:
70 _faft_template: The default FAFT_STEP of each step. The actions would
71 be over-written if the registered FAFT_SEQUENCE is valid.
72 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +080073 _customized_key_commands: The dict of the customized key commands,
74 including Ctrl-D, Ctrl-U, Enter, Space, and recovery reboot.
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080075 _install_image_path: The URL or the path on the host to the Chrome OS
76 test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080077 _firmware_update: Boolean. True if firmware update needed after
78 installing the image.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +080079 _trapped_in_recovery_reason: Keep the recovery reason when the test is
80 trapped in the recovery screen.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080081 """
82 version = 1
83
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080084 # Mapping of partition number of kernel and rootfs.
85 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
86 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
87 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
88 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
89
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +080090 CHROMEOS_MAGIC = "CHROMEOS"
91 CORRUPTED_MAGIC = "CORRUPTD"
92
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080093 _HTTP_PREFIX = 'http://'
94 _DEVSERVER_PORT = '8090'
95
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +080096 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080097 _faft_sequence = ()
98
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +080099 _customized_key_commands = {
100 'ctrl_d': None,
101 'ctrl_u': None,
102 'enter': None,
103 'rec_reboot': None,
104 'space': None,
105 }
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800106 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800107 _firmware_update = False
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800108 _trapped_in_recovery_reason = 0
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800109
ctchang38ae4922012-09-03 17:01:16 +0800110 _backup_firmware_sha = ()
111
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800112 # Class level variable, keep track the states of one time setup.
113 # This variable is preserved across tests which inherit this class.
114 _global_setup_done = {
115 'gbb_flags': False,
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800116 'reimage': False,
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800117 'usb_check': False,
118 }
Vic Yang54f70572012-10-19 17:05:26 +0800119
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800120 @classmethod
121 def check_setup_done(cls, label):
122 """Check if the given setup is done.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800123
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800124 Args:
125 label: The label of the setup.
126 """
127 return cls._global_setup_done[label]
128
129
130 @classmethod
131 def mark_setup_done(cls, label):
132 """Mark the given setup done.
133
134 Args:
135 label: The label of the setup.
136 """
137 cls._global_setup_done[label] = True
138
139
140 @classmethod
141 def unmark_setup_done(cls, label):
142 """Mark the given setup not done.
143
144 Args:
145 label: The label of the setup.
146 """
147 cls._global_setup_done[label] = False
Vic Yang54f70572012-10-19 17:05:26 +0800148
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800149
150 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
151 # Parse arguments from command line
152 args = {}
153 for arg in cmdline_args:
154 match = re.search("^(\w+)=(.+)", arg)
155 if match:
156 args[match.group(1)] = match.group(2)
157
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800158 # Keep the arguments which will be used later.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800159 for key in self._customized_key_commands:
160 key_cmd = key + '_cmd'
161 if key_cmd in args:
162 self._customized_key_commands[key] = args[key_cmd]
163 logging.info('Customized %s key command: %s' %
164 (key, args[key_cmd]))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800165 if 'image' in args:
166 self._install_image_path = args['image']
167 logging.info('Install Chrome OS test image path: %s' %
168 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800169 if 'firmware_update' in args and args['firmware_update'].lower() \
170 not in ('0', 'false', 'no'):
171 if self._install_image_path:
172 self._firmware_update = True
173 logging.info('Also update firmware after installing.')
174 else:
175 logging.warning('Firmware update will not not performed '
176 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800177
178 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
179 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800180 if use_faft:
181 self.client_attr = FAFTClientAttribute(
182 self.faft_client.get_platform_name())
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800183 self.delay = FAFTDelayConstants(
184 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800185
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800186 if self.client_attr.chrome_ec:
187 self.ec = ChromeEC(self.servo)
188
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700189 # Setting up key matrix mapping
190 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
191
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800192
193 def setup(self):
194 """Autotest setup function."""
195 super(FAFTSequence, self).setup()
196 if not self._remote_infos['faft']['used']:
197 raise error.TestError('The use_faft flag should be enabled.')
198 self.register_faft_template({
199 'state_checker': (None),
200 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800201 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800202 'firmware_action': (None)
203 })
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800204 self.install_test_image(self._install_image_path, self._firmware_update)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800205 self.setup_gbb_flags()
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800206
207
208 def cleanup(self):
209 """Autotest cleanup function."""
210 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800211 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800212 super(FAFTSequence, self).cleanup()
213
214
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800215 def invalidate_firmware_setup(self):
216 """Invalidate all firmware related setup state.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800217
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800218 This method is called when the firmware is re-flashed. It resets all
219 firmware related setup states so that the next test setup properly
220 again.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800221 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800222 self.unmark_setup_done('gbb_flags')
Vic Yangdbaba8f2012-10-17 16:05:35 +0800223
224
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800225 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800226 """Reset client, if necessary.
227
228 This method is called when the client is not responsive. It may be
229 caused by the following cases:
230 - network flaky (can be recovered by replugging the Ethernet);
231 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
232 - corrupted firmware;
233 - corrutped OS image.
234 """
235 # DUT works fine, done.
236 if self._ping_test(self._client.ip, timeout=5):
237 return
238
239 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
240 # first reset item.
241
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800242 # DUT may be trapped in the recovery screen. Try to boot into USB to
243 # retrieve the recovery reason.
244 logging.info('Try to retrieve recovery reason...')
245 if self.servo.get('usb_mux_sel1') == 'dut_sees_usbkey':
246 self.wait_fw_screen_and_plug_usb()
247 else:
248 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
249
250 try:
251 self.wait_for_client(install_deps=True)
252 lines = self.faft_client.run_shell_command_get_output(
253 'crossystem recovery_reason')
254 self._trapped_in_recovery_reason = int(lines[0])
255 logging.info('Got the recovery reason %d.' %
256 self._trapped_in_recovery_reason)
257 except AssertionError:
258 logging.info('Failed to get the recovery reason.')
259
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800260 # DUT may halt on a firmware screen. Try cold reboot.
261 logging.info('Try cold reboot...')
262 self.cold_reboot()
263 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800264 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800265 return
266 except AssertionError:
267 pass
268
269 # DUT may be broken by a corrupted firmware. Restore firmware.
270 # We assume the recovery boot still works fine. Since the recovery
271 # code is in RO region and all FAFT tests don't change the RO region
272 # except GBB.
273 if self.is_firmware_saved():
274 self.ensure_client_in_recovery()
275 logging.info('Try restore the original firmware...')
276 if self.is_firmware_changed():
277 try:
278 self.restore_firmware()
279 return
280 except AssertionError:
281 logging.info('Restoring firmware doesn\'t help.')
282
283 # DUT may be broken by a corrupted OS image. Restore OS image.
284 self.ensure_client_in_recovery()
285 logging.info('Try restore the OS image...')
286 self.faft_client.run_shell_command('chromeos-install --yes')
287 self.sync_and_warm_reboot()
288 self.wait_for_client_offline()
289 try:
290 self.wait_for_client(install_deps=True)
291 logging.info('Successfully restore OS image.')
292 return
293 except AssertionError:
294 logging.info('Restoring OS image doesn\'t help.')
295
296
297 def ensure_client_in_recovery(self):
298 """Ensure client in recovery boot; reboot into it if necessary.
299
300 Raises:
301 error.TestError: if failed to boot the USB image.
302 """
303 # DUT works fine and is already in recovery boot, done.
304 if self._ping_test(self._client.ip, timeout=5):
305 if self.crossystem_checker({'mainfw_type': 'recovery'}):
306 return
307
308 logging.info('Try boot into USB image...')
309 self.servo.enable_usb_hub(host=True)
310 self.enable_rec_mode_and_reboot()
311 self.wait_fw_screen_and_plug_usb()
312 try:
313 self.wait_for_client(install_deps=True)
314 except AssertionError:
315 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800316
317
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800318 def assert_test_image_in_path(self, image_path):
319 """Assert the image of image_path be a Chrome OS test image.
320
321 Args:
322 image_path: A path on the host to the test image.
323
324 Raises:
325 error.TestError: if the image is not a test image.
326 """
327 try:
328 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
329 image_path)
330 logging.info('Build of image: %s %s' % (build_ver, build_hash))
331 except ChromeOSTestError:
332 raise error.TestError(
333 'An USB disk containning a test image should be plugged '
334 'in the servo board.')
335
336
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800337 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800338 """Assert an USB disk plugged-in on servo and a test image inside.
339
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800340 Args:
341 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
342 If None, it is detected automatically.
343
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800344 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800345 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800346 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800347 if self.check_setup_done('usb_check'):
Vic Yang54f70572012-10-19 17:05:26 +0800348 return
349
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800350 # TODO(waihong@chromium.org): We skip the check when servod runs in
351 # a different host since no easy way to access the servo host so far.
352 # Should find a way to work-around it.
353 if not self.servo.is_localhost():
354 logging.info('Skip checking Chrome OS test image in USB as servod '
355 'runs in a different host.')
356 return
357
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800358 if usb_dev:
359 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
360 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700361 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800362 usb_dev = self.servo.probe_host_usb_dev()
363 if not usb_dev:
364 raise error.TestError(
365 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800366 self.assert_test_image_in_path(usb_dev)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800367 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800368
369
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800370 def get_server_address(self):
371 """Get the server address seen from the client.
372
373 Returns:
374 A string of the server address.
375 """
376 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
377 return r[0].split()[0]
378
379
Simran Basi741b5d42012-05-18 11:27:15 -0700380 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800381 """Install the test image specied by the path onto the USB and DUT disk.
382
383 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500384 recovery mode. Then runs 'chromeos-install' (and possible
385 chromeos-firmwareupdate') to install it to DUT disk.
386
387 Sample command line:
388
389 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
390 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
391 server/site_tests/firmware_XXXX/control
392
393 This test requires an automated recovery to occur while simulating
394 inserting and removing the usb key from the servo. To allow this the
395 following hardware setup is required:
396 1. servo2 board connected via servoflex.
397 2. USB key inserted in the servo2.
398 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
399 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800400
401 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800402 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800403 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800404
405 Raises:
406 error.TestError: If devserver failed to start.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800407 """
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800408 if not image_path:
409 return
410
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800411 if self.check_setup_done('reimage'):
412 return
413
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800414 if image_path.startswith(self._HTTP_PREFIX):
415 # TODO(waihong@chromium.org): Add the check of the URL to ensure
416 # it is a test image.
417 devserver = None
418 image_url = image_path
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800419 elif self.servo.is_localhost():
420 self.assert_test_image_in_path(image_path)
421 # If servod is localhost, i.e. both servod and FAFT see the same
422 # file system, do nothing.
423 devserver = None
424 image_url = image_path
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800425 else:
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800426 self.assert_test_image_in_path(image_path)
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800427 image_dir, image_base = os.path.split(image_path)
428 logging.info('Starting devserver to serve the image...')
429 # The following stdout and stderr arguments should not be None,
430 # even we don't use them. Otherwise, the socket of devserve is
431 # created as fd 1 (as no stdout) but it still thinks stdout is fd
432 # 1 and dump the log to the socket. Wrong HTTP protocol happens.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800433 devserver = subprocess.Popen(['/usr/lib/devserver/devserver.py',
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800434 '--archive_dir=%s' % image_dir,
435 '--port=%s' % self._DEVSERVER_PORT],
436 stdout=subprocess.PIPE,
437 stderr=subprocess.PIPE)
438 image_url = '%s%s:%s/static/archive/%s' % (
439 self._HTTP_PREFIX,
440 self.get_server_address(),
441 self._DEVSERVER_PORT,
442 image_base)
443
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800444 # Wait devserver startup completely
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800445 time.sleep(self.delay.devserver)
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800446 # devserver is a service running forever. If it is terminated,
447 # some error does happen.
448 if devserver.poll():
449 raise error.TestError('Starting devserver failed, '
450 'returning %d.' % devserver.returncode)
451
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800452 logging.info('Ask Servo to install the image from %s' % image_url)
453 self.servo.image_to_servo_usb(image_url)
454
455 if devserver and devserver.poll() is None:
456 logging.info('Shutting down devserver...')
457 devserver.terminate()
Mike Truty49153d82012-08-21 22:27:30 -0500458
459 # DUT is powered off while imaging servo USB.
460 # Now turn it on.
461 self.servo.power_short_press()
462 self.wait_for_client()
463 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
464
465 install_cmd = 'chromeos-install --yes'
466 if firmware_update:
467 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
468
469 self.register_faft_sequence((
470 { # Step 1, request recovery boot
471 'state_checker': (self.crossystem_checker, {
472 'mainfw_type': ('developer', 'normal'),
473 }),
474 'userspace_action': self.faft_client.request_recovery_boot,
475 'firmware_action': self.wait_fw_screen_and_plug_usb,
476 'install_deps_after_boot': True,
477 },
478 { # Step 2, expected recovery boot
479 'state_checker': (self.crossystem_checker, {
480 'mainfw_type': 'recovery',
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800481 'recovery_reason' : vboot.RECOVERY_REASON['US_TEST'],
Mike Truty49153d82012-08-21 22:27:30 -0500482 }),
483 'userspace_action': (self.faft_client.run_shell_command,
484 install_cmd),
485 'reboot_action': self.cold_reboot,
486 'install_deps_after_boot': True,
487 },
488 { # Step 3, expected normal or developer boot (not recovery)
489 'state_checker': (self.crossystem_checker, {
490 'mainfw_type': ('developer', 'normal')
491 }),
492 },
493 ))
494 self.run_faft_sequence()
495 # 'Unplug' any USB keys in the servo from the dut.
Tom Wai-Hong Tam953c7742012-10-16 21:09:31 +0800496 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam6668b762012-10-23 11:45:36 +0800497 # Mark usb_check done so it won't check a test image in USB anymore.
498 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800499 self.mark_setup_done('reimage')
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800500
501
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800502 def clear_set_gbb_flags(self, clear_mask, set_mask):
503 """Clear and set the GBB flags in the current flashrom.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800504
505 Args:
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800506 clear_mask: A mask of flags to be cleared.
507 set_mask: A mask of flags to be set.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800508 """
509 gbb_flags = self.faft_client.get_gbb_flags()
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800510 new_flags = gbb_flags & ctypes.c_uint32(~clear_mask).value | set_mask
511
512 if (gbb_flags != new_flags):
513 logging.info('Change the GBB flags from 0x%x to 0x%x.' %
514 (gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800515 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800516 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800517 self.faft_client.reload_firmware()
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800518 # If changing FORCE_DEV_SWITCH_ON flag, reboot to get a clear state
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800519 if ((gbb_flags ^ new_flags) & vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON):
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800520 self.run_faft_step({
521 'firmware_action': self.wait_fw_screen_and_ctrl_d,
522 })
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800523
524
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800525 def check_ec_capability(self, required_cap=[], suppress_warning=False):
Vic Yang4d72cb62012-07-24 11:51:09 +0800526 """Check if current platform has required EC capabilities.
527
528 Args:
529 required_cap: A list containing required EC capabilities. Pass in
530 None to only check for presence of Chrome EC.
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800531 suppress_warning: True to suppress any warning messages.
Vic Yang4d72cb62012-07-24 11:51:09 +0800532
533 Returns:
534 True if requirements are met. Otherwise, False.
535 """
536 if not self.client_attr.chrome_ec:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800537 if not suppress_warning:
538 logging.warn('Requires Chrome EC to run this test.')
Vic Yang4d72cb62012-07-24 11:51:09 +0800539 return False
540
541 for cap in required_cap:
542 if cap not in self.client_attr.ec_capability:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800543 if not suppress_warning:
544 logging.warn('Requires EC capability "%s" to run this '
545 'test.' % cap)
Vic Yang4d72cb62012-07-24 11:51:09 +0800546 return False
547
548 return True
549
550
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800551 def _parse_crossystem_output(self, lines):
552 """Parse the crossystem output into a dict.
553
554 Args:
555 lines: The list of crossystem output strings.
556
557 Returns:
558 A dict which contains the crossystem keys/values.
559
560 Raises:
561 error.TestError: If wrong format in crossystem output.
562
563 >>> seq = FAFTSequence()
564 >>> seq._parse_crossystem_output([ \
565 "arch = x86 # Platform architecture", \
566 "cros_debug = 1 # OS should allow debug", \
567 ])
568 {'cros_debug': '1', 'arch': 'x86'}
569 >>> seq._parse_crossystem_output([ \
570 "arch=x86", \
571 ])
572 Traceback (most recent call last):
573 ...
574 TestError: Failed to parse crossystem output: arch=x86
575 >>> seq._parse_crossystem_output([ \
576 "arch = x86 # Platform architecture", \
577 "arch = arm # Platform architecture", \
578 ])
579 Traceback (most recent call last):
580 ...
581 TestError: Duplicated crossystem key: arch
582 """
583 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
584 parsed_list = {}
585 for line in lines:
586 matched = re.match(pattern, line.strip())
587 if not matched:
588 raise error.TestError("Failed to parse crossystem output: %s"
589 % line)
590 (name, value) = (matched.group(1), matched.group(2))
591 if name in parsed_list:
592 raise error.TestError("Duplicated crossystem key: %s" % name)
593 parsed_list[name] = value
594 return parsed_list
595
596
597 def crossystem_checker(self, expected_dict):
598 """Check the crossystem values matched.
599
600 Given an expect_dict which describes the expected crossystem values,
601 this function check the current crossystem values are matched or not.
602
603 Args:
604 expected_dict: A dict which contains the expected values.
605
606 Returns:
607 True if the crossystem value matched; otherwise, False.
608 """
609 lines = self.faft_client.run_shell_command_get_output('crossystem')
610 got_dict = self._parse_crossystem_output(lines)
611 for key in expected_dict:
612 if key not in got_dict:
613 logging.info('Expected key "%s" not in crossystem result' % key)
614 return False
615 if isinstance(expected_dict[key], str):
616 if got_dict[key] != expected_dict[key]:
617 logging.info("Expected '%s' value '%s' but got '%s'" %
618 (key, expected_dict[key], got_dict[key]))
619 return False
620 elif isinstance(expected_dict[key], tuple):
621 # Expected value is a tuple of possible actual values.
622 if got_dict[key] not in expected_dict[key]:
623 logging.info("Expected '%s' values %s but got '%s'" %
624 (key, str(expected_dict[key]), got_dict[key]))
625 return False
626 else:
627 logging.info("The expected_dict is neither a str nor a dict.")
628 return False
629 return True
630
631
Tom Wai-Hong Tam39b93b92012-09-04 16:56:05 +0800632 def vdat_flags_checker(self, mask, value):
633 """Check the flags from VbSharedData matched.
634
635 This function checks the masked flags from VbSharedData using crossystem
636 are matched the given value.
637
638 Args:
639 mask: A bitmask of flags to be matched.
640 value: An expected value.
641
642 Returns:
643 True if the flags matched; otherwise, False.
644 """
645 lines = self.faft_client.run_shell_command_get_output(
646 'crossystem vdat_flags')
647 vdat_flags = int(lines[0], 16)
648 if vdat_flags & mask != value:
649 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x" %
650 (value, mask, vdat_flags))
651 return False
652 return True
653
654
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800655 def ro_normal_checker(self, expected_fw=None, twostop=False):
656 """Check the current boot uses RO boot.
657
658 Args:
659 expected_fw: A string of expected firmware, 'A', 'B', or
660 None if don't care.
661 twostop: True to expect a TwoStop boot; False to expect a RO boot.
662
663 Returns:
664 True if the currect boot firmware matched and used RO boot;
665 otherwise, False.
666 """
667 crossystem_dict = {'tried_fwb': '0'}
668 if expected_fw:
669 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800670 if self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800671 crossystem_dict['ecfw_act'] = ('RW' if twostop else 'RO')
672
673 return (self.vdat_flags_checker(
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800674 vboot.VDAT_FLAG_LF_USE_RO_NORMAL,
675 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL) and
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800676 self.crossystem_checker(crossystem_dict))
677
678
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +0800679 def dev_boot_usb_checker(self, dev_boot_usb=True):
680 """Check the current boot is from a developer USB (Ctrl-U trigger).
681
682 Args:
683 dev_boot_usb: True to expect an USB boot;
684 False to expect an internal device boot.
685
686 Returns:
687 True if the currect boot device matched; otherwise, False.
688 """
689 return (self.crossystem_checker({'mainfw_type': 'developer'})
690 and self.faft_client.is_removable_device_boot() == dev_boot_usb)
691
692
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800693 def root_part_checker(self, expected_part):
694 """Check the partition number of the root device matched.
695
696 Args:
697 expected_part: A string containing the number of the expected root
698 partition.
699
700 Returns:
701 True if the currect root partition number matched; otherwise, False.
702 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800703 part = self.faft_client.get_root_part()[-1]
704 if self.ROOTFS_MAP[expected_part] != part:
705 logging.info("Expected root part %s but got %s" %
706 (self.ROOTFS_MAP[expected_part], part))
707 return False
708 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800709
710
Vic Yang59cac9c2012-05-21 15:28:42 +0800711 def ec_act_copy_checker(self, expected_copy):
712 """Check the EC running firmware copy matches.
713
714 Args:
715 expected_copy: A string containing 'RO', 'A', or 'B' indicating
716 the expected copy of EC running firmware.
717
718 Returns:
719 True if the current EC running copy matches; otherwise, False.
720 """
721 lines = self.faft_client.run_shell_command_get_output('ectool version')
722 pattern = re.compile("Firmware copy: (.*)")
723 for line in lines:
724 matched = pattern.match(line)
725 if matched and matched.group(1) == expected_copy:
726 return True
727 return False
728
729
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800730 def check_root_part_on_non_recovery(self, part):
731 """Check the partition number of root device and on normal/dev boot.
732
733 Returns:
734 True if the root device matched and on normal/dev boot;
735 otherwise, False.
736 """
737 return self.root_part_checker(part) and \
738 self.crossystem_checker({
739 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800740 })
741
742
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800743 def _join_part(self, dev, part):
744 """Return a concatenated string of device and partition number.
745
746 Args:
747 dev: A string of device, e.g.'/dev/sda'.
748 part: A string of partition number, e.g.'3'.
749
750 Returns:
751 A concatenated string of device and partition number, e.g.'/dev/sda3'.
752
753 >>> seq = FAFTSequence()
754 >>> seq._join_part('/dev/sda', '3')
755 '/dev/sda3'
756 >>> seq._join_part('/dev/mmcblk0', '2')
757 '/dev/mmcblk0p2'
758 """
759 if 'mmcblk' in dev:
760 return dev + 'p' + part
761 else:
762 return dev + part
763
764
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800765 def copy_kernel_and_rootfs(self, from_part, to_part):
766 """Copy kernel and rootfs from from_part to to_part.
767
768 Args:
769 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800770 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800771 """
772 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800773 logging.info('Copying kernel from %s to %s. Please wait...' %
774 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800775 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800776 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
777 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
778 logging.info('Copying rootfs from %s to %s. Please wait...' %
779 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800780 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800781 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
782 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800783
784
785 def ensure_kernel_boot(self, part):
786 """Ensure the request kernel boot.
787
788 If not, it duplicates the current kernel to the requested kernel
789 and sets the requested higher priority to ensure it boot.
790
791 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800792 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800793 """
794 if not self.root_part_checker(part):
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800795 if self.faft_client.diff_kernel_a_b():
796 self.copy_kernel_and_rootfs(
797 from_part=self.OTHER_KERNEL_MAP[part],
798 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800799 self.run_faft_step({
800 'userspace_action': (self.reset_and_prioritize_kernel, part),
801 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800802
803
Vic Yang416f2032012-08-28 10:18:03 +0800804 def set_hardware_write_protect(self, enabled):
Vic Yang2cabf812012-08-28 02:39:04 +0800805 """Set hardware write protect pin.
806
807 Args:
808 enable: True if asserting write protect pin. Otherwise, False.
809 """
810 self.servo.set('fw_wp_vref', self.client_attr.wp_voltage)
811 self.servo.set('fw_wp_en', 'on')
Vic Yang416f2032012-08-28 10:18:03 +0800812 self.servo.set('fw_wp', 'on' if enabled else 'off')
813
814
815 def set_EC_write_protect_and_reboot(self, enabled):
816 """Set EC write protect status and reboot to take effect.
817
818 EC write protect is only activated if both hardware write protect pin
819 is asserted and software write protect flag is set. Also, a reboot is
820 required for write protect to take effect.
821
822 Since the software write protect flag cannot be unset if hardware write
823 protect pin is asserted, we need to deasserted the pin first if we are
824 deactivating write protect. Similarly, a reboot is required before we
825 can modify the software flag.
826
827 This method asserts/deasserts hardware write protect pin first, and
828 set corresponding EC software write protect flag.
829
830 Args:
831 enable: True if activating EC write protect. Otherwise, False.
832 """
833 self.set_hardware_write_protect(enabled)
834 if enabled:
835 # Set write protect flag and reboot to take effect.
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800836 self.ec.send_command("flashwp enable")
Vic Yang416f2032012-08-28 10:18:03 +0800837 self.sync_and_ec_reboot()
838 else:
839 # Reboot after deasserting hardware write protect pin to deactivate
840 # write protect. And then remove software write protect flag.
841 self.sync_and_ec_reboot()
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800842 self.ec.send_command("flashwp disable")
Vic Yang2cabf812012-08-28 02:39:04 +0800843
844
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800845 def send_ctrl_d_to_dut(self):
846 """Send Ctrl-D key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800847 if self._customized_key_commands['ctrl_d']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800848 logging.info('running the customized Ctrl-D key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800849 os.system(self._customized_key_commands['ctrl_d'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800850 else:
851 self.servo.ctrl_d()
852
853
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800854 def send_ctrl_u_to_dut(self):
855 """Send Ctrl-U key to DUT.
856
857 Raises:
858 error.TestError: if a non-Chrome EC device or no Ctrl-U command given
859 on a no-build-in-keyboard device.
860 """
861 if self._customized_key_commands['ctrl_u']:
862 logging.info('running the customized Ctrl-U key command')
863 os.system(self._customized_key_commands['ctrl_u'])
864 elif self.check_ec_capability(['keyboard'], suppress_warning=True):
865 self.ec.key_down('<ctrl_l>')
866 self.ec.key_down('u')
867 self.ec.key_up('u')
868 self.ec.key_up('<ctrl_l>')
869 elif self.client_attr.has_keyboard:
870 raise error.TestError(
871 "Can't send Ctrl-U to DUT without using Chrome EC.")
872 else:
873 raise error.TestError(
874 "Should specify the ctrl_u_cmd argument.")
875
876
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800877 def send_enter_to_dut(self):
878 """Send Enter key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800879 if self._customized_key_commands['enter']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800880 logging.info('running the customized Enter key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800881 os.system(self._customized_key_commands['enter'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800882 else:
883 self.servo.enter_key()
884
885
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800886 def send_space_to_dut(self):
887 """Send Space key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800888 if self._customized_key_commands['space']:
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800889 logging.info('running the customized Space key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800890 os.system(self._customized_key_commands['space'])
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800891 else:
892 # Send the alternative key combinaton of space key to servo.
893 self.servo.ctrl_refresh_key()
894
895
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800896 def wait_fw_screen_and_ctrl_d(self):
897 """Wait for firmware warning screen and press Ctrl-D."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800898 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800899 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800900
901
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800902 def wait_fw_screen_and_ctrl_u(self):
903 """Wait for firmware warning screen and press Ctrl-U."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800904 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800905 self.send_ctrl_u_to_dut()
906
907
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800908 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
909 """Wait for firmware warning screen and trigger recovery boot."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800910 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800911 self.send_enter_to_dut()
912
913 # For Alex/ZGB, there is a dev warning screen in text mode.
914 # Skip it by pressing Ctrl-D.
915 if need_dev_transition:
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800916 time.sleep(self.delay.legacy_text_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800917 self.send_ctrl_d_to_dut()
918
919
Mike Truty49153d82012-08-21 22:27:30 -0500920 def wait_fw_screen_and_unplug_usb(self):
921 """Wait for firmware warning screen and then unplug the servo USB."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800922 time.sleep(self.delay.load_usb)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800923 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800924 time.sleep(self.delay.between_usb_plug)
Mike Truty49153d82012-08-21 22:27:30 -0500925
926
927 def wait_fw_screen_and_plug_usb(self):
928 """Wait for firmware warning screen and then unplug and plug the USB."""
929 self.wait_fw_screen_and_unplug_usb()
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800930 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
931
932
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800933 def wait_fw_screen_and_press_power(self):
934 """Wait for firmware warning screen and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800935 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam7317c042012-08-14 11:59:06 +0800936 # While the firmware screen, the power button probing loop sleeps
937 # 0.25 second on every scan. Use the normal delay (1.2 second) for
938 # power press.
939 self.servo.power_normal_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800940
941
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800942 def wait_longer_fw_screen_and_press_power(self):
943 """Wait for firmware screen without timeout and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800944 time.sleep(self.delay.dev_screen_timeout)
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800945 self.wait_fw_screen_and_press_power()
946
947
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800948 def wait_fw_screen_and_close_lid(self):
949 """Wait for firmware warning screen and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800950 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800951 self.servo.lid_close()
952
953
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800954 def wait_longer_fw_screen_and_close_lid(self):
955 """Wait for firmware screen without timeout and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800956 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800957 self.wait_fw_screen_and_close_lid()
958
959
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800960 def setup_gbb_flags(self):
961 """Setup the GBB flags for FAFT test."""
962 if self.check_setup_done('gbb_flags'):
963 return
964
965 logging.info('Set proper GBB flags for test.')
966 self.clear_set_gbb_flags(vboot.GBB_FLAG_DEV_SCREEN_SHORT_DELAY |
967 vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
968 vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
969 vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
970 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
971 self.mark_setup_done('gbb_flags')
972
973
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800974 def setup_tried_fwb(self, tried_fwb):
975 """Setup for fw B tried state.
976
977 It makes sure the system in the requested fw B tried state. If not, it
978 tries to do so.
979
980 Args:
981 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
982 """
983 if tried_fwb:
984 if not self.crossystem_checker({'tried_fwb': '1'}):
985 logging.info(
986 'Firmware is not booted with tried_fwb. Reboot into it.')
987 self.run_faft_step({
988 'userspace_action': self.faft_client.set_try_fw_b,
989 })
990 else:
991 if not self.crossystem_checker({'tried_fwb': '0'}):
992 logging.info(
993 'Firmware is booted with tried_fwb. Reboot to clear.')
994 self.run_faft_step({})
995
996
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800997 def enable_rec_mode_and_reboot(self):
998 """Switch to rec mode and reboot.
999
1000 This method emulates the behavior of the old physical recovery switch,
1001 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
1002 recovery mode, i.e. just press Power + Esc + Refresh.
1003 """
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +08001004 if self._customized_key_commands['rec_reboot']:
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001005 logging.info('running the customized rec reboot command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +08001006 os.system(self._customized_key_commands['rec_reboot'])
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001007 elif self.client_attr.chrome_ec:
Vic Yang81273092012-08-21 15:57:09 +08001008 # Cold reset to clear EC_IN_RW signal
Vic Yanga7250662012-08-31 04:00:08 +08001009 self.servo.set('cold_reset', 'on')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001010 time.sleep(self.delay.hold_cold_reset)
Vic Yanga7250662012-08-31 04:00:08 +08001011 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001012 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001013 self.ec.send_command("reboot ap-off")
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001014 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001015 self.ec.send_command("hostevent set 0x4000")
Vic Yang611dd852012-08-02 15:36:31 +08001016 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001017 else:
1018 self.servo.enable_recovery_mode()
1019 self.cold_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001020 time.sleep(self.delay.ec_reboot_cmd)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001021 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001022
1023
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001024 def enable_dev_mode_and_reboot(self):
1025 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001026 if self.client_attr.keyboard_dev:
1027 self.enable_keyboard_dev_mode()
1028 else:
1029 self.servo.enable_development_mode()
1030 self.faft_client.run_shell_command(
1031 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001032
1033
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001034 def enable_normal_mode_and_reboot(self):
1035 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001036 if self.client_attr.keyboard_dev:
1037 self.disable_keyboard_dev_mode()
1038 else:
1039 self.servo.disable_development_mode()
1040 self.faft_client.run_shell_command(
1041 'chromeos-firmwareupdate --mode tonormal && reboot')
1042
1043
1044 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
1045 """Wait for firmware screen and then switch into or out of dev mode.
1046
1047 Args:
1048 dev: True if switching into dev mode. Otherwise, False.
1049 """
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001050 time.sleep(self.delay.firmware_screen)
Vic Yange7553162012-06-20 16:20:47 +08001051 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +08001052 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +08001053 else:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +08001054 self.send_enter_to_dut()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001055 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +08001056 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +08001057
1058
1059 def enable_keyboard_dev_mode(self):
1060 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +08001061 # Plug out USB disk for preventing recovery boot without warning
1062 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +08001063 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001064 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001065 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001066 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +08001067
1068
1069 def disable_keyboard_dev_mode(self):
1070 logging.info("Disabling keyboard controlled developer mode")
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001071 if not self.client_attr.chrome_ec:
Vic Yang611dd852012-08-02 15:36:31 +08001072 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001073 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001074 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001075 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001076
1077
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001078 def setup_dev_mode(self, dev_mode):
1079 """Setup for development mode.
1080
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001081 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001082 tries to do so.
1083
1084 Args:
1085 dev_mode: True if requested in dev mode; False if normal mode.
1086 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001087 # Change the default firmware_action for dev mode passing the fw screen.
1088 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001089 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
1090 else None),
1091 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001092 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +08001093 if (not self.client_attr.keyboard_dev and
1094 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001095 logging.info('Dev switch is not on. Now switch it on.')
1096 self.servo.enable_development_mode()
1097 if not self.crossystem_checker({'devsw_boot': '1',
1098 'mainfw_type': 'developer'}):
1099 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001100 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001101 'userspace_action': None if self.client_attr.keyboard_dev
1102 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001103 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001104 'reboot_action': self.enable_keyboard_dev_mode if
1105 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001106 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001107 else:
Vic Yange7553162012-06-20 16:20:47 +08001108 if (not self.client_attr.keyboard_dev and
1109 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001110 logging.info('Dev switch is not off. Now switch it off.')
1111 self.servo.disable_development_mode()
1112 if not self.crossystem_checker({'devsw_boot': '0',
1113 'mainfw_type': 'normal'}):
1114 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001115 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001116 'userspace_action': None if self.client_attr.keyboard_dev
1117 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001118 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001119 'reboot_action': self.disable_keyboard_dev_mode if
1120 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001121 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001122
1123
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001124 def setup_kernel(self, part):
1125 """Setup for kernel test.
1126
1127 It makes sure both kernel A and B bootable and the current boot is
1128 the requested kernel part.
1129
1130 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001131 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001132 """
1133 self.ensure_kernel_boot(part)
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +08001134 if self.faft_client.diff_kernel_a_b():
1135 self.copy_kernel_and_rootfs(from_part=part,
1136 to_part=self.OTHER_KERNEL_MAP[part])
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001137 self.reset_and_prioritize_kernel(part)
1138
1139
1140 def reset_and_prioritize_kernel(self, part):
1141 """Make the requested partition highest priority.
1142
1143 This function also reset kerenl A and B to bootable.
1144
1145 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001146 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001147 """
1148 root_dev = self.faft_client.get_root_dev()
1149 # Reset kernel A and B to bootable.
1150 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1151 (self.KERNEL_MAP['a'], root_dev))
1152 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1153 (self.KERNEL_MAP['b'], root_dev))
1154 # Set kernel part highest priority.
1155 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
1156 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001157 # Safer to sync and wait until the cgpt status written to the disk.
1158 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001159 time.sleep(self.delay.sync)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001160
1161
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001162 def warm_reboot(self):
1163 """Request a warm reboot.
1164
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001165 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001166 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001167 # Use cold reset if the warm reset is broken.
1168 if self.client_attr.broken_warm_reset:
Gediminas Ramanauskase021e152012-09-04 19:10:59 -07001169 logging.info('broken_warm_reset is True. Cold rebooting instead.')
1170 self.cold_reboot()
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001171 else:
1172 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001173
1174
1175 def cold_reboot(self):
1176 """Request a cold reboot.
1177
1178 A wrapper for underlying servo cold reset.
1179 """
Gediminas Ramanauskasc6025692012-10-23 14:33:40 -07001180 if self.client_attr.broken_warm_reset:
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001181 self.servo.set('pwr_button', 'press')
1182 self.servo.set('cold_reset', 'on')
1183 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001184 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001185 self.servo.set('pwr_button', 'release')
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +08001186 elif self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001187 # We don't use servo.cold_reset() here because software sync is
1188 # not yet finished, and device may or may not come up after cold
1189 # reset. Pressing power button before firmware comes up solves this.
1190 #
1191 # The correct behavior should be (not work now):
1192 # - If rebooting EC with rec mode on, power on AP and it boots
1193 # into recovery mode.
1194 # - If rebooting EC with rec mode off, power on AP for software
1195 # sync. Then AP checks if lid open or not. If lid open, continue;
1196 # otherwise, shut AP down and need servo for a power button
1197 # press.
1198 self.servo.set('cold_reset', 'on')
1199 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001200 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001201 self.servo.power_short_press()
1202 else:
1203 self.servo.cold_reset()
1204
1205
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001206 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001207 """Request the client sync and do a warm reboot.
1208
1209 This is the default reboot action on FAFT.
1210 """
1211 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001212 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001213 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001214
1215
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001216 def sync_and_cold_reboot(self):
1217 """Request the client sync and do a cold reboot.
1218
1219 This reboot action is used to reset EC for recovery mode.
1220 """
1221 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001222 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001223 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001224
1225
Vic Yangaeb10392012-08-28 09:25:09 +08001226 def sync_and_ec_reboot(self, args=''):
1227 """Request the client sync and do a EC triggered reboot.
1228
1229 Args:
1230 args: Arguments passed to "ectool reboot_ec". Including:
1231 RO: jump to EC RO firmware.
1232 RW: jump to EC RW firmware.
1233 cold: Cold/hard reboot.
1234 """
Vic Yang59cac9c2012-05-21 15:28:42 +08001235 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001236 time.sleep(self.delay.sync)
Vic Yangaeb10392012-08-28 09:25:09 +08001237 # Since EC reboot happens immediately, delay before actual reboot to
1238 # allow FAFT client returning.
1239 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec %s)&' %
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001240 (self.delay.ec_reboot_cmd, args))
1241 time.sleep(self.delay.ec_reboot_cmd)
Vic Yangf86728a2012-07-30 10:44:07 +08001242 self.check_lid_and_power_on()
1243
1244
Chun-ting Changa4f65532012-10-17 16:57:28 +08001245 def sync_and_reboot_with_factory_install_shim(self):
1246 """Request the client sync and do a warm reboot to recovery mode.
1247
1248 After reboot, the client will use factory install shim to reset TPM
1249 values. The client ignore TPM rollback, so here forces it to recovery
1250 mode.
1251 """
1252 is_dev = self.crossystem_checker({'devsw_boot': '1'})
1253 if not is_dev:
1254 self.enable_dev_mode_and_reboot()
1255 time.sleep(self.SYNC_DELAY)
1256 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001257 time.sleep(self.delay.install_shim_done)
Chun-ting Changa4f65532012-10-17 16:57:28 +08001258 self.warm_reboot()
1259
1260
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001261 def full_power_off_and_on(self):
1262 """Shutdown the device by pressing power button and power on again."""
1263 # Press power button to trigger Chrome OS normal shutdown process.
1264 self.servo.power_normal_press()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001265 time.sleep(self.delay.shutdown)
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001266 # Short press power button to boot DUT again.
1267 self.servo.power_short_press()
1268
1269
Vic Yangf86728a2012-07-30 10:44:07 +08001270 def check_lid_and_power_on(self):
1271 """
1272 On devices with EC software sync, system powers on after EC reboots if
1273 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1274 This method checks lid switch state and presses power button if
1275 necessary.
1276 """
1277 if self.servo.get("lid_open") == "no":
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001278 time.sleep(self.delay.software_sync)
Vic Yangf86728a2012-07-30 10:44:07 +08001279 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001280
1281
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001282 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1283 """Modify the kernel header magic in USB stick.
1284
1285 The kernel header magic is the first 8-byte of kernel partition.
1286 We modify it to make it fail on kernel verification check.
1287
1288 Args:
1289 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1290 from_magic: A string of magic which we change it from.
1291 to_magic: A string of magic which we change it to.
1292
1293 Raises:
1294 error.TestError: if failed to change magic.
1295 """
1296 assert len(from_magic) == 8
1297 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001298 # USB image only contains one kernel.
1299 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001300 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1301 current_magic = utils.system_output(read_cmd)
1302 if current_magic == to_magic:
1303 logging.info("The kernel magic is already %s." % current_magic)
1304 return
1305 if current_magic != from_magic:
1306 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1307
1308 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1309 (from_magic, to_magic))
1310 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1311 " 2>/dev/null" % (to_magic, kernel_part))
1312 utils.system(write_cmd)
1313
1314 if utils.system_output(read_cmd) != to_magic:
1315 raise error.TestError("Failed to write new magic.")
1316
1317
1318 def corrupt_usb_kernel(self, usb_dev):
1319 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1320
1321 Args:
1322 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1323 """
1324 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1325 self.CORRUPTED_MAGIC)
1326
1327
1328 def restore_usb_kernel(self, usb_dev):
1329 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1330
1331 Args:
1332 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1333 """
1334 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1335 self.CHROMEOS_MAGIC)
1336
1337
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001338 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001339 """Call the action function with/without arguments.
1340
1341 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001342 action_tuple: A function, or a tuple (function, args, error_msg),
1343 in which, args and error_msg are optional. args is
1344 either a value or a tuple if multiple arguments.
1345 check_status: Check the return value of action function. If not
1346 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001347
1348 Returns:
1349 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001350
1351 Raises:
1352 error.TestError: An error when the action function is not callable.
1353 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001354 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001355 action = action_tuple
1356 args = ()
1357 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001358 if isinstance(action_tuple, tuple):
1359 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001360 if len(action_tuple) >= 2:
1361 args = action_tuple[1]
1362 if not isinstance(args, tuple):
1363 args = (args,)
1364 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001365 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001366
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001367 if action is None:
1368 return
1369
1370 if not callable(action):
1371 raise error.TestError('action is not callable!')
1372
1373 info_msg = 'calling %s' % str(action)
1374 if args:
1375 info_msg += ' with args %s' % str(args)
1376 logging.info(info_msg)
1377 ret = action(*args)
1378
1379 if check_status and not ret:
1380 raise error.TestFail('%s: %s returning %s' %
1381 (error_msg, info_msg, str(ret)))
1382 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001383
1384
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001385 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1386 post_power_action=None):
1387 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1388
1389 Args:
1390 shutdown_action: a function which makes DUT shutdown, like pressing
1391 power key.
1392 pre_power_action: a function which is called before next power on.
1393 post_power_action: a function which is called after next power on.
1394
1395 Raises:
1396 error.TestFail: if the shutdown_action() failed to turn DUT off.
1397 """
1398 self._call_action(shutdown_action)
1399 logging.info('Wait to ensure DUT shut down...')
1400 try:
1401 self.wait_for_client()
1402 raise error.TestFail(
1403 'Should shut the device down after calling %s.' %
1404 str(shutdown_action))
1405 except AssertionError:
1406 logging.info(
1407 'DUT is surely shutdown. We are going to power it on again...')
1408
1409 if pre_power_action:
1410 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001411 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001412 if post_power_action:
1413 self._call_action(post_power_action)
1414
1415
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001416 def register_faft_template(self, template):
1417 """Register FAFT template, the default FAFT_STEP of each step.
1418
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001419 Any missing field falls back to the original faft_template.
1420
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001421 Args:
1422 template: A FAFT_STEP dict.
1423 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001424 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001425
1426
1427 def register_faft_sequence(self, sequence):
1428 """Register FAFT sequence.
1429
1430 Args:
1431 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1432 """
1433 self._faft_sequence = sequence
1434
1435
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001436 def run_faft_step(self, step, no_reboot=False):
1437 """Run a single FAFT step.
1438
1439 Any missing field falls back to faft_template. An empty step means
1440 running the default faft_template.
1441
1442 Args:
1443 step: A FAFT_STEP dict.
1444 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001445
1446 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001447 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001448 error.TestFail: Test failed in waiting DUT reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001449 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001450 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1451 'firmware_action', 'install_deps_after_boot')
1452
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001453 test = {}
1454 test.update(self._faft_template)
1455 test.update(step)
1456
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001457 for key in test:
1458 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001459 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001460
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001461 if test['state_checker']:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001462 self._call_action(test['state_checker'], check_status=True)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001463
1464 self._call_action(test['userspace_action'])
1465
1466 # Don't run reboot_action and firmware_action if no_reboot is True.
1467 if not no_reboot:
1468 self._call_action(test['reboot_action'])
1469 self.wait_for_client_offline()
1470 self._call_action(test['firmware_action'])
1471
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001472 try:
1473 if 'install_deps_after_boot' in test:
1474 self.wait_for_client(
1475 install_deps=test['install_deps_after_boot'])
1476 else:
1477 self.wait_for_client()
1478 except AssertionError:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001479 logging.info('wait_for_client() timed out.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001480 self.reset_client()
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001481 if self._trapped_in_recovery_reason:
1482 raise error.TestFail('Trapped in the recovery reason: %d' %
1483 self._trapped_in_recovery_reason)
1484 else:
1485 raise error.TestFail('Timed out waiting for DUT reboot.')
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001486
1487
1488 def run_faft_sequence(self):
1489 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001490 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001491 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001492 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001493 logging.info('======== Running FAFT sequence step %d ========' %
1494 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001495 # Don't reboot in the last step.
1496 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001497 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001498
1499
ctchang38ae4922012-09-03 17:01:16 +08001500 def get_current_firmware_sha(self):
1501 """Get current firmware sha of body and vblock.
1502
1503 Returns:
1504 Current firmware sha follows the order (
1505 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1506 """
1507 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1508 self.faft_client.get_firmware_sha('a'),
1509 self.faft_client.get_firmware_sig_sha('b'),
1510 self.faft_client.get_firmware_sha('b'))
1511 return current_firmware_sha
1512
1513
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001514 def is_firmware_changed(self):
1515 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001516
1517 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001518 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001519 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001520 # Device may not be rebooted after test.
1521 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001522
1523 current_sha = self.get_current_firmware_sha()
1524
1525 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001526 return False
ctchang38ae4922012-09-03 17:01:16 +08001527 else:
ctchang38ae4922012-09-03 17:01:16 +08001528 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1529 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1530 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1531 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001532 logging.info("Firmware changed:")
1533 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1534 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1535 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1536 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1537 return True
ctchang38ae4922012-09-03 17:01:16 +08001538
1539
1540 def backup_firmware(self, suffix='.original'):
1541 """Backup firmware to file, and then send it to host.
1542
1543 Args:
1544 suffix: a string appended to backup file name
1545 """
1546 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001547 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1548 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1549 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001550
1551 self._backup_firmware_sha = self.get_current_firmware_sha()
1552 logging.info('Backup firmware stored in %s with suffix %s' % (
1553 self.resultsdir, suffix))
1554
1555
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001556 def is_firmware_saved(self):
1557 """Check if a firmware saved (called backup_firmware before).
1558
1559 Returns:
1560 True if the firmware is backuped; otherwise False.
1561 """
1562 return self._backup_firmware_sha != ()
1563
1564
ctchang38ae4922012-09-03 17:01:16 +08001565 def restore_firmware(self, suffix='.original'):
1566 """Restore firmware from host in resultsdir.
1567
1568 Args:
1569 suffix: a string appended to backup file name
1570 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001571 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001572 return
1573
1574 # Backup current corrupted firmware.
1575 self.backup_firmware(suffix='.corrupt')
1576
1577 # Restore firmware.
1578 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001579 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1580 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001581
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001582 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001583 self.sync_and_warm_reboot()
1584 self.wait_for_client_offline()
1585 self.wait_for_client()
1586
ctchang38ae4922012-09-03 17:01:16 +08001587 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001588
1589
1590 def setup_firmwareupdate_shellball(self, shellball=None):
1591 """Deside a shellball to use in firmware update test.
1592
1593 Check if there is a given shellball, and it is a shell script. Then,
1594 send it to the remote host. Otherwise, use
1595 /usr/sbin/chromeos-firmwareupdate.
1596
1597 Args:
1598 shellball: path of a shellball or default to None.
1599
1600 Returns:
1601 Path of shellball in remote host.
1602 If use default shellball, reutrn None.
1603 """
1604 updater_path = None
1605 if shellball:
1606 # Determine the firmware file is a shellball or a raw binary.
1607 is_shellball = (utils.system_output("file %s" % shellball).find(
1608 "shell script") != -1)
1609 if is_shellball:
1610 logging.info('Device will update firmware with shellball %s'
1611 % shellball)
1612 temp_dir = self.faft_client.create_temp_dir('shellball_')
1613 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1614 self._client.send_file(shellball, temp_shellball)
1615 updater_path = temp_shellball
1616 else:
1617 raise error.TestFail(
1618 'The given shellball is not a shell script.')
1619 return updater_path