blob: 9ff147335a038a30589069bceda9ffa06a4b9b77 [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 Tam22b77302011-11-03 13:03:48 +080018from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080019from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +080020from autotest_lib.site_utils.chromeos_test.common_util import ChromeOSTestError
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080021
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080022dirname = os.path.dirname(sys.modules[__name__].__file__)
23autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
24cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080025
26class FAFTSequence(ServoTest):
27 """
28 The base class of Fully Automated Firmware Test Sequence.
29
30 Many firmware tests require several reboot cycles and verify the resulted
31 system states. To do that, an Autotest test case should detailly handle
32 every action on each step. It makes the test case hard to read and many
33 duplicated code. The base class FAFTSequence is to solve this problem.
34
35 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
36 There are four functions in the FAFT_STEP dict:
37 state_checker: a function to check the current is valid or not,
38 returning True if valid, otherwise, False to break the whole
39 test sequence.
40 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080041 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080042 firmware_action: a function to describe the action ran after reboot.
43
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080044 And configurations:
45 install_deps_after_boot: if True, install the Autotest dependency after
46 boot; otherwise, do nothing. It is for the cases of recovery mode
47 test. The test boots a USB/SD image instead of an internal image.
48 The previous installed Autotest dependency on the internal image
49 is lost. So need to install it again.
50
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080051 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080052 userspace_action and firmware_action. Its reboot_action is a hardware
53 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080054 self.register_faft_template(FAFT_STEP).
55
56 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
57 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
58 fall back to default.
59
60 In the run_once(), it should register and run FAFT_SEQUENCE like:
61 def run_once(self):
62 self.register_faft_sequence(FAFT_SEQUENCE)
63 self.run_faft_sequnce()
64
65 Note that in the last step, we only run state_checker. The
66 userspace_action, reboot_action, and firmware_action are not executed.
67
68 Attributes:
69 _faft_template: The default FAFT_STEP of each step. The actions would
70 be over-written if the registered FAFT_SEQUENCE is valid.
71 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +080072 _customized_key_commands: The dict of the customized key commands,
73 including Ctrl-D, Ctrl-U, Enter, Space, and recovery reboot.
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080074 _install_image_path: The URL or the path on the host to the Chrome OS
75 test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080076 _firmware_update: Boolean. True if firmware update needed after
77 installing the image.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080078 """
79 version = 1
80
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080081 # Mapping of partition number of kernel and rootfs.
82 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
83 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
84 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
85 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
86
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080087 # Delay between power-on and firmware screen.
Tom Wai-Hong Tam66af37b2012-08-01 10:48:42 +080088 FIRMWARE_SCREEN_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080089 # Delay between passing firmware screen and text mode warning screen.
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080090 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +080091 # Delay for waiting beep done.
92 BEEP_DELAY = 1
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080093 # Delay of loading the USB kernel.
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080094 USB_LOAD_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080095 # Delay between USB plug-out and plug-in.
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +080096 USB_PLUG_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080097 # Delay after running the 'sync' command.
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +080098 SYNC_DELAY = 5
Vic Yang59cac9c2012-05-21 15:28:42 +080099 # Delay for waiting client to return before EC reboot
100 EC_REBOOT_DELAY = 1
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +0800101 # Delay for waiting client to full power off
102 FULL_POWER_OFF_DELAY = 30
Vic Yang59cac9c2012-05-21 15:28:42 +0800103 # Delay between EC reboot and pressing power button
104 POWER_BTN_DELAY = 0.5
Vic Yangf86728a2012-07-30 10:44:07 +0800105 # Delay of EC software sync hash calculating time
106 SOFTWARE_SYNC_DELAY = 6
Vic Yanga7250662012-08-31 04:00:08 +0800107 # Delay between EC boot and ChromeEC console functional
108 EC_BOOT_DELAY = 0.5
109 # Duration of holding cold_reset to reset device
110 COLD_RESET_DELAY = 0.1
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800111
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800112 # The developer screen timeouts fit our spec.
113 DEV_SCREEN_TIMEOUT = 30
114
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800115 CHROMEOS_MAGIC = "CHROMEOS"
116 CORRUPTED_MAGIC = "CORRUPTD"
117
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800118 _HTTP_PREFIX = 'http://'
119 _DEVSERVER_PORT = '8090'
120
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800121 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800122 _faft_sequence = ()
123
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800124 _customized_key_commands = {
125 'ctrl_d': None,
126 'ctrl_u': None,
127 'enter': None,
128 'rec_reboot': None,
129 'space': None,
130 }
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800131 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800132 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800133
ctchang38ae4922012-09-03 17:01:16 +0800134 _backup_firmware_sha = ()
135
Vic Yangdbaba8f2012-10-17 16:05:35 +0800136 # True if this is the first test in the same run
137 _first_test = True
Vic Yang54f70572012-10-19 17:05:26 +0800138
Vic Yangdbaba8f2012-10-17 16:05:35 +0800139 _setup_invalidated = False
140
Vic Yang54f70572012-10-19 17:05:26 +0800141 # True if the image inside USB stick is confirmed a test image
142 _usb_test_image_checked = False
143
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800144
145 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
146 # Parse arguments from command line
147 args = {}
148 for arg in cmdline_args:
149 match = re.search("^(\w+)=(.+)", arg)
150 if match:
151 args[match.group(1)] = match.group(2)
152
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800153 # Keep the arguments which will be used later.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800154 for key in self._customized_key_commands:
155 key_cmd = key + '_cmd'
156 if key_cmd in args:
157 self._customized_key_commands[key] = args[key_cmd]
158 logging.info('Customized %s key command: %s' %
159 (key, args[key_cmd]))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800160 if 'image' in args:
161 self._install_image_path = args['image']
162 logging.info('Install Chrome OS test image path: %s' %
163 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800164 if 'firmware_update' in args and args['firmware_update'].lower() \
165 not in ('0', 'false', 'no'):
166 if self._install_image_path:
167 self._firmware_update = True
168 logging.info('Also update firmware after installing.')
169 else:
170 logging.warning('Firmware update will not not performed '
171 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800172
173 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
174 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800175 if use_faft:
176 self.client_attr = FAFTClientAttribute(
177 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800178
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800179 if self.client_attr.chrome_ec:
180 self.ec = ChromeEC(self.servo)
181
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700182 # Setting up key matrix mapping
183 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
184
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800185
186 def setup(self):
187 """Autotest setup function."""
188 super(FAFTSequence, self).setup()
189 if not self._remote_infos['faft']['used']:
190 raise error.TestError('The use_faft flag should be enabled.')
191 self.register_faft_template({
192 'state_checker': (None),
193 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800194 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800195 'firmware_action': (None)
196 })
Vic Yangdbaba8f2012-10-17 16:05:35 +0800197 if FAFTSequence._first_test:
198 logging.info('Running first test. Set proper GBB flags.')
199 self.clear_set_gbb_flags(vboot.GBB_FLAG_DEV_SCREEN_SHORT_DELAY |
200 vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
201 vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
202 vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
203 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800204 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800205 self.install_test_image(self._install_image_path,
206 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800207
208
209 def cleanup(self):
210 """Autotest cleanup function."""
211 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800212 self._faft_template = {}
Vic Yangdbaba8f2012-10-17 16:05:35 +0800213 FAFTSequence._first_test = self._setup_invalidated
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800214 super(FAFTSequence, self).cleanup()
215
216
Vic Yangdbaba8f2012-10-17 16:05:35 +0800217 def invalidate_setup(self):
218 """Invalidate current setup flag.
219
220 This reset the first test flag so that the next test setup
221 properly again.
222 """
223 self._setup_invalidated = True
224
225
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800226 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800227 """Reset client, if necessary.
228
229 This method is called when the client is not responsive. It may be
230 caused by the following cases:
231 - network flaky (can be recovered by replugging the Ethernet);
232 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
233 - corrupted firmware;
234 - corrutped OS image.
235 """
236 # DUT works fine, done.
237 if self._ping_test(self._client.ip, timeout=5):
238 return
239
240 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
241 # first reset item.
242
243 # DUT may halt on a firmware screen. Try cold reboot.
244 logging.info('Try cold reboot...')
245 self.cold_reboot()
246 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800247 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800248 return
249 except AssertionError:
250 pass
251
252 # DUT may be broken by a corrupted firmware. Restore firmware.
253 # We assume the recovery boot still works fine. Since the recovery
254 # code is in RO region and all FAFT tests don't change the RO region
255 # except GBB.
256 if self.is_firmware_saved():
257 self.ensure_client_in_recovery()
258 logging.info('Try restore the original firmware...')
259 if self.is_firmware_changed():
260 try:
261 self.restore_firmware()
262 return
263 except AssertionError:
264 logging.info('Restoring firmware doesn\'t help.')
265
266 # DUT may be broken by a corrupted OS image. Restore OS image.
267 self.ensure_client_in_recovery()
268 logging.info('Try restore the OS image...')
269 self.faft_client.run_shell_command('chromeos-install --yes')
270 self.sync_and_warm_reboot()
271 self.wait_for_client_offline()
272 try:
273 self.wait_for_client(install_deps=True)
274 logging.info('Successfully restore OS image.')
275 return
276 except AssertionError:
277 logging.info('Restoring OS image doesn\'t help.')
278
279
280 def ensure_client_in_recovery(self):
281 """Ensure client in recovery boot; reboot into it if necessary.
282
283 Raises:
284 error.TestError: if failed to boot the USB image.
285 """
286 # DUT works fine and is already in recovery boot, done.
287 if self._ping_test(self._client.ip, timeout=5):
288 if self.crossystem_checker({'mainfw_type': 'recovery'}):
289 return
290
291 logging.info('Try boot into USB image...')
292 self.servo.enable_usb_hub(host=True)
293 self.enable_rec_mode_and_reboot()
294 self.wait_fw_screen_and_plug_usb()
295 try:
296 self.wait_for_client(install_deps=True)
297 except AssertionError:
298 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800299
300
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800301 def assert_test_image_in_path(self, image_path):
302 """Assert the image of image_path be a Chrome OS test image.
303
304 Args:
305 image_path: A path on the host to the test image.
306
307 Raises:
308 error.TestError: if the image is not a test image.
309 """
310 try:
311 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
312 image_path)
313 logging.info('Build of image: %s %s' % (build_ver, build_hash))
314 except ChromeOSTestError:
315 raise error.TestError(
316 'An USB disk containning a test image should be plugged '
317 'in the servo board.')
318
319
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800320 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800321 """Assert an USB disk plugged-in on servo and a test image inside.
322
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800323 Args:
324 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
325 If None, it is detected automatically.
326
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800327 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800328 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800329 """
Vic Yang54f70572012-10-19 17:05:26 +0800330 if FAFTSequence._usb_test_image_checked:
331 return
332
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800333 # TODO(waihong@chromium.org): We skip the check when servod runs in
334 # a different host since no easy way to access the servo host so far.
335 # Should find a way to work-around it.
336 if not self.servo.is_localhost():
337 logging.info('Skip checking Chrome OS test image in USB as servod '
338 'runs in a different host.')
339 return
340
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800341 if usb_dev:
342 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
343 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700344 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800345 usb_dev = self.servo.probe_host_usb_dev()
346 if not usb_dev:
347 raise error.TestError(
348 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800349 self.assert_test_image_in_path(usb_dev)
Vic Yang54f70572012-10-19 17:05:26 +0800350 FAFTSequence._usb_test_image_checked = True
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800351
352
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800353 def get_server_address(self):
354 """Get the server address seen from the client.
355
356 Returns:
357 A string of the server address.
358 """
359 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
360 return r[0].split()[0]
361
362
Simran Basi741b5d42012-05-18 11:27:15 -0700363 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800364 """Install the test image specied by the path onto the USB and DUT disk.
365
366 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500367 recovery mode. Then runs 'chromeos-install' (and possible
368 chromeos-firmwareupdate') to install it to DUT disk.
369
370 Sample command line:
371
372 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
373 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
374 server/site_tests/firmware_XXXX/control
375
376 This test requires an automated recovery to occur while simulating
377 inserting and removing the usb key from the servo. To allow this the
378 following hardware setup is required:
379 1. servo2 board connected via servoflex.
380 2. USB key inserted in the servo2.
381 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
382 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800383
384 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800385 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800386 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800387 """
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800388 if image_path.startswith(self._HTTP_PREFIX):
389 # TODO(waihong@chromium.org): Add the check of the URL to ensure
390 # it is a test image.
391 devserver = None
392 image_url = image_path
393 else:
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800394 self.assert_test_image_in_path(image_path)
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800395 image_dir, image_base = os.path.split(image_path)
396 logging.info('Starting devserver to serve the image...')
397 # The following stdout and stderr arguments should not be None,
398 # even we don't use them. Otherwise, the socket of devserve is
399 # created as fd 1 (as no stdout) but it still thinks stdout is fd
400 # 1 and dump the log to the socket. Wrong HTTP protocol happens.
401 devserver = subprocess.Popen(['start_devserver',
402 '--archive_dir=%s' % image_dir,
403 '--port=%s' % self._DEVSERVER_PORT],
404 stdout=subprocess.PIPE,
405 stderr=subprocess.PIPE)
406 image_url = '%s%s:%s/static/archive/%s' % (
407 self._HTTP_PREFIX,
408 self.get_server_address(),
409 self._DEVSERVER_PORT,
410 image_base)
411
412 logging.info('Ask Servo to install the image from %s' % image_url)
413 self.servo.image_to_servo_usb(image_url)
414
415 if devserver and devserver.poll() is None:
416 logging.info('Shutting down devserver...')
417 devserver.terminate()
Mike Truty49153d82012-08-21 22:27:30 -0500418
419 # DUT is powered off while imaging servo USB.
420 # Now turn it on.
421 self.servo.power_short_press()
422 self.wait_for_client()
423 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
424
425 install_cmd = 'chromeos-install --yes'
426 if firmware_update:
427 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
428
429 self.register_faft_sequence((
430 { # Step 1, request recovery boot
431 'state_checker': (self.crossystem_checker, {
432 'mainfw_type': ('developer', 'normal'),
433 }),
434 'userspace_action': self.faft_client.request_recovery_boot,
435 'firmware_action': self.wait_fw_screen_and_plug_usb,
436 'install_deps_after_boot': True,
437 },
438 { # Step 2, expected recovery boot
439 'state_checker': (self.crossystem_checker, {
440 'mainfw_type': 'recovery',
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800441 'recovery_reason' : vboot.RECOVERY_REASON['US_TEST'],
Mike Truty49153d82012-08-21 22:27:30 -0500442 }),
443 'userspace_action': (self.faft_client.run_shell_command,
444 install_cmd),
445 'reboot_action': self.cold_reboot,
446 'install_deps_after_boot': True,
447 },
448 { # Step 3, expected normal or developer boot (not recovery)
449 'state_checker': (self.crossystem_checker, {
450 'mainfw_type': ('developer', 'normal')
451 }),
452 },
453 ))
454 self.run_faft_sequence()
455 # 'Unplug' any USB keys in the servo from the dut.
Tom Wai-Hong Tam953c7742012-10-16 21:09:31 +0800456 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800457
458
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800459 def clear_set_gbb_flags(self, clear_mask, set_mask):
460 """Clear and set the GBB flags in the current flashrom.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800461
462 Args:
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800463 clear_mask: A mask of flags to be cleared.
464 set_mask: A mask of flags to be set.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800465 """
466 gbb_flags = self.faft_client.get_gbb_flags()
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800467 new_flags = gbb_flags & ctypes.c_uint32(~clear_mask).value | set_mask
468
469 if (gbb_flags != new_flags):
470 logging.info('Change the GBB flags from 0x%x to 0x%x.' %
471 (gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800472 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800473 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800474 self.faft_client.reload_firmware()
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800475 # If changing FORCE_DEV_SWITCH_ON flag, reboot to get a clear state
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800476 if ((gbb_flags ^ new_flags) & vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON):
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800477 self.run_faft_step({
478 'firmware_action': self.wait_fw_screen_and_ctrl_d,
479 })
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800480
481
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800482 def check_ec_capability(self, required_cap=[], suppress_warning=False):
Vic Yang4d72cb62012-07-24 11:51:09 +0800483 """Check if current platform has required EC capabilities.
484
485 Args:
486 required_cap: A list containing required EC capabilities. Pass in
487 None to only check for presence of Chrome EC.
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800488 suppress_warning: True to suppress any warning messages.
Vic Yang4d72cb62012-07-24 11:51:09 +0800489
490 Returns:
491 True if requirements are met. Otherwise, False.
492 """
493 if not self.client_attr.chrome_ec:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800494 if not suppress_warning:
495 logging.warn('Requires Chrome EC to run this test.')
Vic Yang4d72cb62012-07-24 11:51:09 +0800496 return False
497
498 for cap in required_cap:
499 if cap not in self.client_attr.ec_capability:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800500 if not suppress_warning:
501 logging.warn('Requires EC capability "%s" to run this '
502 'test.' % cap)
Vic Yang4d72cb62012-07-24 11:51:09 +0800503 return False
504
505 return True
506
507
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800508 def _parse_crossystem_output(self, lines):
509 """Parse the crossystem output into a dict.
510
511 Args:
512 lines: The list of crossystem output strings.
513
514 Returns:
515 A dict which contains the crossystem keys/values.
516
517 Raises:
518 error.TestError: If wrong format in crossystem output.
519
520 >>> seq = FAFTSequence()
521 >>> seq._parse_crossystem_output([ \
522 "arch = x86 # Platform architecture", \
523 "cros_debug = 1 # OS should allow debug", \
524 ])
525 {'cros_debug': '1', 'arch': 'x86'}
526 >>> seq._parse_crossystem_output([ \
527 "arch=x86", \
528 ])
529 Traceback (most recent call last):
530 ...
531 TestError: Failed to parse crossystem output: arch=x86
532 >>> seq._parse_crossystem_output([ \
533 "arch = x86 # Platform architecture", \
534 "arch = arm # Platform architecture", \
535 ])
536 Traceback (most recent call last):
537 ...
538 TestError: Duplicated crossystem key: arch
539 """
540 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
541 parsed_list = {}
542 for line in lines:
543 matched = re.match(pattern, line.strip())
544 if not matched:
545 raise error.TestError("Failed to parse crossystem output: %s"
546 % line)
547 (name, value) = (matched.group(1), matched.group(2))
548 if name in parsed_list:
549 raise error.TestError("Duplicated crossystem key: %s" % name)
550 parsed_list[name] = value
551 return parsed_list
552
553
554 def crossystem_checker(self, expected_dict):
555 """Check the crossystem values matched.
556
557 Given an expect_dict which describes the expected crossystem values,
558 this function check the current crossystem values are matched or not.
559
560 Args:
561 expected_dict: A dict which contains the expected values.
562
563 Returns:
564 True if the crossystem value matched; otherwise, False.
565 """
566 lines = self.faft_client.run_shell_command_get_output('crossystem')
567 got_dict = self._parse_crossystem_output(lines)
568 for key in expected_dict:
569 if key not in got_dict:
570 logging.info('Expected key "%s" not in crossystem result' % key)
571 return False
572 if isinstance(expected_dict[key], str):
573 if got_dict[key] != expected_dict[key]:
574 logging.info("Expected '%s' value '%s' but got '%s'" %
575 (key, expected_dict[key], got_dict[key]))
576 return False
577 elif isinstance(expected_dict[key], tuple):
578 # Expected value is a tuple of possible actual values.
579 if got_dict[key] not in expected_dict[key]:
580 logging.info("Expected '%s' values %s but got '%s'" %
581 (key, str(expected_dict[key]), got_dict[key]))
582 return False
583 else:
584 logging.info("The expected_dict is neither a str nor a dict.")
585 return False
586 return True
587
588
Tom Wai-Hong Tam39b93b92012-09-04 16:56:05 +0800589 def vdat_flags_checker(self, mask, value):
590 """Check the flags from VbSharedData matched.
591
592 This function checks the masked flags from VbSharedData using crossystem
593 are matched the given value.
594
595 Args:
596 mask: A bitmask of flags to be matched.
597 value: An expected value.
598
599 Returns:
600 True if the flags matched; otherwise, False.
601 """
602 lines = self.faft_client.run_shell_command_get_output(
603 'crossystem vdat_flags')
604 vdat_flags = int(lines[0], 16)
605 if vdat_flags & mask != value:
606 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x" %
607 (value, mask, vdat_flags))
608 return False
609 return True
610
611
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800612 def ro_normal_checker(self, expected_fw=None, twostop=False):
613 """Check the current boot uses RO boot.
614
615 Args:
616 expected_fw: A string of expected firmware, 'A', 'B', or
617 None if don't care.
618 twostop: True to expect a TwoStop boot; False to expect a RO boot.
619
620 Returns:
621 True if the currect boot firmware matched and used RO boot;
622 otherwise, False.
623 """
624 crossystem_dict = {'tried_fwb': '0'}
625 if expected_fw:
626 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800627 if self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800628 crossystem_dict['ecfw_act'] = ('RW' if twostop else 'RO')
629
630 return (self.vdat_flags_checker(
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800631 vboot.VDAT_FLAG_LF_USE_RO_NORMAL,
632 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL) and
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800633 self.crossystem_checker(crossystem_dict))
634
635
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +0800636 def dev_boot_usb_checker(self, dev_boot_usb=True):
637 """Check the current boot is from a developer USB (Ctrl-U trigger).
638
639 Args:
640 dev_boot_usb: True to expect an USB boot;
641 False to expect an internal device boot.
642
643 Returns:
644 True if the currect boot device matched; otherwise, False.
645 """
646 return (self.crossystem_checker({'mainfw_type': 'developer'})
647 and self.faft_client.is_removable_device_boot() == dev_boot_usb)
648
649
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800650 def root_part_checker(self, expected_part):
651 """Check the partition number of the root device matched.
652
653 Args:
654 expected_part: A string containing the number of the expected root
655 partition.
656
657 Returns:
658 True if the currect root partition number matched; otherwise, False.
659 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800660 part = self.faft_client.get_root_part()[-1]
661 if self.ROOTFS_MAP[expected_part] != part:
662 logging.info("Expected root part %s but got %s" %
663 (self.ROOTFS_MAP[expected_part], part))
664 return False
665 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800666
667
Vic Yang59cac9c2012-05-21 15:28:42 +0800668 def ec_act_copy_checker(self, expected_copy):
669 """Check the EC running firmware copy matches.
670
671 Args:
672 expected_copy: A string containing 'RO', 'A', or 'B' indicating
673 the expected copy of EC running firmware.
674
675 Returns:
676 True if the current EC running copy matches; otherwise, False.
677 """
678 lines = self.faft_client.run_shell_command_get_output('ectool version')
679 pattern = re.compile("Firmware copy: (.*)")
680 for line in lines:
681 matched = pattern.match(line)
682 if matched and matched.group(1) == expected_copy:
683 return True
684 return False
685
686
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800687 def check_root_part_on_non_recovery(self, part):
688 """Check the partition number of root device and on normal/dev boot.
689
690 Returns:
691 True if the root device matched and on normal/dev boot;
692 otherwise, False.
693 """
694 return self.root_part_checker(part) and \
695 self.crossystem_checker({
696 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800697 })
698
699
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800700 def _join_part(self, dev, part):
701 """Return a concatenated string of device and partition number.
702
703 Args:
704 dev: A string of device, e.g.'/dev/sda'.
705 part: A string of partition number, e.g.'3'.
706
707 Returns:
708 A concatenated string of device and partition number, e.g.'/dev/sda3'.
709
710 >>> seq = FAFTSequence()
711 >>> seq._join_part('/dev/sda', '3')
712 '/dev/sda3'
713 >>> seq._join_part('/dev/mmcblk0', '2')
714 '/dev/mmcblk0p2'
715 """
716 if 'mmcblk' in dev:
717 return dev + 'p' + part
718 else:
719 return dev + part
720
721
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800722 def copy_kernel_and_rootfs(self, from_part, to_part):
723 """Copy kernel and rootfs from from_part to to_part.
724
725 Args:
726 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800727 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800728 """
729 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800730 logging.info('Copying kernel from %s to %s. Please wait...' %
731 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800732 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800733 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
734 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
735 logging.info('Copying rootfs from %s to %s. Please wait...' %
736 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800737 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800738 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
739 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800740
741
742 def ensure_kernel_boot(self, part):
743 """Ensure the request kernel boot.
744
745 If not, it duplicates the current kernel to the requested kernel
746 and sets the requested higher priority to ensure it boot.
747
748 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800749 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800750 """
751 if not self.root_part_checker(part):
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800752 if self.faft_client.diff_kernel_a_b():
753 self.copy_kernel_and_rootfs(
754 from_part=self.OTHER_KERNEL_MAP[part],
755 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800756 self.run_faft_step({
757 'userspace_action': (self.reset_and_prioritize_kernel, part),
758 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800759
760
Vic Yang416f2032012-08-28 10:18:03 +0800761 def set_hardware_write_protect(self, enabled):
Vic Yang2cabf812012-08-28 02:39:04 +0800762 """Set hardware write protect pin.
763
764 Args:
765 enable: True if asserting write protect pin. Otherwise, False.
766 """
767 self.servo.set('fw_wp_vref', self.client_attr.wp_voltage)
768 self.servo.set('fw_wp_en', 'on')
Vic Yang416f2032012-08-28 10:18:03 +0800769 self.servo.set('fw_wp', 'on' if enabled else 'off')
770
771
772 def set_EC_write_protect_and_reboot(self, enabled):
773 """Set EC write protect status and reboot to take effect.
774
775 EC write protect is only activated if both hardware write protect pin
776 is asserted and software write protect flag is set. Also, a reboot is
777 required for write protect to take effect.
778
779 Since the software write protect flag cannot be unset if hardware write
780 protect pin is asserted, we need to deasserted the pin first if we are
781 deactivating write protect. Similarly, a reboot is required before we
782 can modify the software flag.
783
784 This method asserts/deasserts hardware write protect pin first, and
785 set corresponding EC software write protect flag.
786
787 Args:
788 enable: True if activating EC write protect. Otherwise, False.
789 """
790 self.set_hardware_write_protect(enabled)
791 if enabled:
792 # Set write protect flag and reboot to take effect.
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800793 self.ec.send_command("flashwp enable")
Vic Yang416f2032012-08-28 10:18:03 +0800794 self.sync_and_ec_reboot()
795 else:
796 # Reboot after deasserting hardware write protect pin to deactivate
797 # write protect. And then remove software write protect flag.
798 self.sync_and_ec_reboot()
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800799 self.ec.send_command("flashwp disable")
Vic Yang2cabf812012-08-28 02:39:04 +0800800
801
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800802 def send_ctrl_d_to_dut(self):
803 """Send Ctrl-D key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800804 if self._customized_key_commands['ctrl_d']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800805 logging.info('running the customized Ctrl-D key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800806 os.system(self._customized_key_commands['ctrl_d'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800807 else:
808 self.servo.ctrl_d()
809
810
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800811 def send_ctrl_u_to_dut(self):
812 """Send Ctrl-U key to DUT.
813
814 Raises:
815 error.TestError: if a non-Chrome EC device or no Ctrl-U command given
816 on a no-build-in-keyboard device.
817 """
818 if self._customized_key_commands['ctrl_u']:
819 logging.info('running the customized Ctrl-U key command')
820 os.system(self._customized_key_commands['ctrl_u'])
821 elif self.check_ec_capability(['keyboard'], suppress_warning=True):
822 self.ec.key_down('<ctrl_l>')
823 self.ec.key_down('u')
824 self.ec.key_up('u')
825 self.ec.key_up('<ctrl_l>')
826 elif self.client_attr.has_keyboard:
827 raise error.TestError(
828 "Can't send Ctrl-U to DUT without using Chrome EC.")
829 else:
830 raise error.TestError(
831 "Should specify the ctrl_u_cmd argument.")
832
833
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800834 def send_enter_to_dut(self):
835 """Send Enter key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800836 if self._customized_key_commands['enter']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800837 logging.info('running the customized Enter key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800838 os.system(self._customized_key_commands['enter'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800839 else:
840 self.servo.enter_key()
841
842
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800843 def send_space_to_dut(self):
844 """Send Space key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800845 if self._customized_key_commands['space']:
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800846 logging.info('running the customized Space key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800847 os.system(self._customized_key_commands['space'])
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800848 else:
849 # Send the alternative key combinaton of space key to servo.
850 self.servo.ctrl_refresh_key()
851
852
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800853 def wait_fw_screen_and_ctrl_d(self):
854 """Wait for firmware warning screen and press Ctrl-D."""
855 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800856 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800857
858
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800859 def wait_fw_screen_and_ctrl_u(self):
860 """Wait for firmware warning screen and press Ctrl-U."""
861 time.sleep(self.FIRMWARE_SCREEN_DELAY)
862 self.send_ctrl_u_to_dut()
863
864
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800865 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
866 """Wait for firmware warning screen and trigger recovery boot."""
867 time.sleep(self.FIRMWARE_SCREEN_DELAY)
868 self.send_enter_to_dut()
869
870 # For Alex/ZGB, there is a dev warning screen in text mode.
871 # Skip it by pressing Ctrl-D.
872 if need_dev_transition:
873 time.sleep(self.TEXT_SCREEN_DELAY)
874 self.send_ctrl_d_to_dut()
875
876
Mike Truty49153d82012-08-21 22:27:30 -0500877 def wait_fw_screen_and_unplug_usb(self):
878 """Wait for firmware warning screen and then unplug the servo USB."""
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800879 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800880 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
881 time.sleep(self.USB_PLUG_DELAY)
Mike Truty49153d82012-08-21 22:27:30 -0500882
883
884 def wait_fw_screen_and_plug_usb(self):
885 """Wait for firmware warning screen and then unplug and plug the USB."""
886 self.wait_fw_screen_and_unplug_usb()
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800887 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
888
889
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800890 def wait_fw_screen_and_press_power(self):
891 """Wait for firmware warning screen and press power button."""
892 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam7317c042012-08-14 11:59:06 +0800893 # While the firmware screen, the power button probing loop sleeps
894 # 0.25 second on every scan. Use the normal delay (1.2 second) for
895 # power press.
896 self.servo.power_normal_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800897
898
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800899 def wait_longer_fw_screen_and_press_power(self):
900 """Wait for firmware screen without timeout and press power button."""
901 time.sleep(self.DEV_SCREEN_TIMEOUT)
902 self.wait_fw_screen_and_press_power()
903
904
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800905 def wait_fw_screen_and_close_lid(self):
906 """Wait for firmware warning screen and close lid."""
907 time.sleep(self.FIRMWARE_SCREEN_DELAY)
908 self.servo.lid_close()
909
910
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800911 def wait_longer_fw_screen_and_close_lid(self):
912 """Wait for firmware screen without timeout and close lid."""
913 time.sleep(self.FIRMWARE_SCREEN_DELAY)
914 self.wait_fw_screen_and_close_lid()
915
916
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800917 def setup_tried_fwb(self, tried_fwb):
918 """Setup for fw B tried state.
919
920 It makes sure the system in the requested fw B tried state. If not, it
921 tries to do so.
922
923 Args:
924 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
925 """
926 if tried_fwb:
927 if not self.crossystem_checker({'tried_fwb': '1'}):
928 logging.info(
929 'Firmware is not booted with tried_fwb. Reboot into it.')
930 self.run_faft_step({
931 'userspace_action': self.faft_client.set_try_fw_b,
932 })
933 else:
934 if not self.crossystem_checker({'tried_fwb': '0'}):
935 logging.info(
936 'Firmware is booted with tried_fwb. Reboot to clear.')
937 self.run_faft_step({})
938
939
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800940 def enable_rec_mode_and_reboot(self):
941 """Switch to rec mode and reboot.
942
943 This method emulates the behavior of the old physical recovery switch,
944 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
945 recovery mode, i.e. just press Power + Esc + Refresh.
946 """
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800947 if self._customized_key_commands['rec_reboot']:
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800948 logging.info('running the customized rec reboot command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800949 os.system(self._customized_key_commands['rec_reboot'])
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +0800950 elif self.client_attr.chrome_ec:
Vic Yang81273092012-08-21 15:57:09 +0800951 # Cold reset to clear EC_IN_RW signal
Vic Yanga7250662012-08-31 04:00:08 +0800952 self.servo.set('cold_reset', 'on')
953 time.sleep(self.COLD_RESET_DELAY)
954 self.servo.set('cold_reset', 'off')
955 time.sleep(self.EC_BOOT_DELAY)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800956 self.ec.send_command("reboot ap-off")
Vic Yang611dd852012-08-02 15:36:31 +0800957 time.sleep(self.EC_BOOT_DELAY)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800958 self.ec.send_command("hostevent set 0x4000")
Vic Yang611dd852012-08-02 15:36:31 +0800959 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800960 else:
961 self.servo.enable_recovery_mode()
962 self.cold_reboot()
963 time.sleep(self.EC_REBOOT_DELAY)
964 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800965
966
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800967 def enable_dev_mode_and_reboot(self):
968 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800969 if self.client_attr.keyboard_dev:
970 self.enable_keyboard_dev_mode()
971 else:
972 self.servo.enable_development_mode()
973 self.faft_client.run_shell_command(
974 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800975
976
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800977 def enable_normal_mode_and_reboot(self):
978 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800979 if self.client_attr.keyboard_dev:
980 self.disable_keyboard_dev_mode()
981 else:
982 self.servo.disable_development_mode()
983 self.faft_client.run_shell_command(
984 'chromeos-firmwareupdate --mode tonormal && reboot')
985
986
987 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
988 """Wait for firmware screen and then switch into or out of dev mode.
989
990 Args:
991 dev: True if switching into dev mode. Otherwise, False.
992 """
993 time.sleep(self.FIRMWARE_SCREEN_DELAY)
994 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800995 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800996 else:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800997 self.send_enter_to_dut()
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800998 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800999 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +08001000
1001
1002 def enable_keyboard_dev_mode(self):
1003 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +08001004 # Plug out USB disk for preventing recovery boot without warning
1005 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +08001006 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001007 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001008 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001009 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +08001010
1011
1012 def disable_keyboard_dev_mode(self):
1013 logging.info("Disabling keyboard controlled developer mode")
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001014 if not self.client_attr.chrome_ec:
Vic Yang611dd852012-08-02 15:36:31 +08001015 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001016 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001017 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001018 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001019
1020
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001021 def setup_dev_mode(self, dev_mode):
1022 """Setup for development mode.
1023
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001024 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001025 tries to do so.
1026
1027 Args:
1028 dev_mode: True if requested in dev mode; False if normal mode.
1029 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001030 # Change the default firmware_action for dev mode passing the fw screen.
1031 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001032 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
1033 else None),
1034 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001035 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +08001036 if (not self.client_attr.keyboard_dev and
1037 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001038 logging.info('Dev switch is not on. Now switch it on.')
1039 self.servo.enable_development_mode()
1040 if not self.crossystem_checker({'devsw_boot': '1',
1041 'mainfw_type': 'developer'}):
1042 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001043 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001044 'userspace_action': None if self.client_attr.keyboard_dev
1045 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001046 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001047 'reboot_action': self.enable_keyboard_dev_mode if
1048 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001049 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001050 else:
Vic Yange7553162012-06-20 16:20:47 +08001051 if (not self.client_attr.keyboard_dev and
1052 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001053 logging.info('Dev switch is not off. Now switch it off.')
1054 self.servo.disable_development_mode()
1055 if not self.crossystem_checker({'devsw_boot': '0',
1056 'mainfw_type': 'normal'}):
1057 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001058 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001059 'userspace_action': None if self.client_attr.keyboard_dev
1060 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001061 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001062 'reboot_action': self.disable_keyboard_dev_mode if
1063 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001064 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001065
1066
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001067 def setup_kernel(self, part):
1068 """Setup for kernel test.
1069
1070 It makes sure both kernel A and B bootable and the current boot is
1071 the requested kernel part.
1072
1073 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001074 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001075 """
1076 self.ensure_kernel_boot(part)
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +08001077 if self.faft_client.diff_kernel_a_b():
1078 self.copy_kernel_and_rootfs(from_part=part,
1079 to_part=self.OTHER_KERNEL_MAP[part])
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001080 self.reset_and_prioritize_kernel(part)
1081
1082
1083 def reset_and_prioritize_kernel(self, part):
1084 """Make the requested partition highest priority.
1085
1086 This function also reset kerenl A and B to bootable.
1087
1088 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001089 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001090 """
1091 root_dev = self.faft_client.get_root_dev()
1092 # Reset kernel A and B to bootable.
1093 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1094 (self.KERNEL_MAP['a'], root_dev))
1095 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1096 (self.KERNEL_MAP['b'], root_dev))
1097 # Set kernel part highest priority.
1098 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
1099 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001100 # Safer to sync and wait until the cgpt status written to the disk.
1101 self.faft_client.run_shell_command('sync')
1102 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001103
1104
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001105 def warm_reboot(self):
1106 """Request a warm reboot.
1107
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001108 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001109 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001110 # Use cold reset if the warm reset is broken.
1111 if self.client_attr.broken_warm_reset:
Gediminas Ramanauskase021e152012-09-04 19:10:59 -07001112 logging.info('broken_warm_reset is True. Cold rebooting instead.')
1113 self.cold_reboot()
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001114 else:
1115 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001116
1117
1118 def cold_reboot(self):
1119 """Request a cold reboot.
1120
1121 A wrapper for underlying servo cold reset.
1122 """
Gediminas Ramanauskasc6025692012-10-23 14:33:40 -07001123 if self.client_attr.broken_warm_reset:
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001124 self.servo.set('pwr_button', 'press')
1125 self.servo.set('cold_reset', 'on')
1126 self.servo.set('cold_reset', 'off')
1127 time.sleep(self.POWER_BTN_DELAY)
1128 self.servo.set('pwr_button', 'release')
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +08001129 elif self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001130 # We don't use servo.cold_reset() here because software sync is
1131 # not yet finished, and device may or may not come up after cold
1132 # reset. Pressing power button before firmware comes up solves this.
1133 #
1134 # The correct behavior should be (not work now):
1135 # - If rebooting EC with rec mode on, power on AP and it boots
1136 # into recovery mode.
1137 # - If rebooting EC with rec mode off, power on AP for software
1138 # sync. Then AP checks if lid open or not. If lid open, continue;
1139 # otherwise, shut AP down and need servo for a power button
1140 # press.
1141 self.servo.set('cold_reset', 'on')
1142 self.servo.set('cold_reset', 'off')
1143 time.sleep(self.POWER_BTN_DELAY)
1144 self.servo.power_short_press()
1145 else:
1146 self.servo.cold_reset()
1147
1148
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001149 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001150 """Request the client sync and do a warm reboot.
1151
1152 This is the default reboot action on FAFT.
1153 """
1154 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001155 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001156 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001157
1158
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001159 def sync_and_cold_reboot(self):
1160 """Request the client sync and do a cold reboot.
1161
1162 This reboot action is used to reset EC for recovery mode.
1163 """
1164 self.faft_client.run_shell_command('sync')
1165 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001166 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001167
1168
Vic Yangaeb10392012-08-28 09:25:09 +08001169 def sync_and_ec_reboot(self, args=''):
1170 """Request the client sync and do a EC triggered reboot.
1171
1172 Args:
1173 args: Arguments passed to "ectool reboot_ec". Including:
1174 RO: jump to EC RO firmware.
1175 RW: jump to EC RW firmware.
1176 cold: Cold/hard reboot.
1177 """
Vic Yang59cac9c2012-05-21 15:28:42 +08001178 self.faft_client.run_shell_command('sync')
1179 time.sleep(self.SYNC_DELAY)
Vic Yangaeb10392012-08-28 09:25:09 +08001180 # Since EC reboot happens immediately, delay before actual reboot to
1181 # allow FAFT client returning.
1182 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec %s)&' %
1183 (self.EC_REBOOT_DELAY, args))
Vic Yangf86728a2012-07-30 10:44:07 +08001184 time.sleep(self.EC_REBOOT_DELAY)
1185 self.check_lid_and_power_on()
1186
1187
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001188 def full_power_off_and_on(self):
1189 """Shutdown the device by pressing power button and power on again."""
1190 # Press power button to trigger Chrome OS normal shutdown process.
1191 self.servo.power_normal_press()
1192 time.sleep(self.FULL_POWER_OFF_DELAY)
1193 # Short press power button to boot DUT again.
1194 self.servo.power_short_press()
1195
1196
Vic Yangf86728a2012-07-30 10:44:07 +08001197 def check_lid_and_power_on(self):
1198 """
1199 On devices with EC software sync, system powers on after EC reboots if
1200 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1201 This method checks lid switch state and presses power button if
1202 necessary.
1203 """
1204 if self.servo.get("lid_open") == "no":
1205 time.sleep(self.SOFTWARE_SYNC_DELAY)
1206 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001207
1208
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001209 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1210 """Modify the kernel header magic in USB stick.
1211
1212 The kernel header magic is the first 8-byte of kernel partition.
1213 We modify it to make it fail on kernel verification check.
1214
1215 Args:
1216 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1217 from_magic: A string of magic which we change it from.
1218 to_magic: A string of magic which we change it to.
1219
1220 Raises:
1221 error.TestError: if failed to change magic.
1222 """
1223 assert len(from_magic) == 8
1224 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001225 # USB image only contains one kernel.
1226 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001227 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1228 current_magic = utils.system_output(read_cmd)
1229 if current_magic == to_magic:
1230 logging.info("The kernel magic is already %s." % current_magic)
1231 return
1232 if current_magic != from_magic:
1233 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1234
1235 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1236 (from_magic, to_magic))
1237 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1238 " 2>/dev/null" % (to_magic, kernel_part))
1239 utils.system(write_cmd)
1240
1241 if utils.system_output(read_cmd) != to_magic:
1242 raise error.TestError("Failed to write new magic.")
1243
1244
1245 def corrupt_usb_kernel(self, usb_dev):
1246 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1247
1248 Args:
1249 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1250 """
1251 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1252 self.CORRUPTED_MAGIC)
1253
1254
1255 def restore_usb_kernel(self, usb_dev):
1256 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1257
1258 Args:
1259 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1260 """
1261 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1262 self.CHROMEOS_MAGIC)
1263
1264
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001265 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001266 """Call the action function with/without arguments.
1267
1268 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001269 action_tuple: A function, or a tuple (function, args, error_msg),
1270 in which, args and error_msg are optional. args is
1271 either a value or a tuple if multiple arguments.
1272 check_status: Check the return value of action function. If not
1273 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001274
1275 Returns:
1276 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001277
1278 Raises:
1279 error.TestError: An error when the action function is not callable.
1280 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001281 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001282 action = action_tuple
1283 args = ()
1284 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001285 if isinstance(action_tuple, tuple):
1286 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001287 if len(action_tuple) >= 2:
1288 args = action_tuple[1]
1289 if not isinstance(args, tuple):
1290 args = (args,)
1291 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001292 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001293
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001294 if action is None:
1295 return
1296
1297 if not callable(action):
1298 raise error.TestError('action is not callable!')
1299
1300 info_msg = 'calling %s' % str(action)
1301 if args:
1302 info_msg += ' with args %s' % str(args)
1303 logging.info(info_msg)
1304 ret = action(*args)
1305
1306 if check_status and not ret:
1307 raise error.TestFail('%s: %s returning %s' %
1308 (error_msg, info_msg, str(ret)))
1309 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001310
1311
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001312 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1313 post_power_action=None):
1314 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1315
1316 Args:
1317 shutdown_action: a function which makes DUT shutdown, like pressing
1318 power key.
1319 pre_power_action: a function which is called before next power on.
1320 post_power_action: a function which is called after next power on.
1321
1322 Raises:
1323 error.TestFail: if the shutdown_action() failed to turn DUT off.
1324 """
1325 self._call_action(shutdown_action)
1326 logging.info('Wait to ensure DUT shut down...')
1327 try:
1328 self.wait_for_client()
1329 raise error.TestFail(
1330 'Should shut the device down after calling %s.' %
1331 str(shutdown_action))
1332 except AssertionError:
1333 logging.info(
1334 'DUT is surely shutdown. We are going to power it on again...')
1335
1336 if pre_power_action:
1337 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001338 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001339 if post_power_action:
1340 self._call_action(post_power_action)
1341
1342
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001343 def register_faft_template(self, template):
1344 """Register FAFT template, the default FAFT_STEP of each step.
1345
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001346 Any missing field falls back to the original faft_template.
1347
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001348 Args:
1349 template: A FAFT_STEP dict.
1350 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001351 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001352
1353
1354 def register_faft_sequence(self, sequence):
1355 """Register FAFT sequence.
1356
1357 Args:
1358 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1359 """
1360 self._faft_sequence = sequence
1361
1362
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001363 def run_faft_step(self, step, no_reboot=False):
1364 """Run a single FAFT step.
1365
1366 Any missing field falls back to faft_template. An empty step means
1367 running the default faft_template.
1368
1369 Args:
1370 step: A FAFT_STEP dict.
1371 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001372
1373 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001374 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001375 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001376 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1377 'firmware_action', 'install_deps_after_boot')
1378
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001379 test = {}
1380 test.update(self._faft_template)
1381 test.update(step)
1382
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001383 for key in test:
1384 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001385 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001386
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001387 if test['state_checker']:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001388 self._call_action(test['state_checker'], check_status=True)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001389
1390 self._call_action(test['userspace_action'])
1391
1392 # Don't run reboot_action and firmware_action if no_reboot is True.
1393 if not no_reboot:
1394 self._call_action(test['reboot_action'])
1395 self.wait_for_client_offline()
1396 self._call_action(test['firmware_action'])
1397
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001398 try:
1399 if 'install_deps_after_boot' in test:
1400 self.wait_for_client(
1401 install_deps=test['install_deps_after_boot'])
1402 else:
1403 self.wait_for_client()
1404 except AssertionError:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001405 logging.info('wait_for_client() timed out.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001406 self.reset_client()
1407 raise
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001408
1409
1410 def run_faft_sequence(self):
1411 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001412 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001413 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001414 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001415 logging.info('======== Running FAFT sequence step %d ========' %
1416 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001417 # Don't reboot in the last step.
1418 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001419 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001420
1421
ctchang38ae4922012-09-03 17:01:16 +08001422 def get_current_firmware_sha(self):
1423 """Get current firmware sha of body and vblock.
1424
1425 Returns:
1426 Current firmware sha follows the order (
1427 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1428 """
1429 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1430 self.faft_client.get_firmware_sha('a'),
1431 self.faft_client.get_firmware_sig_sha('b'),
1432 self.faft_client.get_firmware_sha('b'))
1433 return current_firmware_sha
1434
1435
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001436 def is_firmware_changed(self):
1437 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001438
1439 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001440 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001441 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001442 # Device may not be rebooted after test.
1443 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001444
1445 current_sha = self.get_current_firmware_sha()
1446
1447 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001448 return False
ctchang38ae4922012-09-03 17:01:16 +08001449 else:
ctchang38ae4922012-09-03 17:01:16 +08001450 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1451 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1452 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1453 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001454 logging.info("Firmware changed:")
1455 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1456 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1457 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1458 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1459 return True
ctchang38ae4922012-09-03 17:01:16 +08001460
1461
1462 def backup_firmware(self, suffix='.original'):
1463 """Backup firmware to file, and then send it to host.
1464
1465 Args:
1466 suffix: a string appended to backup file name
1467 """
1468 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001469 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1470 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1471 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001472
1473 self._backup_firmware_sha = self.get_current_firmware_sha()
1474 logging.info('Backup firmware stored in %s with suffix %s' % (
1475 self.resultsdir, suffix))
1476
1477
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001478 def is_firmware_saved(self):
1479 """Check if a firmware saved (called backup_firmware before).
1480
1481 Returns:
1482 True if the firmware is backuped; otherwise False.
1483 """
1484 return self._backup_firmware_sha != ()
1485
1486
ctchang38ae4922012-09-03 17:01:16 +08001487 def restore_firmware(self, suffix='.original'):
1488 """Restore firmware from host in resultsdir.
1489
1490 Args:
1491 suffix: a string appended to backup file name
1492 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001493 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001494 return
1495
1496 # Backup current corrupted firmware.
1497 self.backup_firmware(suffix='.corrupt')
1498
1499 # Restore firmware.
1500 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001501 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1502 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001503
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001504 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001505 self.sync_and_warm_reboot()
1506 self.wait_for_client_offline()
1507 self.wait_for_client()
1508
ctchang38ae4922012-09-03 17:01:16 +08001509 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001510
1511
1512 def setup_firmwareupdate_shellball(self, shellball=None):
1513 """Deside a shellball to use in firmware update test.
1514
1515 Check if there is a given shellball, and it is a shell script. Then,
1516 send it to the remote host. Otherwise, use
1517 /usr/sbin/chromeos-firmwareupdate.
1518
1519 Args:
1520 shellball: path of a shellball or default to None.
1521
1522 Returns:
1523 Path of shellball in remote host.
1524 If use default shellball, reutrn None.
1525 """
1526 updater_path = None
1527 if shellball:
1528 # Determine the firmware file is a shellball or a raw binary.
1529 is_shellball = (utils.system_output("file %s" % shellball).find(
1530 "shell script") != -1)
1531 if is_shellball:
1532 logging.info('Device will update firmware with shellball %s'
1533 % shellball)
1534 temp_dir = self.faft_client.create_temp_dir('shellball_')
1535 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1536 self._client.send_file(shellball, temp_shellball)
1537 updater_path = temp_shellball
1538 else:
1539 raise error.TestFail(
1540 'The given shellball is not a shell script.')
1541 return updater_path