blob: 83509985e92ebfb63830a3bc88ede44406e3c550 [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 Tam80419a82012-10-30 09:10:00 +080074 including Ctrl-D, Ctrl-U, and Enter.
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,
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800103 }
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800104 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800105 _firmware_update = False
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800106 _trapped_in_recovery_reason = 0
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800107
ctchang38ae4922012-09-03 17:01:16 +0800108 _backup_firmware_sha = ()
109
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800110 # Class level variable, keep track the states of one time setup.
111 # This variable is preserved across tests which inherit this class.
112 _global_setup_done = {
113 'gbb_flags': False,
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800114 'reimage': False,
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800115 'usb_check': False,
116 }
Vic Yang54f70572012-10-19 17:05:26 +0800117
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800118 @classmethod
119 def check_setup_done(cls, label):
120 """Check if the given setup is done.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800121
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800122 Args:
123 label: The label of the setup.
124 """
125 return cls._global_setup_done[label]
126
127
128 @classmethod
129 def mark_setup_done(cls, label):
130 """Mark the given setup done.
131
132 Args:
133 label: The label of the setup.
134 """
135 cls._global_setup_done[label] = True
136
137
138 @classmethod
139 def unmark_setup_done(cls, label):
140 """Mark the given setup not done.
141
142 Args:
143 label: The label of the setup.
144 """
145 cls._global_setup_done[label] = False
Vic Yang54f70572012-10-19 17:05:26 +0800146
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800147
148 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
149 # Parse arguments from command line
150 args = {}
151 for arg in cmdline_args:
152 match = re.search("^(\w+)=(.+)", arg)
153 if match:
154 args[match.group(1)] = match.group(2)
155
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800156 # Keep the arguments which will be used later.
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800157 for key in self._customized_key_commands:
158 key_cmd = key + '_cmd'
159 if key_cmd in args:
160 self._customized_key_commands[key] = args[key_cmd]
161 logging.info('Customized %s key command: %s' %
162 (key, args[key_cmd]))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800163 if 'image' in args:
164 self._install_image_path = args['image']
165 logging.info('Install Chrome OS test image path: %s' %
166 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800167 if 'firmware_update' in args and args['firmware_update'].lower() \
168 not in ('0', 'false', 'no'):
169 if self._install_image_path:
170 self._firmware_update = True
171 logging.info('Also update firmware after installing.')
172 else:
173 logging.warning('Firmware update will not not performed '
174 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800175
176 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
177 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800178 if use_faft:
179 self.client_attr = FAFTClientAttribute(
180 self.faft_client.get_platform_name())
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800181 self.delay = FAFTDelayConstants(
182 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800183
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800184 if self.client_attr.chrome_ec:
185 self.ec = ChromeEC(self.servo)
186
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700187 # Setting up key matrix mapping
188 self.servo.set_key_matrix(self.client_attr.key_matrix_layout)
189
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800190
191 def setup(self):
192 """Autotest setup function."""
193 super(FAFTSequence, self).setup()
194 if not self._remote_infos['faft']['used']:
195 raise error.TestError('The use_faft flag should be enabled.')
196 self.register_faft_template({
197 'state_checker': (None),
198 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800199 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800200 'firmware_action': (None)
201 })
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800202 self.install_test_image(self._install_image_path, self._firmware_update)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800203 self.setup_gbb_flags()
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800204
205
206 def cleanup(self):
207 """Autotest cleanup function."""
208 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800209 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800210 super(FAFTSequence, self).cleanup()
211
212
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800213 def invalidate_firmware_setup(self):
214 """Invalidate all firmware related setup state.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800215
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800216 This method is called when the firmware is re-flashed. It resets all
217 firmware related setup states so that the next test setup properly
218 again.
Vic Yangdbaba8f2012-10-17 16:05:35 +0800219 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800220 self.unmark_setup_done('gbb_flags')
Vic Yangdbaba8f2012-10-17 16:05:35 +0800221
222
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800223 def reset_client(self):
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800224 """Reset client, if necessary.
225
226 This method is called when the client is not responsive. It may be
227 caused by the following cases:
228 - network flaky (can be recovered by replugging the Ethernet);
229 - halt on a firmware screen without timeout, e.g. REC_INSERT screen;
230 - corrupted firmware;
231 - corrutped OS image.
232 """
233 # DUT works fine, done.
234 if self._ping_test(self._client.ip, timeout=5):
235 return
236
237 # TODO(waihong@chromium.org): Implement replugging the Ethernet in the
238 # first reset item.
239
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +0800240 # DUT may be trapped in the recovery screen. Try to boot into USB to
241 # retrieve the recovery reason.
242 logging.info('Try to retrieve recovery reason...')
243 if self.servo.get('usb_mux_sel1') == 'dut_sees_usbkey':
244 self.wait_fw_screen_and_plug_usb()
245 else:
246 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
247
248 try:
249 self.wait_for_client(install_deps=True)
250 lines = self.faft_client.run_shell_command_get_output(
251 'crossystem recovery_reason')
252 self._trapped_in_recovery_reason = int(lines[0])
253 logging.info('Got the recovery reason %d.' %
254 self._trapped_in_recovery_reason)
255 except AssertionError:
256 logging.info('Failed to get the recovery reason.')
257
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800258 # DUT may halt on a firmware screen. Try cold reboot.
259 logging.info('Try cold reboot...')
260 self.cold_reboot()
261 try:
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800262 self.wait_for_client()
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +0800263 return
264 except AssertionError:
265 pass
266
267 # DUT may be broken by a corrupted firmware. Restore firmware.
268 # We assume the recovery boot still works fine. Since the recovery
269 # code is in RO region and all FAFT tests don't change the RO region
270 # except GBB.
271 if self.is_firmware_saved():
272 self.ensure_client_in_recovery()
273 logging.info('Try restore the original firmware...')
274 if self.is_firmware_changed():
275 try:
276 self.restore_firmware()
277 return
278 except AssertionError:
279 logging.info('Restoring firmware doesn\'t help.')
280
281 # DUT may be broken by a corrupted OS image. Restore OS image.
282 self.ensure_client_in_recovery()
283 logging.info('Try restore the OS image...')
284 self.faft_client.run_shell_command('chromeos-install --yes')
285 self.sync_and_warm_reboot()
286 self.wait_for_client_offline()
287 try:
288 self.wait_for_client(install_deps=True)
289 logging.info('Successfully restore OS image.')
290 return
291 except AssertionError:
292 logging.info('Restoring OS image doesn\'t help.')
293
294
295 def ensure_client_in_recovery(self):
296 """Ensure client in recovery boot; reboot into it if necessary.
297
298 Raises:
299 error.TestError: if failed to boot the USB image.
300 """
301 # DUT works fine and is already in recovery boot, done.
302 if self._ping_test(self._client.ip, timeout=5):
303 if self.crossystem_checker({'mainfw_type': 'recovery'}):
304 return
305
306 logging.info('Try boot into USB image...')
307 self.servo.enable_usb_hub(host=True)
308 self.enable_rec_mode_and_reboot()
309 self.wait_fw_screen_and_plug_usb()
310 try:
311 self.wait_for_client(install_deps=True)
312 except AssertionError:
313 raise error.TestError('Failed to boot the USB image.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +0800314
315
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800316 def assert_test_image_in_path(self, image_path):
317 """Assert the image of image_path be a Chrome OS test image.
318
319 Args:
320 image_path: A path on the host to the test image.
321
322 Raises:
323 error.TestError: if the image is not a test image.
324 """
325 try:
326 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
327 image_path)
328 logging.info('Build of image: %s %s' % (build_ver, build_hash))
329 except ChromeOSTestError:
330 raise error.TestError(
331 'An USB disk containning a test image should be plugged '
332 'in the servo board.')
333
334
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800335 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800336 """Assert an USB disk plugged-in on servo and a test image inside.
337
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800338 Args:
339 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
340 If None, it is detected automatically.
341
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800342 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800343 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800344 """
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800345 if self.check_setup_done('usb_check'):
Vic Yang54f70572012-10-19 17:05:26 +0800346 return
347
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800348 # TODO(waihong@chromium.org): We skip the check when servod runs in
349 # a different host since no easy way to access the servo host so far.
350 # Should find a way to work-around it.
351 if not self.servo.is_localhost():
352 logging.info('Skip checking Chrome OS test image in USB as servod '
353 'runs in a different host.')
354 return
355
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800356 if usb_dev:
357 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
358 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700359 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800360 usb_dev = self.servo.probe_host_usb_dev()
361 if not usb_dev:
362 raise error.TestError(
363 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800364 self.assert_test_image_in_path(usb_dev)
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800365 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800366
367
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800368 def get_server_address(self):
369 """Get the server address seen from the client.
370
371 Returns:
372 A string of the server address.
373 """
374 r = self.faft_client.run_shell_command_get_output("echo $SSH_CLIENT")
375 return r[0].split()[0]
376
377
Simran Basi741b5d42012-05-18 11:27:15 -0700378 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800379 """Install the test image specied by the path onto the USB and DUT disk.
380
381 The method first copies the image to USB disk and reboots into it via
Mike Truty49153d82012-08-21 22:27:30 -0500382 recovery mode. Then runs 'chromeos-install' (and possible
383 chromeos-firmwareupdate') to install it to DUT disk.
384
385 Sample command line:
386
387 run_remote_tests.sh --servo --board=daisy --remote=w.x.y.z \
388 --args="image=/tmp/chromiumos_test_image.bin firmware_update=True" \
389 server/site_tests/firmware_XXXX/control
390
391 This test requires an automated recovery to occur while simulating
392 inserting and removing the usb key from the servo. To allow this the
393 following hardware setup is required:
394 1. servo2 board connected via servoflex.
395 2. USB key inserted in the servo2.
396 3. servo2 connected to the dut via dut_hub_in in the usb 2.0 slot.
397 4. network connected via usb dongle in the dut in usb 3.0 slot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800398
399 Args:
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800400 image_path: An URL or a path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800401 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800402
403 Raises:
404 error.TestError: If devserver failed to start.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800405 """
Tom Wai-Hong Tam19ad9682012-10-24 09:33:42 +0800406 if not image_path:
407 return
408
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800409 if self.check_setup_done('reimage'):
410 return
411
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800412 if image_path.startswith(self._HTTP_PREFIX):
413 # TODO(waihong@chromium.org): Add the check of the URL to ensure
414 # it is a test image.
415 devserver = None
416 image_url = image_path
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800417 elif self.servo.is_localhost():
418 self.assert_test_image_in_path(image_path)
419 # If servod is localhost, i.e. both servod and FAFT see the same
420 # file system, do nothing.
421 devserver = None
422 image_url = image_path
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800423 else:
Tom Wai-Hong Tam08885ae2012-10-19 17:16:45 +0800424 self.assert_test_image_in_path(image_path)
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800425 image_dir, image_base = os.path.split(image_path)
426 logging.info('Starting devserver to serve the image...')
427 # The following stdout and stderr arguments should not be None,
428 # even we don't use them. Otherwise, the socket of devserve is
429 # created as fd 1 (as no stdout) but it still thinks stdout is fd
430 # 1 and dump the log to the socket. Wrong HTTP protocol happens.
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800431 devserver = subprocess.Popen(['/usr/lib/devserver/devserver.py',
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800432 '--archive_dir=%s' % image_dir,
433 '--port=%s' % self._DEVSERVER_PORT],
434 stdout=subprocess.PIPE,
435 stderr=subprocess.PIPE)
436 image_url = '%s%s:%s/static/archive/%s' % (
437 self._HTTP_PREFIX,
438 self.get_server_address(),
439 self._DEVSERVER_PORT,
440 image_base)
441
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800442 # Wait devserver startup completely
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800443 time.sleep(self.delay.devserver)
Tom Wai-Hong Tam71818d82012-10-24 14:57:43 +0800444 # devserver is a service running forever. If it is terminated,
445 # some error does happen.
446 if devserver.poll():
447 raise error.TestError('Starting devserver failed, '
448 'returning %d.' % devserver.returncode)
449
Tom Wai-Hong Tame796de42012-10-16 19:42:20 +0800450 logging.info('Ask Servo to install the image from %s' % image_url)
451 self.servo.image_to_servo_usb(image_url)
452
453 if devserver and devserver.poll() is None:
454 logging.info('Shutting down devserver...')
455 devserver.terminate()
Mike Truty49153d82012-08-21 22:27:30 -0500456
457 # DUT is powered off while imaging servo USB.
458 # Now turn it on.
459 self.servo.power_short_press()
460 self.wait_for_client()
461 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
462
463 install_cmd = 'chromeos-install --yes'
464 if firmware_update:
465 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
466
467 self.register_faft_sequence((
468 { # Step 1, request recovery boot
469 'state_checker': (self.crossystem_checker, {
470 'mainfw_type': ('developer', 'normal'),
471 }),
472 'userspace_action': self.faft_client.request_recovery_boot,
473 'firmware_action': self.wait_fw_screen_and_plug_usb,
474 'install_deps_after_boot': True,
475 },
476 { # Step 2, expected recovery boot
477 'state_checker': (self.crossystem_checker, {
478 'mainfw_type': 'recovery',
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800479 'recovery_reason' : vboot.RECOVERY_REASON['US_TEST'],
Mike Truty49153d82012-08-21 22:27:30 -0500480 }),
481 'userspace_action': (self.faft_client.run_shell_command,
482 install_cmd),
483 'reboot_action': self.cold_reboot,
484 'install_deps_after_boot': True,
485 },
486 { # Step 3, expected normal or developer boot (not recovery)
487 'state_checker': (self.crossystem_checker, {
488 'mainfw_type': ('developer', 'normal')
489 }),
490 },
491 ))
492 self.run_faft_sequence()
493 # 'Unplug' any USB keys in the servo from the dut.
Tom Wai-Hong Tam953c7742012-10-16 21:09:31 +0800494 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam6668b762012-10-23 11:45:36 +0800495 # Mark usb_check done so it won't check a test image in USB anymore.
496 self.mark_setup_done('usb_check')
Tom Wai-Hong Tam73229372012-10-23 11:58:16 +0800497 self.mark_setup_done('reimage')
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800498
499
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800500 def clear_set_gbb_flags(self, clear_mask, set_mask):
501 """Clear and set the GBB flags in the current flashrom.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800502
503 Args:
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800504 clear_mask: A mask of flags to be cleared.
505 set_mask: A mask of flags to be set.
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800506 """
507 gbb_flags = self.faft_client.get_gbb_flags()
Tom Wai-Hong Tamfa3142e2012-08-16 11:53:58 +0800508 new_flags = gbb_flags & ctypes.c_uint32(~clear_mask).value | set_mask
509
510 if (gbb_flags != new_flags):
511 logging.info('Change the GBB flags from 0x%x to 0x%x.' %
512 (gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800513 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800514 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800515 self.faft_client.reload_firmware()
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800516 # If changing FORCE_DEV_SWITCH_ON flag, reboot to get a clear state
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800517 if ((gbb_flags ^ new_flags) & vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON):
Tom Wai-Hong Tama2481922012-08-08 17:24:42 +0800518 self.run_faft_step({
519 'firmware_action': self.wait_fw_screen_and_ctrl_d,
520 })
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800521
522
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800523 def check_ec_capability(self, required_cap=[], suppress_warning=False):
Vic Yang4d72cb62012-07-24 11:51:09 +0800524 """Check if current platform has required EC capabilities.
525
526 Args:
527 required_cap: A list containing required EC capabilities. Pass in
528 None to only check for presence of Chrome EC.
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800529 suppress_warning: True to suppress any warning messages.
Vic Yang4d72cb62012-07-24 11:51:09 +0800530
531 Returns:
532 True if requirements are met. Otherwise, False.
533 """
534 if not self.client_attr.chrome_ec:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800535 if not suppress_warning:
536 logging.warn('Requires Chrome EC to run this test.')
Vic Yang4d72cb62012-07-24 11:51:09 +0800537 return False
538
539 for cap in required_cap:
540 if cap not in self.client_attr.ec_capability:
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800541 if not suppress_warning:
542 logging.warn('Requires EC capability "%s" to run this '
543 'test.' % cap)
Vic Yang4d72cb62012-07-24 11:51:09 +0800544 return False
545
546 return True
547
548
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800549 def _parse_crossystem_output(self, lines):
550 """Parse the crossystem output into a dict.
551
552 Args:
553 lines: The list of crossystem output strings.
554
555 Returns:
556 A dict which contains the crossystem keys/values.
557
558 Raises:
559 error.TestError: If wrong format in crossystem output.
560
561 >>> seq = FAFTSequence()
562 >>> seq._parse_crossystem_output([ \
563 "arch = x86 # Platform architecture", \
564 "cros_debug = 1 # OS should allow debug", \
565 ])
566 {'cros_debug': '1', 'arch': 'x86'}
567 >>> seq._parse_crossystem_output([ \
568 "arch=x86", \
569 ])
570 Traceback (most recent call last):
571 ...
572 TestError: Failed to parse crossystem output: arch=x86
573 >>> seq._parse_crossystem_output([ \
574 "arch = x86 # Platform architecture", \
575 "arch = arm # Platform architecture", \
576 ])
577 Traceback (most recent call last):
578 ...
579 TestError: Duplicated crossystem key: arch
580 """
581 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
582 parsed_list = {}
583 for line in lines:
584 matched = re.match(pattern, line.strip())
585 if not matched:
586 raise error.TestError("Failed to parse crossystem output: %s"
587 % line)
588 (name, value) = (matched.group(1), matched.group(2))
589 if name in parsed_list:
590 raise error.TestError("Duplicated crossystem key: %s" % name)
591 parsed_list[name] = value
592 return parsed_list
593
594
595 def crossystem_checker(self, expected_dict):
596 """Check the crossystem values matched.
597
598 Given an expect_dict which describes the expected crossystem values,
599 this function check the current crossystem values are matched or not.
600
601 Args:
602 expected_dict: A dict which contains the expected values.
603
604 Returns:
605 True if the crossystem value matched; otherwise, False.
606 """
607 lines = self.faft_client.run_shell_command_get_output('crossystem')
608 got_dict = self._parse_crossystem_output(lines)
609 for key in expected_dict:
610 if key not in got_dict:
611 logging.info('Expected key "%s" not in crossystem result' % key)
612 return False
613 if isinstance(expected_dict[key], str):
614 if got_dict[key] != expected_dict[key]:
615 logging.info("Expected '%s' value '%s' but got '%s'" %
616 (key, expected_dict[key], got_dict[key]))
617 return False
618 elif isinstance(expected_dict[key], tuple):
619 # Expected value is a tuple of possible actual values.
620 if got_dict[key] not in expected_dict[key]:
621 logging.info("Expected '%s' values %s but got '%s'" %
622 (key, str(expected_dict[key]), got_dict[key]))
623 return False
624 else:
625 logging.info("The expected_dict is neither a str nor a dict.")
626 return False
627 return True
628
629
Tom Wai-Hong Tam39b93b92012-09-04 16:56:05 +0800630 def vdat_flags_checker(self, mask, value):
631 """Check the flags from VbSharedData matched.
632
633 This function checks the masked flags from VbSharedData using crossystem
634 are matched the given value.
635
636 Args:
637 mask: A bitmask of flags to be matched.
638 value: An expected value.
639
640 Returns:
641 True if the flags matched; otherwise, False.
642 """
643 lines = self.faft_client.run_shell_command_get_output(
644 'crossystem vdat_flags')
645 vdat_flags = int(lines[0], 16)
646 if vdat_flags & mask != value:
647 logging.info("Expected vdat_flags 0x%x mask 0x%x but got 0x%x" %
648 (value, mask, vdat_flags))
649 return False
650 return True
651
652
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800653 def ro_normal_checker(self, expected_fw=None, twostop=False):
654 """Check the current boot uses RO boot.
655
656 Args:
657 expected_fw: A string of expected firmware, 'A', 'B', or
658 None if don't care.
659 twostop: True to expect a TwoStop boot; False to expect a RO boot.
660
661 Returns:
662 True if the currect boot firmware matched and used RO boot;
663 otherwise, False.
664 """
665 crossystem_dict = {'tried_fwb': '0'}
666 if expected_fw:
667 crossystem_dict['mainfw_act'] = expected_fw.upper()
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +0800668 if self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800669 crossystem_dict['ecfw_act'] = ('RW' if twostop else 'RO')
670
671 return (self.vdat_flags_checker(
Tom Wai-Hong Tam6ec46e32012-10-05 16:39:21 +0800672 vboot.VDAT_FLAG_LF_USE_RO_NORMAL,
673 0 if twostop else vboot.VDAT_FLAG_LF_USE_RO_NORMAL) and
Tom Wai-Hong Tam3e82e362012-09-05 10:17:55 +0800674 self.crossystem_checker(crossystem_dict))
675
676
Tom Wai-Hong Tam0a7b2be2012-10-15 16:44:12 +0800677 def dev_boot_usb_checker(self, dev_boot_usb=True):
678 """Check the current boot is from a developer USB (Ctrl-U trigger).
679
680 Args:
681 dev_boot_usb: True to expect an USB boot;
682 False to expect an internal device boot.
683
684 Returns:
685 True if the currect boot device matched; otherwise, False.
686 """
687 return (self.crossystem_checker({'mainfw_type': 'developer'})
688 and self.faft_client.is_removable_device_boot() == dev_boot_usb)
689
690
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800691 def root_part_checker(self, expected_part):
692 """Check the partition number of the root device matched.
693
694 Args:
695 expected_part: A string containing the number of the expected root
696 partition.
697
698 Returns:
699 True if the currect root partition number matched; otherwise, False.
700 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800701 part = self.faft_client.get_root_part()[-1]
702 if self.ROOTFS_MAP[expected_part] != part:
703 logging.info("Expected root part %s but got %s" %
704 (self.ROOTFS_MAP[expected_part], part))
705 return False
706 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800707
708
Vic Yang59cac9c2012-05-21 15:28:42 +0800709 def ec_act_copy_checker(self, expected_copy):
710 """Check the EC running firmware copy matches.
711
712 Args:
713 expected_copy: A string containing 'RO', 'A', or 'B' indicating
714 the expected copy of EC running firmware.
715
716 Returns:
717 True if the current EC running copy matches; otherwise, False.
718 """
719 lines = self.faft_client.run_shell_command_get_output('ectool version')
720 pattern = re.compile("Firmware copy: (.*)")
721 for line in lines:
722 matched = pattern.match(line)
723 if matched and matched.group(1) == expected_copy:
724 return True
725 return False
726
727
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800728 def check_root_part_on_non_recovery(self, part):
729 """Check the partition number of root device and on normal/dev boot.
730
731 Returns:
732 True if the root device matched and on normal/dev boot;
733 otherwise, False.
734 """
735 return self.root_part_checker(part) and \
736 self.crossystem_checker({
737 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800738 })
739
740
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800741 def _join_part(self, dev, part):
742 """Return a concatenated string of device and partition number.
743
744 Args:
745 dev: A string of device, e.g.'/dev/sda'.
746 part: A string of partition number, e.g.'3'.
747
748 Returns:
749 A concatenated string of device and partition number, e.g.'/dev/sda3'.
750
751 >>> seq = FAFTSequence()
752 >>> seq._join_part('/dev/sda', '3')
753 '/dev/sda3'
754 >>> seq._join_part('/dev/mmcblk0', '2')
755 '/dev/mmcblk0p2'
756 """
757 if 'mmcblk' in dev:
758 return dev + 'p' + part
759 else:
760 return dev + part
761
762
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800763 def copy_kernel_and_rootfs(self, from_part, to_part):
764 """Copy kernel and rootfs from from_part to to_part.
765
766 Args:
767 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800768 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800769 """
770 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800771 logging.info('Copying kernel from %s to %s. Please wait...' %
772 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800773 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800774 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
775 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
776 logging.info('Copying rootfs from %s to %s. Please wait...' %
777 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800778 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800779 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
780 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800781
782
783 def ensure_kernel_boot(self, part):
784 """Ensure the request kernel boot.
785
786 If not, it duplicates the current kernel to the requested kernel
787 and sets the requested higher priority to ensure it boot.
788
789 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800790 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800791 """
792 if not self.root_part_checker(part):
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +0800793 if self.faft_client.diff_kernel_a_b():
794 self.copy_kernel_and_rootfs(
795 from_part=self.OTHER_KERNEL_MAP[part],
796 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800797 self.run_faft_step({
798 'userspace_action': (self.reset_and_prioritize_kernel, part),
799 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800800
801
Vic Yang416f2032012-08-28 10:18:03 +0800802 def set_hardware_write_protect(self, enabled):
Vic Yang2cabf812012-08-28 02:39:04 +0800803 """Set hardware write protect pin.
804
805 Args:
806 enable: True if asserting write protect pin. Otherwise, False.
807 """
808 self.servo.set('fw_wp_vref', self.client_attr.wp_voltage)
809 self.servo.set('fw_wp_en', 'on')
Vic Yang416f2032012-08-28 10:18:03 +0800810 self.servo.set('fw_wp', 'on' if enabled else 'off')
811
812
813 def set_EC_write_protect_and_reboot(self, enabled):
814 """Set EC write protect status and reboot to take effect.
815
816 EC write protect is only activated if both hardware write protect pin
817 is asserted and software write protect flag is set. Also, a reboot is
818 required for write protect to take effect.
819
820 Since the software write protect flag cannot be unset if hardware write
821 protect pin is asserted, we need to deasserted the pin first if we are
822 deactivating write protect. Similarly, a reboot is required before we
823 can modify the software flag.
824
825 This method asserts/deasserts hardware write protect pin first, and
826 set corresponding EC software write protect flag.
827
828 Args:
829 enable: True if activating EC write protect. Otherwise, False.
830 """
831 self.set_hardware_write_protect(enabled)
832 if enabled:
833 # Set write protect flag and reboot to take effect.
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800834 self.ec.send_command("flashwp enable")
Vic Yang416f2032012-08-28 10:18:03 +0800835 self.sync_and_ec_reboot()
836 else:
837 # Reboot after deasserting hardware write protect pin to deactivate
838 # write protect. And then remove software write protect flag.
839 self.sync_and_ec_reboot()
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +0800840 self.ec.send_command("flashwp disable")
Vic Yang2cabf812012-08-28 02:39:04 +0800841
842
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800843 def press_ctrl_d(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800844 """Send Ctrl-D key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800845 if self._customized_key_commands['ctrl_d']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800846 logging.info('running the customized Ctrl-D key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800847 os.system(self._customized_key_commands['ctrl_d'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800848 else:
849 self.servo.ctrl_d()
850
851
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800852 def press_ctrl_u(self):
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800853 """Send Ctrl-U key to DUT.
854
855 Raises:
856 error.TestError: if a non-Chrome EC device or no Ctrl-U command given
857 on a no-build-in-keyboard device.
858 """
859 if self._customized_key_commands['ctrl_u']:
860 logging.info('running the customized Ctrl-U key command')
861 os.system(self._customized_key_commands['ctrl_u'])
862 elif self.check_ec_capability(['keyboard'], suppress_warning=True):
863 self.ec.key_down('<ctrl_l>')
864 self.ec.key_down('u')
865 self.ec.key_up('u')
866 self.ec.key_up('<ctrl_l>')
867 elif self.client_attr.has_keyboard:
868 raise error.TestError(
869 "Can't send Ctrl-U to DUT without using Chrome EC.")
870 else:
871 raise error.TestError(
872 "Should specify the ctrl_u_cmd argument.")
873
874
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800875 def press_enter(self):
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800876 """Send Enter key to DUT."""
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800877 if self._customized_key_commands['enter']:
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800878 logging.info('running the customized Enter key command')
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800879 os.system(self._customized_key_commands['enter'])
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800880 else:
881 self.servo.enter_key()
882
883
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800884 def wait_fw_screen_and_ctrl_d(self):
885 """Wait for firmware warning screen and press Ctrl-D."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800886 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800887 self.press_ctrl_d()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800888
889
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800890 def wait_fw_screen_and_ctrl_u(self):
891 """Wait for firmware warning screen and press Ctrl-U."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800892 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800893 self.press_ctrl_u()
Tom Wai-Hong Tamadbec3e2012-10-15 14:20:15 +0800894
895
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800896 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
897 """Wait for firmware warning screen and trigger recovery boot."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800898 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800899 self.press_enter()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800900
901 # For Alex/ZGB, there is a dev warning screen in text mode.
902 # Skip it by pressing Ctrl-D.
903 if need_dev_transition:
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800904 time.sleep(self.delay.legacy_text_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +0800905 self.press_ctrl_d()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800906
907
Mike Truty49153d82012-08-21 22:27:30 -0500908 def wait_fw_screen_and_unplug_usb(self):
909 """Wait for firmware warning screen and then unplug the servo USB."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800910 time.sleep(self.delay.load_usb)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800911 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800912 time.sleep(self.delay.between_usb_plug)
Mike Truty49153d82012-08-21 22:27:30 -0500913
914
915 def wait_fw_screen_and_plug_usb(self):
916 """Wait for firmware warning screen and then unplug and plug the USB."""
917 self.wait_fw_screen_and_unplug_usb()
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800918 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
919
920
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800921 def wait_fw_screen_and_press_power(self):
922 """Wait for firmware warning screen and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800923 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam7317c042012-08-14 11:59:06 +0800924 # While the firmware screen, the power button probing loop sleeps
925 # 0.25 second on every scan. Use the normal delay (1.2 second) for
926 # power press.
927 self.servo.power_normal_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800928
929
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800930 def wait_longer_fw_screen_and_press_power(self):
931 """Wait for firmware screen without timeout and press power button."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800932 time.sleep(self.delay.dev_screen_timeout)
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800933 self.wait_fw_screen_and_press_power()
934
935
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800936 def wait_fw_screen_and_close_lid(self):
937 """Wait for firmware warning screen and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800938 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800939 self.servo.lid_close()
940
941
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800942 def wait_longer_fw_screen_and_close_lid(self):
943 """Wait for firmware screen without timeout and close lid."""
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800944 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800945 self.wait_fw_screen_and_close_lid()
946
947
Tom Wai-Hong Tam01d5e572012-10-23 10:07:11 +0800948 def setup_gbb_flags(self):
949 """Setup the GBB flags for FAFT test."""
950 if self.check_setup_done('gbb_flags'):
951 return
952
953 logging.info('Set proper GBB flags for test.')
954 self.clear_set_gbb_flags(vboot.GBB_FLAG_DEV_SCREEN_SHORT_DELAY |
955 vboot.GBB_FLAG_FORCE_DEV_SWITCH_ON |
956 vboot.GBB_FLAG_FORCE_DEV_BOOT_USB |
957 vboot.GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK,
958 vboot.GBB_FLAG_ENTER_TRIGGERS_TONORM)
959 self.mark_setup_done('gbb_flags')
960
961
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800962 def setup_tried_fwb(self, tried_fwb):
963 """Setup for fw B tried state.
964
965 It makes sure the system in the requested fw B tried state. If not, it
966 tries to do so.
967
968 Args:
969 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
970 """
971 if tried_fwb:
972 if not self.crossystem_checker({'tried_fwb': '1'}):
973 logging.info(
974 'Firmware is not booted with tried_fwb. Reboot into it.')
975 self.run_faft_step({
976 'userspace_action': self.faft_client.set_try_fw_b,
977 })
978 else:
979 if not self.crossystem_checker({'tried_fwb': '0'}):
980 logging.info(
981 'Firmware is booted with tried_fwb. Reboot to clear.')
982 self.run_faft_step({})
983
984
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800985 def enable_rec_mode_and_reboot(self):
986 """Switch to rec mode and reboot.
987
988 This method emulates the behavior of the old physical recovery switch,
989 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
990 recovery mode, i.e. just press Power + Esc + Refresh.
991 """
Tom Wai-Hong Tam80419a82012-10-30 09:10:00 +0800992 if self.client_attr.chrome_ec:
Vic Yang81273092012-08-21 15:57:09 +0800993 # Cold reset to clear EC_IN_RW signal
Vic Yanga7250662012-08-31 04:00:08 +0800994 self.servo.set('cold_reset', 'on')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800995 time.sleep(self.delay.hold_cold_reset)
Vic Yanga7250662012-08-31 04:00:08 +0800996 self.servo.set('cold_reset', '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("reboot ap-off")
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +0800999 time.sleep(self.delay.ec_boot_to_console)
Tom Wai-Hong Tam6019a1a2012-10-12 14:03:34 +08001000 self.ec.send_command("hostevent set 0x4000")
Vic Yang611dd852012-08-02 15:36:31 +08001001 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001002 else:
1003 self.servo.enable_recovery_mode()
1004 self.cold_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001005 time.sleep(self.delay.ec_reboot_cmd)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +08001006 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001007
1008
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001009 def enable_dev_mode_and_reboot(self):
1010 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001011 if self.client_attr.keyboard_dev:
1012 self.enable_keyboard_dev_mode()
1013 else:
1014 self.servo.enable_development_mode()
1015 self.faft_client.run_shell_command(
1016 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001017
1018
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +08001019 def enable_normal_mode_and_reboot(self):
1020 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +08001021 if self.client_attr.keyboard_dev:
1022 self.disable_keyboard_dev_mode()
1023 else:
1024 self.servo.disable_development_mode()
1025 self.faft_client.run_shell_command(
1026 'chromeos-firmwareupdate --mode tonormal && reboot')
1027
1028
1029 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
1030 """Wait for firmware screen and then switch into or out of dev mode.
1031
1032 Args:
1033 dev: True if switching into dev mode. Otherwise, False.
1034 """
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001035 time.sleep(self.delay.firmware_screen)
Vic Yange7553162012-06-20 16:20:47 +08001036 if dev:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001037 self.press_ctrl_d()
Vic Yange7553162012-06-20 16:20:47 +08001038 else:
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001039 self.press_enter()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001040 time.sleep(self.delay.firmware_screen)
Tom Wai-Hong Tam91612bc2012-10-29 16:04:21 +08001041 self.press_enter()
Vic Yange7553162012-06-20 16:20:47 +08001042
1043
1044 def enable_keyboard_dev_mode(self):
1045 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +08001046 # Plug out USB disk for preventing recovery boot without warning
1047 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +08001048 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +08001049 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001050 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001051 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +08001052
1053
1054 def disable_keyboard_dev_mode(self):
1055 logging.info("Disabling keyboard controlled developer mode")
Tom Wai-Hong Tamb0b3f412012-08-13 17:17:06 +08001056 if not self.client_attr.chrome_ec:
Vic Yang611dd852012-08-02 15:36:31 +08001057 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001058 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +08001059 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +08001060 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001061
1062
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001063 def setup_dev_mode(self, dev_mode):
1064 """Setup for development mode.
1065
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001066 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001067 tries to do so.
1068
1069 Args:
1070 dev_mode: True if requested in dev mode; False if normal mode.
1071 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001072 # Change the default firmware_action for dev mode passing the fw screen.
1073 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001074 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
1075 else None),
1076 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001077 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +08001078 if (not self.client_attr.keyboard_dev and
1079 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001080 logging.info('Dev switch is not on. Now switch it on.')
1081 self.servo.enable_development_mode()
1082 if not self.crossystem_checker({'devsw_boot': '1',
1083 'mainfw_type': 'developer'}):
1084 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001085 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001086 'userspace_action': None if self.client_attr.keyboard_dev
1087 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001088 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001089 'reboot_action': self.enable_keyboard_dev_mode if
1090 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001091 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001092 else:
Vic Yange7553162012-06-20 16:20:47 +08001093 if (not self.client_attr.keyboard_dev and
1094 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001095 logging.info('Dev switch is not off. Now switch it off.')
1096 self.servo.disable_development_mode()
1097 if not self.crossystem_checker({'devsw_boot': '0',
1098 'mainfw_type': 'normal'}):
1099 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001100 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +08001101 'userspace_action': None if self.client_attr.keyboard_dev
1102 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +08001103 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +08001104 'reboot_action': self.disable_keyboard_dev_mode if
1105 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +08001106 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001107
1108
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001109 def setup_kernel(self, part):
1110 """Setup for kernel test.
1111
1112 It makes sure both kernel A and B bootable and the current boot is
1113 the requested kernel part.
1114
1115 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001116 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001117 """
1118 self.ensure_kernel_boot(part)
Tom Wai-Hong Tam622d0ba2012-08-15 16:29:05 +08001119 if self.faft_client.diff_kernel_a_b():
1120 self.copy_kernel_and_rootfs(from_part=part,
1121 to_part=self.OTHER_KERNEL_MAP[part])
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001122 self.reset_and_prioritize_kernel(part)
1123
1124
1125 def reset_and_prioritize_kernel(self, part):
1126 """Make the requested partition highest priority.
1127
1128 This function also reset kerenl A and B to bootable.
1129
1130 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001131 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001132 """
1133 root_dev = self.faft_client.get_root_dev()
1134 # Reset kernel A and B to bootable.
1135 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1136 (self.KERNEL_MAP['a'], root_dev))
1137 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
1138 (self.KERNEL_MAP['b'], root_dev))
1139 # Set kernel part highest priority.
1140 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
1141 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +08001142 # Safer to sync and wait until the cgpt status written to the disk.
1143 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001144 time.sleep(self.delay.sync)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +08001145
1146
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001147 def warm_reboot(self):
1148 """Request a warm reboot.
1149
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001150 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001151 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001152 # Use cold reset if the warm reset is broken.
1153 if self.client_attr.broken_warm_reset:
Gediminas Ramanauskase021e152012-09-04 19:10:59 -07001154 logging.info('broken_warm_reset is True. Cold rebooting instead.')
1155 self.cold_reboot()
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +08001156 else:
1157 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001158
1159
1160 def cold_reboot(self):
1161 """Request a cold reboot.
1162
1163 A wrapper for underlying servo cold reset.
1164 """
Gediminas Ramanauskasc6025692012-10-23 14:33:40 -07001165 if self.client_attr.broken_warm_reset:
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001166 self.servo.set('pwr_button', 'press')
1167 self.servo.set('cold_reset', 'on')
1168 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001169 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tama276d0a2012-08-22 11:15:17 +08001170 self.servo.set('pwr_button', 'release')
Tom Wai-Hong Tamb8a91392012-09-27 10:45:32 +08001171 elif self.check_ec_capability(suppress_warning=True):
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001172 # We don't use servo.cold_reset() here because software sync is
1173 # not yet finished, and device may or may not come up after cold
1174 # reset. Pressing power button before firmware comes up solves this.
1175 #
1176 # The correct behavior should be (not work now):
1177 # - If rebooting EC with rec mode on, power on AP and it boots
1178 # into recovery mode.
1179 # - If rebooting EC with rec mode off, power on AP for software
1180 # sync. Then AP checks if lid open or not. If lid open, continue;
1181 # otherwise, shut AP down and need servo for a power button
1182 # press.
1183 self.servo.set('cold_reset', 'on')
1184 self.servo.set('cold_reset', 'off')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001185 time.sleep(self.delay.ec_boot_to_pwr_button)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001186 self.servo.power_short_press()
1187 else:
1188 self.servo.cold_reset()
1189
1190
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001191 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001192 """Request the client sync and do a warm reboot.
1193
1194 This is the default reboot action on FAFT.
1195 """
1196 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001197 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001198 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +08001199
1200
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001201 def sync_and_cold_reboot(self):
1202 """Request the client sync and do a cold reboot.
1203
1204 This reboot action is used to reset EC for recovery mode.
1205 """
1206 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001207 time.sleep(self.delay.sync)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +08001208 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +08001209
1210
Vic Yangaeb10392012-08-28 09:25:09 +08001211 def sync_and_ec_reboot(self, args=''):
1212 """Request the client sync and do a EC triggered reboot.
1213
1214 Args:
1215 args: Arguments passed to "ectool reboot_ec". Including:
1216 RO: jump to EC RO firmware.
1217 RW: jump to EC RW firmware.
1218 cold: Cold/hard reboot.
1219 """
Vic Yang59cac9c2012-05-21 15:28:42 +08001220 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001221 time.sleep(self.delay.sync)
Vic Yangaeb10392012-08-28 09:25:09 +08001222 # Since EC reboot happens immediately, delay before actual reboot to
1223 # allow FAFT client returning.
1224 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec %s)&' %
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001225 (self.delay.ec_reboot_cmd, args))
1226 time.sleep(self.delay.ec_reboot_cmd)
Vic Yangf86728a2012-07-30 10:44:07 +08001227 self.check_lid_and_power_on()
1228
1229
Chun-ting Changa4f65532012-10-17 16:57:28 +08001230 def sync_and_reboot_with_factory_install_shim(self):
1231 """Request the client sync and do a warm reboot to recovery mode.
1232
1233 After reboot, the client will use factory install shim to reset TPM
1234 values. The client ignore TPM rollback, so here forces it to recovery
1235 mode.
1236 """
1237 is_dev = self.crossystem_checker({'devsw_boot': '1'})
1238 if not is_dev:
1239 self.enable_dev_mode_and_reboot()
1240 time.sleep(self.SYNC_DELAY)
1241 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001242 time.sleep(self.delay.install_shim_done)
Chun-ting Changa4f65532012-10-17 16:57:28 +08001243 self.warm_reboot()
1244
1245
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001246 def full_power_off_and_on(self):
1247 """Shutdown the device by pressing power button and power on again."""
1248 # Press power button to trigger Chrome OS normal shutdown process.
1249 self.servo.power_normal_press()
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001250 time.sleep(self.delay.shutdown)
Tom Wai-Hong Tamc8f2ca02012-09-14 11:18:01 +08001251 # Short press power button to boot DUT again.
1252 self.servo.power_short_press()
1253
1254
Vic Yangf86728a2012-07-30 10:44:07 +08001255 def check_lid_and_power_on(self):
1256 """
1257 On devices with EC software sync, system powers on after EC reboots if
1258 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1259 This method checks lid switch state and presses power button if
1260 necessary.
1261 """
1262 if self.servo.get("lid_open") == "no":
Tom Wai-Hong Tam41738762012-10-29 14:32:39 +08001263 time.sleep(self.delay.software_sync)
Vic Yangf86728a2012-07-30 10:44:07 +08001264 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001265
1266
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001267 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1268 """Modify the kernel header magic in USB stick.
1269
1270 The kernel header magic is the first 8-byte of kernel partition.
1271 We modify it to make it fail on kernel verification check.
1272
1273 Args:
1274 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1275 from_magic: A string of magic which we change it from.
1276 to_magic: A string of magic which we change it to.
1277
1278 Raises:
1279 error.TestError: if failed to change magic.
1280 """
1281 assert len(from_magic) == 8
1282 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001283 # USB image only contains one kernel.
1284 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001285 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1286 current_magic = utils.system_output(read_cmd)
1287 if current_magic == to_magic:
1288 logging.info("The kernel magic is already %s." % current_magic)
1289 return
1290 if current_magic != from_magic:
1291 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1292
1293 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1294 (from_magic, to_magic))
1295 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1296 " 2>/dev/null" % (to_magic, kernel_part))
1297 utils.system(write_cmd)
1298
1299 if utils.system_output(read_cmd) != to_magic:
1300 raise error.TestError("Failed to write new magic.")
1301
1302
1303 def corrupt_usb_kernel(self, usb_dev):
1304 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1305
1306 Args:
1307 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1308 """
1309 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1310 self.CORRUPTED_MAGIC)
1311
1312
1313 def restore_usb_kernel(self, usb_dev):
1314 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1315
1316 Args:
1317 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1318 """
1319 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1320 self.CHROMEOS_MAGIC)
1321
1322
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001323 def _call_action(self, action_tuple, check_status=False):
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001324 """Call the action function with/without arguments.
1325
1326 Args:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001327 action_tuple: A function, or a tuple (function, args, error_msg),
1328 in which, args and error_msg are optional. args is
1329 either a value or a tuple if multiple arguments.
1330 check_status: Check the return value of action function. If not
1331 succeed, raises a TestFail exception.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001332
1333 Returns:
1334 The result value of the action function.
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001335
1336 Raises:
1337 error.TestError: An error when the action function is not callable.
1338 error.TestFail: When check_status=True, action function not succeed.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001339 """
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001340 action = action_tuple
1341 args = ()
1342 error_msg = 'Not succeed'
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001343 if isinstance(action_tuple, tuple):
1344 action = action_tuple[0]
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001345 if len(action_tuple) >= 2:
1346 args = action_tuple[1]
1347 if not isinstance(args, tuple):
1348 args = (args,)
1349 if len(action_tuple) >= 3:
Tom Wai-Hong Tamff560882012-10-15 16:50:06 +08001350 error_msg = action_tuple[2]
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001351
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001352 if action is None:
1353 return
1354
1355 if not callable(action):
1356 raise error.TestError('action is not callable!')
1357
1358 info_msg = 'calling %s' % str(action)
1359 if args:
1360 info_msg += ' with args %s' % str(args)
1361 logging.info(info_msg)
1362 ret = action(*args)
1363
1364 if check_status and not ret:
1365 raise error.TestFail('%s: %s returning %s' %
1366 (error_msg, info_msg, str(ret)))
1367 return ret
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001368
1369
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001370 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1371 post_power_action=None):
1372 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1373
1374 Args:
1375 shutdown_action: a function which makes DUT shutdown, like pressing
1376 power key.
1377 pre_power_action: a function which is called before next power on.
1378 post_power_action: a function which is called after next power on.
1379
1380 Raises:
1381 error.TestFail: if the shutdown_action() failed to turn DUT off.
1382 """
1383 self._call_action(shutdown_action)
1384 logging.info('Wait to ensure DUT shut down...')
1385 try:
1386 self.wait_for_client()
1387 raise error.TestFail(
1388 'Should shut the device down after calling %s.' %
1389 str(shutdown_action))
1390 except AssertionError:
1391 logging.info(
1392 'DUT is surely shutdown. We are going to power it on again...')
1393
1394 if pre_power_action:
1395 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001396 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001397 if post_power_action:
1398 self._call_action(post_power_action)
1399
1400
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001401 def register_faft_template(self, template):
1402 """Register FAFT template, the default FAFT_STEP of each step.
1403
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001404 Any missing field falls back to the original faft_template.
1405
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001406 Args:
1407 template: A FAFT_STEP dict.
1408 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001409 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001410
1411
1412 def register_faft_sequence(self, sequence):
1413 """Register FAFT sequence.
1414
1415 Args:
1416 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1417 """
1418 self._faft_sequence = sequence
1419
1420
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001421 def run_faft_step(self, step, no_reboot=False):
1422 """Run a single FAFT step.
1423
1424 Any missing field falls back to faft_template. An empty step means
1425 running the default faft_template.
1426
1427 Args:
1428 step: A FAFT_STEP dict.
1429 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001430
1431 Raises:
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001432 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001433 error.TestFail: Test failed in waiting DUT reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001434 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001435 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1436 'firmware_action', 'install_deps_after_boot')
1437
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001438 test = {}
1439 test.update(self._faft_template)
1440 test.update(step)
1441
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001442 for key in test:
1443 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001444 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001445
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001446 if test['state_checker']:
Tom Wai-Hong Tamfc700b52012-09-13 21:33:52 +08001447 self._call_action(test['state_checker'], check_status=True)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001448
1449 self._call_action(test['userspace_action'])
1450
1451 # Don't run reboot_action and firmware_action if no_reboot is True.
1452 if not no_reboot:
1453 self._call_action(test['reboot_action'])
1454 self.wait_for_client_offline()
1455 self._call_action(test['firmware_action'])
1456
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001457 try:
1458 if 'install_deps_after_boot' in test:
1459 self.wait_for_client(
1460 install_deps=test['install_deps_after_boot'])
1461 else:
1462 self.wait_for_client()
1463 except AssertionError:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001464 logging.info('wait_for_client() timed out.')
Vic Yang8eaf5ad2012-09-13 14:05:37 +08001465 self.reset_client()
Tom Wai-Hong Tam4bb85e22012-10-25 14:35:24 +08001466 if self._trapped_in_recovery_reason:
1467 raise error.TestFail('Trapped in the recovery reason: %d' %
1468 self._trapped_in_recovery_reason)
1469 else:
1470 raise error.TestFail('Timed out waiting for DUT reboot.')
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001471
1472
1473 def run_faft_sequence(self):
1474 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001475 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001476 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001477 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001478 logging.info('======== Running FAFT sequence step %d ========' %
1479 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001480 # Don't reboot in the last step.
1481 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001482 index += 1
ctchang38ae4922012-09-03 17:01:16 +08001483
1484
ctchang38ae4922012-09-03 17:01:16 +08001485 def get_current_firmware_sha(self):
1486 """Get current firmware sha of body and vblock.
1487
1488 Returns:
1489 Current firmware sha follows the order (
1490 vblock_a_sha, body_a_sha, vblock_b_sha, body_b_sha)
1491 """
1492 current_firmware_sha = (self.faft_client.get_firmware_sig_sha('a'),
1493 self.faft_client.get_firmware_sha('a'),
1494 self.faft_client.get_firmware_sig_sha('b'),
1495 self.faft_client.get_firmware_sha('b'))
1496 return current_firmware_sha
1497
1498
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001499 def is_firmware_changed(self):
1500 """Check if the current firmware changed, by comparing its SHA.
ctchang38ae4922012-09-03 17:01:16 +08001501
1502 Returns:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001503 True if it is changed, otherwise Flase.
ctchang38ae4922012-09-03 17:01:16 +08001504 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001505 # Device may not be rebooted after test.
1506 self.faft_client.reload_firmware()
ctchang38ae4922012-09-03 17:01:16 +08001507
1508 current_sha = self.get_current_firmware_sha()
1509
1510 if current_sha == self._backup_firmware_sha:
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001511 return False
ctchang38ae4922012-09-03 17:01:16 +08001512 else:
ctchang38ae4922012-09-03 17:01:16 +08001513 corrupt_VBOOTA = (current_sha[0] != self._backup_firmware_sha[0])
1514 corrupt_FVMAIN = (current_sha[1] != self._backup_firmware_sha[1])
1515 corrupt_VBOOTB = (current_sha[2] != self._backup_firmware_sha[2])
1516 corrupt_FVMAINB = (current_sha[3] != self._backup_firmware_sha[3])
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001517 logging.info("Firmware changed:")
1518 logging.info('VBOOTA is changed: %s' % corrupt_VBOOTA)
1519 logging.info('VBOOTB is changed: %s' % corrupt_VBOOTB)
1520 logging.info('FVMAIN is changed: %s' % corrupt_FVMAIN)
1521 logging.info('FVMAINB is changed: %s' % corrupt_FVMAINB)
1522 return True
ctchang38ae4922012-09-03 17:01:16 +08001523
1524
1525 def backup_firmware(self, suffix='.original'):
1526 """Backup firmware to file, and then send it to host.
1527
1528 Args:
1529 suffix: a string appended to backup file name
1530 """
1531 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001532 self.faft_client.dump_firmware(os.path.join(remote_temp_dir, 'bios'))
1533 self._client.get_file(os.path.join(remote_temp_dir, 'bios'),
1534 os.path.join(self.resultsdir, 'bios' + suffix))
ctchang38ae4922012-09-03 17:01:16 +08001535
1536 self._backup_firmware_sha = self.get_current_firmware_sha()
1537 logging.info('Backup firmware stored in %s with suffix %s' % (
1538 self.resultsdir, suffix))
1539
1540
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001541 def is_firmware_saved(self):
1542 """Check if a firmware saved (called backup_firmware before).
1543
1544 Returns:
1545 True if the firmware is backuped; otherwise False.
1546 """
1547 return self._backup_firmware_sha != ()
1548
1549
ctchang38ae4922012-09-03 17:01:16 +08001550 def restore_firmware(self, suffix='.original'):
1551 """Restore firmware from host in resultsdir.
1552
1553 Args:
1554 suffix: a string appended to backup file name
1555 """
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001556 if not self.is_firmware_changed():
ctchang38ae4922012-09-03 17:01:16 +08001557 return
1558
1559 # Backup current corrupted firmware.
1560 self.backup_firmware(suffix='.corrupt')
1561
1562 # Restore firmware.
1563 remote_temp_dir = self.faft_client.create_temp_dir()
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001564 self._client.send_file(os.path.join(self.resultsdir, 'bios' + suffix),
1565 os.path.join(remote_temp_dir, 'bios'))
ctchang38ae4922012-09-03 17:01:16 +08001566
Chun-ting Chang9380c2c2012-10-05 17:31:05 +08001567 self.faft_client.write_firmware(os.path.join(remote_temp_dir, 'bios'))
Tom Wai-Hong Tame6232342012-09-24 16:18:01 +08001568 self.sync_and_warm_reboot()
1569 self.wait_for_client_offline()
1570 self.wait_for_client()
1571
ctchang38ae4922012-09-03 17:01:16 +08001572 logging.info('Successfully restore firmware.')
Chun-ting Changf91ee0f2012-09-17 18:31:54 +08001573
1574
1575 def setup_firmwareupdate_shellball(self, shellball=None):
1576 """Deside a shellball to use in firmware update test.
1577
1578 Check if there is a given shellball, and it is a shell script. Then,
1579 send it to the remote host. Otherwise, use
1580 /usr/sbin/chromeos-firmwareupdate.
1581
1582 Args:
1583 shellball: path of a shellball or default to None.
1584
1585 Returns:
1586 Path of shellball in remote host.
1587 If use default shellball, reutrn None.
1588 """
1589 updater_path = None
1590 if shellball:
1591 # Determine the firmware file is a shellball or a raw binary.
1592 is_shellball = (utils.system_output("file %s" % shellball).find(
1593 "shell script") != -1)
1594 if is_shellball:
1595 logging.info('Device will update firmware with shellball %s'
1596 % shellball)
1597 temp_dir = self.faft_client.create_temp_dir('shellball_')
1598 temp_shellball = os.path.join(temp_dir, 'updater.sh')
1599 self._client.send_file(shellball, temp_shellball)
1600 updater_path = temp_shellball
1601 else:
1602 raise error.TestFail(
1603 'The given shellball is not a shell script.')
1604 return updater_path