blob: dd3e0ab4b361c291ec52e56f40b804bbe427b104 [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
Vic Yangb4e3e742012-06-02 13:17:38 +08006import fdpexpect
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08007import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08008import os
Vic Yangb4e3e742012-06-02 13:17:38 +08009import pexpect
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080010import re
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080011import sys
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080012import tempfile
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080013import time
14import xmlrpclib
15
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080016from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080017from autotest_lib.client.common_lib import error
Vic Yangebd6de62012-06-26 14:25:57 +080018from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
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 Tama70f0fe2011-09-02 18:28:47 +080021
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080022dirname = os.path.dirname(sys.modules[__name__].__file__)
23autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
24cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080025
26class FAFTSequence(ServoTest):
27 """
28 The base class of Fully Automated Firmware Test Sequence.
29
30 Many firmware tests require several reboot cycles and verify the resulted
31 system states. To do that, an Autotest test case should detailly handle
32 every action on each step. It makes the test case hard to read and many
33 duplicated code. The base class FAFTSequence is to solve this problem.
34
35 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
36 There are four functions in the FAFT_STEP dict:
37 state_checker: a function to check the current is valid or not,
38 returning True if valid, otherwise, False to break the whole
39 test sequence.
40 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080041 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080042 firmware_action: a function to describe the action ran after reboot.
43
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080044 And configurations:
45 install_deps_after_boot: if True, install the Autotest dependency after
46 boot; otherwise, do nothing. It is for the cases of recovery mode
47 test. The test boots a USB/SD image instead of an internal image.
48 The previous installed Autotest dependency on the internal image
49 is lost. So need to install it again.
50
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080051 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080052 userspace_action and firmware_action. Its reboot_action is a hardware
53 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080054 self.register_faft_template(FAFT_STEP).
55
56 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
57 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
58 fall back to default.
59
60 In the run_once(), it should register and run FAFT_SEQUENCE like:
61 def run_once(self):
62 self.register_faft_sequence(FAFT_SEQUENCE)
63 self.run_faft_sequnce()
64
65 Note that in the last step, we only run state_checker. The
66 userspace_action, reboot_action, and firmware_action are not executed.
67
68 Attributes:
69 _faft_template: The default FAFT_STEP of each step. The actions would
70 be over-written if the registered FAFT_SEQUENCE is valid.
71 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080072 _customized_ctrl_d_key_command: The customized Ctrl-D key command
73 instead of sending key via servo board.
74 _customized_enter_key_command: The customized Enter key command instead
75 of sending key via servo board.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +080076 _customized_space_key_command: The customized Space key command instead
77 of sending key via servo board.
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +080078 _customized_rec_reboot_command: The customized recovery reboot command
79 instead of sending key combination of Power + Esc + F3 for
80 triggering recovery reboot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080081 _install_image_path: The path of Chrome OS test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080082 _firmware_update: Boolean. True if firmware update needed after
83 installing the image.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080084 """
85 version = 1
86
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080087
88 # Mapping of partition number of kernel and rootfs.
89 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
90 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
91 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
92 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
93
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080094 # Delay between power-on and firmware screen.
Tom Wai-Hong Tam66af37b2012-08-01 10:48:42 +080095 FIRMWARE_SCREEN_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080096 # Delay between passing firmware screen and text mode warning screen.
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080097 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080098 # Delay of loading the USB kernel.
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080099 USB_LOAD_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800100 # Delay between USB plug-out and plug-in.
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +0800101 USB_PLUG_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800102 # Delay after running the 'sync' command.
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800103 SYNC_DELAY = 5
Vic Yang59cac9c2012-05-21 15:28:42 +0800104 # Delay for waiting client to return before EC reboot
105 EC_REBOOT_DELAY = 1
106 # Delay between EC reboot and pressing power button
107 POWER_BTN_DELAY = 0.5
Vic Yangf86728a2012-07-30 10:44:07 +0800108 # Delay of EC software sync hash calculating time
109 SOFTWARE_SYNC_DELAY = 6
Vic Yang611dd852012-08-02 15:36:31 +0800110 # Delay between EC boot and EC console functional
111 EC_BOOT_DELAY = 2
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800112
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800113 # The developer screen timeouts fit our spec.
114 DEV_SCREEN_TIMEOUT = 30
115
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800116 CHROMEOS_MAGIC = "CHROMEOS"
117 CORRUPTED_MAGIC = "CORRUPTD"
118
Tom Wai-Hong Tamf954d172011-12-08 17:14:15 +0800119 # Recovery reason codes, copied from:
120 # vboot_reference/firmware/lib/vboot_nvstorage.h
121 # vboot_reference/firmware/lib/vboot_struct.h
122 RECOVERY_REASON = {
123 # Recovery not requested
124 'NOT_REQUESTED': '0', # 0x00
125 # Recovery requested from legacy utility
126 'LEGACY': '1', # 0x01
127 # User manually requested recovery via recovery button
128 'RO_MANUAL': '2', # 0x02
129 # RW firmware failed signature check
130 'RO_INVALID_RW': '3', # 0x03
131 # S3 resume failed
132 'RO_S3_RESUME': '4', # 0x04
133 # TPM error in read-only firmware
134 'RO_TPM_ERROR': '5', # 0x05
135 # Shared data error in read-only firmware
136 'RO_SHARED_DATA': '6', # 0x06
137 # Test error from S3Resume()
138 'RO_TEST_S3': '7', # 0x07
139 # Test error from LoadFirmwareSetup()
140 'RO_TEST_LFS': '8', # 0x08
141 # Test error from LoadFirmware()
142 'RO_TEST_LF': '9', # 0x09
143 # RW firmware failed signature check
144 'RW_NOT_DONE': '16', # 0x10
145 'RW_DEV_MISMATCH': '17', # 0x11
146 'RW_REC_MISMATCH': '18', # 0x12
147 'RW_VERIFY_KEYBLOCK': '19', # 0x13
148 'RW_KEY_ROLLBACK': '20', # 0x14
149 'RW_DATA_KEY_PARSE': '21', # 0x15
150 'RW_VERIFY_PREAMBLE': '22', # 0x16
151 'RW_FW_ROLLBACK': '23', # 0x17
152 'RW_HEADER_VALID': '24', # 0x18
153 'RW_GET_FW_BODY': '25', # 0x19
154 'RW_HASH_WRONG_SIZE': '26', # 0x1A
155 'RW_VERIFY_BODY': '27', # 0x1B
156 'RW_VALID': '28', # 0x1C
157 # Read-only normal path requested by firmware preamble, but
158 # unsupported by firmware.
159 'RW_NO_RO_NORMAL': '29', # 0x1D
160 # Firmware boot failure outside of verified boot
161 'RO_FIRMWARE': '32', # 0x20
162 # Recovery mode TPM initialization requires a system reboot.
163 # The system was already in recovery mode for some other reason
164 # when this happened.
165 'RO_TPM_REBOOT': '33', # 0x21
166 # Unspecified/unknown error in read-only firmware
167 'RO_UNSPECIFIED': '63', # 0x3F
168 # User manually requested recovery by pressing a key at developer
169 # warning screen.
170 'RW_DEV_SCREEN': '65', # 0x41
171 # No OS kernel detected
172 'RW_NO_OS': '66', # 0x42
173 # OS kernel failed signature check
174 'RW_INVALID_OS': '67', # 0x43
175 # TPM error in rewritable firmware
176 'RW_TPM_ERROR': '68', # 0x44
177 # RW firmware in dev mode, but dev switch is off.
178 'RW_DEV_MISMATCH': '69', # 0x45
179 # Shared data error in rewritable firmware
180 'RW_SHARED_DATA': '70', # 0x46
181 # Test error from LoadKernel()
182 'RW_TEST_LK': '71', # 0x47
183 # No bootable disk found
184 'RW_NO_DISK': '72', # 0x48
185 # Unspecified/unknown error in rewritable firmware
186 'RW_UNSPECIFIED': '127', # 0x7F
187 # DM-verity error
188 'KE_DM_VERITY': '129', # 0x81
189 # Unspecified/unknown error in kernel
190 'KE_UNSPECIFIED': '191', # 0xBF
191 # Recovery mode test from user-mode
192 'US_TEST': '193', # 0xC1
193 # Unspecified/unknown error in user-mode
194 'US_UNSPECIFIED': '255', # 0xFF
195 }
196
Tom Wai-Hong Tam1e40fa12012-07-25 16:22:24 +0800197 # GBB flags
198 GBB_FLAG_DEV_SCREEN_SHORT_DELAY = 0x00000001
199 GBB_FLAG_LOAD_OPTION_ROMS = 0x00000002
200 GBB_FLAG_ENABLE_ALTERNATE_OS = 0x00000004
201 GBB_FLAG_FORCE_DEV_SWITCH_ON = 0x00000008
202 GBB_FLAG_FORCE_DEV_BOOT_USB = 0x00000010
203 GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = 0x00000020
204
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800205 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800206 _faft_sequence = ()
207
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800208 _customized_ctrl_d_key_command = None
209 _customized_enter_key_command = None
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800210 _customized_space_key_command = None
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800211 _customized_rec_reboot_command = None
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800212 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800213 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800214
215
216 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
217 # Parse arguments from command line
218 args = {}
219 for arg in cmdline_args:
220 match = re.search("^(\w+)=(.+)", arg)
221 if match:
222 args[match.group(1)] = match.group(2)
223
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800224 # Keep the arguments which will be used later.
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800225 if 'ctrl_d_cmd' in args:
226 self._customized_ctrl_d_key_command = args['ctrl_d_cmd']
227 logging.info('Customized Ctrl-D key command: %s' %
228 self._customized_ctrl_d_key_command)
229 if 'enter_cmd' in args:
230 self._customized_enter_key_command = args['enter_cmd']
231 logging.info('Customized Enter key command: %s' %
232 self._customized_enter_key_command)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800233 if 'space_cmd' in args:
234 self._customized_space_key_command = args['space_cmd']
235 logging.info('Customized Space key command: %s' %
236 self._customized_space_key_command)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800237 if 'rec_reboot_cmd' in args:
238 self._customized_rec_reboot_command = args['rec_reboot_cmd']
239 logging.info('Customized recovery reboot command: %s' %
240 self._customized_rec_reboot_command)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800241 if 'image' in args:
242 self._install_image_path = args['image']
243 logging.info('Install Chrome OS test image path: %s' %
244 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800245 if 'firmware_update' in args and args['firmware_update'].lower() \
246 not in ('0', 'false', 'no'):
247 if self._install_image_path:
248 self._firmware_update = True
249 logging.info('Also update firmware after installing.')
250 else:
251 logging.warning('Firmware update will not not performed '
252 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800253
254 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
255 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800256 if use_faft:
257 self.client_attr = FAFTClientAttribute(
258 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800259
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800260
261 def setup(self):
262 """Autotest setup function."""
263 super(FAFTSequence, self).setup()
264 if not self._remote_infos['faft']['used']:
265 raise error.TestError('The use_faft flag should be enabled.')
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800266 self.clear_gbb_flags(self.GBB_FLAG_FORCE_DEV_SWITCH_ON |
267 self.GBB_FLAG_DEV_SCREEN_SHORT_DELAY)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800268 self.register_faft_template({
269 'state_checker': (None),
270 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800271 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800272 'firmware_action': (None)
273 })
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800274 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800275 self.install_test_image(self._install_image_path,
276 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800277
278
279 def cleanup(self):
280 """Autotest cleanup function."""
281 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800282 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800283 super(FAFTSequence, self).cleanup()
284
285
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800286 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800287 """Assert an USB disk plugged-in on servo and a test image inside.
288
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800289 Args:
290 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
291 If None, it is detected automatically.
292
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800293 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800294 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800295 """
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800296 if usb_dev:
297 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
298 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700299 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800300 usb_dev = self.servo.probe_host_usb_dev()
301 if not usb_dev:
302 raise error.TestError(
303 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800304
305 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800306 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800307 code = utils.system(
308 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
309 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800310 utils.system('sudo umount %s' % tmp_dir)
311 os.removedirs(tmp_dir)
312 if code != 0:
313 raise error.TestError(
314 'The image in the USB disk should be a test image.')
315
316
Simran Basi741b5d42012-05-18 11:27:15 -0700317 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800318 """Install the test image specied by the path onto the USB and DUT disk.
319
320 The method first copies the image to USB disk and reboots into it via
321 recovery mode. Then runs 'chromeos-install' to install it to DUT disk.
322
323 Args:
324 image_path: Path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800325 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800326 """
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800327 install_cmd = 'chromeos-install --yes'
328 if firmware_update:
329 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800330 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
331 image_path)
332 logging.info('Processing build: %s %s' % (build_ver, build_hash))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800333
334 # Reuse the install_recovery_image method by using a test image.
335 # Don't wait for completion but run chromeos-install to install it.
Simran Basi741b5d42012-05-18 11:27:15 -0700336 self.servo.install_recovery_image(image_path)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800337 self.wait_for_client(install_deps=True)
338 self.run_faft_step({
339 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800340 install_cmd)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800341 })
342
343
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800344 def clear_gbb_flags(self, mask):
345 """Clear the GBB flags in the current flashrom.
346
347 Args:
348 mask: A mask of flags to be cleared.
349 """
350 gbb_flags = self.faft_client.get_gbb_flags()
351 if (gbb_flags & mask):
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800352 new_flags = gbb_flags & ctypes.c_uint32(~mask).value
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800353 logging.info('Clear the GBB flags of 0x%x, from 0x%x to 0x%x.' %
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800354 (mask, gbb_flags, new_flags))
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800355 self.faft_client.run_shell_command(
Tom Wai-Hong Tamfda76e22012-08-08 17:19:10 +0800356 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' % new_flags)
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800357 self.faft_client.reload_firmware()
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800358
359
Vic Yangb4e3e742012-06-02 13:17:38 +0800360 def _open_uart_pty(self):
361 """Open UART pty and spawn pexpect object.
362
363 Returns:
364 Tuple (fd, child): fd is the file descriptor of opened UART pty, and
365 child is a fdpexpect object tied to it.
366 """
367 fd = os.open(self.servo.get("uart1_pty"), os.O_RDWR | os.O_NONBLOCK)
368 child = fdpexpect.fdspawn(fd)
369 return (fd, child)
370
371
372 def _flush_uart_pty(self, child):
373 """Flush UART output to prevent previous pending message interferring.
374
375 Args:
376 child: The fdpexpect object tied to UART pty.
377 """
378 child.sendline("")
379 while True:
380 try:
381 child.expect(".", timeout=0.01)
382 except pexpect.TIMEOUT:
383 break
384
385
386 def _uart_send(self, child, line):
387 """Flush and send command through UART.
388
389 Args:
390 child: The pexpect object tied to UART pty.
391 line: String to send through UART.
392
393 Raises:
394 error.TestFail: Raised when writing to UART fails.
395 """
396 logging.info("Sending UART command: %s" % line)
397 self._flush_uart_pty(child)
398 if child.sendline(line) != len(line) + 1:
399 raise error.TestFail("Failed to send UART command.")
400
401
402 def send_uart_command(self, command):
403 """Send command through UART.
404
405 This function open UART pty when called, and then command is sent
406 through UART.
407
408 Args:
409 command: The command string to send.
410
411 Raises:
412 error.TestFail: Raised when writing to UART fails.
413 """
414 (fd, child) = self._open_uart_pty()
415 try:
416 self._uart_send(child, command)
417 finally:
418 os.close(fd)
419
420
421 def send_uart_command_get_output(self, command, regex_list, timeout=1):
422 """Send command through UART and wait for response.
423
424 This function waits for response message matching regular expressions.
425
426 Args:
427 command: The command sent.
428 regex_list: List of regular expressions used to match response message.
429 Note, list must be ordered.
430
431 Returns:
432 List of match objects of response message.
433
434 Raises:
435 error.TestFail: If timed out waiting for EC response.
436 """
437 if not isinstance(regex_list, list):
438 regex_list = [regex_list]
439 result_list = []
440 (fd, child) = self._open_uart_pty()
441 try:
442 self._uart_send(child, command)
443 for regex in regex_list:
444 child.expect(regex, timeout=timeout)
445 result_list.append(child.match)
446 except pexpect.TIMEOUT:
447 raise error.TestFail("Timeout waiting for UART response.")
448 finally:
449 os.close(fd)
450 return result_list
451
452
Vic Yang4d72cb62012-07-24 11:51:09 +0800453 def check_ec_capability(self, required_cap=[]):
454 """Check if current platform has required EC capabilities.
455
456 Args:
457 required_cap: A list containing required EC capabilities. Pass in
458 None to only check for presence of Chrome EC.
459
460 Returns:
461 True if requirements are met. Otherwise, False.
462 """
463 if not self.client_attr.chrome_ec:
464 logging.warn('Requires Chrome EC to run this test.')
465 return False
466
467 for cap in required_cap:
468 if cap not in self.client_attr.ec_capability:
469 logging.warn('Requires EC capability "%s" to run this test.' %
470 cap)
471 return False
472
473 return True
474
475
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800476 def _parse_crossystem_output(self, lines):
477 """Parse the crossystem output into a dict.
478
479 Args:
480 lines: The list of crossystem output strings.
481
482 Returns:
483 A dict which contains the crossystem keys/values.
484
485 Raises:
486 error.TestError: If wrong format in crossystem output.
487
488 >>> seq = FAFTSequence()
489 >>> seq._parse_crossystem_output([ \
490 "arch = x86 # Platform architecture", \
491 "cros_debug = 1 # OS should allow debug", \
492 ])
493 {'cros_debug': '1', 'arch': 'x86'}
494 >>> seq._parse_crossystem_output([ \
495 "arch=x86", \
496 ])
497 Traceback (most recent call last):
498 ...
499 TestError: Failed to parse crossystem output: arch=x86
500 >>> seq._parse_crossystem_output([ \
501 "arch = x86 # Platform architecture", \
502 "arch = arm # Platform architecture", \
503 ])
504 Traceback (most recent call last):
505 ...
506 TestError: Duplicated crossystem key: arch
507 """
508 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
509 parsed_list = {}
510 for line in lines:
511 matched = re.match(pattern, line.strip())
512 if not matched:
513 raise error.TestError("Failed to parse crossystem output: %s"
514 % line)
515 (name, value) = (matched.group(1), matched.group(2))
516 if name in parsed_list:
517 raise error.TestError("Duplicated crossystem key: %s" % name)
518 parsed_list[name] = value
519 return parsed_list
520
521
522 def crossystem_checker(self, expected_dict):
523 """Check the crossystem values matched.
524
525 Given an expect_dict which describes the expected crossystem values,
526 this function check the current crossystem values are matched or not.
527
528 Args:
529 expected_dict: A dict which contains the expected values.
530
531 Returns:
532 True if the crossystem value matched; otherwise, False.
533 """
534 lines = self.faft_client.run_shell_command_get_output('crossystem')
535 got_dict = self._parse_crossystem_output(lines)
536 for key in expected_dict:
537 if key not in got_dict:
538 logging.info('Expected key "%s" not in crossystem result' % key)
539 return False
540 if isinstance(expected_dict[key], str):
541 if got_dict[key] != expected_dict[key]:
542 logging.info("Expected '%s' value '%s' but got '%s'" %
543 (key, expected_dict[key], got_dict[key]))
544 return False
545 elif isinstance(expected_dict[key], tuple):
546 # Expected value is a tuple of possible actual values.
547 if got_dict[key] not in expected_dict[key]:
548 logging.info("Expected '%s' values %s but got '%s'" %
549 (key, str(expected_dict[key]), got_dict[key]))
550 return False
551 else:
552 logging.info("The expected_dict is neither a str nor a dict.")
553 return False
554 return True
555
556
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800557 def root_part_checker(self, expected_part):
558 """Check the partition number of the root device matched.
559
560 Args:
561 expected_part: A string containing the number of the expected root
562 partition.
563
564 Returns:
565 True if the currect root partition number matched; otherwise, False.
566 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800567 part = self.faft_client.get_root_part()[-1]
568 if self.ROOTFS_MAP[expected_part] != part:
569 logging.info("Expected root part %s but got %s" %
570 (self.ROOTFS_MAP[expected_part], part))
571 return False
572 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800573
574
Vic Yang59cac9c2012-05-21 15:28:42 +0800575 def ec_act_copy_checker(self, expected_copy):
576 """Check the EC running firmware copy matches.
577
578 Args:
579 expected_copy: A string containing 'RO', 'A', or 'B' indicating
580 the expected copy of EC running firmware.
581
582 Returns:
583 True if the current EC running copy matches; otherwise, False.
584 """
585 lines = self.faft_client.run_shell_command_get_output('ectool version')
586 pattern = re.compile("Firmware copy: (.*)")
587 for line in lines:
588 matched = pattern.match(line)
589 if matched and matched.group(1) == expected_copy:
590 return True
591 return False
592
593
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800594 def check_root_part_on_non_recovery(self, part):
595 """Check the partition number of root device and on normal/dev boot.
596
597 Returns:
598 True if the root device matched and on normal/dev boot;
599 otherwise, False.
600 """
601 return self.root_part_checker(part) and \
602 self.crossystem_checker({
603 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800604 })
605
606
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800607 def _join_part(self, dev, part):
608 """Return a concatenated string of device and partition number.
609
610 Args:
611 dev: A string of device, e.g.'/dev/sda'.
612 part: A string of partition number, e.g.'3'.
613
614 Returns:
615 A concatenated string of device and partition number, e.g.'/dev/sda3'.
616
617 >>> seq = FAFTSequence()
618 >>> seq._join_part('/dev/sda', '3')
619 '/dev/sda3'
620 >>> seq._join_part('/dev/mmcblk0', '2')
621 '/dev/mmcblk0p2'
622 """
623 if 'mmcblk' in dev:
624 return dev + 'p' + part
625 else:
626 return dev + part
627
628
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800629 def copy_kernel_and_rootfs(self, from_part, to_part):
630 """Copy kernel and rootfs from from_part to to_part.
631
632 Args:
633 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800634 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800635 """
636 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800637 logging.info('Copying kernel from %s to %s. Please wait...' %
638 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800639 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800640 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
641 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
642 logging.info('Copying rootfs from %s to %s. Please wait...' %
643 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800644 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800645 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
646 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800647
648
649 def ensure_kernel_boot(self, part):
650 """Ensure the request kernel boot.
651
652 If not, it duplicates the current kernel to the requested kernel
653 and sets the requested higher priority to ensure it boot.
654
655 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800656 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800657 """
658 if not self.root_part_checker(part):
659 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
660 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800661 self.run_faft_step({
662 'userspace_action': (self.reset_and_prioritize_kernel, part),
663 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800664
665
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800666 def send_ctrl_d_to_dut(self):
667 """Send Ctrl-D key to DUT."""
668 if self._customized_ctrl_d_key_command:
669 logging.info('running the customized Ctrl-D key command')
670 os.system(self._customized_ctrl_d_key_command)
671 else:
672 self.servo.ctrl_d()
673
674
675 def send_enter_to_dut(self):
676 """Send Enter key to DUT."""
677 if self._customized_enter_key_command:
678 logging.info('running the customized Enter key command')
679 os.system(self._customized_enter_key_command)
680 else:
681 self.servo.enter_key()
682
683
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800684 def send_space_to_dut(self):
685 """Send Space key to DUT."""
686 if self._customized_space_key_command:
687 logging.info('running the customized Space key command')
688 os.system(self._customized_space_key_command)
689 else:
690 # Send the alternative key combinaton of space key to servo.
691 self.servo.ctrl_refresh_key()
692
693
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800694 def wait_fw_screen_and_ctrl_d(self):
695 """Wait for firmware warning screen and press Ctrl-D."""
696 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800697 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800698
699
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800700 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
701 """Wait for firmware warning screen and trigger recovery boot."""
702 time.sleep(self.FIRMWARE_SCREEN_DELAY)
703 self.send_enter_to_dut()
704
705 # For Alex/ZGB, there is a dev warning screen in text mode.
706 # Skip it by pressing Ctrl-D.
707 if need_dev_transition:
708 time.sleep(self.TEXT_SCREEN_DELAY)
709 self.send_ctrl_d_to_dut()
710
711
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800712 def wait_fw_screen_and_plug_usb(self):
713 """Wait for firmware warning screen and then unplug and plug the USB."""
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800714 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800715 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
716 time.sleep(self.USB_PLUG_DELAY)
717 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
718
719
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800720 def wait_fw_screen_and_press_power(self):
721 """Wait for firmware warning screen and press power button."""
722 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800723 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800724
725
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800726 def wait_longer_fw_screen_and_press_power(self):
727 """Wait for firmware screen without timeout and press power button."""
728 time.sleep(self.DEV_SCREEN_TIMEOUT)
729 self.wait_fw_screen_and_press_power()
730
731
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800732 def wait_fw_screen_and_close_lid(self):
733 """Wait for firmware warning screen and close lid."""
734 time.sleep(self.FIRMWARE_SCREEN_DELAY)
735 self.servo.lid_close()
736
737
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800738 def wait_longer_fw_screen_and_close_lid(self):
739 """Wait for firmware screen without timeout and close lid."""
740 time.sleep(self.FIRMWARE_SCREEN_DELAY)
741 self.wait_fw_screen_and_close_lid()
742
743
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800744 def setup_tried_fwb(self, tried_fwb):
745 """Setup for fw B tried state.
746
747 It makes sure the system in the requested fw B tried state. If not, it
748 tries to do so.
749
750 Args:
751 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
752 """
753 if tried_fwb:
754 if not self.crossystem_checker({'tried_fwb': '1'}):
755 logging.info(
756 'Firmware is not booted with tried_fwb. Reboot into it.')
757 self.run_faft_step({
758 'userspace_action': self.faft_client.set_try_fw_b,
759 })
760 else:
761 if not self.crossystem_checker({'tried_fwb': '0'}):
762 logging.info(
763 'Firmware is booted with tried_fwb. Reboot to clear.')
764 self.run_faft_step({})
765
766
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800767 def enable_rec_mode_and_reboot(self):
768 """Switch to rec mode and reboot.
769
770 This method emulates the behavior of the old physical recovery switch,
771 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
772 recovery mode, i.e. just press Power + Esc + Refresh.
773 """
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800774 if self._customized_rec_reboot_command:
775 logging.info('running the customized rec reboot command')
776 os.system(self._customized_rec_reboot_command)
Vic Yang611dd852012-08-02 15:36:31 +0800777 elif self.client_attr.ec_fake_rec_mode:
778 self.send_uart_command("reboot hard ap-off")
779 time.sleep(self.EC_BOOT_DELAY)
780 self.send_uart_command("hostevent set 0x4000")
781 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800782 else:
783 self.servo.enable_recovery_mode()
784 self.cold_reboot()
785 time.sleep(self.EC_REBOOT_DELAY)
786 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800787
788
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800789 def enable_dev_mode_and_reboot(self):
790 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800791 if self.client_attr.keyboard_dev:
792 self.enable_keyboard_dev_mode()
793 else:
794 self.servo.enable_development_mode()
795 self.faft_client.run_shell_command(
796 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800797
798
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800799 def enable_normal_mode_and_reboot(self):
800 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800801 if self.client_attr.keyboard_dev:
802 self.disable_keyboard_dev_mode()
803 else:
804 self.servo.disable_development_mode()
805 self.faft_client.run_shell_command(
806 'chromeos-firmwareupdate --mode tonormal && reboot')
807
808
809 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
810 """Wait for firmware screen and then switch into or out of dev mode.
811
812 Args:
813 dev: True if switching into dev mode. Otherwise, False.
814 """
815 time.sleep(self.FIRMWARE_SCREEN_DELAY)
816 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800817 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800818 else:
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800819 # Only SPACE can trigger TO_NORM screen officially.
820 # We send both SPACE and ENTER in order to make the old and new
821 # firmware still work. It won't trigger twice since only one of
822 # them takes effect.
823 # TODO Remove to ENTER when all devices use new firmware.
824 self.send_space_to_dut()
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800825 self.send_enter_to_dut()
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800826 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800827 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800828
829
830 def enable_keyboard_dev_mode(self):
831 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +0800832 # Plug out USB disk for preventing recovery boot without warning
833 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +0800834 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800835 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800836 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800837 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +0800838
839
840 def disable_keyboard_dev_mode(self):
841 logging.info("Disabling keyboard controlled developer mode")
Vic Yang611dd852012-08-02 15:36:31 +0800842 if not self.client_attr.ec_fake_rec_mode:
843 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800844 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800845 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800846 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800847
848
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800849 def setup_dev_mode(self, dev_mode):
850 """Setup for development mode.
851
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800852 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800853 tries to do so.
854
855 Args:
856 dev_mode: True if requested in dev mode; False if normal mode.
857 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800858 # Change the default firmware_action for dev mode passing the fw screen.
859 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800860 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
861 else None),
862 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800863 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +0800864 if (not self.client_attr.keyboard_dev and
865 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800866 logging.info('Dev switch is not on. Now switch it on.')
867 self.servo.enable_development_mode()
868 if not self.crossystem_checker({'devsw_boot': '1',
869 'mainfw_type': 'developer'}):
870 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800871 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800872 'userspace_action': None if self.client_attr.keyboard_dev
873 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800874 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800875 'reboot_action': self.enable_keyboard_dev_mode if
876 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800877 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800878 else:
Vic Yange7553162012-06-20 16:20:47 +0800879 if (not self.client_attr.keyboard_dev and
880 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800881 logging.info('Dev switch is not off. Now switch it off.')
882 self.servo.disable_development_mode()
883 if not self.crossystem_checker({'devsw_boot': '0',
884 'mainfw_type': 'normal'}):
885 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800886 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800887 'userspace_action': None if self.client_attr.keyboard_dev
888 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800889 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800890 'reboot_action': self.disable_keyboard_dev_mode if
891 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800892 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800893
894
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800895 def setup_kernel(self, part):
896 """Setup for kernel test.
897
898 It makes sure both kernel A and B bootable and the current boot is
899 the requested kernel part.
900
901 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800902 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800903 """
904 self.ensure_kernel_boot(part)
905 self.copy_kernel_and_rootfs(from_part=part,
906 to_part=self.OTHER_KERNEL_MAP[part])
907 self.reset_and_prioritize_kernel(part)
908
909
910 def reset_and_prioritize_kernel(self, part):
911 """Make the requested partition highest priority.
912
913 This function also reset kerenl A and B to bootable.
914
915 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800916 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800917 """
918 root_dev = self.faft_client.get_root_dev()
919 # Reset kernel A and B to bootable.
920 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
921 (self.KERNEL_MAP['a'], root_dev))
922 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
923 (self.KERNEL_MAP['b'], root_dev))
924 # Set kernel part highest priority.
925 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
926 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800927 # Safer to sync and wait until the cgpt status written to the disk.
928 self.faft_client.run_shell_command('sync')
929 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800930
931
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800932 def warm_reboot(self):
933 """Request a warm reboot.
934
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800935 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800936 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800937 # Use cold reset if the warm reset is broken.
938 if self.client_attr.broken_warm_reset:
939 self.servo.cold_reset()
940 else:
941 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800942
943
944 def cold_reboot(self):
945 """Request a cold reboot.
946
947 A wrapper for underlying servo cold reset.
948 """
949 if self.check_ec_capability():
950 # We don't use servo.cold_reset() here because software sync is
951 # not yet finished, and device may or may not come up after cold
952 # reset. Pressing power button before firmware comes up solves this.
953 #
954 # The correct behavior should be (not work now):
955 # - If rebooting EC with rec mode on, power on AP and it boots
956 # into recovery mode.
957 # - If rebooting EC with rec mode off, power on AP for software
958 # sync. Then AP checks if lid open or not. If lid open, continue;
959 # otherwise, shut AP down and need servo for a power button
960 # press.
961 self.servo.set('cold_reset', 'on')
962 self.servo.set('cold_reset', 'off')
963 time.sleep(self.POWER_BTN_DELAY)
964 self.servo.power_short_press()
965 else:
966 self.servo.cold_reset()
967
968
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800969 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800970 """Request the client sync and do a warm reboot.
971
972 This is the default reboot action on FAFT.
973 """
974 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800975 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800976 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800977
978
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800979 def sync_and_cold_reboot(self):
980 """Request the client sync and do a cold reboot.
981
982 This reboot action is used to reset EC for recovery mode.
983 """
984 self.faft_client.run_shell_command('sync')
985 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800986 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800987
988
Vic Yang59cac9c2012-05-21 15:28:42 +0800989 def sync_and_ec_reboot(self):
990 """Request the client sync and do a EC triggered reboot."""
991 self.faft_client.run_shell_command('sync')
992 time.sleep(self.SYNC_DELAY)
993 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec)&' %
994 self.EC_REBOOT_DELAY)
Vic Yangf86728a2012-07-30 10:44:07 +0800995 time.sleep(self.EC_REBOOT_DELAY)
996 self.check_lid_and_power_on()
997
998
999 def check_lid_and_power_on(self):
1000 """
1001 On devices with EC software sync, system powers on after EC reboots if
1002 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1003 This method checks lid switch state and presses power button if
1004 necessary.
1005 """
1006 if self.servo.get("lid_open") == "no":
1007 time.sleep(self.SOFTWARE_SYNC_DELAY)
1008 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001009
1010
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001011 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1012 """Modify the kernel header magic in USB stick.
1013
1014 The kernel header magic is the first 8-byte of kernel partition.
1015 We modify it to make it fail on kernel verification check.
1016
1017 Args:
1018 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1019 from_magic: A string of magic which we change it from.
1020 to_magic: A string of magic which we change it to.
1021
1022 Raises:
1023 error.TestError: if failed to change magic.
1024 """
1025 assert len(from_magic) == 8
1026 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001027 # USB image only contains one kernel.
1028 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001029 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1030 current_magic = utils.system_output(read_cmd)
1031 if current_magic == to_magic:
1032 logging.info("The kernel magic is already %s." % current_magic)
1033 return
1034 if current_magic != from_magic:
1035 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1036
1037 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1038 (from_magic, to_magic))
1039 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1040 " 2>/dev/null" % (to_magic, kernel_part))
1041 utils.system(write_cmd)
1042
1043 if utils.system_output(read_cmd) != to_magic:
1044 raise error.TestError("Failed to write new magic.")
1045
1046
1047 def corrupt_usb_kernel(self, usb_dev):
1048 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1049
1050 Args:
1051 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1052 """
1053 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1054 self.CORRUPTED_MAGIC)
1055
1056
1057 def restore_usb_kernel(self, usb_dev):
1058 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1059
1060 Args:
1061 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1062 """
1063 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1064 self.CHROMEOS_MAGIC)
1065
1066
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001067 def _call_action(self, action_tuple):
1068 """Call the action function with/without arguments.
1069
1070 Args:
1071 action_tuple: A function, or a tuple which consisted of a function
1072 and its arguments (if any).
1073
1074 Returns:
1075 The result value of the action function.
1076 """
1077 if isinstance(action_tuple, tuple):
1078 action = action_tuple[0]
1079 args = action_tuple[1:]
1080 if callable(action):
1081 logging.info('calling %s with parameter %s' % (
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001082 str(action), str(action_tuple[1])))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001083 return action(*args)
1084 else:
1085 logging.info('action is not callable!')
1086 else:
1087 action = action_tuple
1088 if action is not None:
1089 if callable(action):
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001090 logging.info('calling %s' % str(action))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001091 return action()
1092 else:
1093 logging.info('action is not callable!')
1094
1095 return None
1096
1097
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001098 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1099 post_power_action=None):
1100 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1101
1102 Args:
1103 shutdown_action: a function which makes DUT shutdown, like pressing
1104 power key.
1105 pre_power_action: a function which is called before next power on.
1106 post_power_action: a function which is called after next power on.
1107
1108 Raises:
1109 error.TestFail: if the shutdown_action() failed to turn DUT off.
1110 """
1111 self._call_action(shutdown_action)
1112 logging.info('Wait to ensure DUT shut down...')
1113 try:
1114 self.wait_for_client()
1115 raise error.TestFail(
1116 'Should shut the device down after calling %s.' %
1117 str(shutdown_action))
1118 except AssertionError:
1119 logging.info(
1120 'DUT is surely shutdown. We are going to power it on again...')
1121
1122 if pre_power_action:
1123 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001124 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001125 if post_power_action:
1126 self._call_action(post_power_action)
1127
1128
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001129 def register_faft_template(self, template):
1130 """Register FAFT template, the default FAFT_STEP of each step.
1131
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001132 Any missing field falls back to the original faft_template.
1133
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001134 Args:
1135 template: A FAFT_STEP dict.
1136 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001137 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001138
1139
1140 def register_faft_sequence(self, sequence):
1141 """Register FAFT sequence.
1142
1143 Args:
1144 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1145 """
1146 self._faft_sequence = sequence
1147
1148
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001149 def run_faft_step(self, step, no_reboot=False):
1150 """Run a single FAFT step.
1151
1152 Any missing field falls back to faft_template. An empty step means
1153 running the default faft_template.
1154
1155 Args:
1156 step: A FAFT_STEP dict.
1157 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001158
1159 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001160 error.TestFail: An error when the test failed.
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001161 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001162 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001163 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1164 'firmware_action', 'install_deps_after_boot')
1165
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001166 test = {}
1167 test.update(self._faft_template)
1168 test.update(step)
1169
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001170 for key in test:
1171 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001172 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001173
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001174 if test['state_checker']:
1175 if not self._call_action(test['state_checker']):
1176 raise error.TestFail('State checker failed!')
1177
1178 self._call_action(test['userspace_action'])
1179
1180 # Don't run reboot_action and firmware_action if no_reboot is True.
1181 if not no_reboot:
1182 self._call_action(test['reboot_action'])
1183 self.wait_for_client_offline()
1184 self._call_action(test['firmware_action'])
1185
1186 if 'install_deps_after_boot' in test:
1187 self.wait_for_client(
1188 install_deps=test['install_deps_after_boot'])
1189 else:
1190 self.wait_for_client()
1191
1192
1193 def run_faft_sequence(self):
1194 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001195 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001196 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001197 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001198 logging.info('======== Running FAFT sequence step %d ========' %
1199 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001200 # Don't reboot in the last step.
1201 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001202 index += 1