blob: 8fbcc461d0912f22f7299a12409b360a142ac6be [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
5import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08006import os
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08007import re
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +08008import sys
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08009import tempfile
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080010import time
11import xmlrpclib
12
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 Tam22b77302011-11-03 13:03:48 +080015from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080016from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080017
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080018dirname = os.path.dirname(sys.modules[__name__].__file__)
19autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
20cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080021
22class FAFTSequence(ServoTest):
23 """
24 The base class of Fully Automated Firmware Test Sequence.
25
26 Many firmware tests require several reboot cycles and verify the resulted
27 system states. To do that, an Autotest test case should detailly handle
28 every action on each step. It makes the test case hard to read and many
29 duplicated code. The base class FAFTSequence is to solve this problem.
30
31 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
32 There are four functions in the FAFT_STEP dict:
33 state_checker: a function to check the current is valid or not,
34 returning True if valid, otherwise, False to break the whole
35 test sequence.
36 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080037 reboot_action: a function to do reboot, default: sync_and_hw_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080038 firmware_action: a function to describe the action ran after reboot.
39
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080040 And configurations:
41 install_deps_after_boot: if True, install the Autotest dependency after
42 boot; otherwise, do nothing. It is for the cases of recovery mode
43 test. The test boots a USB/SD image instead of an internal image.
44 The previous installed Autotest dependency on the internal image
45 is lost. So need to install it again.
46
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080047 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080048 userspace_action and firmware_action. Its reboot_action is a hardware
49 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080050 self.register_faft_template(FAFT_STEP).
51
52 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
53 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
54 fall back to default.
55
56 In the run_once(), it should register and run FAFT_SEQUENCE like:
57 def run_once(self):
58 self.register_faft_sequence(FAFT_SEQUENCE)
59 self.run_faft_sequnce()
60
61 Note that in the last step, we only run state_checker. The
62 userspace_action, reboot_action, and firmware_action are not executed.
63
64 Attributes:
65 _faft_template: The default FAFT_STEP of each step. The actions would
66 be over-written if the registered FAFT_SEQUENCE is valid.
67 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080068 _customized_ctrl_d_key_command: The customized Ctrl-D key command
69 instead of sending key via servo board.
70 _customized_enter_key_command: The customized Enter key command instead
71 of sending key via servo board.
72 _install_image_path: The path of Chrome OS test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080073 _firmware_update: Boolean. True if firmware update needed after
74 installing the image.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080075 """
76 version = 1
77
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080078
79 # Mapping of partition number of kernel and rootfs.
80 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
81 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
82 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
83 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
84
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080085 # Delay timing
Tom Wai-Hong Tam211ccba2012-01-13 15:35:53 +080086 FIRMWARE_SCREEN_DELAY = 2
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080087 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +080088 USB_PLUG_DELAY = 10
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +080089 SYNC_DELAY = 5
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080090
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +080091 CHROMEOS_MAGIC = "CHROMEOS"
92 CORRUPTED_MAGIC = "CORRUPTD"
93
Tom Wai-Hong Tamf954d172011-12-08 17:14:15 +080094 # Recovery reason codes, copied from:
95 # vboot_reference/firmware/lib/vboot_nvstorage.h
96 # vboot_reference/firmware/lib/vboot_struct.h
97 RECOVERY_REASON = {
98 # Recovery not requested
99 'NOT_REQUESTED': '0', # 0x00
100 # Recovery requested from legacy utility
101 'LEGACY': '1', # 0x01
102 # User manually requested recovery via recovery button
103 'RO_MANUAL': '2', # 0x02
104 # RW firmware failed signature check
105 'RO_INVALID_RW': '3', # 0x03
106 # S3 resume failed
107 'RO_S3_RESUME': '4', # 0x04
108 # TPM error in read-only firmware
109 'RO_TPM_ERROR': '5', # 0x05
110 # Shared data error in read-only firmware
111 'RO_SHARED_DATA': '6', # 0x06
112 # Test error from S3Resume()
113 'RO_TEST_S3': '7', # 0x07
114 # Test error from LoadFirmwareSetup()
115 'RO_TEST_LFS': '8', # 0x08
116 # Test error from LoadFirmware()
117 'RO_TEST_LF': '9', # 0x09
118 # RW firmware failed signature check
119 'RW_NOT_DONE': '16', # 0x10
120 'RW_DEV_MISMATCH': '17', # 0x11
121 'RW_REC_MISMATCH': '18', # 0x12
122 'RW_VERIFY_KEYBLOCK': '19', # 0x13
123 'RW_KEY_ROLLBACK': '20', # 0x14
124 'RW_DATA_KEY_PARSE': '21', # 0x15
125 'RW_VERIFY_PREAMBLE': '22', # 0x16
126 'RW_FW_ROLLBACK': '23', # 0x17
127 'RW_HEADER_VALID': '24', # 0x18
128 'RW_GET_FW_BODY': '25', # 0x19
129 'RW_HASH_WRONG_SIZE': '26', # 0x1A
130 'RW_VERIFY_BODY': '27', # 0x1B
131 'RW_VALID': '28', # 0x1C
132 # Read-only normal path requested by firmware preamble, but
133 # unsupported by firmware.
134 'RW_NO_RO_NORMAL': '29', # 0x1D
135 # Firmware boot failure outside of verified boot
136 'RO_FIRMWARE': '32', # 0x20
137 # Recovery mode TPM initialization requires a system reboot.
138 # The system was already in recovery mode for some other reason
139 # when this happened.
140 'RO_TPM_REBOOT': '33', # 0x21
141 # Unspecified/unknown error in read-only firmware
142 'RO_UNSPECIFIED': '63', # 0x3F
143 # User manually requested recovery by pressing a key at developer
144 # warning screen.
145 'RW_DEV_SCREEN': '65', # 0x41
146 # No OS kernel detected
147 'RW_NO_OS': '66', # 0x42
148 # OS kernel failed signature check
149 'RW_INVALID_OS': '67', # 0x43
150 # TPM error in rewritable firmware
151 'RW_TPM_ERROR': '68', # 0x44
152 # RW firmware in dev mode, but dev switch is off.
153 'RW_DEV_MISMATCH': '69', # 0x45
154 # Shared data error in rewritable firmware
155 'RW_SHARED_DATA': '70', # 0x46
156 # Test error from LoadKernel()
157 'RW_TEST_LK': '71', # 0x47
158 # No bootable disk found
159 'RW_NO_DISK': '72', # 0x48
160 # Unspecified/unknown error in rewritable firmware
161 'RW_UNSPECIFIED': '127', # 0x7F
162 # DM-verity error
163 'KE_DM_VERITY': '129', # 0x81
164 # Unspecified/unknown error in kernel
165 'KE_UNSPECIFIED': '191', # 0xBF
166 # Recovery mode test from user-mode
167 'US_TEST': '193', # 0xC1
168 # Unspecified/unknown error in user-mode
169 'US_UNSPECIFIED': '255', # 0xFF
170 }
171
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800172 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800173 _faft_sequence = ()
174
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800175 _customized_ctrl_d_key_command = None
176 _customized_enter_key_command = None
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800177 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800178 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800179
180
181 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
182 # Parse arguments from command line
183 args = {}
184 for arg in cmdline_args:
185 match = re.search("^(\w+)=(.+)", arg)
186 if match:
187 args[match.group(1)] = match.group(2)
188
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800189 # Keep the arguments which will be used later.
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800190 if 'ctrl_d_cmd' in args:
191 self._customized_ctrl_d_key_command = args['ctrl_d_cmd']
192 logging.info('Customized Ctrl-D key command: %s' %
193 self._customized_ctrl_d_key_command)
194 if 'enter_cmd' in args:
195 self._customized_enter_key_command = args['enter_cmd']
196 logging.info('Customized Enter key command: %s' %
197 self._customized_enter_key_command)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800198 if 'image' in args:
199 self._install_image_path = args['image']
200 logging.info('Install Chrome OS test image path: %s' %
201 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800202 if 'firmware_update' in args and args['firmware_update'].lower() \
203 not in ('0', 'false', 'no'):
204 if self._install_image_path:
205 self._firmware_update = True
206 logging.info('Also update firmware after installing.')
207 else:
208 logging.warning('Firmware update will not not performed '
209 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800210
211 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
212 use_faft)
213
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800214
215 def setup(self):
216 """Autotest setup function."""
217 super(FAFTSequence, self).setup()
218 if not self._remote_infos['faft']['used']:
219 raise error.TestError('The use_faft flag should be enabled.')
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800220
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800221 self.register_faft_template({
222 'state_checker': (None),
223 'userspace_action': (None),
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800224 'reboot_action': (self.sync_and_hw_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800225 'firmware_action': (None)
226 })
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800227 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800228 self.install_test_image(self._install_image_path,
229 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800230
231
232 def cleanup(self):
233 """Autotest cleanup function."""
234 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800235 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800236 super(FAFTSequence, self).cleanup()
237
238
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800239 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800240 """Assert an USB disk plugged-in on servo and a test image inside.
241
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800242 Args:
243 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
244 If None, it is detected automatically.
245
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800246 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800247 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800248 """
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800249 if usb_dev:
250 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
251 else:
252 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
253 usb_dev = self.servo.probe_host_usb_dev()
254 if not usb_dev:
255 raise error.TestError(
256 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800257
258 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800259 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800260 code = utils.system(
261 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
262 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800263 utils.system('sudo umount %s' % tmp_dir)
264 os.removedirs(tmp_dir)
265 if code != 0:
266 raise error.TestError(
267 'The image in the USB disk should be a test image.')
268
269
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800270 def install_test_image(self, image_path=None, firmware_update=False,
271 usb_dev=None):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800272 """Install the test image specied by the path onto the USB and DUT disk.
273
274 The method first copies the image to USB disk and reboots into it via
275 recovery mode. Then runs 'chromeos-install' to install it to DUT disk.
276
277 Args:
278 image_path: Path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800279 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800280 usb_dev: When servo_sees_usbkey is enabled, which dev
281 e.g. /dev/sdb will the usb key show up as.
282 If None, detects it automatically.
283 """
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800284 install_cmd = 'chromeos-install --yes'
285 if firmware_update:
286 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800287 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
288 image_path)
289 logging.info('Processing build: %s %s' % (build_ver, build_hash))
290 if not usb_dev:
291 usb_dev = self.servo.probe_host_usb_dev()
292
293 # Reuse the install_recovery_image method by using a test image.
294 # Don't wait for completion but run chromeos-install to install it.
295 self.servo.install_recovery_image(image_path, usb_dev,
296 wait_for_completion=False)
297 self.wait_for_client(install_deps=True)
298 self.run_faft_step({
299 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800300 install_cmd)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800301 })
302
303
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800304 def _parse_crossystem_output(self, lines):
305 """Parse the crossystem output into a dict.
306
307 Args:
308 lines: The list of crossystem output strings.
309
310 Returns:
311 A dict which contains the crossystem keys/values.
312
313 Raises:
314 error.TestError: If wrong format in crossystem output.
315
316 >>> seq = FAFTSequence()
317 >>> seq._parse_crossystem_output([ \
318 "arch = x86 # Platform architecture", \
319 "cros_debug = 1 # OS should allow debug", \
320 ])
321 {'cros_debug': '1', 'arch': 'x86'}
322 >>> seq._parse_crossystem_output([ \
323 "arch=x86", \
324 ])
325 Traceback (most recent call last):
326 ...
327 TestError: Failed to parse crossystem output: arch=x86
328 >>> seq._parse_crossystem_output([ \
329 "arch = x86 # Platform architecture", \
330 "arch = arm # Platform architecture", \
331 ])
332 Traceback (most recent call last):
333 ...
334 TestError: Duplicated crossystem key: arch
335 """
336 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
337 parsed_list = {}
338 for line in lines:
339 matched = re.match(pattern, line.strip())
340 if not matched:
341 raise error.TestError("Failed to parse crossystem output: %s"
342 % line)
343 (name, value) = (matched.group(1), matched.group(2))
344 if name in parsed_list:
345 raise error.TestError("Duplicated crossystem key: %s" % name)
346 parsed_list[name] = value
347 return parsed_list
348
349
350 def crossystem_checker(self, expected_dict):
351 """Check the crossystem values matched.
352
353 Given an expect_dict which describes the expected crossystem values,
354 this function check the current crossystem values are matched or not.
355
356 Args:
357 expected_dict: A dict which contains the expected values.
358
359 Returns:
360 True if the crossystem value matched; otherwise, False.
361 """
362 lines = self.faft_client.run_shell_command_get_output('crossystem')
363 got_dict = self._parse_crossystem_output(lines)
364 for key in expected_dict:
365 if key not in got_dict:
366 logging.info('Expected key "%s" not in crossystem result' % key)
367 return False
368 if isinstance(expected_dict[key], str):
369 if got_dict[key] != expected_dict[key]:
370 logging.info("Expected '%s' value '%s' but got '%s'" %
371 (key, expected_dict[key], got_dict[key]))
372 return False
373 elif isinstance(expected_dict[key], tuple):
374 # Expected value is a tuple of possible actual values.
375 if got_dict[key] not in expected_dict[key]:
376 logging.info("Expected '%s' values %s but got '%s'" %
377 (key, str(expected_dict[key]), got_dict[key]))
378 return False
379 else:
380 logging.info("The expected_dict is neither a str nor a dict.")
381 return False
382 return True
383
384
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800385 def root_part_checker(self, expected_part):
386 """Check the partition number of the root device matched.
387
388 Args:
389 expected_part: A string containing the number of the expected root
390 partition.
391
392 Returns:
393 True if the currect root partition number matched; otherwise, False.
394 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800395 part = self.faft_client.get_root_part()[-1]
396 if self.ROOTFS_MAP[expected_part] != part:
397 logging.info("Expected root part %s but got %s" %
398 (self.ROOTFS_MAP[expected_part], part))
399 return False
400 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800401
402
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800403 def _join_part(self, dev, part):
404 """Return a concatenated string of device and partition number.
405
406 Args:
407 dev: A string of device, e.g.'/dev/sda'.
408 part: A string of partition number, e.g.'3'.
409
410 Returns:
411 A concatenated string of device and partition number, e.g.'/dev/sda3'.
412
413 >>> seq = FAFTSequence()
414 >>> seq._join_part('/dev/sda', '3')
415 '/dev/sda3'
416 >>> seq._join_part('/dev/mmcblk0', '2')
417 '/dev/mmcblk0p2'
418 """
419 if 'mmcblk' in dev:
420 return dev + 'p' + part
421 else:
422 return dev + part
423
424
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800425 def copy_kernel_and_rootfs(self, from_part, to_part):
426 """Copy kernel and rootfs from from_part to to_part.
427
428 Args:
429 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800430 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800431 """
432 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800433 logging.info('Copying kernel from %s to %s. Please wait...' %
434 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800435 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800436 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
437 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
438 logging.info('Copying rootfs from %s to %s. Please wait...' %
439 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800440 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800441 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
442 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800443
444
445 def ensure_kernel_boot(self, part):
446 """Ensure the request kernel boot.
447
448 If not, it duplicates the current kernel to the requested kernel
449 and sets the requested higher priority to ensure it boot.
450
451 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800452 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800453 """
454 if not self.root_part_checker(part):
455 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
456 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800457 self.run_faft_step({
458 'userspace_action': (self.reset_and_prioritize_kernel, part),
459 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800460
461
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800462 def send_ctrl_d_to_dut(self):
463 """Send Ctrl-D key to DUT."""
464 if self._customized_ctrl_d_key_command:
465 logging.info('running the customized Ctrl-D key command')
466 os.system(self._customized_ctrl_d_key_command)
467 else:
468 self.servo.ctrl_d()
469
470
471 def send_enter_to_dut(self):
472 """Send Enter key to DUT."""
473 if self._customized_enter_key_command:
474 logging.info('running the customized Enter key command')
475 os.system(self._customized_enter_key_command)
476 else:
477 self.servo.enter_key()
478
479
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800480 def wait_fw_screen_and_ctrl_d(self):
481 """Wait for firmware warning screen and press Ctrl-D."""
482 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800483 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800484
485
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800486 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
487 """Wait for firmware warning screen and trigger recovery boot."""
488 time.sleep(self.FIRMWARE_SCREEN_DELAY)
489 self.send_enter_to_dut()
490
491 # For Alex/ZGB, there is a dev warning screen in text mode.
492 # Skip it by pressing Ctrl-D.
493 if need_dev_transition:
494 time.sleep(self.TEXT_SCREEN_DELAY)
495 self.send_ctrl_d_to_dut()
496
497
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800498 def wait_fw_screen_and_plug_usb(self):
499 """Wait for firmware warning screen and then unplug and plug the USB."""
500 time.sleep(self.FIRMWARE_SCREEN_DELAY)
501 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
502 time.sleep(self.USB_PLUG_DELAY)
503 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
504
505
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800506 def wait_fw_screen_and_press_power(self):
507 """Wait for firmware warning screen and press power button."""
508 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800509 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800510
511
512 def wait_fw_screen_and_close_lid(self):
513 """Wait for firmware warning screen and close lid."""
514 time.sleep(self.FIRMWARE_SCREEN_DELAY)
515 self.servo.lid_close()
516
517
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800518 def setup_tried_fwb(self, tried_fwb):
519 """Setup for fw B tried state.
520
521 It makes sure the system in the requested fw B tried state. If not, it
522 tries to do so.
523
524 Args:
525 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
526 """
527 if tried_fwb:
528 if not self.crossystem_checker({'tried_fwb': '1'}):
529 logging.info(
530 'Firmware is not booted with tried_fwb. Reboot into it.')
531 self.run_faft_step({
532 'userspace_action': self.faft_client.set_try_fw_b,
533 })
534 else:
535 if not self.crossystem_checker({'tried_fwb': '0'}):
536 logging.info(
537 'Firmware is booted with tried_fwb. Reboot to clear.')
538 self.run_faft_step({})
539
540
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800541 def enable_dev_mode_and_fw(self):
542 """Enable developer mode and use developer firmware."""
543 self.servo.enable_development_mode()
544 self.faft_client.run_shell_command(
545 'chromeos-firmwareupdate --mode todev && reboot')
546
547
548 def enable_normal_mode_and_fw(self):
549 """Enable normal mode and use normal firmware."""
550 self.servo.disable_development_mode()
551 self.faft_client.run_shell_command(
552 'chromeos-firmwareupdate --mode tonormal && reboot')
553
554
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800555 def setup_dev_mode(self, dev_mode):
556 """Setup for development mode.
557
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800558 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800559 tries to do so.
560
561 Args:
562 dev_mode: True if requested in dev mode; False if normal mode.
563 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800564 # Change the default firmware_action for dev mode passing the fw screen.
565 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800566 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
567 else None),
568 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800569 if dev_mode:
570 if not self.crossystem_checker({'devsw_cur': '1'}):
571 logging.info('Dev switch is not on. Now switch it on.')
572 self.servo.enable_development_mode()
573 if not self.crossystem_checker({'devsw_boot': '1',
574 'mainfw_type': 'developer'}):
575 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800576 self.run_faft_step({
577 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800578 'chromeos-firmwareupdate --mode todev && reboot'),
579 'reboot_action': None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800580 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800581 else:
582 if not self.crossystem_checker({'devsw_cur': '0'}):
583 logging.info('Dev switch is not off. Now switch it off.')
584 self.servo.disable_development_mode()
585 if not self.crossystem_checker({'devsw_boot': '0',
586 'mainfw_type': 'normal'}):
587 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800588 self.run_faft_step({
589 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800590 'chromeos-firmwareupdate --mode tonormal && reboot'),
591 'reboot_action': None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800592 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800593
594
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800595 def setup_kernel(self, part):
596 """Setup for kernel test.
597
598 It makes sure both kernel A and B bootable and the current boot is
599 the requested kernel part.
600
601 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800602 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800603 """
604 self.ensure_kernel_boot(part)
605 self.copy_kernel_and_rootfs(from_part=part,
606 to_part=self.OTHER_KERNEL_MAP[part])
607 self.reset_and_prioritize_kernel(part)
608
609
610 def reset_and_prioritize_kernel(self, part):
611 """Make the requested partition highest priority.
612
613 This function also reset kerenl A and B to bootable.
614
615 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800616 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800617 """
618 root_dev = self.faft_client.get_root_dev()
619 # Reset kernel A and B to bootable.
620 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
621 (self.KERNEL_MAP['a'], root_dev))
622 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
623 (self.KERNEL_MAP['b'], root_dev))
624 # Set kernel part highest priority.
625 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
626 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800627 # Safer to sync and wait until the cgpt status written to the disk.
628 self.faft_client.run_shell_command('sync')
629 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800630
631
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800632 def sync_and_hw_reboot(self):
633 """Request the client sync and do a warm reboot.
634
635 This is the default reboot action on FAFT.
636 """
637 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800638 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800639 self.servo.warm_reset()
640
641
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800642 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
643 """Modify the kernel header magic in USB stick.
644
645 The kernel header magic is the first 8-byte of kernel partition.
646 We modify it to make it fail on kernel verification check.
647
648 Args:
649 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
650 from_magic: A string of magic which we change it from.
651 to_magic: A string of magic which we change it to.
652
653 Raises:
654 error.TestError: if failed to change magic.
655 """
656 assert len(from_magic) == 8
657 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +0800658 # USB image only contains one kernel.
659 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800660 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
661 current_magic = utils.system_output(read_cmd)
662 if current_magic == to_magic:
663 logging.info("The kernel magic is already %s." % current_magic)
664 return
665 if current_magic != from_magic:
666 raise error.TestError("Invalid kernel image on USB: wrong magic.")
667
668 logging.info('Modify the kernel magic in USB, from %s to %s.' %
669 (from_magic, to_magic))
670 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
671 " 2>/dev/null" % (to_magic, kernel_part))
672 utils.system(write_cmd)
673
674 if utils.system_output(read_cmd) != to_magic:
675 raise error.TestError("Failed to write new magic.")
676
677
678 def corrupt_usb_kernel(self, usb_dev):
679 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
680
681 Args:
682 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
683 """
684 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
685 self.CORRUPTED_MAGIC)
686
687
688 def restore_usb_kernel(self, usb_dev):
689 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
690
691 Args:
692 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
693 """
694 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
695 self.CHROMEOS_MAGIC)
696
697
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800698 def _call_action(self, action_tuple):
699 """Call the action function with/without arguments.
700
701 Args:
702 action_tuple: A function, or a tuple which consisted of a function
703 and its arguments (if any).
704
705 Returns:
706 The result value of the action function.
707 """
708 if isinstance(action_tuple, tuple):
709 action = action_tuple[0]
710 args = action_tuple[1:]
711 if callable(action):
712 logging.info('calling %s with parameter %s' % (
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800713 str(action), str(action_tuple[1])))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800714 return action(*args)
715 else:
716 logging.info('action is not callable!')
717 else:
718 action = action_tuple
719 if action is not None:
720 if callable(action):
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800721 logging.info('calling %s' % str(action))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800722 return action()
723 else:
724 logging.info('action is not callable!')
725
726 return None
727
728
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800729 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
730 post_power_action=None):
731 """Run shutdown_action(), which makes DUT shutdown, and power it on.
732
733 Args:
734 shutdown_action: a function which makes DUT shutdown, like pressing
735 power key.
736 pre_power_action: a function which is called before next power on.
737 post_power_action: a function which is called after next power on.
738
739 Raises:
740 error.TestFail: if the shutdown_action() failed to turn DUT off.
741 """
742 self._call_action(shutdown_action)
743 logging.info('Wait to ensure DUT shut down...')
744 try:
745 self.wait_for_client()
746 raise error.TestFail(
747 'Should shut the device down after calling %s.' %
748 str(shutdown_action))
749 except AssertionError:
750 logging.info(
751 'DUT is surely shutdown. We are going to power it on again...')
752
753 if pre_power_action:
754 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800755 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800756 if post_power_action:
757 self._call_action(post_power_action)
758
759
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800760 def register_faft_template(self, template):
761 """Register FAFT template, the default FAFT_STEP of each step.
762
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800763 Any missing field falls back to the original faft_template.
764
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800765 Args:
766 template: A FAFT_STEP dict.
767 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800768 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800769
770
771 def register_faft_sequence(self, sequence):
772 """Register FAFT sequence.
773
774 Args:
775 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
776 """
777 self._faft_sequence = sequence
778
779
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800780 def run_faft_step(self, step, no_reboot=False):
781 """Run a single FAFT step.
782
783 Any missing field falls back to faft_template. An empty step means
784 running the default faft_template.
785
786 Args:
787 step: A FAFT_STEP dict.
788 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800789
790 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800791 error.TestFail: An error when the test failed.
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +0800792 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800793 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +0800794 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
795 'firmware_action', 'install_deps_after_boot')
796
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800797 test = {}
798 test.update(self._faft_template)
799 test.update(step)
800
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +0800801 for key in test:
802 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800803 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +0800804
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800805 if test['state_checker']:
806 if not self._call_action(test['state_checker']):
807 raise error.TestFail('State checker failed!')
808
809 self._call_action(test['userspace_action'])
810
811 # Don't run reboot_action and firmware_action if no_reboot is True.
812 if not no_reboot:
813 self._call_action(test['reboot_action'])
814 self.wait_for_client_offline()
815 self._call_action(test['firmware_action'])
816
817 if 'install_deps_after_boot' in test:
818 self.wait_for_client(
819 install_deps=test['install_deps_after_boot'])
820 else:
821 self.wait_for_client()
822
823
824 def run_faft_sequence(self):
825 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800826 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800827 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800828 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800829 logging.info('======== Running FAFT sequence step %d ========' %
830 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +0800831 # Don't reboot in the last step.
832 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +0800833 index += 1