blob: bc1540f9adf027fbcd9f9db40959084076c93bf2 [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
Vic Yangb4e3e742012-06-02 13:17:38 +08005import fdpexpect
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08006import logging
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +08007import os
Vic Yangb4e3e742012-06-02 13:17:38 +08008import pexpect
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08009import re
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080010import sys
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080011import tempfile
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080012import time
13import xmlrpclib
14
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +080015from autotest_lib.client.bin import utils
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080016from autotest_lib.client.common_lib import error
Vic Yangebd6de62012-06-26 14:25:57 +080017from autotest_lib.server.cros.faft_client_attribute import FAFTClientAttribute
Tom Wai-Hong Tam22b77302011-11-03 13:03:48 +080018from autotest_lib.server.cros.servo_test import ServoTest
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080019from autotest_lib.site_utils import lab_test
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080020
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080021dirname = os.path.dirname(sys.modules[__name__].__file__)
22autotest_dir = os.path.abspath(os.path.join(dirname, "..", ".."))
23cros_dir = os.path.join(autotest_dir, "..", "..", "..", "..")
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080024
25class FAFTSequence(ServoTest):
26 """
27 The base class of Fully Automated Firmware Test Sequence.
28
29 Many firmware tests require several reboot cycles and verify the resulted
30 system states. To do that, an Autotest test case should detailly handle
31 every action on each step. It makes the test case hard to read and many
32 duplicated code. The base class FAFTSequence is to solve this problem.
33
34 The actions of one reboot cycle is defined in a dict, namely FAFT_STEP.
35 There are four functions in the FAFT_STEP dict:
36 state_checker: a function to check the current is valid or not,
37 returning True if valid, otherwise, False to break the whole
38 test sequence.
39 userspace_action: a function to describe the action ran in userspace.
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +080040 reboot_action: a function to do reboot, default: sync_and_warm_reboot.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080041 firmware_action: a function to describe the action ran after reboot.
42
Tom Wai-Hong Tam7c17ff22011-10-26 09:44:09 +080043 And configurations:
44 install_deps_after_boot: if True, install the Autotest dependency after
45 boot; otherwise, do nothing. It is for the cases of recovery mode
46 test. The test boots a USB/SD image instead of an internal image.
47 The previous installed Autotest dependency on the internal image
48 is lost. So need to install it again.
49
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080050 The default FAFT_STEP checks nothing in state_checker and does nothing in
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +080051 userspace_action and firmware_action. Its reboot_action is a hardware
52 reboot. You can change the default FAFT_STEP by calling
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080053 self.register_faft_template(FAFT_STEP).
54
55 A FAFT test case consists of several FAFT_STEP's, namely FAFT_SEQUENCE.
56 FAFT_SEQUENCE is an array of FAFT_STEP's. Any missing fields on FAFT_STEP
57 fall back to default.
58
59 In the run_once(), it should register and run FAFT_SEQUENCE like:
60 def run_once(self):
61 self.register_faft_sequence(FAFT_SEQUENCE)
62 self.run_faft_sequnce()
63
64 Note that in the last step, we only run state_checker. The
65 userspace_action, reboot_action, and firmware_action are not executed.
66
67 Attributes:
68 _faft_template: The default FAFT_STEP of each step. The actions would
69 be over-written if the registered FAFT_SEQUENCE is valid.
70 _faft_sequence: The registered FAFT_SEQUENCE.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080071 _customized_ctrl_d_key_command: The customized Ctrl-D key command
72 instead of sending key via servo board.
73 _customized_enter_key_command: The customized Enter key command instead
74 of sending key via servo board.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +080075 _customized_space_key_command: The customized Space key command instead
76 of sending key via servo board.
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +080077 _customized_rec_reboot_command: The customized recovery reboot command
78 instead of sending key combination of Power + Esc + F3 for
79 triggering recovery reboot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080080 _install_image_path: The path of Chrome OS test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080081 _firmware_update: Boolean. True if firmware update needed after
82 installing the image.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080083 """
84 version = 1
85
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080086
87 # Mapping of partition number of kernel and rootfs.
88 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
89 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
90 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
91 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
92
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080093 # Delay between power-on and firmware screen.
Tom Wai-Hong Tam66af37b2012-08-01 10:48:42 +080094 FIRMWARE_SCREEN_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080095 # Delay between passing firmware screen and text mode warning screen.
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080096 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080097 # Delay of loading the USB kernel.
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080098 USB_LOAD_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080099 # Delay between USB plug-out and plug-in.
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +0800100 USB_PLUG_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800101 # Delay after running the 'sync' command.
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800102 SYNC_DELAY = 5
Vic Yang59cac9c2012-05-21 15:28:42 +0800103 # Delay for waiting client to return before EC reboot
104 EC_REBOOT_DELAY = 1
105 # Delay between EC reboot and pressing power button
106 POWER_BTN_DELAY = 0.5
Vic Yangf86728a2012-07-30 10:44:07 +0800107 # Delay of EC software sync hash calculating time
108 SOFTWARE_SYNC_DELAY = 6
Vic Yang611dd852012-08-02 15:36:31 +0800109 # Delay between EC boot and EC console functional
110 EC_BOOT_DELAY = 2
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800111
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800112 # The developer screen timeouts fit our spec.
113 DEV_SCREEN_TIMEOUT = 30
114
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800115 CHROMEOS_MAGIC = "CHROMEOS"
116 CORRUPTED_MAGIC = "CORRUPTD"
117
Tom Wai-Hong Tamf954d172011-12-08 17:14:15 +0800118 # Recovery reason codes, copied from:
119 # vboot_reference/firmware/lib/vboot_nvstorage.h
120 # vboot_reference/firmware/lib/vboot_struct.h
121 RECOVERY_REASON = {
122 # Recovery not requested
123 'NOT_REQUESTED': '0', # 0x00
124 # Recovery requested from legacy utility
125 'LEGACY': '1', # 0x01
126 # User manually requested recovery via recovery button
127 'RO_MANUAL': '2', # 0x02
128 # RW firmware failed signature check
129 'RO_INVALID_RW': '3', # 0x03
130 # S3 resume failed
131 'RO_S3_RESUME': '4', # 0x04
132 # TPM error in read-only firmware
133 'RO_TPM_ERROR': '5', # 0x05
134 # Shared data error in read-only firmware
135 'RO_SHARED_DATA': '6', # 0x06
136 # Test error from S3Resume()
137 'RO_TEST_S3': '7', # 0x07
138 # Test error from LoadFirmwareSetup()
139 'RO_TEST_LFS': '8', # 0x08
140 # Test error from LoadFirmware()
141 'RO_TEST_LF': '9', # 0x09
142 # RW firmware failed signature check
143 'RW_NOT_DONE': '16', # 0x10
144 'RW_DEV_MISMATCH': '17', # 0x11
145 'RW_REC_MISMATCH': '18', # 0x12
146 'RW_VERIFY_KEYBLOCK': '19', # 0x13
147 'RW_KEY_ROLLBACK': '20', # 0x14
148 'RW_DATA_KEY_PARSE': '21', # 0x15
149 'RW_VERIFY_PREAMBLE': '22', # 0x16
150 'RW_FW_ROLLBACK': '23', # 0x17
151 'RW_HEADER_VALID': '24', # 0x18
152 'RW_GET_FW_BODY': '25', # 0x19
153 'RW_HASH_WRONG_SIZE': '26', # 0x1A
154 'RW_VERIFY_BODY': '27', # 0x1B
155 'RW_VALID': '28', # 0x1C
156 # Read-only normal path requested by firmware preamble, but
157 # unsupported by firmware.
158 'RW_NO_RO_NORMAL': '29', # 0x1D
159 # Firmware boot failure outside of verified boot
160 'RO_FIRMWARE': '32', # 0x20
161 # Recovery mode TPM initialization requires a system reboot.
162 # The system was already in recovery mode for some other reason
163 # when this happened.
164 'RO_TPM_REBOOT': '33', # 0x21
165 # Unspecified/unknown error in read-only firmware
166 'RO_UNSPECIFIED': '63', # 0x3F
167 # User manually requested recovery by pressing a key at developer
168 # warning screen.
169 'RW_DEV_SCREEN': '65', # 0x41
170 # No OS kernel detected
171 'RW_NO_OS': '66', # 0x42
172 # OS kernel failed signature check
173 'RW_INVALID_OS': '67', # 0x43
174 # TPM error in rewritable firmware
175 'RW_TPM_ERROR': '68', # 0x44
176 # RW firmware in dev mode, but dev switch is off.
177 'RW_DEV_MISMATCH': '69', # 0x45
178 # Shared data error in rewritable firmware
179 'RW_SHARED_DATA': '70', # 0x46
180 # Test error from LoadKernel()
181 'RW_TEST_LK': '71', # 0x47
182 # No bootable disk found
183 'RW_NO_DISK': '72', # 0x48
184 # Unspecified/unknown error in rewritable firmware
185 'RW_UNSPECIFIED': '127', # 0x7F
186 # DM-verity error
187 'KE_DM_VERITY': '129', # 0x81
188 # Unspecified/unknown error in kernel
189 'KE_UNSPECIFIED': '191', # 0xBF
190 # Recovery mode test from user-mode
191 'US_TEST': '193', # 0xC1
192 # Unspecified/unknown error in user-mode
193 'US_UNSPECIFIED': '255', # 0xFF
194 }
195
Tom Wai-Hong Tam1e40fa12012-07-25 16:22:24 +0800196 # GBB flags
197 GBB_FLAG_DEV_SCREEN_SHORT_DELAY = 0x00000001
198 GBB_FLAG_LOAD_OPTION_ROMS = 0x00000002
199 GBB_FLAG_ENABLE_ALTERNATE_OS = 0x00000004
200 GBB_FLAG_FORCE_DEV_SWITCH_ON = 0x00000008
201 GBB_FLAG_FORCE_DEV_BOOT_USB = 0x00000010
202 GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = 0x00000020
203
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800204 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800205 _faft_sequence = ()
206
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800207 _customized_ctrl_d_key_command = None
208 _customized_enter_key_command = None
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800209 _customized_space_key_command = None
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800210 _customized_rec_reboot_command = None
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800211 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800212 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800213
214
215 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
216 # Parse arguments from command line
217 args = {}
218 for arg in cmdline_args:
219 match = re.search("^(\w+)=(.+)", arg)
220 if match:
221 args[match.group(1)] = match.group(2)
222
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800223 # Keep the arguments which will be used later.
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800224 if 'ctrl_d_cmd' in args:
225 self._customized_ctrl_d_key_command = args['ctrl_d_cmd']
226 logging.info('Customized Ctrl-D key command: %s' %
227 self._customized_ctrl_d_key_command)
228 if 'enter_cmd' in args:
229 self._customized_enter_key_command = args['enter_cmd']
230 logging.info('Customized Enter key command: %s' %
231 self._customized_enter_key_command)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800232 if 'space_cmd' in args:
233 self._customized_space_key_command = args['space_cmd']
234 logging.info('Customized Space key command: %s' %
235 self._customized_space_key_command)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800236 if 'rec_reboot_cmd' in args:
237 self._customized_rec_reboot_command = args['rec_reboot_cmd']
238 logging.info('Customized recovery reboot command: %s' %
239 self._customized_rec_reboot_command)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800240 if 'image' in args:
241 self._install_image_path = args['image']
242 logging.info('Install Chrome OS test image path: %s' %
243 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800244 if 'firmware_update' in args and args['firmware_update'].lower() \
245 not in ('0', 'false', 'no'):
246 if self._install_image_path:
247 self._firmware_update = True
248 logging.info('Also update firmware after installing.')
249 else:
250 logging.warning('Firmware update will not not performed '
251 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800252
253 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
254 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800255 if use_faft:
256 self.client_attr = FAFTClientAttribute(
257 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800258
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800259
260 def setup(self):
261 """Autotest setup function."""
262 super(FAFTSequence, self).setup()
263 if not self._remote_infos['faft']['used']:
264 raise error.TestError('The use_faft flag should be enabled.')
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800265 self.clear_gbb_flags(self.GBB_FLAG_FORCE_DEV_SWITCH_ON)
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800266 self.clear_gbb_flags(self.GBB_FLAG_DEV_SCREEN_SHORT_DELAY)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800267 self.register_faft_template({
268 'state_checker': (None),
269 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800270 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800271 'firmware_action': (None)
272 })
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800273 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800274 self.install_test_image(self._install_image_path,
275 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800276
277
278 def cleanup(self):
279 """Autotest cleanup function."""
280 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800281 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800282 super(FAFTSequence, self).cleanup()
283
284
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800285 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800286 """Assert an USB disk plugged-in on servo and a test image inside.
287
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800288 Args:
289 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
290 If None, it is detected automatically.
291
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800292 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800293 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800294 """
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800295 if usb_dev:
296 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
297 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700298 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800299 usb_dev = self.servo.probe_host_usb_dev()
300 if not usb_dev:
301 raise error.TestError(
302 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800303
304 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800305 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800306 code = utils.system(
307 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
308 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800309 utils.system('sudo umount %s' % tmp_dir)
310 os.removedirs(tmp_dir)
311 if code != 0:
312 raise error.TestError(
313 'The image in the USB disk should be a test image.')
314
315
Simran Basi741b5d42012-05-18 11:27:15 -0700316 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800317 """Install the test image specied by the path onto the USB and DUT disk.
318
319 The method first copies the image to USB disk and reboots into it via
320 recovery mode. Then runs 'chromeos-install' to install it to DUT disk.
321
322 Args:
323 image_path: Path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800324 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800325 """
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800326 install_cmd = 'chromeos-install --yes'
327 if firmware_update:
328 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800329 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
330 image_path)
331 logging.info('Processing build: %s %s' % (build_ver, build_hash))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800332
333 # Reuse the install_recovery_image method by using a test image.
334 # Don't wait for completion but run chromeos-install to install it.
Simran Basi741b5d42012-05-18 11:27:15 -0700335 self.servo.install_recovery_image(image_path)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800336 self.wait_for_client(install_deps=True)
337 self.run_faft_step({
338 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800339 install_cmd)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800340 })
341
342
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800343 def clear_gbb_flags(self, mask):
344 """Clear the GBB flags in the current flashrom.
345
346 Args:
347 mask: A mask of flags to be cleared.
348 """
349 gbb_flags = self.faft_client.get_gbb_flags()
350 if (gbb_flags & mask):
351 logging.info('Clear the GBB flags of 0x%x, from 0x%x to 0x%x.' %
352 (mask, gbb_flags, gbb_flags ^ mask))
353 self.faft_client.run_shell_command(
354 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' %
355 (gbb_flags ^ mask))
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800356 self.faft_client.reload_firmware()
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800357
358
Vic Yangb4e3e742012-06-02 13:17:38 +0800359 def _open_uart_pty(self):
360 """Open UART pty and spawn pexpect object.
361
362 Returns:
363 Tuple (fd, child): fd is the file descriptor of opened UART pty, and
364 child is a fdpexpect object tied to it.
365 """
366 fd = os.open(self.servo.get("uart1_pty"), os.O_RDWR | os.O_NONBLOCK)
367 child = fdpexpect.fdspawn(fd)
368 return (fd, child)
369
370
371 def _flush_uart_pty(self, child):
372 """Flush UART output to prevent previous pending message interferring.
373
374 Args:
375 child: The fdpexpect object tied to UART pty.
376 """
377 child.sendline("")
378 while True:
379 try:
380 child.expect(".", timeout=0.01)
381 except pexpect.TIMEOUT:
382 break
383
384
385 def _uart_send(self, child, line):
386 """Flush and send command through UART.
387
388 Args:
389 child: The pexpect object tied to UART pty.
390 line: String to send through UART.
391
392 Raises:
393 error.TestFail: Raised when writing to UART fails.
394 """
395 logging.info("Sending UART command: %s" % line)
396 self._flush_uart_pty(child)
397 if child.sendline(line) != len(line) + 1:
398 raise error.TestFail("Failed to send UART command.")
399
400
401 def send_uart_command(self, command):
402 """Send command through UART.
403
404 This function open UART pty when called, and then command is sent
405 through UART.
406
407 Args:
408 command: The command string to send.
409
410 Raises:
411 error.TestFail: Raised when writing to UART fails.
412 """
413 (fd, child) = self._open_uart_pty()
414 try:
415 self._uart_send(child, command)
416 finally:
417 os.close(fd)
418
419
420 def send_uart_command_get_output(self, command, regex_list, timeout=1):
421 """Send command through UART and wait for response.
422
423 This function waits for response message matching regular expressions.
424
425 Args:
426 command: The command sent.
427 regex_list: List of regular expressions used to match response message.
428 Note, list must be ordered.
429
430 Returns:
431 List of match objects of response message.
432
433 Raises:
434 error.TestFail: If timed out waiting for EC response.
435 """
436 if not isinstance(regex_list, list):
437 regex_list = [regex_list]
438 result_list = []
439 (fd, child) = self._open_uart_pty()
440 try:
441 self._uart_send(child, command)
442 for regex in regex_list:
443 child.expect(regex, timeout=timeout)
444 result_list.append(child.match)
445 except pexpect.TIMEOUT:
446 raise error.TestFail("Timeout waiting for UART response.")
447 finally:
448 os.close(fd)
449 return result_list
450
451
Vic Yang4d72cb62012-07-24 11:51:09 +0800452 def check_ec_capability(self, required_cap=[]):
453 """Check if current platform has required EC capabilities.
454
455 Args:
456 required_cap: A list containing required EC capabilities. Pass in
457 None to only check for presence of Chrome EC.
458
459 Returns:
460 True if requirements are met. Otherwise, False.
461 """
462 if not self.client_attr.chrome_ec:
463 logging.warn('Requires Chrome EC to run this test.')
464 return False
465
466 for cap in required_cap:
467 if cap not in self.client_attr.ec_capability:
468 logging.warn('Requires EC capability "%s" to run this test.' %
469 cap)
470 return False
471
472 return True
473
474
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800475 def _parse_crossystem_output(self, lines):
476 """Parse the crossystem output into a dict.
477
478 Args:
479 lines: The list of crossystem output strings.
480
481 Returns:
482 A dict which contains the crossystem keys/values.
483
484 Raises:
485 error.TestError: If wrong format in crossystem output.
486
487 >>> seq = FAFTSequence()
488 >>> seq._parse_crossystem_output([ \
489 "arch = x86 # Platform architecture", \
490 "cros_debug = 1 # OS should allow debug", \
491 ])
492 {'cros_debug': '1', 'arch': 'x86'}
493 >>> seq._parse_crossystem_output([ \
494 "arch=x86", \
495 ])
496 Traceback (most recent call last):
497 ...
498 TestError: Failed to parse crossystem output: arch=x86
499 >>> seq._parse_crossystem_output([ \
500 "arch = x86 # Platform architecture", \
501 "arch = arm # Platform architecture", \
502 ])
503 Traceback (most recent call last):
504 ...
505 TestError: Duplicated crossystem key: arch
506 """
507 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
508 parsed_list = {}
509 for line in lines:
510 matched = re.match(pattern, line.strip())
511 if not matched:
512 raise error.TestError("Failed to parse crossystem output: %s"
513 % line)
514 (name, value) = (matched.group(1), matched.group(2))
515 if name in parsed_list:
516 raise error.TestError("Duplicated crossystem key: %s" % name)
517 parsed_list[name] = value
518 return parsed_list
519
520
521 def crossystem_checker(self, expected_dict):
522 """Check the crossystem values matched.
523
524 Given an expect_dict which describes the expected crossystem values,
525 this function check the current crossystem values are matched or not.
526
527 Args:
528 expected_dict: A dict which contains the expected values.
529
530 Returns:
531 True if the crossystem value matched; otherwise, False.
532 """
533 lines = self.faft_client.run_shell_command_get_output('crossystem')
534 got_dict = self._parse_crossystem_output(lines)
535 for key in expected_dict:
536 if key not in got_dict:
537 logging.info('Expected key "%s" not in crossystem result' % key)
538 return False
539 if isinstance(expected_dict[key], str):
540 if got_dict[key] != expected_dict[key]:
541 logging.info("Expected '%s' value '%s' but got '%s'" %
542 (key, expected_dict[key], got_dict[key]))
543 return False
544 elif isinstance(expected_dict[key], tuple):
545 # Expected value is a tuple of possible actual values.
546 if got_dict[key] not in expected_dict[key]:
547 logging.info("Expected '%s' values %s but got '%s'" %
548 (key, str(expected_dict[key]), got_dict[key]))
549 return False
550 else:
551 logging.info("The expected_dict is neither a str nor a dict.")
552 return False
553 return True
554
555
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800556 def root_part_checker(self, expected_part):
557 """Check the partition number of the root device matched.
558
559 Args:
560 expected_part: A string containing the number of the expected root
561 partition.
562
563 Returns:
564 True if the currect root partition number matched; otherwise, False.
565 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800566 part = self.faft_client.get_root_part()[-1]
567 if self.ROOTFS_MAP[expected_part] != part:
568 logging.info("Expected root part %s but got %s" %
569 (self.ROOTFS_MAP[expected_part], part))
570 return False
571 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800572
573
Vic Yang59cac9c2012-05-21 15:28:42 +0800574 def ec_act_copy_checker(self, expected_copy):
575 """Check the EC running firmware copy matches.
576
577 Args:
578 expected_copy: A string containing 'RO', 'A', or 'B' indicating
579 the expected copy of EC running firmware.
580
581 Returns:
582 True if the current EC running copy matches; otherwise, False.
583 """
584 lines = self.faft_client.run_shell_command_get_output('ectool version')
585 pattern = re.compile("Firmware copy: (.*)")
586 for line in lines:
587 matched = pattern.match(line)
588 if matched and matched.group(1) == expected_copy:
589 return True
590 return False
591
592
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800593 def check_root_part_on_non_recovery(self, part):
594 """Check the partition number of root device and on normal/dev boot.
595
596 Returns:
597 True if the root device matched and on normal/dev boot;
598 otherwise, False.
599 """
600 return self.root_part_checker(part) and \
601 self.crossystem_checker({
602 'mainfw_type': ('normal', 'developer'),
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800603 })
604
605
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800606 def _join_part(self, dev, part):
607 """Return a concatenated string of device and partition number.
608
609 Args:
610 dev: A string of device, e.g.'/dev/sda'.
611 part: A string of partition number, e.g.'3'.
612
613 Returns:
614 A concatenated string of device and partition number, e.g.'/dev/sda3'.
615
616 >>> seq = FAFTSequence()
617 >>> seq._join_part('/dev/sda', '3')
618 '/dev/sda3'
619 >>> seq._join_part('/dev/mmcblk0', '2')
620 '/dev/mmcblk0p2'
621 """
622 if 'mmcblk' in dev:
623 return dev + 'p' + part
624 else:
625 return dev + part
626
627
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800628 def copy_kernel_and_rootfs(self, from_part, to_part):
629 """Copy kernel and rootfs from from_part to to_part.
630
631 Args:
632 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800633 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800634 """
635 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800636 logging.info('Copying kernel from %s to %s. Please wait...' %
637 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800638 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800639 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
640 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
641 logging.info('Copying rootfs from %s to %s. Please wait...' %
642 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800643 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800644 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
645 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800646
647
648 def ensure_kernel_boot(self, part):
649 """Ensure the request kernel boot.
650
651 If not, it duplicates the current kernel to the requested kernel
652 and sets the requested higher priority to ensure it boot.
653
654 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800655 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800656 """
657 if not self.root_part_checker(part):
658 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
659 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800660 self.run_faft_step({
661 'userspace_action': (self.reset_and_prioritize_kernel, part),
662 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800663
664
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800665 def send_ctrl_d_to_dut(self):
666 """Send Ctrl-D key to DUT."""
667 if self._customized_ctrl_d_key_command:
668 logging.info('running the customized Ctrl-D key command')
669 os.system(self._customized_ctrl_d_key_command)
670 else:
671 self.servo.ctrl_d()
672
673
674 def send_enter_to_dut(self):
675 """Send Enter key to DUT."""
676 if self._customized_enter_key_command:
677 logging.info('running the customized Enter key command')
678 os.system(self._customized_enter_key_command)
679 else:
680 self.servo.enter_key()
681
682
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800683 def send_space_to_dut(self):
684 """Send Space key to DUT."""
685 if self._customized_space_key_command:
686 logging.info('running the customized Space key command')
687 os.system(self._customized_space_key_command)
688 else:
689 # Send the alternative key combinaton of space key to servo.
690 self.servo.ctrl_refresh_key()
691
692
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800693 def wait_fw_screen_and_ctrl_d(self):
694 """Wait for firmware warning screen and press Ctrl-D."""
695 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800696 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800697
698
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800699 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
700 """Wait for firmware warning screen and trigger recovery boot."""
701 time.sleep(self.FIRMWARE_SCREEN_DELAY)
702 self.send_enter_to_dut()
703
704 # For Alex/ZGB, there is a dev warning screen in text mode.
705 # Skip it by pressing Ctrl-D.
706 if need_dev_transition:
707 time.sleep(self.TEXT_SCREEN_DELAY)
708 self.send_ctrl_d_to_dut()
709
710
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800711 def wait_fw_screen_and_plug_usb(self):
712 """Wait for firmware warning screen and then unplug and plug the USB."""
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800713 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800714 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
715 time.sleep(self.USB_PLUG_DELAY)
716 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
717
718
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800719 def wait_fw_screen_and_press_power(self):
720 """Wait for firmware warning screen and press power button."""
721 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800722 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800723
724
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800725 def wait_longer_fw_screen_and_press_power(self):
726 """Wait for firmware screen without timeout and press power button."""
727 time.sleep(self.DEV_SCREEN_TIMEOUT)
728 self.wait_fw_screen_and_press_power()
729
730
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800731 def wait_fw_screen_and_close_lid(self):
732 """Wait for firmware warning screen and close lid."""
733 time.sleep(self.FIRMWARE_SCREEN_DELAY)
734 self.servo.lid_close()
735
736
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800737 def wait_longer_fw_screen_and_close_lid(self):
738 """Wait for firmware screen without timeout and close lid."""
739 time.sleep(self.FIRMWARE_SCREEN_DELAY)
740 self.wait_fw_screen_and_close_lid()
741
742
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800743 def setup_tried_fwb(self, tried_fwb):
744 """Setup for fw B tried state.
745
746 It makes sure the system in the requested fw B tried state. If not, it
747 tries to do so.
748
749 Args:
750 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
751 """
752 if tried_fwb:
753 if not self.crossystem_checker({'tried_fwb': '1'}):
754 logging.info(
755 'Firmware is not booted with tried_fwb. Reboot into it.')
756 self.run_faft_step({
757 'userspace_action': self.faft_client.set_try_fw_b,
758 })
759 else:
760 if not self.crossystem_checker({'tried_fwb': '0'}):
761 logging.info(
762 'Firmware is booted with tried_fwb. Reboot to clear.')
763 self.run_faft_step({})
764
765
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800766 def enable_rec_mode_and_reboot(self):
767 """Switch to rec mode and reboot.
768
769 This method emulates the behavior of the old physical recovery switch,
770 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
771 recovery mode, i.e. just press Power + Esc + Refresh.
772 """
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800773 if self._customized_rec_reboot_command:
774 logging.info('running the customized rec reboot command')
775 os.system(self._customized_rec_reboot_command)
Vic Yang611dd852012-08-02 15:36:31 +0800776 elif self.client_attr.ec_fake_rec_mode:
777 self.send_uart_command("reboot hard ap-off")
778 time.sleep(self.EC_BOOT_DELAY)
779 self.send_uart_command("hostevent set 0x4000")
780 self.servo.power_short_press()
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800781 else:
782 self.servo.enable_recovery_mode()
783 self.cold_reboot()
784 time.sleep(self.EC_REBOOT_DELAY)
785 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800786
787
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800788 def enable_dev_mode_and_reboot(self):
789 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800790 if self.client_attr.keyboard_dev:
791 self.enable_keyboard_dev_mode()
792 else:
793 self.servo.enable_development_mode()
794 self.faft_client.run_shell_command(
795 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800796
797
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800798 def enable_normal_mode_and_reboot(self):
799 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800800 if self.client_attr.keyboard_dev:
801 self.disable_keyboard_dev_mode()
802 else:
803 self.servo.disable_development_mode()
804 self.faft_client.run_shell_command(
805 'chromeos-firmwareupdate --mode tonormal && reboot')
806
807
808 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
809 """Wait for firmware screen and then switch into or out of dev mode.
810
811 Args:
812 dev: True if switching into dev mode. Otherwise, False.
813 """
814 time.sleep(self.FIRMWARE_SCREEN_DELAY)
815 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800816 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800817 else:
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800818 # Only SPACE can trigger TO_NORM screen officially.
819 # We send both SPACE and ENTER in order to make the old and new
820 # firmware still work. It won't trigger twice since only one of
821 # them takes effect.
822 # TODO Remove to ENTER when all devices use new firmware.
823 self.send_space_to_dut()
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800824 self.send_enter_to_dut()
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800825 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800826 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800827
828
829 def enable_keyboard_dev_mode(self):
830 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +0800831 # Plug out USB disk for preventing recovery boot without warning
832 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +0800833 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800834 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800835 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800836 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +0800837
838
839 def disable_keyboard_dev_mode(self):
840 logging.info("Disabling keyboard controlled developer mode")
Vic Yang611dd852012-08-02 15:36:31 +0800841 if not self.client_attr.ec_fake_rec_mode:
842 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800843 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800844 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800845 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800846
847
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800848 def setup_dev_mode(self, dev_mode):
849 """Setup for development mode.
850
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800851 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800852 tries to do so.
853
854 Args:
855 dev_mode: True if requested in dev mode; False if normal mode.
856 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800857 # Change the default firmware_action for dev mode passing the fw screen.
858 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800859 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
860 else None),
861 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800862 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +0800863 if (not self.client_attr.keyboard_dev and
864 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800865 logging.info('Dev switch is not on. Now switch it on.')
866 self.servo.enable_development_mode()
867 if not self.crossystem_checker({'devsw_boot': '1',
868 'mainfw_type': 'developer'}):
869 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800870 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800871 'userspace_action': None if self.client_attr.keyboard_dev
872 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800873 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800874 'reboot_action': self.enable_keyboard_dev_mode if
875 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800876 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800877 else:
Vic Yange7553162012-06-20 16:20:47 +0800878 if (not self.client_attr.keyboard_dev and
879 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800880 logging.info('Dev switch is not off. Now switch it off.')
881 self.servo.disable_development_mode()
882 if not self.crossystem_checker({'devsw_boot': '0',
883 'mainfw_type': 'normal'}):
884 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800885 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800886 'userspace_action': None if self.client_attr.keyboard_dev
887 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800888 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800889 'reboot_action': self.disable_keyboard_dev_mode if
890 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800891 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800892
893
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800894 def setup_kernel(self, part):
895 """Setup for kernel test.
896
897 It makes sure both kernel A and B bootable and the current boot is
898 the requested kernel part.
899
900 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800901 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800902 """
903 self.ensure_kernel_boot(part)
904 self.copy_kernel_and_rootfs(from_part=part,
905 to_part=self.OTHER_KERNEL_MAP[part])
906 self.reset_and_prioritize_kernel(part)
907
908
909 def reset_and_prioritize_kernel(self, part):
910 """Make the requested partition highest priority.
911
912 This function also reset kerenl A and B to bootable.
913
914 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800915 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800916 """
917 root_dev = self.faft_client.get_root_dev()
918 # Reset kernel A and B to bootable.
919 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
920 (self.KERNEL_MAP['a'], root_dev))
921 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
922 (self.KERNEL_MAP['b'], root_dev))
923 # Set kernel part highest priority.
924 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
925 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800926 # Safer to sync and wait until the cgpt status written to the disk.
927 self.faft_client.run_shell_command('sync')
928 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800929
930
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800931 def warm_reboot(self):
932 """Request a warm reboot.
933
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800934 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800935 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800936 # Use cold reset if the warm reset is broken.
937 if self.client_attr.broken_warm_reset:
938 self.servo.cold_reset()
939 else:
940 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800941
942
943 def cold_reboot(self):
944 """Request a cold reboot.
945
946 A wrapper for underlying servo cold reset.
947 """
948 if self.check_ec_capability():
949 # We don't use servo.cold_reset() here because software sync is
950 # not yet finished, and device may or may not come up after cold
951 # reset. Pressing power button before firmware comes up solves this.
952 #
953 # The correct behavior should be (not work now):
954 # - If rebooting EC with rec mode on, power on AP and it boots
955 # into recovery mode.
956 # - If rebooting EC with rec mode off, power on AP for software
957 # sync. Then AP checks if lid open or not. If lid open, continue;
958 # otherwise, shut AP down and need servo for a power button
959 # press.
960 self.servo.set('cold_reset', 'on')
961 self.servo.set('cold_reset', 'off')
962 time.sleep(self.POWER_BTN_DELAY)
963 self.servo.power_short_press()
964 else:
965 self.servo.cold_reset()
966
967
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800968 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800969 """Request the client sync and do a warm reboot.
970
971 This is the default reboot action on FAFT.
972 """
973 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800974 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800975 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800976
977
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800978 def sync_and_cold_reboot(self):
979 """Request the client sync and do a cold reboot.
980
981 This reboot action is used to reset EC for recovery mode.
982 """
983 self.faft_client.run_shell_command('sync')
984 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800985 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800986
987
Vic Yang59cac9c2012-05-21 15:28:42 +0800988 def sync_and_ec_reboot(self):
989 """Request the client sync and do a EC triggered reboot."""
990 self.faft_client.run_shell_command('sync')
991 time.sleep(self.SYNC_DELAY)
992 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec)&' %
993 self.EC_REBOOT_DELAY)
Vic Yangf86728a2012-07-30 10:44:07 +0800994 time.sleep(self.EC_REBOOT_DELAY)
995 self.check_lid_and_power_on()
996
997
998 def check_lid_and_power_on(self):
999 """
1000 On devices with EC software sync, system powers on after EC reboots if
1001 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
1002 This method checks lid switch state and presses power button if
1003 necessary.
1004 """
1005 if self.servo.get("lid_open") == "no":
1006 time.sleep(self.SOFTWARE_SYNC_DELAY)
1007 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001008
1009
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001010 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1011 """Modify the kernel header magic in USB stick.
1012
1013 The kernel header magic is the first 8-byte of kernel partition.
1014 We modify it to make it fail on kernel verification check.
1015
1016 Args:
1017 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1018 from_magic: A string of magic which we change it from.
1019 to_magic: A string of magic which we change it to.
1020
1021 Raises:
1022 error.TestError: if failed to change magic.
1023 """
1024 assert len(from_magic) == 8
1025 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001026 # USB image only contains one kernel.
1027 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001028 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1029 current_magic = utils.system_output(read_cmd)
1030 if current_magic == to_magic:
1031 logging.info("The kernel magic is already %s." % current_magic)
1032 return
1033 if current_magic != from_magic:
1034 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1035
1036 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1037 (from_magic, to_magic))
1038 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1039 " 2>/dev/null" % (to_magic, kernel_part))
1040 utils.system(write_cmd)
1041
1042 if utils.system_output(read_cmd) != to_magic:
1043 raise error.TestError("Failed to write new magic.")
1044
1045
1046 def corrupt_usb_kernel(self, usb_dev):
1047 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1048
1049 Args:
1050 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1051 """
1052 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1053 self.CORRUPTED_MAGIC)
1054
1055
1056 def restore_usb_kernel(self, usb_dev):
1057 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1058
1059 Args:
1060 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1061 """
1062 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1063 self.CHROMEOS_MAGIC)
1064
1065
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001066 def _call_action(self, action_tuple):
1067 """Call the action function with/without arguments.
1068
1069 Args:
1070 action_tuple: A function, or a tuple which consisted of a function
1071 and its arguments (if any).
1072
1073 Returns:
1074 The result value of the action function.
1075 """
1076 if isinstance(action_tuple, tuple):
1077 action = action_tuple[0]
1078 args = action_tuple[1:]
1079 if callable(action):
1080 logging.info('calling %s with parameter %s' % (
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001081 str(action), str(action_tuple[1])))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001082 return action(*args)
1083 else:
1084 logging.info('action is not callable!')
1085 else:
1086 action = action_tuple
1087 if action is not None:
1088 if callable(action):
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001089 logging.info('calling %s' % str(action))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001090 return action()
1091 else:
1092 logging.info('action is not callable!')
1093
1094 return None
1095
1096
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001097 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1098 post_power_action=None):
1099 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1100
1101 Args:
1102 shutdown_action: a function which makes DUT shutdown, like pressing
1103 power key.
1104 pre_power_action: a function which is called before next power on.
1105 post_power_action: a function which is called after next power on.
1106
1107 Raises:
1108 error.TestFail: if the shutdown_action() failed to turn DUT off.
1109 """
1110 self._call_action(shutdown_action)
1111 logging.info('Wait to ensure DUT shut down...')
1112 try:
1113 self.wait_for_client()
1114 raise error.TestFail(
1115 'Should shut the device down after calling %s.' %
1116 str(shutdown_action))
1117 except AssertionError:
1118 logging.info(
1119 'DUT is surely shutdown. We are going to power it on again...')
1120
1121 if pre_power_action:
1122 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001123 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001124 if post_power_action:
1125 self._call_action(post_power_action)
1126
1127
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001128 def register_faft_template(self, template):
1129 """Register FAFT template, the default FAFT_STEP of each step.
1130
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001131 Any missing field falls back to the original faft_template.
1132
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001133 Args:
1134 template: A FAFT_STEP dict.
1135 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001136 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001137
1138
1139 def register_faft_sequence(self, sequence):
1140 """Register FAFT sequence.
1141
1142 Args:
1143 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1144 """
1145 self._faft_sequence = sequence
1146
1147
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001148 def run_faft_step(self, step, no_reboot=False):
1149 """Run a single FAFT step.
1150
1151 Any missing field falls back to faft_template. An empty step means
1152 running the default faft_template.
1153
1154 Args:
1155 step: A FAFT_STEP dict.
1156 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001157
1158 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001159 error.TestFail: An error when the test failed.
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001160 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001161 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001162 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1163 'firmware_action', 'install_deps_after_boot')
1164
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001165 test = {}
1166 test.update(self._faft_template)
1167 test.update(step)
1168
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001169 for key in test:
1170 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001171 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001172
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001173 if test['state_checker']:
1174 if not self._call_action(test['state_checker']):
1175 raise error.TestFail('State checker failed!')
1176
1177 self._call_action(test['userspace_action'])
1178
1179 # Don't run reboot_action and firmware_action if no_reboot is True.
1180 if not no_reboot:
1181 self._call_action(test['reboot_action'])
1182 self.wait_for_client_offline()
1183 self._call_action(test['firmware_action'])
1184
1185 if 'install_deps_after_boot' in test:
1186 self.wait_for_client(
1187 install_deps=test['install_deps_after_boot'])
1188 else:
1189 self.wait_for_client()
1190
1191
1192 def run_faft_sequence(self):
1193 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001194 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001195 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001196 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001197 logging.info('======== Running FAFT sequence step %d ========' %
1198 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001199 # Don't reboot in the last step.
1200 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001201 index += 1