blob: 8009f837f1678cba03256860a0caf3f3b07698ef [file] [log] [blame]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +08005import ctypes
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08006import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08007import os
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08008import re
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +08009import subprocess
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080010import sys
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080011import time
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080012
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080013from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080014from autotest_lib.client.common_lib import error
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +080015from autotest_lib.server.cros import vboot_constants as vboot
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +080016from autotest_lib.server.cros.chrome_ec import ChromeEC
Vic Yangebd6de62012-06-26 14:25:57 +080017from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +080018from autotest_lib.server.cros.faft_delay_constants import FAFTDelayConstants
Tom Wai-Hong Tam22b77302011-11-03 13:03:48 +080019from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080020from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +080021from autotest_lib.site_utils.chromeos_test.common_util import ChromeOSTestError
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080022
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080023dirname = os.path.dirname(sys.modules[__name__].__file__)
24autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
25cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080026
27class FAFTSequence(ServoTest):
28 """
29 The base class of Fully Automated Firmware Test Sequence.
30
31 Many firmware tests require several reboot cycles and verify the resulted
32 system states. To do that, an Autotest test case should detailly handle
33 every action on each step. It makes the test case hard to read and many
34 duplicated code. The base class FAFTSequence is to solve this problem.
35
36 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
37 There are four functions in the FAFT_STEP dict:
38 state_checker: a function to check the current is valid or not,
39 returning True if valid, otherwise, False to break the whole
40 test sequence.
41 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080042 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080043 firmware_action: a function to describe the action ran after reboot.
44
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080045 And configurations:
46 install_deps_after_boot: if True, install the Autotest dependency after
47 boot; otherwise, do nothing. It is for the cases of recovery mode
48 test. The test boots a USB/SD image instead of an internal image.
49 The previous installed Autotest dependency on the internal image
50 is lost. So need to install it again.
51
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080052 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080053 userspace_action and firmware_action. Its reboot_action is a hardware
54 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080055 self.register_faft_template(FAFT_STEP).
56
57 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
58 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
59 fall back to default.
60
61 In the run_once(), it should register and run FAFT_SEQUENCE like:
62 def run_once(self):
63 self.register_faft_sequence(FAFT_SEQUENCE)
64 self.run_faft_sequnce()
65
66 Note that in the last step, we only run state_checker. The
67 userspace_action, reboot_action, and firmware_action are not executed.
68
69 Attributes:
70 _faft_template: The default FAFT_STEP of each step. The actions would
71 be over-written if the registered FAFT_SEQUENCE is valid.
72 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +080073 _customized_key_commands: The dict of the customized key commands,
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +080074 including Ctrl-D, Ctrl-U, Enter, and recovery reboot.
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080075 _install_image_path: The URL or the path on the host to the Chrome OS
76 test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080077 _firmware_update: Boolean. True if firmware update needed after
78 installing the image.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +080079 _trapped_in_recovery_reason: Keep the recovery reason when the test is
80 trapped in the recovery screen.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080081 """
82 version = 1
83
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080084 # Mapping of partition number of kernel and rootfs.
85 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
86 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
87 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
88 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
89
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +080090 CHROMEOS_MAGIC = "CHROMEOS"
91 CORRUPTED_MAGIC = "CORRUPTD"
92
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +080093 _HTTP_PREFIX = 'http://'
94 _DEVSERVER_PORT = '8090'
95
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +080096 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080097 _faft_sequence = ()
98
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +080099 _customized_key_commands = {
100 'ctrl_d': None,
101 'ctrl_u': None,
102 'enter': None,
103 'rec_reboot': None,
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800104 }
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800105 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800106 _firmware_update = False
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800107 _trapped_in_recovery_reason = 0
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800108
ctchang38ae4922012-09-03 17:01:16 +0800109 _backup_firmware_sha = ()
110
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800111 # Class level variable, keep track the states of one time setup.
112 # This variable is preserved across tests which inherit this class.
113 _global_setup_done = {
114 'gbb_flags': False,
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800115 'reimage': False,
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800116 'usb_check': False,
117 }
Vic Yang54f70572012-10-19 17:05:26 +0800118
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800119 @classmethod
120 def check_setup_done(cls, label):
121 """Check if the given setup is done.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800122
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800123 Args:
124 label: The label of the setup.
125 """
126 return cls._global_setup_done[label]
127
128
129 @classmethod
130 def mark_setup_done(cls, label):
131 """Mark the given setup done.
132
133 Args:
134 label: The label of the setup.
135 """
136 cls._global_setup_done[label] = True
137
138
139 @classmethod
140 def unmark_setup_done(cls, label):
141 """Mark the given setup not done.
142
143 Args:
144 label: The label of the setup.
145 """
146 cls._global_setup_done[label] = False
Vic Yang54f70572012-10-19 17:05:26 +0800147
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800148
149 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
150 # Parse arguments from command line
151 args = {}
152 for arg in cmdline_args:
153 match = re.search("^(\w+)=(.+)", arg)
154 if match:
155 args[match.group(1)] = match.group(2)
156
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800157 # Keep the arguments which will be used later.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800158 for key in self._customized_key_commands:
159 key_cmd = key + '_cmd'
160 if key_cmd in args:
161 self._customized_key_commands[key] = args[key_cmd]
162 logging.info('Customized %s key command: %s' %
163 (key, args[key_cmd]))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800164 if 'image' in args:
165 self._install_image_path = args['image']
166 logging.info('Install Chrome OS test image path: %s' %
167 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800168 if 'firmware_update' in args and args['firmware_update'].lower() \
169 not in ('0', 'false', 'no'):
170 if self._install_image_path:
171 self._firmware_update = True
172 logging.info('Also update firmware after installing.')
173 else:
174 logging.warning('Firmware update will not not performed '
175 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800176
177 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
178 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800179 if use_faft:
180 self.client_attr = FAFTClientAttribute(
181 self.faft_client.get_platform_name())
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800182 self.delay = FAFTDelayConstants(
183 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800184
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800185 if self.client_attr.chrome_ec:
186 self.ec = ChromeEC(self.servo)
187
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700188 # Setting up key matrix mapping
189 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
190
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800191
192 def setup(self):
193 """Autotest setup function."""
194 super(FAFTSequence, self).setup()
195 if not self._remote_infos['faft']['used']:
196 raise error.TestError('The use_faft flag should be enabled.')
197 self.register_faft_template({
198 'state_checker': (None),
199 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800200 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800201 'firmware_action': (None)
202 })
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800203 self.install_test_image(self._install_image_path, self._firmware_update)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800204 self.setup_gbb_flags()
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800205
206
207 def cleanup(self):
208 """Autotest cleanup function."""
209 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800210 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800211 super(FAFTSequence, self).cleanup()
212
213
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800214 def invalidate_firmware_setup(self):
215 """Invalidate all firmware related setup state.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800216
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800217 This method is called when the firmware is re-flashed. It resets all
218 firmware related setup states so that the next test setup properly
219 again.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800220 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800221 self.unmark_setup_done('gbb_flags')
Vic Yangdbaba8f2012-10-17 16:05:35 +0800222
223
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800224 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800225 """Reset client, if necessary.
226
227 This method is called when the client is not responsive. It may be
228 caused by the following cases:
229 - network flaky (can be recovered by replugging the Ethernet);
230 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
231 - corrupted firmware;
232 - corrutped OS image.
233 """
234 # DUT works fine, done.
235 if self._ping_test(self._client.ip, timeout=5):
236 return
237
238 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
239 # first reset item.
240
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800241 # DUT may be trapped in the recovery screen. Try to boot into USB to
242 # retrieve the recovery reason.
243 logging.info('Try to retrieve recovery reason...')
244 if self.servo.get('usb_mux_sel1') == 'dut_sees_usbkey':
245 self.wait_fw_screen_and_plug_usb()
246 else:
247 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
248
249 try:
250 self.wait_for_client(install_deps=True)
251 lines = self.faft_client.run_shell_command_get_output(
252 'crossystem recovery_reason')
253 self._trapped_in_recovery_reason = int(lines[0])
254 logging.info('Got the recovery reason %d.' %
255 self._trapped_in_recovery_reason)
256 except AssertionError:
257 logging.info('Failed to get the recovery reason.')
258
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800259 # DUT may halt on a firmware screen. Try cold reboot.
260 logging.info('Try cold reboot...')
261 self.cold_reboot()
262 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800263 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800264 return
265 except AssertionError:
266 pass
267
268 # DUT may be broken by a corrupted firmware. Restore firmware.
269 # We assume the recovery boot still works fine. Since the recovery
270 # code is in RO region and all FAFT tests don't change the RO region
271 # except GBB.
272 if self.is_firmware_saved():
273 self.ensure_client_in_recovery()
274 logging.info('Try restore the original firmware...')
275 if self.is_firmware_changed():
276 try:
277 self.restore_firmware()
278 return
279 except AssertionError:
280 logging.info('Restoring firmware doesn\'t help.')
281
282 # DUT may be broken by a corrupted OS image. Restore OS image.
283 self.ensure_client_in_recovery()
284 logging.info('Try restore the OS image...')
285 self.faft_client.run_shell_command('chromeos-install --yes')
286 self.sync_and_warm_reboot()
287 self.wait_for_client_offline()
288 try:
289 self.wait_for_client(install_deps=True)
290 logging.info('Successfully restore OS image.')
291 return
292 except AssertionError:
293 logging.info('Restoring OS image doesn\'t help.')
294
295
296 def ensure_client_in_recovery(self):
297 """Ensure client in recovery boot; reboot into it if necessary.
298
299 Raises:
300 error.TestError: if failed to boot the USB image.
301 """
302 # DUT works fine and is already in recovery boot, done.
303 if self._ping_test(self._client.ip, timeout=5):
304 if self.crossystem_checker({'mainfw_type': 'recovery'}):
305 return
306
307 logging.info('Try boot into USB image...')
308 self.servo.enable_usb_hub(host=True)
309 self.enable_rec_mode_and_reboot()
310 self.wait_fw_screen_and_plug_usb()
311 try:
312 self.wait_for_client(install_deps=True)
313 except AssertionError:
314 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800315
316
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800317 def assert_test_image_in_path(self, image_path):
318 """Assert the image of image_path be a Chrome OS test image.
319
320 Args:
321 image_path: A path on the host to the test image.
322
323 Raises:
324 error.TestError: if the image is not a test image.
325 """
326 try:
327 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
328 image_path)
329 logging.info('Build of image: %s %s' % (build_ver, build_hash))
330 except ChromeOSTestError:
331 raise error.TestError(
332 'An USB disk containning a test image should be plugged '
333 'in the servo board.')
334
335
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800336 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800337 """Assert an USB disk plugged-in on servo and a test image inside.
338
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800339 Args:
340 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
341 If None, it is detected automatically.
342
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800343 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800344 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800345 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800346 if self.check_setup_done('usb_check'):
Vic Yang54f70572012-10-19 17:05:26 +0800347 return
348
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800349 # TODO(waihong@chromium.org): We skip the check when servod runs in
350 # a different host since no easy way to access the servo host so far.
351 # Should find a way to work-around it.
352 if not self.servo.is_localhost():
353 logging.info('Skip checking Chrome OS test image in USB as servod '
354 'runs in a different host.')
355 return
356
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800357 if usb_dev:
358 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
359 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700360 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800361 usb_dev = self.servo.probe_host_usb_dev()
362 if not usb_dev:
363 raise error.TestError(
364 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800365 self.assert_test_image_in_path(usb_dev)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800366 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800367
368
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800369 def get_server_address(self):
370 """Get the server address seen from the client.
371
372 Returns:
373 A string of the server address.
374 """
375 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
376 return r[0].split()[0]
377
378
Simran Basi741b5d42012-05-18 11:27:15 -0700379 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800380 """Install the test image specied by the path onto the USB and DUT disk.
381
382 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500383 recovery mode. Then runs 'chromeos-install' (and possible
384 chromeos-firmwareupdate') to install it to DUT disk.
385
386 Sample command line:
387
388 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
389 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
390 server/site_tests/firmware_XXXX/control
391
392 This test requires an automated recovery to occur while simulating
393 inserting and removing the usb key from the servo. To allow this the
394 following hardware setup is required:
395 1. servo2 board connected via servoflex.
396 2. USB key inserted in the servo2.
397 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
398 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800399
400 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800401 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800402 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800403
404 Raises:
405 error.TestError: If devserver failed to start.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800406 """
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800407 if not image_path:
408 return
409
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800410 if self.check_setup_done('reimage'):
411 return
412
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800413 if image_path.startswith(self._HTTP_PREFIX):
414 # TODO(waihong@chromium.org): Add the check of the URL to ensure
415 # it is a test image.
416 devserver = None
417 image_url = image_path
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800418 elif self.servo.is_localhost():
419 self.assert_test_image_in_path(image_path)
420 # If servod is localhost, i.e. both servod and FAFT see the same
421 # file system, do nothing.
422 devserver = None
423 image_url = image_path
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800424 else:
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800425 self.assert_test_image_in_path(image_path)
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800426 image_dir, image_base = os.path.split(image_path)
427 logging.info('Starting devserver to serve the image...')
428 # The following stdout and stderr arguments should not be None,
429 # even we don't use them. Otherwise, the socket of devserve is
430 # created as fd 1 (as no stdout) but it still thinks stdout is fd
431 # 1 and dump the log to the socket. Wrong HTTP protocol happens.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800432 devserver = subprocess.Popen(['/usr/lib/devserver/devserver.py',
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800433 '--archive_dir=%s' % image_dir,
434 '--port=%s' % self._DEVSERVER_PORT],
435 stdout=subprocess.PIPE,
436 stderr=subprocess.PIPE)
437 image_url = '%s%s:%s/static/archive/%s' % (
438 self._HTTP_PREFIX,
439 self.get_server_address(),
440 self._DEVSERVER_PORT,
441 image_base)
442
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800443 # Wait devserver startup completely
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800444 time.sleep(self.delay.devserver)
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800445 # devserver is a service running forever. If it is terminated,
446 # some error does happen.
447 if devserver.poll():
448 raise error.TestError('Starting devserver failed, '
449 'returning %d.' % devserver.returncode)
450
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800451 logging.info('Ask Servo to install the image from %s' % image_url)
452 self.servo.image_to_servo_usb(image_url)
453
454 if devserver and devserver.poll() is None:
455 logging.info('Shutting down devserver...')
456 devserver.terminate()
Mike Truty49153d82012-08-21 22:27:30 -0500457
458 # DUT is powered off while imaging servo USB.
459 # Now turn it on.
460 self.servo.power_short_press()
461 self.wait_for_client()
462 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
463
464 install_cmd = 'chromeos-install --yes'
465 if firmware_update:
466 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
467
468 self.register_faft_sequence((
469 { # Step 1, request recovery boot
470 'state_checker': (self.crossystem_checker, {
471 'mainfw_type': ('developer', 'normal'),
472 }),
473 'userspace_action': self.faft_client.request_recovery_boot,
474 'firmware_action': self.wait_fw_screen_and_plug_usb,
475 'install_deps_after_boot': True,
476 },
477 { # Step 2, expected recovery boot
478 'state_checker': (self.crossystem_checker, {
479 'mainfw_type': 'recovery',
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800480 'recovery_reason' : vboot.RECOVERY_REASON['US_TEST'],
Mike Truty49153d82012-08-21 22:27:30 -0500481 }),
482 'userspace_action': (self.faft_client.run_shell_command,
483 install_cmd),
484 'reboot_action': self.cold_reboot,
485 'install_deps_after_boot': True,
486 },
487 { # Step 3, expected normal or developer boot (not recovery)
488 'state_checker': (self.crossystem_checker, {
489 'mainfw_type': ('developer', 'normal')
490 }),
491 },
492 ))
493 self.run_faft_sequence()
494 # 'Unplug' any USB keys in the servo from the dut.
Tom Wai-Hong Tam953c7742012-10-16 21:09:31 +0800495 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam6668b762012-10-23 11:45:36 +0800496 # Mark usb_check done so it won't check a test image in USB anymore.
497 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800498 self.mark_setup_done('reimage')
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800499
500
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800501 def clear_set_gbb_flags(self, clear_mask, set_mask):
502 """Clear and set the GBB flags in the current flashrom.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800503
504 Args:
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800505 clear_mask: A mask of flags to be cleared.
506 set_mask: A mask of flags to be set.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800507 """
508 gbb_flags = self.faft_client.get_gbb_flags()
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800509 new_flags = gbb_flags & ctypes.c_uint32(~clear_mask).value | set_mask
510
511 if (gbb_flags != new_flags):
512 logging.info('Change the GBB flags from 0x%x to 0x%x.' %
513 (gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800514 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800515 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800516 self.faft_client.reload_firmware()
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800517 # If changing FORCE_DEV_SWITCH_ON flag, reboot to get a clear state
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800518 if ((gbb_flags ^ new_flags) & vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON):
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800519 self.run_faft_step({
520 'firmware_action': self.wait_fw_screen_and_ctrl_d,
521 })
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800522
523
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800524 def check_ec_capability(self, required_cap=[], suppress_warning=False):
Vic Yang4d72cb62012-07-24 11:51:09 +0800525 """Check if current platform has required EC capabilities.
526
527 Args:
528 required_cap: A list containing required EC capabilities. Pass in
529 None to only check for presence of Chrome EC.
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800530 suppress_warning: True to suppress any warning messages.
Vic Yang4d72cb62012-07-24 11:51:09 +0800531
532 Returns:
533 True if requirements are met. Otherwise, False.
534 """
535 if not self.client_attr.chrome_ec:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800536 if not suppress_warning:
537 logging.warn('Requires Chrome EC to run this test.')
Vic Yang4d72cb62012-07-24 11:51:09 +0800538 return False
539
540 for cap in required_cap:
541 if cap not in self.client_attr.ec_capability:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800542 if not suppress_warning:
543 logging.warn('Requires EC capability "%s" to run this '
544 'test.' % cap)
Vic Yang4d72cb62012-07-24 11:51:09 +0800545 return False
546
547 return True
548
549
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800550 def _parse_crossystem_output(self, lines):
551 """Parse the crossystem output into a dict.
552
553 Args:
554 lines: The list of crossystem output strings.
555
556 Returns:
557 A dict which contains the crossystem keys/values.
558
559 Raises:
560 error.TestError: If wrong format in crossystem output.
561
562 >>> seq = FAFTSequence()
563 >>> seq._parse_crossystem_output([ \
564 "arch = x86 # Platform architecture", \
565 "cros_debug = 1 # OS should allow debug", \
566 ])
567 {'cros_debug': '1', 'arch': 'x86'}
568 >>> seq._parse_crossystem_output([ \
569 "arch=x86", \
570 ])
571 Traceback (most recent call last):
572 ...
573 TestError: Failed to parse crossystem output: arch=x86
574 >>> seq._parse_crossystem_output([ \
575 "arch = x86 # Platform architecture", \
576 "arch = arm # Platform architecture", \
577 ])
578 Traceback (most recent call last):
579 ...
580 TestError: Duplicated crossystem key: arch
581 """
582 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
583 parsed_list = {}
584 for line in lines:
585 matched = re.match(pattern, line.strip())
586 if not matched:
587 raise error.TestError("Failed to parse crossystem output: %s"
588 % line)
589 (name, value) = (matched.group(1), matched.group(2))
590 if name in parsed_list:
591 raise error.TestError("Duplicated crossystem key: %s" % name)
592 parsed_list[name] = value
593 return parsed_list
594
595
596 def crossystem_checker(self, expected_dict):
597 """Check the crossystem values matched.
598
599 Given an expect_dict which describes the expected crossystem values,
600 this function check the current crossystem values are matched or not.
601
602 Args:
603 expected_dict: A dict which contains the expected values.
604
605 Returns:
606 True if the crossystem value matched; otherwise, False.
607 """
608 lines = self.faft_client.run_shell_command_get_output('crossystem')
609 got_dict = self._parse_crossystem_output(lines)
610 for key in expected_dict:
611 if key not in got_dict:
612 logging.info('Expected key "%s" not in crossystem result' % key)
613 return False
614 if isinstance(expected_dict[key], str):
615 if got_dict[key] != expected_dict[key]:
616 logging.info("Expected '%s' value '%s' but got '%s'" %
617 (key, expected_dict[key], got_dict[key]))
618 return False
619 elif isinstance(expected_dict[key], tuple):
620 # Expected value is a tuple of possible actual values.
621 if got_dict[key] not in expected_dict[key]:
622 logging.info("Expected '%s' values %s but got '%s'" %
623 (key, str(expected_dict[key]), got_dict[key]))
624 return False
625 else:
626 logging.info("The expected_dict is neither a str nor a dict.")
627 return False
628 return True
629
630
Tom Wai-Hong Tam39b93b92012-09-04 16:56:05 +0800631 def vdat_flags_checker(self, mask, value):
632 """Check the flags from VbSharedData matched.
633
634 This function checks the masked flags from VbSharedData using crossystem
635 are matched the given value.
636
637 Args:
638 mask: A bitmask of flags to be matched.
639 value: An expected value.
640
641 Returns:
642 True if the flags matched; otherwise, False.
643 """
644 lines = self.faft_client.run_shell_command_get_output(
645 'crossystem vdat_flags')
646 vdat_flags = int(lines[0], 16)
647 if vdat_flags & mask != value:
648 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x" %
649 (value, mask, vdat_flags))
650 return False
651 return True
652
653
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800654 def ro_normal_checker(self, expected_fw=None, twostop=False):
655 """Check the current boot uses RO boot.
656
657 Args:
658 expected_fw: A string of expected firmware, 'A', 'B', or
659 None if don't care.
660 twostop: True to expect a TwoStop boot; False to expect a RO boot.
661
662 Returns:
663 True if the currect boot firmware matched and used RO boot;
664 otherwise, False.
665 """
666 crossystem_dict = {'tried_fwb': '0'}
667 if expected_fw:
668 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800669 if self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800670 crossystem_dict['ecfw_act'] = ('RW' if twostop else 'RO')
671
672 return (self.vdat_flags_checker(
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800673 vboot.VDAT_FLAG_LF_USE_RO_NORMAL,
674 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL) and
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800675 self.crossystem_checker(crossystem_dict))
676
677
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +0800678 def dev_boot_usb_checker(self, dev_boot_usb=True):
679 """Check the current boot is from a developer USB (Ctrl-U trigger).
680
681 Args:
682 dev_boot_usb: True to expect an USB boot;
683 False to expect an internal device boot.
684
685 Returns:
686 True if the currect boot device matched; otherwise, False.
687 """
688 return (self.crossystem_checker({'mainfw_type': 'developer'})
689 and self.faft_client.is_removable_device_boot() == dev_boot_usb)
690
691
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800692 def root_part_checker(self, expected_part):
693 """Check the partition number of the root device matched.
694
695 Args:
696 expected_part: A string containing the number of the expected root
697 partition.
698
699 Returns:
700 True if the currect root partition number matched; otherwise, False.
701 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800702 part = self.faft_client.get_root_part()[-1]
703 if self.ROOTFS_MAP[expected_part] != part:
704 logging.info("Expected root part %s but got %s" %
705 (self.ROOTFS_MAP[expected_part], part))
706 return False
707 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800708
709
Vic Yang59cac9c2012-05-21 15:28:42 +0800710 def ec_act_copy_checker(self, expected_copy):
711 """Check the EC running firmware copy matches.
712
713 Args:
714 expected_copy: A string containing 'RO', 'A', or 'B' indicating
715 the expected copy of EC running firmware.
716
717 Returns:
718 True if the current EC running copy matches; otherwise, False.
719 """
720 lines = self.faft_client.run_shell_command_get_output('ectool version')
721 pattern = re.compile("Firmware copy: (.*)")
722 for line in lines:
723 matched = pattern.match(line)
724 if matched and matched.group(1) == expected_copy:
725 return True
726 return False
727
728
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800729 def check_root_part_on_non_recovery(self, part):
730 """Check the partition number of root device and on normal/dev boot.
731
732 Returns:
733 True if the root device matched and on normal/dev boot;
734 otherwise, False.
735 """
736 return self.root_part_checker(part) and \
737 self.crossystem_checker({
738 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800739 })
740
741
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800742 def _join_part(self, dev, part):
743 """Return a concatenated string of device and partition number.
744
745 Args:
746 dev: A string of device, e.g.'/dev/sda'.
747 part: A string of partition number, e.g.'3'.
748
749 Returns:
750 A concatenated string of device and partition number, e.g.'/dev/sda3'.
751
752 >>> seq = FAFTSequence()
753 >>> seq._join_part('/dev/sda', '3')
754 '/dev/sda3'
755 >>> seq._join_part('/dev/mmcblk0', '2')
756 '/dev/mmcblk0p2'
757 """
758 if 'mmcblk' in dev:
759 return dev + 'p' + part
760 else:
761 return dev + part
762
763
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800764 def copy_kernel_and_rootfs(self, from_part, to_part):
765 """Copy kernel and rootfs from from_part to to_part.
766
767 Args:
768 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800769 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800770 """
771 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800772 logging.info('Copying kernel from %s to %s. Please wait...' %
773 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800774 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800775 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
776 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
777 logging.info('Copying rootfs from %s to %s. Please wait...' %
778 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800779 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800780 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
781 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800782
783
784 def ensure_kernel_boot(self, part):
785 """Ensure the request kernel boot.
786
787 If not, it duplicates the current kernel to the requested kernel
788 and sets the requested higher priority to ensure it boot.
789
790 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800791 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800792 """
793 if not self.root_part_checker(part):
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800794 if self.faft_client.diff_kernel_a_b():
795 self.copy_kernel_and_rootfs(
796 from_part=self.OTHER_KERNEL_MAP[part],
797 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800798 self.run_faft_step({
799 'userspace_action': (self.reset_and_prioritize_kernel, part),
800 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800801
802
Vic Yang416f2032012-08-28 10:18:03 +0800803 def set_hardware_write_protect(self, enabled):
Vic Yang2cabf812012-08-28 02:39:04 +0800804 """Set hardware write protect pin.
805
806 Args:
807 enable: True if asserting write protect pin. Otherwise, False.
808 """
809 self.servo.set('fw_wp_vref', self.client_attr.wp_voltage)
810 self.servo.set('fw_wp_en', 'on')
Vic Yang416f2032012-08-28 10:18:03 +0800811 self.servo.set('fw_wp', 'on' if enabled else 'off')
812
813
814 def set_EC_write_protect_and_reboot(self, enabled):
815 """Set EC write protect status and reboot to take effect.
816
817 EC write protect is only activated if both hardware write protect pin
818 is asserted and software write protect flag is set. Also, a reboot is
819 required for write protect to take effect.
820
821 Since the software write protect flag cannot be unset if hardware write
822 protect pin is asserted, we need to deasserted the pin first if we are
823 deactivating write protect. Similarly, a reboot is required before we
824 can modify the software flag.
825
826 This method asserts/deasserts hardware write protect pin first, and
827 set corresponding EC software write protect flag.
828
829 Args:
830 enable: True if activating EC write protect. Otherwise, False.
831 """
832 self.set_hardware_write_protect(enabled)
833 if enabled:
834 # Set write protect flag and reboot to take effect.
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800835 self.ec.send_command("flashwp enable")
Vic Yang416f2032012-08-28 10:18:03 +0800836 self.sync_and_ec_reboot()
837 else:
838 # Reboot after deasserting hardware write protect pin to deactivate
839 # write protect. And then remove software write protect flag.
840 self.sync_and_ec_reboot()
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800841 self.ec.send_command("flashwp disable")
Vic Yang2cabf812012-08-28 02:39:04 +0800842
843
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800844 def press_ctrl_d(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800845 """Send Ctrl-D key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800846 if self._customized_key_commands['ctrl_d']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800847 logging.info('running the customized Ctrl-D key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800848 os.system(self._customized_key_commands['ctrl_d'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800849 else:
850 self.servo.ctrl_d()
851
852
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800853 def press_ctrl_u(self):
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800854 """Send Ctrl-U key to DUT.
855
856 Raises:
857 error.TestError: if a non-Chrome EC device or no Ctrl-U command given
858 on a no-build-in-keyboard device.
859 """
860 if self._customized_key_commands['ctrl_u']:
861 logging.info('running the customized Ctrl-U key command')
862 os.system(self._customized_key_commands['ctrl_u'])
863 elif self.check_ec_capability(['keyboard'], suppress_warning=True):
864 self.ec.key_down('<ctrl_l>')
865 self.ec.key_down('u')
866 self.ec.key_up('u')
867 self.ec.key_up('<ctrl_l>')
868 elif self.client_attr.has_keyboard:
869 raise error.TestError(
870 "Can't send Ctrl-U to DUT without using Chrome EC.")
871 else:
872 raise error.TestError(
873 "Should specify the ctrl_u_cmd argument.")
874
875
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800876 def press_enter(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800877 """Send Enter key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800878 if self._customized_key_commands['enter']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800879 logging.info('running the customized Enter key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800880 os.system(self._customized_key_commands['enter'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800881 else:
882 self.servo.enter_key()
883
884
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800885 def wait_fw_screen_and_ctrl_d(self):
886 """Wait for firmware warning screen and press Ctrl-D."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800887 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800888 self.press_ctrl_d()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800889
890
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800891 def wait_fw_screen_and_ctrl_u(self):
892 """Wait for firmware warning screen and press Ctrl-U."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800893 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800894 self.press_ctrl_u()
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800895
896
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800897 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
898 """Wait for firmware warning screen and trigger recovery boot."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800899 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800900 self.press_enter()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800901
902 # For Alex/ZGB, there is a dev warning screen in text mode.
903 # Skip it by pressing Ctrl-D.
904 if need_dev_transition:
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800905 time.sleep(self.delay.legacy_text_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800906 self.press_ctrl_d()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800907
908
Mike Truty49153d82012-08-21 22:27:30 -0500909 def wait_fw_screen_and_unplug_usb(self):
910 """Wait for firmware warning screen and then unplug the servo USB."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800911 time.sleep(self.delay.load_usb)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800912 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800913 time.sleep(self.delay.between_usb_plug)
Mike Truty49153d82012-08-21 22:27:30 -0500914
915
916 def wait_fw_screen_and_plug_usb(self):
917 """Wait for firmware warning screen and then unplug and plug the USB."""
918 self.wait_fw_screen_and_unplug_usb()
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800919 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
920
921
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800922 def wait_fw_screen_and_press_power(self):
923 """Wait for firmware warning screen and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800924 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam7317c042012-08-14 11:59:06 +0800925 # While the firmware screen, the power button probing loop sleeps
926 # 0.25 second on every scan. Use the normal delay (1.2 second) for
927 # power press.
928 self.servo.power_normal_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800929
930
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800931 def wait_longer_fw_screen_and_press_power(self):
932 """Wait for firmware screen without timeout and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800933 time.sleep(self.delay.dev_screen_timeout)
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800934 self.wait_fw_screen_and_press_power()
935
936
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800937 def wait_fw_screen_and_close_lid(self):
938 """Wait for firmware warning screen and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800939 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800940 self.servo.lid_close()
941
942
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800943 def wait_longer_fw_screen_and_close_lid(self):
944 """Wait for firmware screen without timeout and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800945 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800946 self.wait_fw_screen_and_close_lid()
947
948
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800949 def setup_gbb_flags(self):
950 """Setup the GBB flags for FAFT test."""
951 if self.check_setup_done('gbb_flags'):
952 return
953
954 logging.info('Set proper GBB flags for test.')
955 self.clear_set_gbb_flags(vboot.GBB_FLAG_DEV_SCREEN_SHORT_DELAY |
956 vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
957 vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
958 vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
959 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
960 self.mark_setup_done('gbb_flags')
961
962
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800963 def setup_tried_fwb(self, tried_fwb):
964 """Setup for fw B tried state.
965
966 It makes sure the system in the requested fw B tried state. If not, it
967 tries to do so.
968
969 Args:
970 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
971 """
972 if tried_fwb:
973 if not self.crossystem_checker({'tried_fwb': '1'}):
974 logging.info(
975 'Firmware is not booted with tried_fwb. Reboot into it.')
976 self.run_faft_step({
977 'userspace_action': self.faft_client.set_try_fw_b,
978 })
979 else:
980 if not self.crossystem_checker({'tried_fwb': '0'}):
981 logging.info(
982 'Firmware is booted with tried_fwb. Reboot to clear.')
983 self.run_faft_step({})
984
985
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800986 def enable_rec_mode_and_reboot(self):
987 """Switch to rec mode and reboot.
988
989 This method emulates the behavior of the old physical recovery switch,
990 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
991 recovery mode, i.e. just press Power + Esc + Refresh.
992 """
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800993 if self._customized_key_commands['rec_reboot']:
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800994 logging.info('running the customized rec reboot command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800995 os.system(self._customized_key_commands['rec_reboot'])
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +0800996 elif self.client_attr.chrome_ec:
Vic Yang81273092012-08-21 15:57:09 +0800997 # Cold reset to clear EC_IN_RW signal
Vic Yanga7250662012-08-31 04:00:08 +0800998 self.servo.set('cold_reset', 'on')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800999 time.sleep(self.delay.hold_cold_reset)
Vic Yanga7250662012-08-31 04:00:08 +08001000 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001001 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001002 self.ec.send_command("reboot ap-off")
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001003 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001004 self.ec.send_command("hostevent set 0x4000")
Vic Yang611dd852012-08-02 15:36:31 +08001005 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001006 else:
1007 self.servo.enable_recovery_mode()
1008 self.cold_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001009 time.sleep(self.delay.ec_reboot_cmd)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001010 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001011
1012
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001013 def enable_dev_mode_and_reboot(self):
1014 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001015 if self.client_attr.keyboard_dev:
1016 self.enable_keyboard_dev_mode()
1017 else:
1018 self.servo.enable_development_mode()
1019 self.faft_client.run_shell_command(
1020 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001021
1022
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001023 def enable_normal_mode_and_reboot(self):
1024 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001025 if self.client_attr.keyboard_dev:
1026 self.disable_keyboard_dev_mode()
1027 else:
1028 self.servo.disable_development_mode()
1029 self.faft_client.run_shell_command(
1030 'chromeos-firmwareupdate --mode tonormal && reboot')
1031
1032
1033 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
1034 """Wait for firmware screen and then switch into or out of dev mode.
1035
1036 Args:
1037 dev: True if switching into dev mode. Otherwise, False.
1038 """
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001039 time.sleep(self.delay.firmware_screen)
Vic Yange7553162012-06-20 16:20:47 +08001040 if dev:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001041 self.press_ctrl_d()
Vic Yange7553162012-06-20 16:20:47 +08001042 else:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001043 self.press_enter()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001044 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001045 self.press_enter()
Vic Yange7553162012-06-20 16:20:47 +08001046
1047
1048 def enable_keyboard_dev_mode(self):
1049 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +08001050 # Plug out USB disk for preventing recovery boot without warning
1051 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +08001052 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001053 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001054 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001055 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +08001056
1057
1058 def disable_keyboard_dev_mode(self):
1059 logging.info("Disabling keyboard controlled developer mode")
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001060 if not self.client_attr.chrome_ec:
Vic Yang611dd852012-08-02 15:36:31 +08001061 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001062 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001063 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001064 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001065
1066
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001067 def setup_dev_mode(self, dev_mode):
1068 """Setup for development mode.
1069
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001070 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001071 tries to do so.
1072
1073 Args:
1074 dev_mode: True if requested in dev mode; False if normal mode.
1075 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001076 # Change the default firmware_action for dev mode passing the fw screen.
1077 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001078 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
1079 else None),
1080 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001081 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +08001082 if (not self.client_attr.keyboard_dev and
1083 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001084 logging.info('Dev switch is not on. Now switch it on.')
1085 self.servo.enable_development_mode()
1086 if not self.crossystem_checker({'devsw_boot': '1',
1087 'mainfw_type': 'developer'}):
1088 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001089 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001090 'userspace_action': None if self.client_attr.keyboard_dev
1091 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001092 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001093 'reboot_action': self.enable_keyboard_dev_mode if
1094 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001095 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001096 else:
Vic Yange7553162012-06-20 16:20:47 +08001097 if (not self.client_attr.keyboard_dev and
1098 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001099 logging.info('Dev switch is not off. Now switch it off.')
1100 self.servo.disable_development_mode()
1101 if not self.crossystem_checker({'devsw_boot': '0',
1102 'mainfw_type': 'normal'}):
1103 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001104 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001105 'userspace_action': None if self.client_attr.keyboard_dev
1106 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001107 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001108 'reboot_action': self.disable_keyboard_dev_mode if
1109 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001110 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001111
1112
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001113 def setup_kernel(self, part):
1114 """Setup for kernel test.
1115
1116 It makes sure both kernel A and B bootable and the current boot is
1117 the requested kernel part.
1118
1119 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001120 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001121 """
1122 self.ensure_kernel_boot(part)
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +08001123 if self.faft_client.diff_kernel_a_b():
1124 self.copy_kernel_and_rootfs(from_part=part,
1125 to_part=self.OTHER_KERNEL_MAP[part])
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001126 self.reset_and_prioritize_kernel(part)
1127
1128
1129 def reset_and_prioritize_kernel(self, part):
1130 """Make the requested partition highest priority.
1131
1132 This function also reset kerenl A and B to bootable.
1133
1134 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001135 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001136 """
1137 root_dev = self.faft_client.get_root_dev()
1138 # Reset kernel A and B to bootable.
1139 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1140 (self.KERNEL_MAP['a'], root_dev))
1141 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1142 (self.KERNEL_MAP['b'], root_dev))
1143 # Set kernel part highest priority.
1144 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
1145 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001146 # Safer to sync and wait until the cgpt status written to the disk.
1147 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001148 time.sleep(self.delay.sync)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001149
1150
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001151 def warm_reboot(self):
1152 """Request a warm reboot.
1153
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001154 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001155 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001156 # Use cold reset if the warm reset is broken.
1157 if self.client_attr.broken_warm_reset:
Gediminas Ramanauskase021e152012-09-04 19:10:59 -07001158 logging.info('broken_warm_reset is True. Cold rebooting instead.')
1159 self.cold_reboot()
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001160 else:
1161 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001162
1163
1164 def cold_reboot(self):
1165 """Request a cold reboot.
1166
1167 A wrapper for underlying servo cold reset.
1168 """
Gediminas Ramanauskasc6025692012-10-23 14:33:40 -07001169 if self.client_attr.broken_warm_reset:
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001170 self.servo.set('pwr_button', 'press')
1171 self.servo.set('cold_reset', 'on')
1172 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001173 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001174 self.servo.set('pwr_button', 'release')
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +08001175 elif self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001176 # We don't use servo.cold_reset() here because software sync is
1177 # not yet finished, and device may or may not come up after cold
1178 # reset. Pressing power button before firmware comes up solves this.
1179 #
1180 # The correct behavior should be (not work now):
1181 # - If rebooting EC with rec mode on, power on AP and it boots
1182 # into recovery mode.
1183 # - If rebooting EC with rec mode off, power on AP for software
1184 # sync. Then AP checks if lid open or not. If lid open, continue;
1185 # otherwise, shut AP down and need servo for a power button
1186 # press.
1187 self.servo.set('cold_reset', 'on')
1188 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001189 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001190 self.servo.power_short_press()
1191 else:
1192 self.servo.cold_reset()
1193
1194
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001195 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001196 """Request the client sync and do a warm reboot.
1197
1198 This is the default reboot action on FAFT.
1199 """
1200 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001201 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001202 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001203
1204
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001205 def sync_and_cold_reboot(self):
1206 """Request the client sync and do a cold reboot.
1207
1208 This reboot action is used to reset EC for recovery mode.
1209 """
1210 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001211 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001212 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001213
1214
Vic Yangaeb10392012-08-28 09:25:09 +08001215 def sync_and_ec_reboot(self, args=''):
1216 """Request the client sync and do a EC triggered reboot.
1217
1218 Args:
1219 args: Arguments passed to "ectool reboot_ec". Including:
1220 RO: jump to EC RO firmware.
1221 RW: jump to EC RW firmware.
1222 cold: Cold/hard reboot.
1223 """
Vic Yang59cac9c2012-05-21 15:28:42 +08001224 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001225 time.sleep(self.delay.sync)
Vic Yangaeb10392012-08-28 09:25:09 +08001226 # Since EC reboot happens immediately, delay before actual reboot to
1227 # allow FAFT client returning.
1228 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec %s)&' %
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001229 (self.delay.ec_reboot_cmd, args))
1230 time.sleep(self.delay.ec_reboot_cmd)
Vic Yangf86728a2012-07-30 10:44:07 +08001231 self.check_lid_and_power_on()
1232
1233
Chun-ting Changa4f65532012-10-17 16:57:28 +08001234 def sync_and_reboot_with_factory_install_shim(self):
1235 """Request the client sync and do a warm reboot to recovery mode.
1236
1237 After reboot, the client will use factory install shim to reset TPM
1238 values. The client ignore TPM rollback, so here forces it to recovery
1239 mode.
1240 """
1241 is_dev = self.crossystem_checker({'devsw_boot': '1'})
1242 if not is_dev:
1243 self.enable_dev_mode_and_reboot()
1244 time.sleep(self.SYNC_DELAY)
1245 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001246 time.sleep(self.delay.install_shim_done)
Chun-ting Changa4f65532012-10-17 16:57:28 +08001247 self.warm_reboot()
1248
1249
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001250 def full_power_off_and_on(self):
1251 """Shutdown the device by pressing power button and power on again."""
1252 # Press power button to trigger Chrome OS normal shutdown process.
1253 self.servo.power_normal_press()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001254 time.sleep(self.delay.shutdown)
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001255 # Short press power button to boot DUT again.
1256 self.servo.power_short_press()
1257
1258
Vic Yangf86728a2012-07-30 10:44:07 +08001259 def check_lid_and_power_on(self):
1260 """
1261 On devices with EC software sync, system powers on after EC reboots if
1262 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1263 This method checks lid switch state and presses power button if
1264 necessary.
1265 """
1266 if self.servo.get("lid_open") == "no":
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001267 time.sleep(self.delay.software_sync)
Vic Yangf86728a2012-07-30 10:44:07 +08001268 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001269
1270
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001271 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1272 """Modify the kernel header magic in USB stick.
1273
1274 The kernel header magic is the first 8-byte of kernel partition.
1275 We modify it to make it fail on kernel verification check.
1276
1277 Args:
1278 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1279 from_magic: A string of magic which we change it from.
1280 to_magic: A string of magic which we change it to.
1281
1282 Raises:
1283 error.TestError: if failed to change magic.
1284 """
1285 assert len(from_magic) == 8
1286 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001287 # USB image only contains one kernel.
1288 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001289 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1290 current_magic = utils.system_output(read_cmd)
1291 if current_magic == to_magic:
1292 logging.info("The kernel magic is already %s." % current_magic)
1293 return
1294 if current_magic != from_magic:
1295 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1296
1297 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1298 (from_magic, to_magic))
1299 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1300 " 2>/dev/null" % (to_magic, kernel_part))
1301 utils.system(write_cmd)
1302
1303 if utils.system_output(read_cmd) != to_magic:
1304 raise error.TestError("Failed to write new magic.")
1305
1306
1307 def corrupt_usb_kernel(self, usb_dev):
1308 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1309
1310 Args:
1311 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1312 """
1313 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1314 self.CORRUPTED_MAGIC)
1315
1316
1317 def restore_usb_kernel(self, usb_dev):
1318 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1319
1320 Args:
1321 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1322 """
1323 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1324 self.CHROMEOS_MAGIC)
1325
1326
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001327 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001328 """Call the action function with/without arguments.
1329
1330 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001331 action_tuple: A function, or a tuple (function, args, error_msg),
1332 in which, args and error_msg are optional. args is
1333 either a value or a tuple if multiple arguments.
1334 check_status: Check the return value of action function. If not
1335 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001336
1337 Returns:
1338 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001339
1340 Raises:
1341 error.TestError: An error when the action function is not callable.
1342 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001343 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001344 action = action_tuple
1345 args = ()
1346 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001347 if isinstance(action_tuple, tuple):
1348 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001349 if len(action_tuple) >= 2:
1350 args = action_tuple[1]
1351 if not isinstance(args, tuple):
1352 args = (args,)
1353 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001354 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001355
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001356 if action is None:
1357 return
1358
1359 if not callable(action):
1360 raise error.TestError('action is not callable!')
1361
1362 info_msg = 'calling %s' % str(action)
1363 if args:
1364 info_msg += ' with args %s' % str(args)
1365 logging.info(info_msg)
1366 ret = action(*args)
1367
1368 if check_status and not ret:
1369 raise error.TestFail('%s: %s returning %s' %
1370 (error_msg, info_msg, str(ret)))
1371 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001372
1373
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001374 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1375 post_power_action=None):
1376 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1377
1378 Args:
1379 shutdown_action: a function which makes DUT shutdown, like pressing
1380 power key.
1381 pre_power_action: a function which is called before next power on.
1382 post_power_action: a function which is called after next power on.
1383
1384 Raises:
1385 error.TestFail: if the shutdown_action() failed to turn DUT off.
1386 """
1387 self._call_action(shutdown_action)
1388 logging.info('Wait to ensure DUT shut down...')
1389 try:
1390 self.wait_for_client()
1391 raise error.TestFail(
1392 'Should shut the device down after calling %s.' %
1393 str(shutdown_action))
1394 except AssertionError:
1395 logging.info(
1396 'DUT is surely shutdown. We are going to power it on again...')
1397
1398 if pre_power_action:
1399 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001400 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001401 if post_power_action:
1402 self._call_action(post_power_action)
1403
1404
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001405 def register_faft_template(self, template):
1406 """Register FAFT template, the default FAFT_STEP of each step.
1407
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001408 Any missing field falls back to the original faft_template.
1409
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001410 Args:
1411 template: A FAFT_STEP dict.
1412 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001413 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001414
1415
1416 def register_faft_sequence(self, sequence):
1417 """Register FAFT sequence.
1418
1419 Args:
1420 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1421 """
1422 self._faft_sequence = sequence
1423
1424
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001425 def run_faft_step(self, step, no_reboot=False):
1426 """Run a single FAFT step.
1427
1428 Any missing field falls back to faft_template. An empty step means
1429 running the default faft_template.
1430
1431 Args:
1432 step: A FAFT_STEP dict.
1433 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001434
1435 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001436 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001437 error.TestFail: Test failed in waiting DUT reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001438 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001439 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1440 'firmware_action', 'install_deps_after_boot')
1441
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001442 test = {}
1443 test.update(self._faft_template)
1444 test.update(step)
1445
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001446 for key in test:
1447 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001448 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001449
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001450 if test['state_checker']:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001451 self._call_action(test['state_checker'], check_status=True)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001452
1453 self._call_action(test['userspace_action'])
1454
1455 # Don't run reboot_action and firmware_action if no_reboot is True.
1456 if not no_reboot:
1457 self._call_action(test['reboot_action'])
1458 self.wait_for_client_offline()
1459 self._call_action(test['firmware_action'])
1460
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001461 try:
1462 if 'install_deps_after_boot' in test:
1463 self.wait_for_client(
1464 install_deps=test['install_deps_after_boot'])
1465 else:
1466 self.wait_for_client()
1467 except AssertionError:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001468 logging.info('wait_for_client() timed out.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001469 self.reset_client()
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001470 if self._trapped_in_recovery_reason:
1471 raise error.TestFail('Trapped in the recovery reason: %d' %
1472 self._trapped_in_recovery_reason)
1473 else:
1474 raise error.TestFail('Timed out waiting for DUT reboot.')
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001475
1476
1477 def run_faft_sequence(self):
1478 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001479 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001480 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001481 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001482 logging.info('======== Running FAFT sequence step %d ========' %
1483 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001484 # Don't reboot in the last step.
1485 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001486 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001487
1488
ctchang38ae4922012-09-03 17:01:16 +08001489 def get_current_firmware_sha(self):
1490 """Get current firmware sha of body and vblock.
1491
1492 Returns:
1493 Current firmware sha follows the order (
1494 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1495 """
1496 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1497 self.faft_client.get_firmware_sha('a'),
1498 self.faft_client.get_firmware_sig_sha('b'),
1499 self.faft_client.get_firmware_sha('b'))
1500 return current_firmware_sha
1501
1502
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001503 def is_firmware_changed(self):
1504 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001505
1506 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001507 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001508 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001509 # Device may not be rebooted after test.
1510 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001511
1512 current_sha = self.get_current_firmware_sha()
1513
1514 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001515 return False
ctchang38ae4922012-09-03 17:01:16 +08001516 else:
ctchang38ae4922012-09-03 17:01:16 +08001517 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1518 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1519 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1520 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001521 logging.info("Firmware changed:")
1522 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1523 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1524 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1525 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1526 return True
ctchang38ae4922012-09-03 17:01:16 +08001527
1528
1529 def backup_firmware(self, suffix='.original'):
1530 """Backup firmware to file, and then send it to host.
1531
1532 Args:
1533 suffix: a string appended to backup file name
1534 """
1535 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001536 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1537 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1538 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001539
1540 self._backup_firmware_sha = self.get_current_firmware_sha()
1541 logging.info('Backup firmware stored in %s with suffix %s' % (
1542 self.resultsdir, suffix))
1543
1544
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001545 def is_firmware_saved(self):
1546 """Check if a firmware saved (called backup_firmware before).
1547
1548 Returns:
1549 True if the firmware is backuped; otherwise False.
1550 """
1551 return self._backup_firmware_sha != ()
1552
1553
ctchang38ae4922012-09-03 17:01:16 +08001554 def restore_firmware(self, suffix='.original'):
1555 """Restore firmware from host in resultsdir.
1556
1557 Args:
1558 suffix: a string appended to backup file name
1559 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001560 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001561 return
1562
1563 # Backup current corrupted firmware.
1564 self.backup_firmware(suffix='.corrupt')
1565
1566 # Restore firmware.
1567 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001568 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1569 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001570
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001571 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001572 self.sync_and_warm_reboot()
1573 self.wait_for_client_offline()
1574 self.wait_for_client()
1575
ctchang38ae4922012-09-03 17:01:16 +08001576 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001577
1578
1579 def setup_firmwareupdate_shellball(self, shellball=None):
1580 """Deside a shellball to use in firmware update test.
1581
1582 Check if there is a given shellball, and it is a shell script. Then,
1583 send it to the remote host. Otherwise, use
1584 /usr/sbin/chromeos-firmwareupdate.
1585
1586 Args:
1587 shellball: path of a shellball or default to None.
1588
1589 Returns:
1590 Path of shellball in remote host.
1591 If use default shellball, reutrn None.
1592 """
1593 updater_path = None
1594 if shellball:
1595 # Determine the firmware file is a shellball or a raw binary.
1596 is_shellball = (utils.system_output("file %s" % shellball).find(
1597 "shell script") != -1)
1598 if is_shellball:
1599 logging.info('Device will update firmware with shellball %s'
1600 % shellball)
1601 temp_dir = self.faft_client.create_temp_dir('shellball_')
1602 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1603 self._client.send_file(shellball, temp_shellball)
1604 updater_path = temp_shellball
1605 else:
1606 raise error.TestFail(
1607 'The given shellball is not a shell script.')
1608 return updater_path