blob: d3b802677e42bf0564d25c9c969f99554f37489f [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
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800109
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800110 # The developer screen timeouts fit our spec.
111 DEV_SCREEN_TIMEOUT = 30
112
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800113 CHROMEOS_MAGIC = "CHROMEOS"
114 CORRUPTED_MAGIC = "CORRUPTD"
115
Tom Wai-Hong Tamf954d172011-12-08 17:14:15 +0800116 # Recovery reason codes, copied from:
117 # vboot_reference/firmware/lib/vboot_nvstorage.h
118 # vboot_reference/firmware/lib/vboot_struct.h
119 RECOVERY_REASON = {
120 # Recovery not requested
121 'NOT_REQUESTED': '0', # 0x00
122 # Recovery requested from legacy utility
123 'LEGACY': '1', # 0x01
124 # User manually requested recovery via recovery button
125 'RO_MANUAL': '2', # 0x02
126 # RW firmware failed signature check
127 'RO_INVALID_RW': '3', # 0x03
128 # S3 resume failed
129 'RO_S3_RESUME': '4', # 0x04
130 # TPM error in read-only firmware
131 'RO_TPM_ERROR': '5', # 0x05
132 # Shared data error in read-only firmware
133 'RO_SHARED_DATA': '6', # 0x06
134 # Test error from S3Resume()
135 'RO_TEST_S3': '7', # 0x07
136 # Test error from LoadFirmwareSetup()
137 'RO_TEST_LFS': '8', # 0x08
138 # Test error from LoadFirmware()
139 'RO_TEST_LF': '9', # 0x09
140 # RW firmware failed signature check
141 'RW_NOT_DONE': '16', # 0x10
142 'RW_DEV_MISMATCH': '17', # 0x11
143 'RW_REC_MISMATCH': '18', # 0x12
144 'RW_VERIFY_KEYBLOCK': '19', # 0x13
145 'RW_KEY_ROLLBACK': '20', # 0x14
146 'RW_DATA_KEY_PARSE': '21', # 0x15
147 'RW_VERIFY_PREAMBLE': '22', # 0x16
148 'RW_FW_ROLLBACK': '23', # 0x17
149 'RW_HEADER_VALID': '24', # 0x18
150 'RW_GET_FW_BODY': '25', # 0x19
151 'RW_HASH_WRONG_SIZE': '26', # 0x1A
152 'RW_VERIFY_BODY': '27', # 0x1B
153 'RW_VALID': '28', # 0x1C
154 # Read-only normal path requested by firmware preamble, but
155 # unsupported by firmware.
156 'RW_NO_RO_NORMAL': '29', # 0x1D
157 # Firmware boot failure outside of verified boot
158 'RO_FIRMWARE': '32', # 0x20
159 # Recovery mode TPM initialization requires a system reboot.
160 # The system was already in recovery mode for some other reason
161 # when this happened.
162 'RO_TPM_REBOOT': '33', # 0x21
163 # Unspecified/unknown error in read-only firmware
164 'RO_UNSPECIFIED': '63', # 0x3F
165 # User manually requested recovery by pressing a key at developer
166 # warning screen.
167 'RW_DEV_SCREEN': '65', # 0x41
168 # No OS kernel detected
169 'RW_NO_OS': '66', # 0x42
170 # OS kernel failed signature check
171 'RW_INVALID_OS': '67', # 0x43
172 # TPM error in rewritable firmware
173 'RW_TPM_ERROR': '68', # 0x44
174 # RW firmware in dev mode, but dev switch is off.
175 'RW_DEV_MISMATCH': '69', # 0x45
176 # Shared data error in rewritable firmware
177 'RW_SHARED_DATA': '70', # 0x46
178 # Test error from LoadKernel()
179 'RW_TEST_LK': '71', # 0x47
180 # No bootable disk found
181 'RW_NO_DISK': '72', # 0x48
182 # Unspecified/unknown error in rewritable firmware
183 'RW_UNSPECIFIED': '127', # 0x7F
184 # DM-verity error
185 'KE_DM_VERITY': '129', # 0x81
186 # Unspecified/unknown error in kernel
187 'KE_UNSPECIFIED': '191', # 0xBF
188 # Recovery mode test from user-mode
189 'US_TEST': '193', # 0xC1
190 # Unspecified/unknown error in user-mode
191 'US_UNSPECIFIED': '255', # 0xFF
192 }
193
Tom Wai-Hong Tam1e40fa12012-07-25 16:22:24 +0800194 # GBB flags
195 GBB_FLAG_DEV_SCREEN_SHORT_DELAY = 0x00000001
196 GBB_FLAG_LOAD_OPTION_ROMS = 0x00000002
197 GBB_FLAG_ENABLE_ALTERNATE_OS = 0x00000004
198 GBB_FLAG_FORCE_DEV_SWITCH_ON = 0x00000008
199 GBB_FLAG_FORCE_DEV_BOOT_USB = 0x00000010
200 GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = 0x00000020
201
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800202 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800203 _faft_sequence = ()
204
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800205 _customized_ctrl_d_key_command = None
206 _customized_enter_key_command = None
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800207 _customized_space_key_command = None
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800208 _customized_rec_reboot_command = None
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800209 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800210 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800211
212
213 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
214 # Parse arguments from command line
215 args = {}
216 for arg in cmdline_args:
217 match = re.search("^(\w+)=(.+)", arg)
218 if match:
219 args[match.group(1)] = match.group(2)
220
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800221 # Keep the arguments which will be used later.
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800222 if 'ctrl_d_cmd' in args:
223 self._customized_ctrl_d_key_command = args['ctrl_d_cmd']
224 logging.info('Customized Ctrl-D key command: %s' %
225 self._customized_ctrl_d_key_command)
226 if 'enter_cmd' in args:
227 self._customized_enter_key_command = args['enter_cmd']
228 logging.info('Customized Enter key command: %s' %
229 self._customized_enter_key_command)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800230 if 'space_cmd' in args:
231 self._customized_space_key_command = args['space_cmd']
232 logging.info('Customized Space key command: %s' %
233 self._customized_space_key_command)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800234 if 'rec_reboot_cmd' in args:
235 self._customized_rec_reboot_command = args['rec_reboot_cmd']
236 logging.info('Customized recovery reboot command: %s' %
237 self._customized_rec_reboot_command)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800238 if 'image' in args:
239 self._install_image_path = args['image']
240 logging.info('Install Chrome OS test image path: %s' %
241 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800242 if 'firmware_update' in args and args['firmware_update'].lower() \
243 not in ('0', 'false', 'no'):
244 if self._install_image_path:
245 self._firmware_update = True
246 logging.info('Also update firmware after installing.')
247 else:
248 logging.warning('Firmware update will not not performed '
249 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800250
251 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
252 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800253 if use_faft:
254 self.client_attr = FAFTClientAttribute(
255 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800256
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800257
258 def setup(self):
259 """Autotest setup function."""
260 super(FAFTSequence, self).setup()
261 if not self._remote_infos['faft']['used']:
262 raise error.TestError('The use_faft flag should be enabled.')
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800263 self.clear_gbb_flags(self.GBB_FLAG_FORCE_DEV_SWITCH_ON)
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800264 self.clear_gbb_flags(self.GBB_FLAG_DEV_SCREEN_SHORT_DELAY)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800265 self.register_faft_template({
266 'state_checker': (None),
267 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800268 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800269 'firmware_action': (None)
270 })
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800271 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800272 self.install_test_image(self._install_image_path,
273 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800274
275
276 def cleanup(self):
277 """Autotest cleanup function."""
278 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800279 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800280 super(FAFTSequence, self).cleanup()
281
282
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800283 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800284 """Assert an USB disk plugged-in on servo and a test image inside.
285
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800286 Args:
287 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
288 If None, it is detected automatically.
289
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800290 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800291 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800292 """
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800293 if usb_dev:
294 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
295 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700296 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800297 usb_dev = self.servo.probe_host_usb_dev()
298 if not usb_dev:
299 raise error.TestError(
300 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800301
302 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800303 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800304 code = utils.system(
305 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
306 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800307 utils.system('sudo umount %s' % tmp_dir)
308 os.removedirs(tmp_dir)
309 if code != 0:
310 raise error.TestError(
311 'The image in the USB disk should be a test image.')
312
313
Simran Basi741b5d42012-05-18 11:27:15 -0700314 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800315 """Install the test image specied by the path onto the USB and DUT disk.
316
317 The method first copies the image to USB disk and reboots into it via
318 recovery mode. Then runs 'chromeos-install' to install it to DUT disk.
319
320 Args:
321 image_path: Path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800322 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800323 """
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800324 install_cmd = 'chromeos-install --yes'
325 if firmware_update:
326 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800327 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
328 image_path)
329 logging.info('Processing build: %s %s' % (build_ver, build_hash))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800330
331 # Reuse the install_recovery_image method by using a test image.
332 # Don't wait for completion but run chromeos-install to install it.
Simran Basi741b5d42012-05-18 11:27:15 -0700333 self.servo.install_recovery_image(image_path)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800334 self.wait_for_client(install_deps=True)
335 self.run_faft_step({
336 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800337 install_cmd)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800338 })
339
340
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800341 def clear_gbb_flags(self, mask):
342 """Clear the GBB flags in the current flashrom.
343
344 Args:
345 mask: A mask of flags to be cleared.
346 """
347 gbb_flags = self.faft_client.get_gbb_flags()
348 if (gbb_flags & mask):
349 logging.info('Clear the GBB flags of 0x%x, from 0x%x to 0x%x.' %
350 (mask, gbb_flags, gbb_flags ^ mask))
351 self.faft_client.run_shell_command(
352 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' %
353 (gbb_flags ^ mask))
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800354 self.faft_client.reload_firmware()
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800355
356
Vic Yangb4e3e742012-06-02 13:17:38 +0800357 def _open_uart_pty(self):
358 """Open UART pty and spawn pexpect object.
359
360 Returns:
361 Tuple (fd, child): fd is the file descriptor of opened UART pty, and
362 child is a fdpexpect object tied to it.
363 """
364 fd = os.open(self.servo.get("uart1_pty"), os.O_RDWR | os.O_NONBLOCK)
365 child = fdpexpect.fdspawn(fd)
366 return (fd, child)
367
368
369 def _flush_uart_pty(self, child):
370 """Flush UART output to prevent previous pending message interferring.
371
372 Args:
373 child: The fdpexpect object tied to UART pty.
374 """
375 child.sendline("")
376 while True:
377 try:
378 child.expect(".", timeout=0.01)
379 except pexpect.TIMEOUT:
380 break
381
382
383 def _uart_send(self, child, line):
384 """Flush and send command through UART.
385
386 Args:
387 child: The pexpect object tied to UART pty.
388 line: String to send through UART.
389
390 Raises:
391 error.TestFail: Raised when writing to UART fails.
392 """
393 logging.info("Sending UART command: %s" % line)
394 self._flush_uart_pty(child)
395 if child.sendline(line) != len(line) + 1:
396 raise error.TestFail("Failed to send UART command.")
397
398
399 def send_uart_command(self, command):
400 """Send command through UART.
401
402 This function open UART pty when called, and then command is sent
403 through UART.
404
405 Args:
406 command: The command string to send.
407
408 Raises:
409 error.TestFail: Raised when writing to UART fails.
410 """
411 (fd, child) = self._open_uart_pty()
412 try:
413 self._uart_send(child, command)
414 finally:
415 os.close(fd)
416
417
418 def send_uart_command_get_output(self, command, regex_list, timeout=1):
419 """Send command through UART and wait for response.
420
421 This function waits for response message matching regular expressions.
422
423 Args:
424 command: The command sent.
425 regex_list: List of regular expressions used to match response message.
426 Note, list must be ordered.
427
428 Returns:
429 List of match objects of response message.
430
431 Raises:
432 error.TestFail: If timed out waiting for EC response.
433 """
434 if not isinstance(regex_list, list):
435 regex_list = [regex_list]
436 result_list = []
437 (fd, child) = self._open_uart_pty()
438 try:
439 self._uart_send(child, command)
440 for regex in regex_list:
441 child.expect(regex, timeout=timeout)
442 result_list.append(child.match)
443 except pexpect.TIMEOUT:
444 raise error.TestFail("Timeout waiting for UART response.")
445 finally:
446 os.close(fd)
447 return result_list
448
449
Vic Yang4d72cb62012-07-24 11:51:09 +0800450 def check_ec_capability(self, required_cap=[]):
451 """Check if current platform has required EC capabilities.
452
453 Args:
454 required_cap: A list containing required EC capabilities. Pass in
455 None to only check for presence of Chrome EC.
456
457 Returns:
458 True if requirements are met. Otherwise, False.
459 """
460 if not self.client_attr.chrome_ec:
461 logging.warn('Requires Chrome EC to run this test.')
462 return False
463
464 for cap in required_cap:
465 if cap not in self.client_attr.ec_capability:
466 logging.warn('Requires EC capability "%s" to run this test.' %
467 cap)
468 return False
469
470 return True
471
472
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800473 def _parse_crossystem_output(self, lines):
474 """Parse the crossystem output into a dict.
475
476 Args:
477 lines: The list of crossystem output strings.
478
479 Returns:
480 A dict which contains the crossystem keys/values.
481
482 Raises:
483 error.TestError: If wrong format in crossystem output.
484
485 >>> seq = FAFTSequence()
486 >>> seq._parse_crossystem_output([ \
487 "arch = x86 # Platform architecture", \
488 "cros_debug = 1 # OS should allow debug", \
489 ])
490 {'cros_debug': '1', 'arch': 'x86'}
491 >>> seq._parse_crossystem_output([ \
492 "arch=x86", \
493 ])
494 Traceback (most recent call last):
495 ...
496 TestError: Failed to parse crossystem output: arch=x86
497 >>> seq._parse_crossystem_output([ \
498 "arch = x86 # Platform architecture", \
499 "arch = arm # Platform architecture", \
500 ])
501 Traceback (most recent call last):
502 ...
503 TestError: Duplicated crossystem key: arch
504 """
505 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
506 parsed_list = {}
507 for line in lines:
508 matched = re.match(pattern, line.strip())
509 if not matched:
510 raise error.TestError("Failed to parse crossystem output: %s"
511 % line)
512 (name, value) = (matched.group(1), matched.group(2))
513 if name in parsed_list:
514 raise error.TestError("Duplicated crossystem key: %s" % name)
515 parsed_list[name] = value
516 return parsed_list
517
518
519 def crossystem_checker(self, expected_dict):
520 """Check the crossystem values matched.
521
522 Given an expect_dict which describes the expected crossystem values,
523 this function check the current crossystem values are matched or not.
524
525 Args:
526 expected_dict: A dict which contains the expected values.
527
528 Returns:
529 True if the crossystem value matched; otherwise, False.
530 """
531 lines = self.faft_client.run_shell_command_get_output('crossystem')
532 got_dict = self._parse_crossystem_output(lines)
533 for key in expected_dict:
534 if key not in got_dict:
535 logging.info('Expected key "%s" not in crossystem result' % key)
536 return False
537 if isinstance(expected_dict[key], str):
538 if got_dict[key] != expected_dict[key]:
539 logging.info("Expected '%s' value '%s' but got '%s'" %
540 (key, expected_dict[key], got_dict[key]))
541 return False
542 elif isinstance(expected_dict[key], tuple):
543 # Expected value is a tuple of possible actual values.
544 if got_dict[key] not in expected_dict[key]:
545 logging.info("Expected '%s' values %s but got '%s'" %
546 (key, str(expected_dict[key]), got_dict[key]))
547 return False
548 else:
549 logging.info("The expected_dict is neither a str nor a dict.")
550 return False
551 return True
552
553
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800554 def root_part_checker(self, expected_part):
555 """Check the partition number of the root device matched.
556
557 Args:
558 expected_part: A string containing the number of the expected root
559 partition.
560
561 Returns:
562 True if the currect root partition number matched; otherwise, False.
563 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800564 part = self.faft_client.get_root_part()[-1]
565 if self.ROOTFS_MAP[expected_part] != part:
566 logging.info("Expected root part %s but got %s" %
567 (self.ROOTFS_MAP[expected_part], part))
568 return False
569 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800570
571
Vic Yang59cac9c2012-05-21 15:28:42 +0800572 def ec_act_copy_checker(self, expected_copy):
573 """Check the EC running firmware copy matches.
574
575 Args:
576 expected_copy: A string containing 'RO', 'A', or 'B' indicating
577 the expected copy of EC running firmware.
578
579 Returns:
580 True if the current EC running copy matches; otherwise, False.
581 """
582 lines = self.faft_client.run_shell_command_get_output('ectool version')
583 pattern = re.compile("Firmware copy: (.*)")
584 for line in lines:
585 matched = pattern.match(line)
586 if matched and matched.group(1) == expected_copy:
587 return True
588 return False
589
590
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800591 def check_root_part_on_non_recovery(self, part):
592 """Check the partition number of root device and on normal/dev boot.
593
594 Returns:
595 True if the root device matched and on normal/dev boot;
596 otherwise, False.
597 """
598 return self.root_part_checker(part) and \
599 self.crossystem_checker({
600 'mainfw_type': ('normal', 'developer'),
601 'recoverysw_boot': '0',
602 })
603
604
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800605 def _join_part(self, dev, part):
606 """Return a concatenated string of device and partition number.
607
608 Args:
609 dev: A string of device, e.g.'/dev/sda'.
610 part: A string of partition number, e.g.'3'.
611
612 Returns:
613 A concatenated string of device and partition number, e.g.'/dev/sda3'.
614
615 >>> seq = FAFTSequence()
616 >>> seq._join_part('/dev/sda', '3')
617 '/dev/sda3'
618 >>> seq._join_part('/dev/mmcblk0', '2')
619 '/dev/mmcblk0p2'
620 """
621 if 'mmcblk' in dev:
622 return dev + 'p' + part
623 else:
624 return dev + part
625
626
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800627 def copy_kernel_and_rootfs(self, from_part, to_part):
628 """Copy kernel and rootfs from from_part to to_part.
629
630 Args:
631 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800632 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800633 """
634 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800635 logging.info('Copying kernel from %s to %s. Please wait...' %
636 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800637 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800638 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
639 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
640 logging.info('Copying rootfs from %s to %s. Please wait...' %
641 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800642 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800643 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
644 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800645
646
647 def ensure_kernel_boot(self, part):
648 """Ensure the request kernel boot.
649
650 If not, it duplicates the current kernel to the requested kernel
651 and sets the requested higher priority to ensure it boot.
652
653 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800654 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800655 """
656 if not self.root_part_checker(part):
657 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
658 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800659 self.run_faft_step({
660 'userspace_action': (self.reset_and_prioritize_kernel, part),
661 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800662
663
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800664 def send_ctrl_d_to_dut(self):
665 """Send Ctrl-D key to DUT."""
666 if self._customized_ctrl_d_key_command:
667 logging.info('running the customized Ctrl-D key command')
668 os.system(self._customized_ctrl_d_key_command)
669 else:
670 self.servo.ctrl_d()
671
672
673 def send_enter_to_dut(self):
674 """Send Enter key to DUT."""
675 if self._customized_enter_key_command:
676 logging.info('running the customized Enter key command')
677 os.system(self._customized_enter_key_command)
678 else:
679 self.servo.enter_key()
680
681
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800682 def send_space_to_dut(self):
683 """Send Space key to DUT."""
684 if self._customized_space_key_command:
685 logging.info('running the customized Space key command')
686 os.system(self._customized_space_key_command)
687 else:
688 # Send the alternative key combinaton of space key to servo.
689 self.servo.ctrl_refresh_key()
690
691
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800692 def wait_fw_screen_and_ctrl_d(self):
693 """Wait for firmware warning screen and press Ctrl-D."""
694 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800695 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800696
697
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800698 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
699 """Wait for firmware warning screen and trigger recovery boot."""
700 time.sleep(self.FIRMWARE_SCREEN_DELAY)
701 self.send_enter_to_dut()
702
703 # For Alex/ZGB, there is a dev warning screen in text mode.
704 # Skip it by pressing Ctrl-D.
705 if need_dev_transition:
706 time.sleep(self.TEXT_SCREEN_DELAY)
707 self.send_ctrl_d_to_dut()
708
709
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800710 def wait_fw_screen_and_plug_usb(self):
711 """Wait for firmware warning screen and then unplug and plug the USB."""
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800712 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800713 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
714 time.sleep(self.USB_PLUG_DELAY)
715 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
716
717
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800718 def wait_fw_screen_and_press_power(self):
719 """Wait for firmware warning screen and press power button."""
720 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800721 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800722
723
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800724 def wait_longer_fw_screen_and_press_power(self):
725 """Wait for firmware screen without timeout and press power button."""
726 time.sleep(self.DEV_SCREEN_TIMEOUT)
727 self.wait_fw_screen_and_press_power()
728
729
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800730 def wait_fw_screen_and_close_lid(self):
731 """Wait for firmware warning screen and close lid."""
732 time.sleep(self.FIRMWARE_SCREEN_DELAY)
733 self.servo.lid_close()
734
735
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800736 def wait_longer_fw_screen_and_close_lid(self):
737 """Wait for firmware screen without timeout and close lid."""
738 time.sleep(self.FIRMWARE_SCREEN_DELAY)
739 self.wait_fw_screen_and_close_lid()
740
741
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800742 def setup_tried_fwb(self, tried_fwb):
743 """Setup for fw B tried state.
744
745 It makes sure the system in the requested fw B tried state. If not, it
746 tries to do so.
747
748 Args:
749 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
750 """
751 if tried_fwb:
752 if not self.crossystem_checker({'tried_fwb': '1'}):
753 logging.info(
754 'Firmware is not booted with tried_fwb. Reboot into it.')
755 self.run_faft_step({
756 'userspace_action': self.faft_client.set_try_fw_b,
757 })
758 else:
759 if not self.crossystem_checker({'tried_fwb': '0'}):
760 logging.info(
761 'Firmware is booted with tried_fwb. Reboot to clear.')
762 self.run_faft_step({})
763
764
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800765 def enable_rec_mode_and_reboot(self):
766 """Switch to rec mode and reboot.
767
768 This method emulates the behavior of the old physical recovery switch,
769 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
770 recovery mode, i.e. just press Power + Esc + Refresh.
771 """
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800772 if self._customized_rec_reboot_command:
773 logging.info('running the customized rec reboot command')
774 os.system(self._customized_rec_reboot_command)
775 else:
776 self.servo.enable_recovery_mode()
777 self.cold_reboot()
778 time.sleep(self.EC_REBOOT_DELAY)
779 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800780
781
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800782 def enable_dev_mode_and_reboot(self):
783 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800784 if self.client_attr.keyboard_dev:
785 self.enable_keyboard_dev_mode()
786 else:
787 self.servo.enable_development_mode()
788 self.faft_client.run_shell_command(
789 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800790
791
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800792 def enable_normal_mode_and_reboot(self):
793 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800794 if self.client_attr.keyboard_dev:
795 self.disable_keyboard_dev_mode()
796 else:
797 self.servo.disable_development_mode()
798 self.faft_client.run_shell_command(
799 'chromeos-firmwareupdate --mode tonormal && reboot')
800
801
802 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
803 """Wait for firmware screen and then switch into or out of dev mode.
804
805 Args:
806 dev: True if switching into dev mode. Otherwise, False.
807 """
808 time.sleep(self.FIRMWARE_SCREEN_DELAY)
809 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800810 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800811 else:
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800812 # Only SPACE can trigger TO_NORM screen officially.
813 # We send both SPACE and ENTER in order to make the old and new
814 # firmware still work. It won't trigger twice since only one of
815 # them takes effect.
816 # TODO Remove to ENTER when all devices use new firmware.
817 self.send_space_to_dut()
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800818 self.send_enter_to_dut()
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800819 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800820 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800821
822
823 def enable_keyboard_dev_mode(self):
824 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +0800825 # Plug out USB disk for preventing recovery boot without warning
826 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +0800827 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800828 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800829 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800830 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +0800831
832
833 def disable_keyboard_dev_mode(self):
834 logging.info("Disabling keyboard controlled developer mode")
835 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800836 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800837 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800838 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800839
840
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800841 def setup_dev_mode(self, dev_mode):
842 """Setup for development mode.
843
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800844 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800845 tries to do so.
846
847 Args:
848 dev_mode: True if requested in dev mode; False if normal mode.
849 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800850 # Change the default firmware_action for dev mode passing the fw screen.
851 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800852 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
853 else None),
854 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800855 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +0800856 if (not self.client_attr.keyboard_dev and
857 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800858 logging.info('Dev switch is not on. Now switch it on.')
859 self.servo.enable_development_mode()
860 if not self.crossystem_checker({'devsw_boot': '1',
861 'mainfw_type': 'developer'}):
862 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800863 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800864 'userspace_action': None if self.client_attr.keyboard_dev
865 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800866 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800867 'reboot_action': self.enable_keyboard_dev_mode if
868 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800869 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800870 else:
Vic Yange7553162012-06-20 16:20:47 +0800871 if (not self.client_attr.keyboard_dev and
872 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800873 logging.info('Dev switch is not off. Now switch it off.')
874 self.servo.disable_development_mode()
875 if not self.crossystem_checker({'devsw_boot': '0',
876 'mainfw_type': 'normal'}):
877 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800878 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800879 'userspace_action': None if self.client_attr.keyboard_dev
880 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800881 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800882 'reboot_action': self.disable_keyboard_dev_mode if
883 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800884 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800885
886
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800887 def setup_kernel(self, part):
888 """Setup for kernel test.
889
890 It makes sure both kernel A and B bootable and the current boot is
891 the requested kernel part.
892
893 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800894 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800895 """
896 self.ensure_kernel_boot(part)
897 self.copy_kernel_and_rootfs(from_part=part,
898 to_part=self.OTHER_KERNEL_MAP[part])
899 self.reset_and_prioritize_kernel(part)
900
901
902 def reset_and_prioritize_kernel(self, part):
903 """Make the requested partition highest priority.
904
905 This function also reset kerenl A and B to bootable.
906
907 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800908 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800909 """
910 root_dev = self.faft_client.get_root_dev()
911 # Reset kernel A and B to bootable.
912 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
913 (self.KERNEL_MAP['a'], root_dev))
914 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
915 (self.KERNEL_MAP['b'], root_dev))
916 # Set kernel part highest priority.
917 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
918 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800919 # Safer to sync and wait until the cgpt status written to the disk.
920 self.faft_client.run_shell_command('sync')
921 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800922
923
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800924 def warm_reboot(self):
925 """Request a warm reboot.
926
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800927 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800928 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800929 # Use cold reset if the warm reset is broken.
930 if self.client_attr.broken_warm_reset:
931 self.servo.cold_reset()
932 else:
933 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800934
935
936 def cold_reboot(self):
937 """Request a cold reboot.
938
939 A wrapper for underlying servo cold reset.
940 """
941 if self.check_ec_capability():
942 # We don't use servo.cold_reset() here because software sync is
943 # not yet finished, and device may or may not come up after cold
944 # reset. Pressing power button before firmware comes up solves this.
945 #
946 # The correct behavior should be (not work now):
947 # - If rebooting EC with rec mode on, power on AP and it boots
948 # into recovery mode.
949 # - If rebooting EC with rec mode off, power on AP for software
950 # sync. Then AP checks if lid open or not. If lid open, continue;
951 # otherwise, shut AP down and need servo for a power button
952 # press.
953 self.servo.set('cold_reset', 'on')
954 self.servo.set('cold_reset', 'off')
955 time.sleep(self.POWER_BTN_DELAY)
956 self.servo.power_short_press()
957 else:
958 self.servo.cold_reset()
959
960
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800961 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800962 """Request the client sync and do a warm reboot.
963
964 This is the default reboot action on FAFT.
965 """
966 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800967 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800968 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800969
970
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800971 def sync_and_cold_reboot(self):
972 """Request the client sync and do a cold reboot.
973
974 This reboot action is used to reset EC for recovery mode.
975 """
976 self.faft_client.run_shell_command('sync')
977 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800978 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800979
980
Vic Yang59cac9c2012-05-21 15:28:42 +0800981 def sync_and_ec_reboot(self):
982 """Request the client sync and do a EC triggered reboot."""
983 self.faft_client.run_shell_command('sync')
984 time.sleep(self.SYNC_DELAY)
985 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec)&' %
986 self.EC_REBOOT_DELAY)
Vic Yangf86728a2012-07-30 10:44:07 +0800987 time.sleep(self.EC_REBOOT_DELAY)
988 self.check_lid_and_power_on()
989
990
991 def check_lid_and_power_on(self):
992 """
993 On devices with EC software sync, system powers on after EC reboots if
994 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
995 This method checks lid switch state and presses power button if
996 necessary.
997 """
998 if self.servo.get("lid_open") == "no":
999 time.sleep(self.SOFTWARE_SYNC_DELAY)
1000 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +08001001
1002
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001003 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
1004 """Modify the kernel header magic in USB stick.
1005
1006 The kernel header magic is the first 8-byte of kernel partition.
1007 We modify it to make it fail on kernel verification check.
1008
1009 Args:
1010 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1011 from_magic: A string of magic which we change it from.
1012 to_magic: A string of magic which we change it to.
1013
1014 Raises:
1015 error.TestError: if failed to change magic.
1016 """
1017 assert len(from_magic) == 8
1018 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +08001019 # USB image only contains one kernel.
1020 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001021 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
1022 current_magic = utils.system_output(read_cmd)
1023 if current_magic == to_magic:
1024 logging.info("The kernel magic is already %s." % current_magic)
1025 return
1026 if current_magic != from_magic:
1027 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1028
1029 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1030 (from_magic, to_magic))
1031 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1032 " 2>/dev/null" % (to_magic, kernel_part))
1033 utils.system(write_cmd)
1034
1035 if utils.system_output(read_cmd) != to_magic:
1036 raise error.TestError("Failed to write new magic.")
1037
1038
1039 def corrupt_usb_kernel(self, usb_dev):
1040 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1041
1042 Args:
1043 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1044 """
1045 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1046 self.CORRUPTED_MAGIC)
1047
1048
1049 def restore_usb_kernel(self, usb_dev):
1050 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1051
1052 Args:
1053 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1054 """
1055 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1056 self.CHROMEOS_MAGIC)
1057
1058
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001059 def _call_action(self, action_tuple):
1060 """Call the action function with/without arguments.
1061
1062 Args:
1063 action_tuple: A function, or a tuple which consisted of a function
1064 and its arguments (if any).
1065
1066 Returns:
1067 The result value of the action function.
1068 """
1069 if isinstance(action_tuple, tuple):
1070 action = action_tuple[0]
1071 args = action_tuple[1:]
1072 if callable(action):
1073 logging.info('calling %s with parameter %s' % (
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001074 str(action), str(action_tuple[1])))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001075 return action(*args)
1076 else:
1077 logging.info('action is not callable!')
1078 else:
1079 action = action_tuple
1080 if action is not None:
1081 if callable(action):
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001082 logging.info('calling %s' % str(action))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001083 return action()
1084 else:
1085 logging.info('action is not callable!')
1086
1087 return None
1088
1089
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001090 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1091 post_power_action=None):
1092 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1093
1094 Args:
1095 shutdown_action: a function which makes DUT shutdown, like pressing
1096 power key.
1097 pre_power_action: a function which is called before next power on.
1098 post_power_action: a function which is called after next power on.
1099
1100 Raises:
1101 error.TestFail: if the shutdown_action() failed to turn DUT off.
1102 """
1103 self._call_action(shutdown_action)
1104 logging.info('Wait to ensure DUT shut down...')
1105 try:
1106 self.wait_for_client()
1107 raise error.TestFail(
1108 'Should shut the device down after calling %s.' %
1109 str(shutdown_action))
1110 except AssertionError:
1111 logging.info(
1112 'DUT is surely shutdown. We are going to power it on again...')
1113
1114 if pre_power_action:
1115 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001116 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001117 if post_power_action:
1118 self._call_action(post_power_action)
1119
1120
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001121 def register_faft_template(self, template):
1122 """Register FAFT template, the default FAFT_STEP of each step.
1123
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001124 Any missing field falls back to the original faft_template.
1125
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001126 Args:
1127 template: A FAFT_STEP dict.
1128 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001129 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001130
1131
1132 def register_faft_sequence(self, sequence):
1133 """Register FAFT sequence.
1134
1135 Args:
1136 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1137 """
1138 self._faft_sequence = sequence
1139
1140
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001141 def run_faft_step(self, step, no_reboot=False):
1142 """Run a single FAFT step.
1143
1144 Any missing field falls back to faft_template. An empty step means
1145 running the default faft_template.
1146
1147 Args:
1148 step: A FAFT_STEP dict.
1149 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001150
1151 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001152 error.TestFail: An error when the test failed.
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001153 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001154 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001155 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1156 'firmware_action', 'install_deps_after_boot')
1157
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001158 test = {}
1159 test.update(self._faft_template)
1160 test.update(step)
1161
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001162 for key in test:
1163 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001164 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001165
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001166 if test['state_checker']:
1167 if not self._call_action(test['state_checker']):
1168 raise error.TestFail('State checker failed!')
1169
1170 self._call_action(test['userspace_action'])
1171
1172 # Don't run reboot_action and firmware_action if no_reboot is True.
1173 if not no_reboot:
1174 self._call_action(test['reboot_action'])
1175 self.wait_for_client_offline()
1176 self._call_action(test['firmware_action'])
1177
1178 if 'install_deps_after_boot' in test:
1179 self.wait_for_client(
1180 install_deps=test['install_deps_after_boot'])
1181 else:
1182 self.wait_for_client()
1183
1184
1185 def run_faft_sequence(self):
1186 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001187 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001188 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001189 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001190 logging.info('======== Running FAFT sequence step %d ========' %
1191 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001192 # Don't reboot in the last step.
1193 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001194 index += 1