blob: 1fe8e34d1f90836b9b8ca6c34d7c83d6f471ce82 [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 Tam71818d82012-10-24 14:57:43 +0800111 # devserver startup time
112 DEVSERVER_DELAY = 10
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800113
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800114 # The developer screen timeouts fit our spec.
115 DEV_SCREEN_TIMEOUT = 30
116
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800117 CHROMEOS_MAGIC = "CHROMEOS"
118 CORRUPTED_MAGIC = "CORRUPTD"
119
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800120 _HTTP_PREFIX = 'http://'
121 _DEVSERVER_PORT = '8090'
122
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800123 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800124 _faft_sequence = ()
125
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800126 _customized_key_commands = {
127 'ctrl_d': None,
128 'ctrl_u': None,
129 'enter': None,
130 'rec_reboot': None,
131 'space': None,
132 }
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800133 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800134 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800135
ctchang38ae4922012-09-03 17:01:16 +0800136 _backup_firmware_sha = ()
137
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800138 # Class level variable, keep track the states of one time setup.
139 # This variable is preserved across tests which inherit this class.
140 _global_setup_done = {
141 'gbb_flags': False,
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800142 'reimage': False,
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800143 'usb_check': False,
144 }
Vic Yang54f70572012-10-19 17:05:26 +0800145
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800146 @classmethod
147 def check_setup_done(cls, label):
148 """Check if the given setup is done.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800149
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800150 Args:
151 label: The label of the setup.
152 """
153 return cls._global_setup_done[label]
154
155
156 @classmethod
157 def mark_setup_done(cls, label):
158 """Mark the given setup done.
159
160 Args:
161 label: The label of the setup.
162 """
163 cls._global_setup_done[label] = True
164
165
166 @classmethod
167 def unmark_setup_done(cls, label):
168 """Mark the given setup not done.
169
170 Args:
171 label: The label of the setup.
172 """
173 cls._global_setup_done[label] = False
Vic Yang54f70572012-10-19 17:05:26 +0800174
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800175
176 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
177 # Parse arguments from command line
178 args = {}
179 for arg in cmdline_args:
180 match = re.search("^(\w+)=(.+)", arg)
181 if match:
182 args[match.group(1)] = match.group(2)
183
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800184 # Keep the arguments which will be used later.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800185 for key in self._customized_key_commands:
186 key_cmd = key + '_cmd'
187 if key_cmd in args:
188 self._customized_key_commands[key] = args[key_cmd]
189 logging.info('Customized %s key command: %s' %
190 (key, args[key_cmd]))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800191 if 'image' in args:
192 self._install_image_path = args['image']
193 logging.info('Install Chrome OS test image path: %s' %
194 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800195 if 'firmware_update' in args and args['firmware_update'].lower() \
196 not in ('0', 'false', 'no'):
197 if self._install_image_path:
198 self._firmware_update = True
199 logging.info('Also update firmware after installing.')
200 else:
201 logging.warning('Firmware update will not not performed '
202 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800203
204 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
205 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800206 if use_faft:
207 self.client_attr = FAFTClientAttribute(
208 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800209
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800210 if self.client_attr.chrome_ec:
211 self.ec = ChromeEC(self.servo)
212
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700213 # Setting up key matrix mapping
214 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
215
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800216
217 def setup(self):
218 """Autotest setup function."""
219 super(FAFTSequence, self).setup()
220 if not self._remote_infos['faft']['used']:
221 raise error.TestError('The use_faft flag should be enabled.')
222 self.register_faft_template({
223 'state_checker': (None),
224 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800225 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800226 'firmware_action': (None)
227 })
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800228 self.install_test_image(self._install_image_path, self._firmware_update)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800229 self.setup_gbb_flags()
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800230
231
232 def cleanup(self):
233 """Autotest cleanup function."""
234 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800235 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800236 super(FAFTSequence, self).cleanup()
237
238
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800239 def invalidate_firmware_setup(self):
240 """Invalidate all firmware related setup state.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800241
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800242 This method is called when the firmware is re-flashed. It resets all
243 firmware related setup states so that the next test setup properly
244 again.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800245 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800246 self.unmark_setup_done('gbb_flags')
Vic Yangdbaba8f2012-10-17 16:05:35 +0800247
248
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800249 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800250 """Reset client, if necessary.
251
252 This method is called when the client is not responsive. It may be
253 caused by the following cases:
254 - network flaky (can be recovered by replugging the Ethernet);
255 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
256 - corrupted firmware;
257 - corrutped OS image.
258 """
259 # DUT works fine, done.
260 if self._ping_test(self._client.ip, timeout=5):
261 return
262
263 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
264 # first reset item.
265
266 # DUT may halt on a firmware screen. Try cold reboot.
267 logging.info('Try cold reboot...')
268 self.cold_reboot()
269 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800270 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800271 return
272 except AssertionError:
273 pass
274
275 # DUT may be broken by a corrupted firmware. Restore firmware.
276 # We assume the recovery boot still works fine. Since the recovery
277 # code is in RO region and all FAFT tests don't change the RO region
278 # except GBB.
279 if self.is_firmware_saved():
280 self.ensure_client_in_recovery()
281 logging.info('Try restore the original firmware...')
282 if self.is_firmware_changed():
283 try:
284 self.restore_firmware()
285 return
286 except AssertionError:
287 logging.info('Restoring firmware doesn\'t help.')
288
289 # DUT may be broken by a corrupted OS image. Restore OS image.
290 self.ensure_client_in_recovery()
291 logging.info('Try restore the OS image...')
292 self.faft_client.run_shell_command('chromeos-install --yes')
293 self.sync_and_warm_reboot()
294 self.wait_for_client_offline()
295 try:
296 self.wait_for_client(install_deps=True)
297 logging.info('Successfully restore OS image.')
298 return
299 except AssertionError:
300 logging.info('Restoring OS image doesn\'t help.')
301
302
303 def ensure_client_in_recovery(self):
304 """Ensure client in recovery boot; reboot into it if necessary.
305
306 Raises:
307 error.TestError: if failed to boot the USB image.
308 """
309 # DUT works fine and is already in recovery boot, done.
310 if self._ping_test(self._client.ip, timeout=5):
311 if self.crossystem_checker({'mainfw_type': 'recovery'}):
312 return
313
314 logging.info('Try boot into USB image...')
315 self.servo.enable_usb_hub(host=True)
316 self.enable_rec_mode_and_reboot()
317 self.wait_fw_screen_and_plug_usb()
318 try:
319 self.wait_for_client(install_deps=True)
320 except AssertionError:
321 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800322
323
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800324 def assert_test_image_in_path(self, image_path):
325 """Assert the image of image_path be a Chrome OS test image.
326
327 Args:
328 image_path: A path on the host to the test image.
329
330 Raises:
331 error.TestError: if the image is not a test image.
332 """
333 try:
334 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
335 image_path)
336 logging.info('Build of image: %s %s' % (build_ver, build_hash))
337 except ChromeOSTestError:
338 raise error.TestError(
339 'An USB disk containning a test image should be plugged '
340 'in the servo board.')
341
342
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800343 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800344 """Assert an USB disk plugged-in on servo and a test image inside.
345
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800346 Args:
347 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
348 If None, it is detected automatically.
349
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800350 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800351 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800352 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800353 if self.check_setup_done('usb_check'):
Vic Yang54f70572012-10-19 17:05:26 +0800354 return
355
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800356 # TODO(waihong@chromium.org): We skip the check when servod runs in
357 # a different host since no easy way to access the servo host so far.
358 # Should find a way to work-around it.
359 if not self.servo.is_localhost():
360 logging.info('Skip checking Chrome OS test image in USB as servod '
361 'runs in a different host.')
362 return
363
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800364 if usb_dev:
365 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
366 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700367 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800368 usb_dev = self.servo.probe_host_usb_dev()
369 if not usb_dev:
370 raise error.TestError(
371 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800372 self.assert_test_image_in_path(usb_dev)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800373 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800374
375
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800376 def get_server_address(self):
377 """Get the server address seen from the client.
378
379 Returns:
380 A string of the server address.
381 """
382 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
383 return r[0].split()[0]
384
385
Simran Basi741b5d42012-05-18 11:27:15 -0700386 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800387 """Install the test image specied by the path onto the USB and DUT disk.
388
389 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500390 recovery mode. Then runs 'chromeos-install' (and possible
391 chromeos-firmwareupdate') to install it to DUT disk.
392
393 Sample command line:
394
395 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
396 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
397 server/site_tests/firmware_XXXX/control
398
399 This test requires an automated recovery to occur while simulating
400 inserting and removing the usb key from the servo. To allow this the
401 following hardware setup is required:
402 1. servo2 board connected via servoflex.
403 2. USB key inserted in the servo2.
404 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
405 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800406
407 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800408 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800409 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800410
411 Raises:
412 error.TestError: If devserver failed to start.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800413 """
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800414 if not image_path:
415 return
416
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800417 if self.check_setup_done('reimage'):
418 return
419
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800420 if image_path.startswith(self._HTTP_PREFIX):
421 # TODO(waihong@chromium.org): Add the check of the URL to ensure
422 # it is a test image.
423 devserver = None
424 image_url = image_path
425 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
445 time.sleep(self.DEVSERVER_DELAY)
446 # 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."""
898 time.sleep(self.FIRMWARE_SCREEN_DELAY)
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."""
904 time.sleep(self.FIRMWARE_SCREEN_DELAY)
905 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."""
910 time.sleep(self.FIRMWARE_SCREEN_DELAY)
911 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:
916 time.sleep(self.TEXT_SCREEN_DELAY)
917 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 Tama79574c2012-02-07 09:29:03 +0800922 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800923 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
924 time.sleep(self.USB_PLUG_DELAY)
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."""
935 time.sleep(self.FIRMWARE_SCREEN_DELAY)
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."""
944 time.sleep(self.DEV_SCREEN_TIMEOUT)
945 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."""
950 time.sleep(self.FIRMWARE_SCREEN_DELAY)
951 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."""
956 time.sleep(self.FIRMWARE_SCREEN_DELAY)
957 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')
1010 time.sleep(self.COLD_RESET_DELAY)
1011 self.servo.set('cold_reset', 'off')
1012 time.sleep(self.EC_BOOT_DELAY)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001013 self.ec.send_command("reboot ap-off")
Vic Yang611dd852012-08-02 15:36:31 +08001014 time.sleep(self.EC_BOOT_DELAY)
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()
1020 time.sleep(self.EC_REBOOT_DELAY)
1021 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 """
1050 time.sleep(self.FIRMWARE_SCREEN_DELAY)
1051 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 Tam1408f172012-07-31 15:06:21 +08001055 time.sleep(self.FIRMWARE_SCREEN_DELAY)
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')
1159 time.sleep(self.SYNC_DELAY)
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')
1184 time.sleep(self.POWER_BTN_DELAY)
1185 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')
1200 time.sleep(self.POWER_BTN_DELAY)
1201 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 Tam6a863ba2011-12-08 10:13:28 +08001212 time.sleep(self.SYNC_DELAY)
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')
1222 time.sleep(self.SYNC_DELAY)
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')
1236 time.sleep(self.SYNC_DELAY)
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)&' %
1240 (self.EC_REBOOT_DELAY, args))
Vic Yangf86728a2012-07-30 10:44:07 +08001241 time.sleep(self.EC_REBOOT_DELAY)
1242 self.check_lid_and_power_on()
1243
1244
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001245 def full_power_off_and_on(self):
1246 """Shutdown the device by pressing power button and power on again."""
1247 # Press power button to trigger Chrome OS normal shutdown process.
1248 self.servo.power_normal_press()
1249 time.sleep(self.FULL_POWER_OFF_DELAY)
1250 # Short press power button to boot DUT again.
1251 self.servo.power_short_press()
1252
1253
Vic Yangf86728a2012-07-30 10:44:07 +08001254 def check_lid_and_power_on(self):
1255 """
1256 On devices with EC software sync, system powers on after EC reboots if
1257 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1258 This method checks lid switch state and presses power button if
1259 necessary.
1260 """
1261 if self.servo.get("lid_open") == "no":
1262 time.sleep(self.SOFTWARE_SYNC_DELAY)
1263 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001264
1265
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001266 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1267 """Modify the kernel header magic in USB stick.
1268
1269 The kernel header magic is the first 8-byte of kernel partition.
1270 We modify it to make it fail on kernel verification check.
1271
1272 Args:
1273 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1274 from_magic: A string of magic which we change it from.
1275 to_magic: A string of magic which we change it to.
1276
1277 Raises:
1278 error.TestError: if failed to change magic.
1279 """
1280 assert len(from_magic) == 8
1281 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001282 # USB image only contains one kernel.
1283 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001284 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1285 current_magic = utils.system_output(read_cmd)
1286 if current_magic == to_magic:
1287 logging.info("The kernel magic is already %s." % current_magic)
1288 return
1289 if current_magic != from_magic:
1290 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1291
1292 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1293 (from_magic, to_magic))
1294 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1295 " 2>/dev/null" % (to_magic, kernel_part))
1296 utils.system(write_cmd)
1297
1298 if utils.system_output(read_cmd) != to_magic:
1299 raise error.TestError("Failed to write new magic.")
1300
1301
1302 def corrupt_usb_kernel(self, usb_dev):
1303 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1304
1305 Args:
1306 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1307 """
1308 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1309 self.CORRUPTED_MAGIC)
1310
1311
1312 def restore_usb_kernel(self, usb_dev):
1313 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1314
1315 Args:
1316 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1317 """
1318 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1319 self.CHROMEOS_MAGIC)
1320
1321
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001322 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001323 """Call the action function with/without arguments.
1324
1325 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001326 action_tuple: A function, or a tuple (function, args, error_msg),
1327 in which, args and error_msg are optional. args is
1328 either a value or a tuple if multiple arguments.
1329 check_status: Check the return value of action function. If not
1330 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001331
1332 Returns:
1333 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001334
1335 Raises:
1336 error.TestError: An error when the action function is not callable.
1337 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001338 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001339 action = action_tuple
1340 args = ()
1341 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001342 if isinstance(action_tuple, tuple):
1343 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001344 if len(action_tuple) >= 2:
1345 args = action_tuple[1]
1346 if not isinstance(args, tuple):
1347 args = (args,)
1348 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001349 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001350
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001351 if action is None:
1352 return
1353
1354 if not callable(action):
1355 raise error.TestError('action is not callable!')
1356
1357 info_msg = 'calling %s' % str(action)
1358 if args:
1359 info_msg += ' with args %s' % str(args)
1360 logging.info(info_msg)
1361 ret = action(*args)
1362
1363 if check_status and not ret:
1364 raise error.TestFail('%s: %s returning %s' %
1365 (error_msg, info_msg, str(ret)))
1366 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001367
1368
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001369 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1370 post_power_action=None):
1371 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1372
1373 Args:
1374 shutdown_action: a function which makes DUT shutdown, like pressing
1375 power key.
1376 pre_power_action: a function which is called before next power on.
1377 post_power_action: a function which is called after next power on.
1378
1379 Raises:
1380 error.TestFail: if the shutdown_action() failed to turn DUT off.
1381 """
1382 self._call_action(shutdown_action)
1383 logging.info('Wait to ensure DUT shut down...')
1384 try:
1385 self.wait_for_client()
1386 raise error.TestFail(
1387 'Should shut the device down after calling %s.' %
1388 str(shutdown_action))
1389 except AssertionError:
1390 logging.info(
1391 'DUT is surely shutdown. We are going to power it on again...')
1392
1393 if pre_power_action:
1394 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001395 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001396 if post_power_action:
1397 self._call_action(post_power_action)
1398
1399
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001400 def register_faft_template(self, template):
1401 """Register FAFT template, the default FAFT_STEP of each step.
1402
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001403 Any missing field falls back to the original faft_template.
1404
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001405 Args:
1406 template: A FAFT_STEP dict.
1407 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001408 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001409
1410
1411 def register_faft_sequence(self, sequence):
1412 """Register FAFT sequence.
1413
1414 Args:
1415 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1416 """
1417 self._faft_sequence = sequence
1418
1419
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001420 def run_faft_step(self, step, no_reboot=False):
1421 """Run a single FAFT step.
1422
1423 Any missing field falls back to faft_template. An empty step means
1424 running the default faft_template.
1425
1426 Args:
1427 step: A FAFT_STEP dict.
1428 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001429
1430 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001431 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001432 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001433 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1434 'firmware_action', 'install_deps_after_boot')
1435
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001436 test = {}
1437 test.update(self._faft_template)
1438 test.update(step)
1439
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001440 for key in test:
1441 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001442 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001443
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001444 if test['state_checker']:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001445 self._call_action(test['state_checker'], check_status=True)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001446
1447 self._call_action(test['userspace_action'])
1448
1449 # Don't run reboot_action and firmware_action if no_reboot is True.
1450 if not no_reboot:
1451 self._call_action(test['reboot_action'])
1452 self.wait_for_client_offline()
1453 self._call_action(test['firmware_action'])
1454
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001455 try:
1456 if 'install_deps_after_boot' in test:
1457 self.wait_for_client(
1458 install_deps=test['install_deps_after_boot'])
1459 else:
1460 self.wait_for_client()
1461 except AssertionError:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001462 logging.info('wait_for_client() timed out.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001463 self.reset_client()
1464 raise
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001465
1466
1467 def run_faft_sequence(self):
1468 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001469 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001470 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001471 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001472 logging.info('======== Running FAFT sequence step %d ========' %
1473 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001474 # Don't reboot in the last step.
1475 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001476 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001477
1478
ctchang38ae4922012-09-03 17:01:16 +08001479 def get_current_firmware_sha(self):
1480 """Get current firmware sha of body and vblock.
1481
1482 Returns:
1483 Current firmware sha follows the order (
1484 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1485 """
1486 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1487 self.faft_client.get_firmware_sha('a'),
1488 self.faft_client.get_firmware_sig_sha('b'),
1489 self.faft_client.get_firmware_sha('b'))
1490 return current_firmware_sha
1491
1492
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001493 def is_firmware_changed(self):
1494 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001495
1496 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001497 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001498 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001499 # Device may not be rebooted after test.
1500 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001501
1502 current_sha = self.get_current_firmware_sha()
1503
1504 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001505 return False
ctchang38ae4922012-09-03 17:01:16 +08001506 else:
ctchang38ae4922012-09-03 17:01:16 +08001507 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1508 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1509 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1510 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001511 logging.info("Firmware changed:")
1512 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1513 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1514 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1515 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1516 return True
ctchang38ae4922012-09-03 17:01:16 +08001517
1518
1519 def backup_firmware(self, suffix='.original'):
1520 """Backup firmware to file, and then send it to host.
1521
1522 Args:
1523 suffix: a string appended to backup file name
1524 """
1525 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001526 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1527 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1528 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001529
1530 self._backup_firmware_sha = self.get_current_firmware_sha()
1531 logging.info('Backup firmware stored in %s with suffix %s' % (
1532 self.resultsdir, suffix))
1533
1534
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001535 def is_firmware_saved(self):
1536 """Check if a firmware saved (called backup_firmware before).
1537
1538 Returns:
1539 True if the firmware is backuped; otherwise False.
1540 """
1541 return self._backup_firmware_sha != ()
1542
1543
ctchang38ae4922012-09-03 17:01:16 +08001544 def restore_firmware(self, suffix='.original'):
1545 """Restore firmware from host in resultsdir.
1546
1547 Args:
1548 suffix: a string appended to backup file name
1549 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001550 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001551 return
1552
1553 # Backup current corrupted firmware.
1554 self.backup_firmware(suffix='.corrupt')
1555
1556 # Restore firmware.
1557 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001558 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1559 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001560
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001561 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001562 self.sync_and_warm_reboot()
1563 self.wait_for_client_offline()
1564 self.wait_for_client()
1565
ctchang38ae4922012-09-03 17:01:16 +08001566 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001567
1568
1569 def setup_firmwareupdate_shellball(self, shellball=None):
1570 """Deside a shellball to use in firmware update test.
1571
1572 Check if there is a given shellball, and it is a shell script. Then,
1573 send it to the remote host. Otherwise, use
1574 /usr/sbin/chromeos-firmwareupdate.
1575
1576 Args:
1577 shellball: path of a shellball or default to None.
1578
1579 Returns:
1580 Path of shellball in remote host.
1581 If use default shellball, reutrn None.
1582 """
1583 updater_path = None
1584 if shellball:
1585 # Determine the firmware file is a shellball or a raw binary.
1586 is_shellball = (utils.system_output("file %s" % shellball).find(
1587 "shell script") != -1)
1588 if is_shellball:
1589 logging.info('Device will update firmware with shellball %s'
1590 % shellball)
1591 temp_dir = self.faft_client.create_temp_dir('shellball_')
1592 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1593 self._client.send_file(shellball, temp_shellball)
1594 updater_path = temp_shellball
1595 else:
1596 raise error.TestFail(
1597 'The given shellball is not a shell script.')
1598 return updater_path