blob: e3be6014c60b29c03910c763cda30648add203e2 [file] [log] [blame]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +08005import ctypes
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08006import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08007import os
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08008import re
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +08009import subprocess
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080010import sys
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080011import time
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080012
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080013from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080014from autotest_lib.client.common_lib import error
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +080015from autotest_lib.server.cros import vboot_constants as vboot
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +080016from autotest_lib.server.cros.chrome_ec import ChromeEC
Vic Yangebd6de62012-06-26 14:25:57 +080017from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +080018from autotest_lib.server.cros.faft_delay_constants import FAFTDelayConstants
Tom Wai-Hong Tam22b77302011-11-03 13:03:48 +080019from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080020from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +080021from autotest_lib.site_utils.chromeos_test.common_util import ChromeOSTestError
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080022
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080023dirname = os.path.dirname(sys.modules[__name__].__file__)
24autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
25cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080026
27class FAFTSequence(ServoTest):
28 """
29 The base class of Fully Automated Firmware Test Sequence.
30
31 Many firmware tests require several reboot cycles and verify the resulted
32 system states. To do that, an Autotest test case should detailly handle
33 every action on each step. It makes the test case hard to read and many
34 duplicated code. The base class FAFTSequence is to solve this problem.
35
36 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
37 There are four functions in the FAFT_STEP dict:
38 state_checker: a function to check the current is valid or not,
39 returning True if valid, otherwise, False to break the whole
40 test sequence.
41 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080042 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080043 firmware_action: a function to describe the action ran after reboot.
44
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080045 And configurations:
46 install_deps_after_boot: if True, install the Autotest dependency after
47 boot; otherwise, do nothing. It is for the cases of recovery mode
48 test. The test boots a USB/SD image instead of an internal image.
49 The previous installed Autotest dependency on the internal image
50 is lost. So need to install it again.
51
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080052 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080053 userspace_action and firmware_action. Its reboot_action is a hardware
54 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080055 self.register_faft_template(FAFT_STEP).
56
57 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
58 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
59 fall back to default.
60
61 In the run_once(), it should register and run FAFT_SEQUENCE like:
62 def run_once(self):
63 self.register_faft_sequence(FAFT_SEQUENCE)
64 self.run_faft_sequnce()
65
66 Note that in the last step, we only run state_checker. The
67 userspace_action, reboot_action, and firmware_action are not executed.
68
69 Attributes:
70 _faft_template: The default FAFT_STEP of each step. The actions would
71 be over-written if the registered FAFT_SEQUENCE is valid.
72 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080073 _install_image_path: The URL or the path on the host to the Chrome OS
74 test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080075 _firmware_update: Boolean. True if firmware update needed after
76 installing the image.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +080077 _trapped_in_recovery_reason: Keep the recovery reason when the test is
78 trapped in the recovery screen.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080079 """
80 version = 1
81
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080082 # Mapping of partition number of kernel and rootfs.
83 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
84 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
85 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
86 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
87
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +080088 CHROMEOS_MAGIC = "CHROMEOS"
89 CORRUPTED_MAGIC = "CORRUPTD"
90
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080091 _HTTP_PREFIX = 'http://'
92 _DEVSERVER_PORT = '8090'
93
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +080094 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080095 _faft_sequence = ()
96
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080097 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080098 _firmware_update = False
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +080099 _trapped_in_recovery_reason = 0
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800100
ctchang38ae4922012-09-03 17:01:16 +0800101 _backup_firmware_sha = ()
102
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800103 # Class level variable, keep track the states of one time setup.
104 # This variable is preserved across tests which inherit this class.
105 _global_setup_done = {
106 'gbb_flags': False,
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800107 'reimage': False,
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800108 'usb_check': False,
109 }
Vic Yang54f70572012-10-19 17:05:26 +0800110
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800111 @classmethod
112 def check_setup_done(cls, label):
113 """Check if the given setup is done.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800114
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800115 Args:
116 label: The label of the setup.
117 """
118 return cls._global_setup_done[label]
119
120
121 @classmethod
122 def mark_setup_done(cls, label):
123 """Mark the given setup done.
124
125 Args:
126 label: The label of the setup.
127 """
128 cls._global_setup_done[label] = True
129
130
131 @classmethod
132 def unmark_setup_done(cls, label):
133 """Mark the given setup not done.
134
135 Args:
136 label: The label of the setup.
137 """
138 cls._global_setup_done[label] = False
Vic Yang54f70572012-10-19 17:05:26 +0800139
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800140
141 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
142 # Parse arguments from command line
143 args = {}
144 for arg in cmdline_args:
145 match = re.search("^(\w+)=(.+)", arg)
146 if match:
147 args[match.group(1)] = match.group(2)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800148 if 'image' in args:
149 self._install_image_path = args['image']
150 logging.info('Install Chrome OS test image path: %s' %
151 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800152 if 'firmware_update' in args and args['firmware_update'].lower() \
153 not in ('0', 'false', 'no'):
154 if self._install_image_path:
155 self._firmware_update = True
156 logging.info('Also update firmware after installing.')
157 else:
158 logging.warning('Firmware update will not not performed '
159 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800160
161 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
162 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800163 if use_faft:
164 self.client_attr = FAFTClientAttribute(
165 self.faft_client.get_platform_name())
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800166 self.delay = FAFTDelayConstants(
167 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800168
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800169 if self.client_attr.chrome_ec:
170 self.ec = ChromeEC(self.servo)
171
Tom Wai-Hong Tam9c15b4b2012-10-29 17:59:26 +0800172 if not self.client_attr.has_keyboard:
173 # The environment variable USBKM232_UART_DEVICE should point
174 # to the USB-KM232 UART device.
175 if ('USBKM232_UART_DEVICE' not in os.environ or
176 not os.path.exists(os.environ['USBKM232_UART_DEVICE'])):
177 raise error.TestError('Must set a valid environment '
178 'variable USBKM232_UART_DEVICE.')
179
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700180 # Setting up key matrix mapping
181 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
182
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800183
184 def setup(self):
185 """Autotest setup function."""
186 super(FAFTSequence, self).setup()
187 if not self._remote_infos['faft']['used']:
188 raise error.TestError('The use_faft flag should be enabled.')
189 self.register_faft_template({
190 'state_checker': (None),
191 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800192 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800193 'firmware_action': (None)
194 })
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800195 self.install_test_image(self._install_image_path, self._firmware_update)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800196 self.setup_gbb_flags()
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800197
198
199 def cleanup(self):
200 """Autotest cleanup function."""
201 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800202 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800203 super(FAFTSequence, self).cleanup()
204
205
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800206 def invalidate_firmware_setup(self):
207 """Invalidate all firmware related setup state.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800208
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800209 This method is called when the firmware is re-flashed. It resets all
210 firmware related setup states so that the next test setup properly
211 again.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800212 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800213 self.unmark_setup_done('gbb_flags')
Vic Yangdbaba8f2012-10-17 16:05:35 +0800214
215
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800216 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800217 """Reset client, if necessary.
218
219 This method is called when the client is not responsive. It may be
220 caused by the following cases:
221 - network flaky (can be recovered by replugging the Ethernet);
222 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
223 - corrupted firmware;
224 - corrutped OS image.
225 """
226 # DUT works fine, done.
227 if self._ping_test(self._client.ip, timeout=5):
228 return
229
230 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
231 # first reset item.
232
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800233 # DUT may be trapped in the recovery screen. Try to boot into USB to
234 # retrieve the recovery reason.
235 logging.info('Try to retrieve recovery reason...')
236 if self.servo.get('usb_mux_sel1') == 'dut_sees_usbkey':
237 self.wait_fw_screen_and_plug_usb()
238 else:
239 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
240
241 try:
242 self.wait_for_client(install_deps=True)
243 lines = self.faft_client.run_shell_command_get_output(
244 'crossystem recovery_reason')
245 self._trapped_in_recovery_reason = int(lines[0])
246 logging.info('Got the recovery reason %d.' %
247 self._trapped_in_recovery_reason)
248 except AssertionError:
249 logging.info('Failed to get the recovery reason.')
250
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800251 # DUT may halt on a firmware screen. Try cold reboot.
252 logging.info('Try cold reboot...')
253 self.cold_reboot()
254 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800255 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800256 return
257 except AssertionError:
258 pass
259
260 # DUT may be broken by a corrupted firmware. Restore firmware.
261 # We assume the recovery boot still works fine. Since the recovery
262 # code is in RO region and all FAFT tests don't change the RO region
263 # except GBB.
264 if self.is_firmware_saved():
265 self.ensure_client_in_recovery()
266 logging.info('Try restore the original firmware...')
267 if self.is_firmware_changed():
268 try:
269 self.restore_firmware()
270 return
271 except AssertionError:
272 logging.info('Restoring firmware doesn\'t help.')
273
274 # DUT may be broken by a corrupted OS image. Restore OS image.
275 self.ensure_client_in_recovery()
276 logging.info('Try restore the OS image...')
277 self.faft_client.run_shell_command('chromeos-install --yes')
278 self.sync_and_warm_reboot()
279 self.wait_for_client_offline()
280 try:
281 self.wait_for_client(install_deps=True)
282 logging.info('Successfully restore OS image.')
283 return
284 except AssertionError:
285 logging.info('Restoring OS image doesn\'t help.')
286
287
288 def ensure_client_in_recovery(self):
289 """Ensure client in recovery boot; reboot into it if necessary.
290
291 Raises:
292 error.TestError: if failed to boot the USB image.
293 """
294 # DUT works fine and is already in recovery boot, done.
295 if self._ping_test(self._client.ip, timeout=5):
296 if self.crossystem_checker({'mainfw_type': 'recovery'}):
297 return
298
299 logging.info('Try boot into USB image...')
300 self.servo.enable_usb_hub(host=True)
301 self.enable_rec_mode_and_reboot()
302 self.wait_fw_screen_and_plug_usb()
303 try:
304 self.wait_for_client(install_deps=True)
305 except AssertionError:
306 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800307
308
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800309 def assert_test_image_in_path(self, image_path):
310 """Assert the image of image_path be a Chrome OS test image.
311
312 Args:
313 image_path: A path on the host to the test image.
314
315 Raises:
316 error.TestError: if the image is not a test image.
317 """
318 try:
319 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
320 image_path)
321 logging.info('Build of image: %s %s' % (build_ver, build_hash))
322 except ChromeOSTestError:
323 raise error.TestError(
324 'An USB disk containning a test image should be plugged '
325 'in the servo board.')
326
327
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800328 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800329 """Assert an USB disk plugged-in on servo and a test image inside.
330
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800331 Args:
332 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
333 If None, it is detected automatically.
334
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800335 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800336 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800337 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800338 if self.check_setup_done('usb_check'):
Vic Yang54f70572012-10-19 17:05:26 +0800339 return
340
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800341 # TODO(waihong@chromium.org): We skip the check when servod runs in
342 # a different host since no easy way to access the servo host so far.
343 # Should find a way to work-around it.
344 if not self.servo.is_localhost():
345 logging.info('Skip checking Chrome OS test image in USB as servod '
346 'runs in a different host.')
347 return
348
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800349 if usb_dev:
350 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
351 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700352 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800353 usb_dev = self.servo.probe_host_usb_dev()
354 if not usb_dev:
355 raise error.TestError(
356 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800357 self.assert_test_image_in_path(usb_dev)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800358 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800359
360
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800361 def get_server_address(self):
362 """Get the server address seen from the client.
363
364 Returns:
365 A string of the server address.
366 """
367 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
368 return r[0].split()[0]
369
370
Simran Basi741b5d42012-05-18 11:27:15 -0700371 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800372 """Install the test image specied by the path onto the USB and DUT disk.
373
374 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500375 recovery mode. Then runs 'chromeos-install' (and possible
376 chromeos-firmwareupdate') to install it to DUT disk.
377
378 Sample command line:
379
380 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
381 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
382 server/site_tests/firmware_XXXX/control
383
384 This test requires an automated recovery to occur while simulating
385 inserting and removing the usb key from the servo. To allow this the
386 following hardware setup is required:
387 1. servo2 board connected via servoflex.
388 2. USB key inserted in the servo2.
389 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
390 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800391
392 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800393 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800394 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800395
396 Raises:
397 error.TestError: If devserver failed to start.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800398 """
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800399 if not image_path:
400 return
401
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800402 if self.check_setup_done('reimage'):
403 return
404
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800405 if image_path.startswith(self._HTTP_PREFIX):
406 # TODO(waihong@chromium.org): Add the check of the URL to ensure
407 # it is a test image.
408 devserver = None
409 image_url = image_path
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800410 elif self.servo.is_localhost():
411 self.assert_test_image_in_path(image_path)
412 # If servod is localhost, i.e. both servod and FAFT see the same
413 # file system, do nothing.
414 devserver = None
415 image_url = image_path
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800416 else:
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800417 self.assert_test_image_in_path(image_path)
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800418 image_dir, image_base = os.path.split(image_path)
419 logging.info('Starting devserver to serve the image...')
420 # The following stdout and stderr arguments should not be None,
421 # even we don't use them. Otherwise, the socket of devserve is
422 # created as fd 1 (as no stdout) but it still thinks stdout is fd
423 # 1 and dump the log to the socket. Wrong HTTP protocol happens.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800424 devserver = subprocess.Popen(['/usr/lib/devserver/devserver.py',
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800425 '--archive_dir=%s' % image_dir,
426 '--port=%s' % self._DEVSERVER_PORT],
427 stdout=subprocess.PIPE,
428 stderr=subprocess.PIPE)
429 image_url = '%s%s:%s/static/archive/%s' % (
430 self._HTTP_PREFIX,
431 self.get_server_address(),
432 self._DEVSERVER_PORT,
433 image_base)
434
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800435 # Wait devserver startup completely
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800436 time.sleep(self.delay.devserver)
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800437 # devserver is a service running forever. If it is terminated,
438 # some error does happen.
439 if devserver.poll():
440 raise error.TestError('Starting devserver failed, '
441 'returning %d.' % devserver.returncode)
442
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800443 logging.info('Ask Servo to install the image from %s' % image_url)
444 self.servo.image_to_servo_usb(image_url)
445
446 if devserver and devserver.poll() is None:
447 logging.info('Shutting down devserver...')
448 devserver.terminate()
Mike Truty49153d82012-08-21 22:27:30 -0500449
450 # DUT is powered off while imaging servo USB.
451 # Now turn it on.
452 self.servo.power_short_press()
453 self.wait_for_client()
454 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
455
456 install_cmd = 'chromeos-install --yes'
457 if firmware_update:
458 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam1dd11592012-10-26 15:01:45 +0800459 self.backup_firmware()
Mike Truty49153d82012-08-21 22:27:30 -0500460
461 self.register_faft_sequence((
462 { # Step 1, request recovery boot
463 'state_checker': (self.crossystem_checker, {
464 'mainfw_type': ('developer', 'normal'),
465 }),
466 'userspace_action': self.faft_client.request_recovery_boot,
467 'firmware_action': self.wait_fw_screen_and_plug_usb,
468 'install_deps_after_boot': True,
469 },
470 { # Step 2, expected recovery boot
471 'state_checker': (self.crossystem_checker, {
472 'mainfw_type': 'recovery',
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800473 'recovery_reason' : vboot.RECOVERY_REASON['US_TEST'],
Mike Truty49153d82012-08-21 22:27:30 -0500474 }),
475 'userspace_action': (self.faft_client.run_shell_command,
476 install_cmd),
477 'reboot_action': self.cold_reboot,
478 'install_deps_after_boot': True,
479 },
480 { # Step 3, expected normal or developer boot (not recovery)
481 'state_checker': (self.crossystem_checker, {
482 'mainfw_type': ('developer', 'normal')
483 }),
484 },
485 ))
486 self.run_faft_sequence()
Tom Wai-Hong Tam1dd11592012-10-26 15:01:45 +0800487
488 if firmware_update:
489 self.clear_saved_firmware()
490
Mike Truty49153d82012-08-21 22:27:30 -0500491 # 'Unplug' any USB keys in the servo from the dut.
Tom Wai-Hong Tam953c7742012-10-16 21:09:31 +0800492 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam6668b762012-10-23 11:45:36 +0800493 # Mark usb_check done so it won't check a test image in USB anymore.
494 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800495 self.mark_setup_done('reimage')
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800496
497
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800498 def clear_set_gbb_flags(self, clear_mask, set_mask):
499 """Clear and set the GBB flags in the current flashrom.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800500
501 Args:
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800502 clear_mask: A mask of flags to be cleared.
503 set_mask: A mask of flags to be set.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800504 """
505 gbb_flags = self.faft_client.get_gbb_flags()
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800506 new_flags = gbb_flags & ctypes.c_uint32(~clear_mask).value | set_mask
507
508 if (gbb_flags != new_flags):
509 logging.info('Change the GBB flags from 0x%x to 0x%x.' %
510 (gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800511 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800512 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800513 self.faft_client.reload_firmware()
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800514 # If changing FORCE_DEV_SWITCH_ON flag, reboot to get a clear state
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800515 if ((gbb_flags ^ new_flags) & vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON):
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800516 self.run_faft_step({
517 'firmware_action': self.wait_fw_screen_and_ctrl_d,
518 })
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800519
520
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800521 def check_ec_capability(self, required_cap=[], suppress_warning=False):
Vic Yang4d72cb62012-07-24 11:51:09 +0800522 """Check if current platform has required EC capabilities.
523
524 Args:
525 required_cap: A list containing required EC capabilities. Pass in
526 None to only check for presence of Chrome EC.
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800527 suppress_warning: True to suppress any warning messages.
Vic Yang4d72cb62012-07-24 11:51:09 +0800528
529 Returns:
530 True if requirements are met. Otherwise, False.
531 """
532 if not self.client_attr.chrome_ec:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800533 if not suppress_warning:
534 logging.warn('Requires Chrome EC to run this test.')
Vic Yang4d72cb62012-07-24 11:51:09 +0800535 return False
536
537 for cap in required_cap:
538 if cap not in self.client_attr.ec_capability:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800539 if not suppress_warning:
540 logging.warn('Requires EC capability "%s" to run this '
541 'test.' % cap)
Vic Yang4d72cb62012-07-24 11:51:09 +0800542 return False
543
544 return True
545
546
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800547 def _parse_crossystem_output(self, lines):
548 """Parse the crossystem output into a dict.
549
550 Args:
551 lines: The list of crossystem output strings.
552
553 Returns:
554 A dict which contains the crossystem keys/values.
555
556 Raises:
557 error.TestError: If wrong format in crossystem output.
558
559 >>> seq = FAFTSequence()
560 >>> seq._parse_crossystem_output([ \
561 "arch = x86 # Platform architecture", \
562 "cros_debug = 1 # OS should allow debug", \
563 ])
564 {'cros_debug': '1', 'arch': 'x86'}
565 >>> seq._parse_crossystem_output([ \
566 "arch=x86", \
567 ])
568 Traceback (most recent call last):
569 ...
570 TestError: Failed to parse crossystem output: arch=x86
571 >>> seq._parse_crossystem_output([ \
572 "arch = x86 # Platform architecture", \
573 "arch = arm # Platform architecture", \
574 ])
575 Traceback (most recent call last):
576 ...
577 TestError: Duplicated crossystem key: arch
578 """
579 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
580 parsed_list = {}
581 for line in lines:
582 matched = re.match(pattern, line.strip())
583 if not matched:
584 raise error.TestError("Failed to parse crossystem output: %s"
585 % line)
586 (name, value) = (matched.group(1), matched.group(2))
587 if name in parsed_list:
588 raise error.TestError("Duplicated crossystem key: %s" % name)
589 parsed_list[name] = value
590 return parsed_list
591
592
593 def crossystem_checker(self, expected_dict):
594 """Check the crossystem values matched.
595
596 Given an expect_dict which describes the expected crossystem values,
597 this function check the current crossystem values are matched or not.
598
599 Args:
600 expected_dict: A dict which contains the expected values.
601
602 Returns:
603 True if the crossystem value matched; otherwise, False.
604 """
605 lines = self.faft_client.run_shell_command_get_output('crossystem')
606 got_dict = self._parse_crossystem_output(lines)
607 for key in expected_dict:
608 if key not in got_dict:
609 logging.info('Expected key "%s" not in crossystem result' % key)
610 return False
611 if isinstance(expected_dict[key], str):
612 if got_dict[key] != expected_dict[key]:
613 logging.info("Expected '%s' value '%s' but got '%s'" %
614 (key, expected_dict[key], got_dict[key]))
615 return False
616 elif isinstance(expected_dict[key], tuple):
617 # Expected value is a tuple of possible actual values.
618 if got_dict[key] not in expected_dict[key]:
619 logging.info("Expected '%s' values %s but got '%s'" %
620 (key, str(expected_dict[key]), got_dict[key]))
621 return False
622 else:
623 logging.info("The expected_dict is neither a str nor a dict.")
624 return False
625 return True
626
627
Tom Wai-Hong Tam39b93b92012-09-04 16:56:05 +0800628 def vdat_flags_checker(self, mask, value):
629 """Check the flags from VbSharedData matched.
630
631 This function checks the masked flags from VbSharedData using crossystem
632 are matched the given value.
633
634 Args:
635 mask: A bitmask of flags to be matched.
636 value: An expected value.
637
638 Returns:
639 True if the flags matched; otherwise, False.
640 """
641 lines = self.faft_client.run_shell_command_get_output(
642 'crossystem vdat_flags')
643 vdat_flags = int(lines[0], 16)
644 if vdat_flags & mask != value:
645 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x" %
646 (value, mask, vdat_flags))
647 return False
648 return True
649
650
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800651 def ro_normal_checker(self, expected_fw=None, twostop=False):
652 """Check the current boot uses RO boot.
653
654 Args:
655 expected_fw: A string of expected firmware, 'A', 'B', or
656 None if don't care.
657 twostop: True to expect a TwoStop boot; False to expect a RO boot.
658
659 Returns:
660 True if the currect boot firmware matched and used RO boot;
661 otherwise, False.
662 """
663 crossystem_dict = {'tried_fwb': '0'}
664 if expected_fw:
665 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800666 if self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800667 crossystem_dict['ecfw_act'] = ('RW' if twostop else 'RO')
668
669 return (self.vdat_flags_checker(
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800670 vboot.VDAT_FLAG_LF_USE_RO_NORMAL,
671 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL) and
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800672 self.crossystem_checker(crossystem_dict))
673
674
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +0800675 def dev_boot_usb_checker(self, dev_boot_usb=True):
676 """Check the current boot is from a developer USB (Ctrl-U trigger).
677
678 Args:
679 dev_boot_usb: True to expect an USB boot;
680 False to expect an internal device boot.
681
682 Returns:
683 True if the currect boot device matched; otherwise, False.
684 """
685 return (self.crossystem_checker({'mainfw_type': 'developer'})
686 and self.faft_client.is_removable_device_boot() == dev_boot_usb)
687
688
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800689 def root_part_checker(self, expected_part):
690 """Check the partition number of the root device matched.
691
692 Args:
693 expected_part: A string containing the number of the expected root
694 partition.
695
696 Returns:
697 True if the currect root partition number matched; otherwise, False.
698 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800699 part = self.faft_client.get_root_part()[-1]
700 if self.ROOTFS_MAP[expected_part] != part:
701 logging.info("Expected root part %s but got %s" %
702 (self.ROOTFS_MAP[expected_part], part))
703 return False
704 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800705
706
Vic Yang59cac9c2012-05-21 15:28:42 +0800707 def ec_act_copy_checker(self, expected_copy):
708 """Check the EC running firmware copy matches.
709
710 Args:
711 expected_copy: A string containing 'RO', 'A', or 'B' indicating
712 the expected copy of EC running firmware.
713
714 Returns:
715 True if the current EC running copy matches; otherwise, False.
716 """
717 lines = self.faft_client.run_shell_command_get_output('ectool version')
718 pattern = re.compile("Firmware copy: (.*)")
719 for line in lines:
720 matched = pattern.match(line)
721 if matched and matched.group(1) == expected_copy:
722 return True
723 return False
724
725
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800726 def check_root_part_on_non_recovery(self, part):
727 """Check the partition number of root device and on normal/dev boot.
728
729 Returns:
730 True if the root device matched and on normal/dev boot;
731 otherwise, False.
732 """
733 return self.root_part_checker(part) and \
734 self.crossystem_checker({
735 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800736 })
737
738
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800739 def _join_part(self, dev, part):
740 """Return a concatenated string of device and partition number.
741
742 Args:
743 dev: A string of device, e.g.'/dev/sda'.
744 part: A string of partition number, e.g.'3'.
745
746 Returns:
747 A concatenated string of device and partition number, e.g.'/dev/sda3'.
748
749 >>> seq = FAFTSequence()
750 >>> seq._join_part('/dev/sda', '3')
751 '/dev/sda3'
752 >>> seq._join_part('/dev/mmcblk0', '2')
753 '/dev/mmcblk0p2'
754 """
755 if 'mmcblk' in dev:
756 return dev + 'p' + part
757 else:
758 return dev + part
759
760
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800761 def copy_kernel_and_rootfs(self, from_part, to_part):
762 """Copy kernel and rootfs from from_part to to_part.
763
764 Args:
765 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800766 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800767 """
768 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800769 logging.info('Copying kernel from %s to %s. Please wait...' %
770 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800771 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800772 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
773 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
774 logging.info('Copying rootfs from %s to %s. Please wait...' %
775 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800776 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800777 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
778 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800779
780
781 def ensure_kernel_boot(self, part):
782 """Ensure the request kernel boot.
783
784 If not, it duplicates the current kernel to the requested kernel
785 and sets the requested higher priority to ensure it boot.
786
787 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800788 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800789 """
790 if not self.root_part_checker(part):
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800791 if self.faft_client.diff_kernel_a_b():
792 self.copy_kernel_and_rootfs(
793 from_part=self.OTHER_KERNEL_MAP[part],
794 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800795 self.run_faft_step({
796 'userspace_action': (self.reset_and_prioritize_kernel, part),
797 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800798
799
Vic Yang416f2032012-08-28 10:18:03 +0800800 def set_hardware_write_protect(self, enabled):
Vic Yang2cabf812012-08-28 02:39:04 +0800801 """Set hardware write protect pin.
802
803 Args:
804 enable: True if asserting write protect pin. Otherwise, False.
805 """
806 self.servo.set('fw_wp_vref', self.client_attr.wp_voltage)
807 self.servo.set('fw_wp_en', 'on')
Vic Yang416f2032012-08-28 10:18:03 +0800808 self.servo.set('fw_wp', 'on' if enabled else 'off')
809
810
811 def set_EC_write_protect_and_reboot(self, enabled):
812 """Set EC write protect status and reboot to take effect.
813
814 EC write protect is only activated if both hardware write protect pin
815 is asserted and software write protect flag is set. Also, a reboot is
816 required for write protect to take effect.
817
818 Since the software write protect flag cannot be unset if hardware write
819 protect pin is asserted, we need to deasserted the pin first if we are
820 deactivating write protect. Similarly, a reboot is required before we
821 can modify the software flag.
822
823 This method asserts/deasserts hardware write protect pin first, and
824 set corresponding EC software write protect flag.
825
826 Args:
827 enable: True if activating EC write protect. Otherwise, False.
828 """
829 self.set_hardware_write_protect(enabled)
830 if enabled:
831 # Set write protect flag and reboot to take effect.
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800832 self.ec.send_command("flashwp enable")
Vic Yang416f2032012-08-28 10:18:03 +0800833 self.sync_and_ec_reboot()
834 else:
835 # Reboot after deasserting hardware write protect pin to deactivate
836 # write protect. And then remove software write protect flag.
837 self.sync_and_ec_reboot()
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800838 self.ec.send_command("flashwp disable")
Vic Yang2cabf812012-08-28 02:39:04 +0800839
840
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800841 def press_ctrl_d(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800842 """Send Ctrl-D key to DUT."""
Tom Wai-Hong Tam9c15b4b2012-10-29 17:59:26 +0800843 if not self.client_attr.has_keyboard:
844 logging.info('Running usbkm232-ctrld...')
845 os.system('usbkm232-ctrld')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800846 else:
847 self.servo.ctrl_d()
848
849
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800850 def press_ctrl_u(self):
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800851 """Send Ctrl-U key to DUT.
852
853 Raises:
854 error.TestError: if a non-Chrome EC device or no Ctrl-U command given
855 on a no-build-in-keyboard device.
856 """
Tom Wai-Hong Tam9c15b4b2012-10-29 17:59:26 +0800857 if not self.client_attr.has_keyboard:
858 logging.info('Running usbkm232-ctrlu...')
859 os.system('usbkm232-ctrlu')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800860 elif self.check_ec_capability(['keyboard'], suppress_warning=True):
861 self.ec.key_down('<ctrl_l>')
862 self.ec.key_down('u')
863 self.ec.key_up('u')
864 self.ec.key_up('<ctrl_l>')
865 elif self.client_attr.has_keyboard:
866 raise error.TestError(
867 "Can't send Ctrl-U to DUT without using Chrome EC.")
868 else:
869 raise error.TestError(
870 "Should specify the ctrl_u_cmd argument.")
871
872
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800873 def press_enter(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800874 """Send Enter key to DUT."""
Tom Wai-Hong Tam9c15b4b2012-10-29 17:59:26 +0800875 if not self.client_attr.has_keyboard:
876 logging.info('Running usbkm232-enter...')
877 os.system('usbkm232-enter')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800878 else:
879 self.servo.enter_key()
880
881
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800882 def wait_fw_screen_and_ctrl_d(self):
883 """Wait for firmware warning screen and press Ctrl-D."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800884 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800885 self.press_ctrl_d()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800886
887
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800888 def wait_fw_screen_and_ctrl_u(self):
889 """Wait for firmware warning screen and press Ctrl-U."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800890 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800891 self.press_ctrl_u()
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800892
893
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800894 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
895 """Wait for firmware warning screen and trigger recovery boot."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800896 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800897 self.press_enter()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800898
899 # For Alex/ZGB, there is a dev warning screen in text mode.
900 # Skip it by pressing Ctrl-D.
901 if need_dev_transition:
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800902 time.sleep(self.delay.legacy_text_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800903 self.press_ctrl_d()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800904
905
Mike Truty49153d82012-08-21 22:27:30 -0500906 def wait_fw_screen_and_unplug_usb(self):
907 """Wait for firmware warning screen and then unplug the servo USB."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800908 time.sleep(self.delay.load_usb)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800909 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800910 time.sleep(self.delay.between_usb_plug)
Mike Truty49153d82012-08-21 22:27:30 -0500911
912
913 def wait_fw_screen_and_plug_usb(self):
914 """Wait for firmware warning screen and then unplug and plug the USB."""
915 self.wait_fw_screen_and_unplug_usb()
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800916 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
917
918
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800919 def wait_fw_screen_and_press_power(self):
920 """Wait for firmware warning screen and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800921 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam7317c042012-08-14 11:59:06 +0800922 # While the firmware screen, the power button probing loop sleeps
923 # 0.25 second on every scan. Use the normal delay (1.2 second) for
924 # power press.
925 self.servo.power_normal_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800926
927
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800928 def wait_longer_fw_screen_and_press_power(self):
929 """Wait for firmware screen without timeout and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800930 time.sleep(self.delay.dev_screen_timeout)
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800931 self.wait_fw_screen_and_press_power()
932
933
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800934 def wait_fw_screen_and_close_lid(self):
935 """Wait for firmware warning screen and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800936 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800937 self.servo.lid_close()
938
939
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800940 def wait_longer_fw_screen_and_close_lid(self):
941 """Wait for firmware screen without timeout and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800942 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800943 self.wait_fw_screen_and_close_lid()
944
945
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800946 def setup_gbb_flags(self):
947 """Setup the GBB flags for FAFT test."""
948 if self.check_setup_done('gbb_flags'):
949 return
950
951 logging.info('Set proper GBB flags for test.')
952 self.clear_set_gbb_flags(vboot.GBB_FLAG_DEV_SCREEN_SHORT_DELAY |
953 vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
954 vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
955 vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
956 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
957 self.mark_setup_done('gbb_flags')
958
959
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800960 def setup_tried_fwb(self, tried_fwb):
961 """Setup for fw B tried state.
962
963 It makes sure the system in the requested fw B tried state. If not, it
964 tries to do so.
965
966 Args:
967 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
968 """
969 if tried_fwb:
970 if not self.crossystem_checker({'tried_fwb': '1'}):
971 logging.info(
972 'Firmware is not booted with tried_fwb. Reboot into it.')
973 self.run_faft_step({
974 'userspace_action': self.faft_client.set_try_fw_b,
975 })
976 else:
977 if not self.crossystem_checker({'tried_fwb': '0'}):
978 logging.info(
979 'Firmware is booted with tried_fwb. Reboot to clear.')
980 self.run_faft_step({})
981
982
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800983 def enable_rec_mode_and_reboot(self):
984 """Switch to rec mode and reboot.
985
986 This method emulates the behavior of the old physical recovery switch,
987 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
988 recovery mode, i.e. just press Power + Esc + Refresh.
989 """
Tom Wai-Hong Tam80419a82012-10-30 09:10:00 +0800990 if self.client_attr.chrome_ec:
Vic Yang81273092012-08-21 15:57:09 +0800991 # Cold reset to clear EC_IN_RW signal
Vic Yanga7250662012-08-31 04:00:08 +0800992 self.servo.set('cold_reset', 'on')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800993 time.sleep(self.delay.hold_cold_reset)
Vic Yanga7250662012-08-31 04:00:08 +0800994 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800995 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800996 self.ec.send_command("reboot ap-off")
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800997 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800998 self.ec.send_command("hostevent set 0x4000")
Vic Yang611dd852012-08-02 15:36:31 +0800999 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001000 else:
1001 self.servo.enable_recovery_mode()
1002 self.cold_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001003 time.sleep(self.delay.ec_reboot_cmd)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001004 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001005
1006
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001007 def enable_dev_mode_and_reboot(self):
1008 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001009 if self.client_attr.keyboard_dev:
1010 self.enable_keyboard_dev_mode()
1011 else:
1012 self.servo.enable_development_mode()
1013 self.faft_client.run_shell_command(
1014 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001015
1016
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001017 def enable_normal_mode_and_reboot(self):
1018 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001019 if self.client_attr.keyboard_dev:
1020 self.disable_keyboard_dev_mode()
1021 else:
1022 self.servo.disable_development_mode()
1023 self.faft_client.run_shell_command(
1024 'chromeos-firmwareupdate --mode tonormal && reboot')
1025
1026
1027 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
1028 """Wait for firmware screen and then switch into or out of dev mode.
1029
1030 Args:
1031 dev: True if switching into dev mode. Otherwise, False.
1032 """
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001033 time.sleep(self.delay.firmware_screen)
Vic Yange7553162012-06-20 16:20:47 +08001034 if dev:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001035 self.press_ctrl_d()
Vic Yange7553162012-06-20 16:20:47 +08001036 else:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001037 self.press_enter()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001038 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001039 self.press_enter()
Vic Yange7553162012-06-20 16:20:47 +08001040
1041
1042 def enable_keyboard_dev_mode(self):
1043 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +08001044 # Plug out USB disk for preventing recovery boot without warning
1045 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +08001046 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001047 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001048 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001049 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +08001050
1051
1052 def disable_keyboard_dev_mode(self):
1053 logging.info("Disabling keyboard controlled developer mode")
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001054 if not self.client_attr.chrome_ec:
Vic Yang611dd852012-08-02 15:36:31 +08001055 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001056 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001057 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001058 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001059
1060
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001061 def setup_dev_mode(self, dev_mode):
1062 """Setup for development mode.
1063
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001064 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001065 tries to do so.
1066
1067 Args:
1068 dev_mode: True if requested in dev mode; False if normal mode.
1069 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001070 # Change the default firmware_action for dev mode passing the fw screen.
1071 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001072 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
1073 else None),
1074 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001075 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +08001076 if (not self.client_attr.keyboard_dev and
1077 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001078 logging.info('Dev switch is not on. Now switch it on.')
1079 self.servo.enable_development_mode()
1080 if not self.crossystem_checker({'devsw_boot': '1',
1081 'mainfw_type': 'developer'}):
1082 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001083 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001084 'userspace_action': None if self.client_attr.keyboard_dev
1085 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001086 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001087 'reboot_action': self.enable_keyboard_dev_mode if
1088 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001089 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001090 else:
Vic Yange7553162012-06-20 16:20:47 +08001091 if (not self.client_attr.keyboard_dev and
1092 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001093 logging.info('Dev switch is not off. Now switch it off.')
1094 self.servo.disable_development_mode()
1095 if not self.crossystem_checker({'devsw_boot': '0',
1096 'mainfw_type': 'normal'}):
1097 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001098 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001099 'userspace_action': None if self.client_attr.keyboard_dev
1100 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001101 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001102 'reboot_action': self.disable_keyboard_dev_mode if
1103 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001104 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001105
1106
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001107 def setup_kernel(self, part):
1108 """Setup for kernel test.
1109
1110 It makes sure both kernel A and B bootable and the current boot is
1111 the requested kernel part.
1112
1113 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001114 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001115 """
1116 self.ensure_kernel_boot(part)
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +08001117 if self.faft_client.diff_kernel_a_b():
1118 self.copy_kernel_and_rootfs(from_part=part,
1119 to_part=self.OTHER_KERNEL_MAP[part])
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001120 self.reset_and_prioritize_kernel(part)
1121
1122
1123 def reset_and_prioritize_kernel(self, part):
1124 """Make the requested partition highest priority.
1125
1126 This function also reset kerenl A and B to bootable.
1127
1128 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001129 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001130 """
1131 root_dev = self.faft_client.get_root_dev()
1132 # Reset kernel A and B to bootable.
1133 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1134 (self.KERNEL_MAP['a'], root_dev))
1135 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1136 (self.KERNEL_MAP['b'], root_dev))
1137 # Set kernel part highest priority.
1138 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
1139 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001140 # Safer to sync and wait until the cgpt status written to the disk.
1141 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001142 time.sleep(self.delay.sync)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001143
1144
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001145 def warm_reboot(self):
1146 """Request a warm reboot.
1147
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001148 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001149 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001150 # Use cold reset if the warm reset is broken.
1151 if self.client_attr.broken_warm_reset:
Gediminas Ramanauskase021e152012-09-04 19:10:59 -07001152 logging.info('broken_warm_reset is True. Cold rebooting instead.')
1153 self.cold_reboot()
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001154 else:
1155 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001156
1157
1158 def cold_reboot(self):
1159 """Request a cold reboot.
1160
1161 A wrapper for underlying servo cold reset.
1162 """
Gediminas Ramanauskasc6025692012-10-23 14:33:40 -07001163 if self.client_attr.broken_warm_reset:
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001164 self.servo.set('pwr_button', 'press')
1165 self.servo.set('cold_reset', 'on')
1166 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001167 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001168 self.servo.set('pwr_button', 'release')
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +08001169 elif self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001170 # We don't use servo.cold_reset() here because software sync is
1171 # not yet finished, and device may or may not come up after cold
1172 # reset. Pressing power button before firmware comes up solves this.
1173 #
1174 # The correct behavior should be (not work now):
1175 # - If rebooting EC with rec mode on, power on AP and it boots
1176 # into recovery mode.
1177 # - If rebooting EC with rec mode off, power on AP for software
1178 # sync. Then AP checks if lid open or not. If lid open, continue;
1179 # otherwise, shut AP down and need servo for a power button
1180 # press.
1181 self.servo.set('cold_reset', 'on')
1182 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001183 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001184 self.servo.power_short_press()
1185 else:
1186 self.servo.cold_reset()
1187
1188
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001189 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001190 """Request the client sync and do a warm reboot.
1191
1192 This is the default reboot action on FAFT.
1193 """
1194 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001195 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001196 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001197
1198
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001199 def sync_and_cold_reboot(self):
1200 """Request the client sync and do a cold reboot.
1201
1202 This reboot action is used to reset EC for recovery mode.
1203 """
1204 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001205 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001206 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001207
1208
Vic Yangaeb10392012-08-28 09:25:09 +08001209 def sync_and_ec_reboot(self, args=''):
1210 """Request the client sync and do a EC triggered reboot.
1211
1212 Args:
1213 args: Arguments passed to "ectool reboot_ec". Including:
1214 RO: jump to EC RO firmware.
1215 RW: jump to EC RW firmware.
1216 cold: Cold/hard reboot.
1217 """
Vic Yang59cac9c2012-05-21 15:28:42 +08001218 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001219 time.sleep(self.delay.sync)
Vic Yangaeb10392012-08-28 09:25:09 +08001220 # Since EC reboot happens immediately, delay before actual reboot to
1221 # allow FAFT client returning.
1222 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec %s)&' %
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001223 (self.delay.ec_reboot_cmd, args))
1224 time.sleep(self.delay.ec_reboot_cmd)
Vic Yangf86728a2012-07-30 10:44:07 +08001225 self.check_lid_and_power_on()
1226
1227
Chun-ting Changa4f65532012-10-17 16:57:28 +08001228 def sync_and_reboot_with_factory_install_shim(self):
1229 """Request the client sync and do a warm reboot to recovery mode.
1230
1231 After reboot, the client will use factory install shim to reset TPM
1232 values. The client ignore TPM rollback, so here forces it to recovery
1233 mode.
1234 """
1235 is_dev = self.crossystem_checker({'devsw_boot': '1'})
1236 if not is_dev:
1237 self.enable_dev_mode_and_reboot()
1238 time.sleep(self.SYNC_DELAY)
1239 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001240 time.sleep(self.delay.install_shim_done)
Chun-ting Changa4f65532012-10-17 16:57:28 +08001241 self.warm_reboot()
1242
1243
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001244 def full_power_off_and_on(self):
1245 """Shutdown the device by pressing power button and power on again."""
1246 # Press power button to trigger Chrome OS normal shutdown process.
1247 self.servo.power_normal_press()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001248 time.sleep(self.delay.shutdown)
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001249 # Short press power button to boot DUT again.
1250 self.servo.power_short_press()
1251
1252
Vic Yangf86728a2012-07-30 10:44:07 +08001253 def check_lid_and_power_on(self):
1254 """
1255 On devices with EC software sync, system powers on after EC reboots if
1256 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1257 This method checks lid switch state and presses power button if
1258 necessary.
1259 """
1260 if self.servo.get("lid_open") == "no":
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001261 time.sleep(self.delay.software_sync)
Vic Yangf86728a2012-07-30 10:44:07 +08001262 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001263
1264
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001265 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1266 """Modify the kernel header magic in USB stick.
1267
1268 The kernel header magic is the first 8-byte of kernel partition.
1269 We modify it to make it fail on kernel verification check.
1270
1271 Args:
1272 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1273 from_magic: A string of magic which we change it from.
1274 to_magic: A string of magic which we change it to.
1275
1276 Raises:
1277 error.TestError: if failed to change magic.
1278 """
1279 assert len(from_magic) == 8
1280 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001281 # USB image only contains one kernel.
1282 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001283 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1284 current_magic = utils.system_output(read_cmd)
1285 if current_magic == to_magic:
1286 logging.info("The kernel magic is already %s." % current_magic)
1287 return
1288 if current_magic != from_magic:
1289 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1290
1291 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1292 (from_magic, to_magic))
1293 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1294 " 2>/dev/null" % (to_magic, kernel_part))
1295 utils.system(write_cmd)
1296
1297 if utils.system_output(read_cmd) != to_magic:
1298 raise error.TestError("Failed to write new magic.")
1299
1300
1301 def corrupt_usb_kernel(self, usb_dev):
1302 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1303
1304 Args:
1305 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1306 """
1307 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1308 self.CORRUPTED_MAGIC)
1309
1310
1311 def restore_usb_kernel(self, usb_dev):
1312 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1313
1314 Args:
1315 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1316 """
1317 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1318 self.CHROMEOS_MAGIC)
1319
1320
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001321 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001322 """Call the action function with/without arguments.
1323
1324 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001325 action_tuple: A function, or a tuple (function, args, error_msg),
1326 in which, args and error_msg are optional. args is
1327 either a value or a tuple if multiple arguments.
1328 check_status: Check the return value of action function. If not
1329 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001330
1331 Returns:
1332 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001333
1334 Raises:
1335 error.TestError: An error when the action function is not callable.
1336 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001337 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001338 action = action_tuple
1339 args = ()
1340 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001341 if isinstance(action_tuple, tuple):
1342 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001343 if len(action_tuple) >= 2:
1344 args = action_tuple[1]
1345 if not isinstance(args, tuple):
1346 args = (args,)
1347 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001348 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001349
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001350 if action is None:
1351 return
1352
1353 if not callable(action):
1354 raise error.TestError('action is not callable!')
1355
1356 info_msg = 'calling %s' % str(action)
1357 if args:
1358 info_msg += ' with args %s' % str(args)
1359 logging.info(info_msg)
1360 ret = action(*args)
1361
1362 if check_status and not ret:
1363 raise error.TestFail('%s: %s returning %s' %
1364 (error_msg, info_msg, str(ret)))
1365 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001366
1367
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001368 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1369 post_power_action=None):
1370 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1371
1372 Args:
1373 shutdown_action: a function which makes DUT shutdown, like pressing
1374 power key.
1375 pre_power_action: a function which is called before next power on.
1376 post_power_action: a function which is called after next power on.
1377
1378 Raises:
1379 error.TestFail: if the shutdown_action() failed to turn DUT off.
1380 """
1381 self._call_action(shutdown_action)
1382 logging.info('Wait to ensure DUT shut down...')
1383 try:
1384 self.wait_for_client()
1385 raise error.TestFail(
1386 'Should shut the device down after calling %s.' %
1387 str(shutdown_action))
1388 except AssertionError:
1389 logging.info(
1390 'DUT is surely shutdown. We are going to power it on again...')
1391
1392 if pre_power_action:
1393 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001394 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001395 if post_power_action:
1396 self._call_action(post_power_action)
1397
1398
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001399 def register_faft_template(self, template):
1400 """Register FAFT template, the default FAFT_STEP of each step.
1401
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001402 Any missing field falls back to the original faft_template.
1403
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001404 Args:
1405 template: A FAFT_STEP dict.
1406 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001407 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001408
1409
1410 def register_faft_sequence(self, sequence):
1411 """Register FAFT sequence.
1412
1413 Args:
1414 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1415 """
1416 self._faft_sequence = sequence
1417
1418
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001419 def run_faft_step(self, step, no_reboot=False):
1420 """Run a single FAFT step.
1421
1422 Any missing field falls back to faft_template. An empty step means
1423 running the default faft_template.
1424
1425 Args:
1426 step: A FAFT_STEP dict.
1427 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001428
1429 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001430 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001431 error.TestFail: Test failed in waiting DUT reboot.
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()
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001464 if self._trapped_in_recovery_reason:
1465 raise error.TestFail('Trapped in the recovery reason: %d' %
1466 self._trapped_in_recovery_reason)
1467 else:
1468 raise error.TestFail('Timed out waiting for DUT reboot.')
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001469
1470
1471 def run_faft_sequence(self):
1472 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001473 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001474 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001475 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001476 logging.info('======== Running FAFT sequence step %d ========' %
1477 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001478 # Don't reboot in the last step.
1479 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001480 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001481
1482
ctchang38ae4922012-09-03 17:01:16 +08001483 def get_current_firmware_sha(self):
1484 """Get current firmware sha of body and vblock.
1485
1486 Returns:
1487 Current firmware sha follows the order (
1488 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1489 """
1490 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1491 self.faft_client.get_firmware_sha('a'),
1492 self.faft_client.get_firmware_sig_sha('b'),
1493 self.faft_client.get_firmware_sha('b'))
1494 return current_firmware_sha
1495
1496
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001497 def is_firmware_changed(self):
1498 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001499
1500 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001501 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001502 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001503 # Device may not be rebooted after test.
1504 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001505
1506 current_sha = self.get_current_firmware_sha()
1507
1508 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001509 return False
ctchang38ae4922012-09-03 17:01:16 +08001510 else:
ctchang38ae4922012-09-03 17:01:16 +08001511 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1512 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1513 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1514 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001515 logging.info("Firmware changed:")
1516 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1517 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1518 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1519 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1520 return True
ctchang38ae4922012-09-03 17:01:16 +08001521
1522
1523 def backup_firmware(self, suffix='.original'):
1524 """Backup firmware to file, and then send it to host.
1525
1526 Args:
1527 suffix: a string appended to backup file name
1528 """
1529 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001530 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1531 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1532 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001533
1534 self._backup_firmware_sha = self.get_current_firmware_sha()
1535 logging.info('Backup firmware stored in %s with suffix %s' % (
1536 self.resultsdir, suffix))
1537
1538
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001539 def is_firmware_saved(self):
1540 """Check if a firmware saved (called backup_firmware before).
1541
1542 Returns:
1543 True if the firmware is backuped; otherwise False.
1544 """
1545 return self._backup_firmware_sha != ()
1546
1547
Tom Wai-Hong Tam1dd11592012-10-26 15:01:45 +08001548 def clear_saved_firmware(self):
1549 """Clear the firmware saved by the method backup_firmware."""
1550 self._backup_firmware_sha = ()
1551
1552
ctchang38ae4922012-09-03 17:01:16 +08001553 def restore_firmware(self, suffix='.original'):
1554 """Restore firmware from host in resultsdir.
1555
1556 Args:
1557 suffix: a string appended to backup file name
1558 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001559 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001560 return
1561
1562 # Backup current corrupted firmware.
1563 self.backup_firmware(suffix='.corrupt')
1564
1565 # Restore firmware.
1566 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001567 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1568 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001569
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001570 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001571 self.sync_and_warm_reboot()
1572 self.wait_for_client_offline()
1573 self.wait_for_client()
1574
ctchang38ae4922012-09-03 17:01:16 +08001575 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001576
1577
1578 def setup_firmwareupdate_shellball(self, shellball=None):
1579 """Deside a shellball to use in firmware update test.
1580
1581 Check if there is a given shellball, and it is a shell script. Then,
1582 send it to the remote host. Otherwise, use
1583 /usr/sbin/chromeos-firmwareupdate.
1584
1585 Args:
1586 shellball: path of a shellball or default to None.
1587
1588 Returns:
1589 Path of shellball in remote host.
1590 If use default shellball, reutrn None.
1591 """
1592 updater_path = None
1593 if shellball:
1594 # Determine the firmware file is a shellball or a raw binary.
1595 is_shellball = (utils.system_output("file %s" % shellball).find(
1596 "shell script") != -1)
1597 if is_shellball:
1598 logging.info('Device will update firmware with shellball %s'
1599 % shellball)
1600 temp_dir = self.faft_client.create_temp_dir('shellball_')
1601 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1602 self._client.send_file(shellball, temp_shellball)
1603 updater_path = temp_shellball
1604 else:
1605 raise error.TestFail(
1606 'The given shellball is not a shell script.')
1607 return updater_path