blob: bfa72a12bcc02177fe7cd6fd0ee9b08c15d9ef3c [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 Tamac943172012-08-01 10:38:39 +080075 _customized_rec_reboot_command: The customized recovery reboot command
76 instead of sending key combination of Power + Esc + F3 for
77 triggering recovery reboot.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +080078 _install_image_path: The path of Chrome OS test image to be installed.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +080079 _firmware_update: Boolean. True if firmware update needed after
80 installing the image.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +080081 """
82 version = 1
83
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +080084
85 # Mapping of partition number of kernel and rootfs.
86 KERNEL_MAP = {'a':'2', 'b':'4', '2':'2', '4':'4', '3':'2', '5':'4'}
87 ROOTFS_MAP = {'a':'3', 'b':'5', '2':'3', '4':'5', '3':'3', '5':'5'}
88 OTHER_KERNEL_MAP = {'a':'4', 'b':'2', '2':'4', '4':'2', '3':'4', '5':'2'}
89 OTHER_ROOTFS_MAP = {'a':'5', 'b':'3', '2':'5', '4':'3', '3':'5', '5':'3'}
90
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080091 # Delay between power-on and firmware screen.
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +080092 FIRMWARE_SCREEN_DELAY = 5
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080093 # Delay between passing firmware screen and text mode warning screen.
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +080094 TEXT_SCREEN_DELAY = 20
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080095 # Delay of loading the USB kernel.
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +080096 USB_LOAD_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080097 # Delay between USB plug-out and plug-in.
Tom Wai-Hong Tam9ca742a2011-12-05 15:48:57 +080098 USB_PLUG_DELAY = 10
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +080099 # Delay after running the 'sync' command.
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800100 SYNC_DELAY = 5
Vic Yang59cac9c2012-05-21 15:28:42 +0800101 # Delay for waiting client to return before EC reboot
102 EC_REBOOT_DELAY = 1
103 # Delay between EC reboot and pressing power button
104 POWER_BTN_DELAY = 0.5
Vic Yangf86728a2012-07-30 10:44:07 +0800105 # Delay of EC software sync hash calculating time
106 SOFTWARE_SYNC_DELAY = 6
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800107
Tom Wai-Hong Tam51ef2e12012-07-27 15:04:12 +0800108 # The developer screen timeouts fit our spec.
109 DEV_SCREEN_TIMEOUT = 30
110
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800111 CHROMEOS_MAGIC = "CHROMEOS"
112 CORRUPTED_MAGIC = "CORRUPTD"
113
Tom Wai-Hong Tamf954d172011-12-08 17:14:15 +0800114 # Recovery reason codes, copied from:
115 # vboot_reference/firmware/lib/vboot_nvstorage.h
116 # vboot_reference/firmware/lib/vboot_struct.h
117 RECOVERY_REASON = {
118 # Recovery not requested
119 'NOT_REQUESTED': '0', # 0x00
120 # Recovery requested from legacy utility
121 'LEGACY': '1', # 0x01
122 # User manually requested recovery via recovery button
123 'RO_MANUAL': '2', # 0x02
124 # RW firmware failed signature check
125 'RO_INVALID_RW': '3', # 0x03
126 # S3 resume failed
127 'RO_S3_RESUME': '4', # 0x04
128 # TPM error in read-only firmware
129 'RO_TPM_ERROR': '5', # 0x05
130 # Shared data error in read-only firmware
131 'RO_SHARED_DATA': '6', # 0x06
132 # Test error from S3Resume()
133 'RO_TEST_S3': '7', # 0x07
134 # Test error from LoadFirmwareSetup()
135 'RO_TEST_LFS': '8', # 0x08
136 # Test error from LoadFirmware()
137 'RO_TEST_LF': '9', # 0x09
138 # RW firmware failed signature check
139 'RW_NOT_DONE': '16', # 0x10
140 'RW_DEV_MISMATCH': '17', # 0x11
141 'RW_REC_MISMATCH': '18', # 0x12
142 'RW_VERIFY_KEYBLOCK': '19', # 0x13
143 'RW_KEY_ROLLBACK': '20', # 0x14
144 'RW_DATA_KEY_PARSE': '21', # 0x15
145 'RW_VERIFY_PREAMBLE': '22', # 0x16
146 'RW_FW_ROLLBACK': '23', # 0x17
147 'RW_HEADER_VALID': '24', # 0x18
148 'RW_GET_FW_BODY': '25', # 0x19
149 'RW_HASH_WRONG_SIZE': '26', # 0x1A
150 'RW_VERIFY_BODY': '27', # 0x1B
151 'RW_VALID': '28', # 0x1C
152 # Read-only normal path requested by firmware preamble, but
153 # unsupported by firmware.
154 'RW_NO_RO_NORMAL': '29', # 0x1D
155 # Firmware boot failure outside of verified boot
156 'RO_FIRMWARE': '32', # 0x20
157 # Recovery mode TPM initialization requires a system reboot.
158 # The system was already in recovery mode for some other reason
159 # when this happened.
160 'RO_TPM_REBOOT': '33', # 0x21
161 # Unspecified/unknown error in read-only firmware
162 'RO_UNSPECIFIED': '63', # 0x3F
163 # User manually requested recovery by pressing a key at developer
164 # warning screen.
165 'RW_DEV_SCREEN': '65', # 0x41
166 # No OS kernel detected
167 'RW_NO_OS': '66', # 0x42
168 # OS kernel failed signature check
169 'RW_INVALID_OS': '67', # 0x43
170 # TPM error in rewritable firmware
171 'RW_TPM_ERROR': '68', # 0x44
172 # RW firmware in dev mode, but dev switch is off.
173 'RW_DEV_MISMATCH': '69', # 0x45
174 # Shared data error in rewritable firmware
175 'RW_SHARED_DATA': '70', # 0x46
176 # Test error from LoadKernel()
177 'RW_TEST_LK': '71', # 0x47
178 # No bootable disk found
179 'RW_NO_DISK': '72', # 0x48
180 # Unspecified/unknown error in rewritable firmware
181 'RW_UNSPECIFIED': '127', # 0x7F
182 # DM-verity error
183 'KE_DM_VERITY': '129', # 0x81
184 # Unspecified/unknown error in kernel
185 'KE_UNSPECIFIED': '191', # 0xBF
186 # Recovery mode test from user-mode
187 'US_TEST': '193', # 0xC1
188 # Unspecified/unknown error in user-mode
189 'US_UNSPECIFIED': '255', # 0xFF
190 }
191
Tom Wai-Hong Tam1e40fa12012-07-25 16:22:24 +0800192 # GBB flags
193 GBB_FLAG_DEV_SCREEN_SHORT_DELAY = 0x00000001
194 GBB_FLAG_LOAD_OPTION_ROMS = 0x00000002
195 GBB_FLAG_ENABLE_ALTERNATE_OS = 0x00000004
196 GBB_FLAG_FORCE_DEV_SWITCH_ON = 0x00000008
197 GBB_FLAG_FORCE_DEV_BOOT_USB = 0x00000010
198 GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK = 0x00000020
199
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800200 _faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800201 _faft_sequence = ()
202
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800203 _customized_ctrl_d_key_command = None
204 _customized_enter_key_command = None
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800205 _customized_rec_reboot_command = None
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800206 _install_image_path = None
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800207 _firmware_update = False
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800208
209
210 def initialize(self, host, cmdline_args, use_pyauto=False, use_faft=False):
211 # Parse arguments from command line
212 args = {}
213 for arg in cmdline_args:
214 match = re.search("^(\w+)=(.+)", arg)
215 if match:
216 args[match.group(1)] = match.group(2)
217
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800218 # Keep the arguments which will be used later.
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800219 if 'ctrl_d_cmd' in args:
220 self._customized_ctrl_d_key_command = args['ctrl_d_cmd']
221 logging.info('Customized Ctrl-D key command: %s' %
222 self._customized_ctrl_d_key_command)
223 if 'enter_cmd' in args:
224 self._customized_enter_key_command = args['enter_cmd']
225 logging.info('Customized Enter key command: %s' %
226 self._customized_enter_key_command)
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800227 if 'rec_reboot_cmd' in args:
228 self._customized_rec_reboot_command = args['rec_reboot_cmd']
229 logging.info('Customized recovery reboot command: %s' %
230 self._customized_rec_reboot_command)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800231 if 'image' in args:
232 self._install_image_path = args['image']
233 logging.info('Install Chrome OS test image path: %s' %
234 self._install_image_path)
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800235 if 'firmware_update' in args and args['firmware_update'].lower() \
236 not in ('0', 'false', 'no'):
237 if self._install_image_path:
238 self._firmware_update = True
239 logging.info('Also update firmware after installing.')
240 else:
241 logging.warning('Firmware update will not not performed '
242 'since no image is specified.')
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800243
244 super(FAFTSequence, self).initialize(host, cmdline_args, use_pyauto,
245 use_faft)
Vic Yangebd6de62012-06-26 14:25:57 +0800246 if use_faft:
247 self.client_attr = FAFTClientAttribute(
248 self.faft_client.get_platform_name())
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800249
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800250
251 def setup(self):
252 """Autotest setup function."""
253 super(FAFTSequence, self).setup()
254 if not self._remote_infos['faft']['used']:
255 raise error.TestError('The use_faft flag should be enabled.')
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800256 self.clear_gbb_flags(self.GBB_FLAG_FORCE_DEV_SWITCH_ON)
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800257 self.clear_gbb_flags(self.GBB_FLAG_DEV_SCREEN_SHORT_DELAY)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800258 self.register_faft_template({
259 'state_checker': (None),
260 'userspace_action': (None),
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800261 'reboot_action': (self.sync_and_warm_reboot),
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800262 'firmware_action': (None)
263 })
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800264 if self._install_image_path:
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800265 self.install_test_image(self._install_image_path,
266 self._firmware_update)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800267
268
269 def cleanup(self):
270 """Autotest cleanup function."""
271 self._faft_sequence = ()
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +0800272 self._faft_template = {}
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800273 super(FAFTSequence, self).cleanup()
274
275
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800276 def assert_test_image_in_usb_disk(self, usb_dev=None):
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800277 """Assert an USB disk plugged-in on servo and a test image inside.
278
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800279 Args:
280 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
281 If None, it is detected automatically.
282
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800283 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800284 error.TestError: if USB disk not detected or not a test image.
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800285 """
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800286 if usb_dev:
287 assert self.servo.get('usb_mux_sel1') == 'servo_sees_usbkey'
288 else:
Vadim Bendeburycacf29f2012-07-30 17:49:11 -0700289 self.servo.enable_usb_hub(host=True)
Tom Wai-Hong Tam91f49822011-12-28 15:44:15 +0800290 usb_dev = self.servo.probe_host_usb_dev()
291 if not usb_dev:
292 raise error.TestError(
293 'An USB disk should be plugged in the servo board.')
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800294
295 tmp_dir = tempfile.mkdtemp()
Tom Wai-Hong Tamb0e80852011-12-07 16:15:06 +0800296 utils.system('sudo mount -r -t ext2 %s3 %s' % (usb_dev, tmp_dir))
Tom Wai-Hong Tame77459e2011-11-03 17:19:46 +0800297 code = utils.system(
298 'grep -qE "(Test Build|testimage-channel)" %s/etc/lsb-release' %
299 tmp_dir, ignore_status=True)
Tom Wai-Hong Tam76c75072011-10-25 18:00:12 +0800300 utils.system('sudo umount %s' % tmp_dir)
301 os.removedirs(tmp_dir)
302 if code != 0:
303 raise error.TestError(
304 'The image in the USB disk should be a test image.')
305
306
Simran Basi741b5d42012-05-18 11:27:15 -0700307 def install_test_image(self, image_path=None, firmware_update=False):
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800308 """Install the test image specied by the path onto the USB and DUT disk.
309
310 The method first copies the image to USB disk and reboots into it via
311 recovery mode. Then runs 'chromeos-install' to install it to DUT disk.
312
313 Args:
314 image_path: Path on the host to the test image.
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800315 firmware_update: Also update the firmware after installing.
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800316 """
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800317 install_cmd = 'chromeos-install --yes'
318 if firmware_update:
319 install_cmd += ' && chromeos-firmwareupdate --mode recovery'
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800320 build_ver, build_hash = lab_test.VerifyImageAndGetId(cros_dir,
321 image_path)
322 logging.info('Processing build: %s %s' % (build_ver, build_hash))
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800323
324 # Reuse the install_recovery_image method by using a test image.
325 # Don't wait for completion but run chromeos-install to install it.
Simran Basi741b5d42012-05-18 11:27:15 -0700326 self.servo.install_recovery_image(image_path)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800327 self.wait_for_client(install_deps=True)
328 self.run_faft_step({
329 'userspace_action': (self.faft_client.run_shell_command,
Tom Wai-Hong Tam1a3ff742012-01-11 16:36:46 +0800330 install_cmd)
Tom Wai-Hong Tam40fd9472012-01-09 17:11:02 +0800331 })
332
333
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800334 def clear_gbb_flags(self, mask):
335 """Clear the GBB flags in the current flashrom.
336
337 Args:
338 mask: A mask of flags to be cleared.
339 """
340 gbb_flags = self.faft_client.get_gbb_flags()
341 if (gbb_flags & mask):
342 logging.info('Clear the GBB flags of 0x%x, from 0x%x to 0x%x.' %
343 (mask, gbb_flags, gbb_flags ^ mask))
344 self.faft_client.run_shell_command(
345 '/usr/share/vboot/bin/set_gbb_flags.sh 0x%x' %
346 (gbb_flags ^ mask))
Tom Wai-Hong Tamc1c4deb2012-07-26 14:28:11 +0800347 self.faft_client.reload_firmware()
Tom Wai-Hong Tam15ce5812012-07-26 14:14:18 +0800348
349
Vic Yangb4e3e742012-06-02 13:17:38 +0800350 def _open_uart_pty(self):
351 """Open UART pty and spawn pexpect object.
352
353 Returns:
354 Tuple (fd, child): fd is the file descriptor of opened UART pty, and
355 child is a fdpexpect object tied to it.
356 """
357 fd = os.open(self.servo.get("uart1_pty"), os.O_RDWR | os.O_NONBLOCK)
358 child = fdpexpect.fdspawn(fd)
359 return (fd, child)
360
361
362 def _flush_uart_pty(self, child):
363 """Flush UART output to prevent previous pending message interferring.
364
365 Args:
366 child: The fdpexpect object tied to UART pty.
367 """
368 child.sendline("")
369 while True:
370 try:
371 child.expect(".", timeout=0.01)
372 except pexpect.TIMEOUT:
373 break
374
375
376 def _uart_send(self, child, line):
377 """Flush and send command through UART.
378
379 Args:
380 child: The pexpect object tied to UART pty.
381 line: String to send through UART.
382
383 Raises:
384 error.TestFail: Raised when writing to UART fails.
385 """
386 logging.info("Sending UART command: %s" % line)
387 self._flush_uart_pty(child)
388 if child.sendline(line) != len(line) + 1:
389 raise error.TestFail("Failed to send UART command.")
390
391
392 def send_uart_command(self, command):
393 """Send command through UART.
394
395 This function open UART pty when called, and then command is sent
396 through UART.
397
398 Args:
399 command: The command string to send.
400
401 Raises:
402 error.TestFail: Raised when writing to UART fails.
403 """
404 (fd, child) = self._open_uart_pty()
405 try:
406 self._uart_send(child, command)
407 finally:
408 os.close(fd)
409
410
411 def send_uart_command_get_output(self, command, regex_list, timeout=1):
412 """Send command through UART and wait for response.
413
414 This function waits for response message matching regular expressions.
415
416 Args:
417 command: The command sent.
418 regex_list: List of regular expressions used to match response message.
419 Note, list must be ordered.
420
421 Returns:
422 List of match objects of response message.
423
424 Raises:
425 error.TestFail: If timed out waiting for EC response.
426 """
427 if not isinstance(regex_list, list):
428 regex_list = [regex_list]
429 result_list = []
430 (fd, child) = self._open_uart_pty()
431 try:
432 self._uart_send(child, command)
433 for regex in regex_list:
434 child.expect(regex, timeout=timeout)
435 result_list.append(child.match)
436 except pexpect.TIMEOUT:
437 raise error.TestFail("Timeout waiting for UART response.")
438 finally:
439 os.close(fd)
440 return result_list
441
442
Vic Yang4d72cb62012-07-24 11:51:09 +0800443 def check_ec_capability(self, required_cap=[]):
444 """Check if current platform has required EC capabilities.
445
446 Args:
447 required_cap: A list containing required EC capabilities. Pass in
448 None to only check for presence of Chrome EC.
449
450 Returns:
451 True if requirements are met. Otherwise, False.
452 """
453 if not self.client_attr.chrome_ec:
454 logging.warn('Requires Chrome EC to run this test.')
455 return False
456
457 for cap in required_cap:
458 if cap not in self.client_attr.ec_capability:
459 logging.warn('Requires EC capability "%s" to run this test.' %
460 cap)
461 return False
462
463 return True
464
465
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +0800466 def _parse_crossystem_output(self, lines):
467 """Parse the crossystem output into a dict.
468
469 Args:
470 lines: The list of crossystem output strings.
471
472 Returns:
473 A dict which contains the crossystem keys/values.
474
475 Raises:
476 error.TestError: If wrong format in crossystem output.
477
478 >>> seq = FAFTSequence()
479 >>> seq._parse_crossystem_output([ \
480 "arch = x86 # Platform architecture", \
481 "cros_debug = 1 # OS should allow debug", \
482 ])
483 {'cros_debug': '1', 'arch': 'x86'}
484 >>> seq._parse_crossystem_output([ \
485 "arch=x86", \
486 ])
487 Traceback (most recent call last):
488 ...
489 TestError: Failed to parse crossystem output: arch=x86
490 >>> seq._parse_crossystem_output([ \
491 "arch = x86 # Platform architecture", \
492 "arch = arm # Platform architecture", \
493 ])
494 Traceback (most recent call last):
495 ...
496 TestError: Duplicated crossystem key: arch
497 """
498 pattern = "^([^ =]*) *= *(.*[^ ]) *# [^#]*$"
499 parsed_list = {}
500 for line in lines:
501 matched = re.match(pattern, line.strip())
502 if not matched:
503 raise error.TestError("Failed to parse crossystem output: %s"
504 % line)
505 (name, value) = (matched.group(1), matched.group(2))
506 if name in parsed_list:
507 raise error.TestError("Duplicated crossystem key: %s" % name)
508 parsed_list[name] = value
509 return parsed_list
510
511
512 def crossystem_checker(self, expected_dict):
513 """Check the crossystem values matched.
514
515 Given an expect_dict which describes the expected crossystem values,
516 this function check the current crossystem values are matched or not.
517
518 Args:
519 expected_dict: A dict which contains the expected values.
520
521 Returns:
522 True if the crossystem value matched; otherwise, False.
523 """
524 lines = self.faft_client.run_shell_command_get_output('crossystem')
525 got_dict = self._parse_crossystem_output(lines)
526 for key in expected_dict:
527 if key not in got_dict:
528 logging.info('Expected key "%s" not in crossystem result' % key)
529 return False
530 if isinstance(expected_dict[key], str):
531 if got_dict[key] != expected_dict[key]:
532 logging.info("Expected '%s' value '%s' but got '%s'" %
533 (key, expected_dict[key], got_dict[key]))
534 return False
535 elif isinstance(expected_dict[key], tuple):
536 # Expected value is a tuple of possible actual values.
537 if got_dict[key] not in expected_dict[key]:
538 logging.info("Expected '%s' values %s but got '%s'" %
539 (key, str(expected_dict[key]), got_dict[key]))
540 return False
541 else:
542 logging.info("The expected_dict is neither a str nor a dict.")
543 return False
544 return True
545
546
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800547 def root_part_checker(self, expected_part):
548 """Check the partition number of the root device matched.
549
550 Args:
551 expected_part: A string containing the number of the expected root
552 partition.
553
554 Returns:
555 True if the currect root partition number matched; otherwise, False.
556 """
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800557 part = self.faft_client.get_root_part()[-1]
558 if self.ROOTFS_MAP[expected_part] != part:
559 logging.info("Expected root part %s but got %s" %
560 (self.ROOTFS_MAP[expected_part], part))
561 return False
562 return True
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800563
564
Vic Yang59cac9c2012-05-21 15:28:42 +0800565 def ec_act_copy_checker(self, expected_copy):
566 """Check the EC running firmware copy matches.
567
568 Args:
569 expected_copy: A string containing 'RO', 'A', or 'B' indicating
570 the expected copy of EC running firmware.
571
572 Returns:
573 True if the current EC running copy matches; otherwise, False.
574 """
575 lines = self.faft_client.run_shell_command_get_output('ectool version')
576 pattern = re.compile("Firmware copy: (.*)")
577 for line in lines:
578 matched = pattern.match(line)
579 if matched and matched.group(1) == expected_copy:
580 return True
581 return False
582
583
Tom Wai-Hong Tam07278c22012-02-08 16:53:00 +0800584 def check_root_part_on_non_recovery(self, part):
585 """Check the partition number of root device and on normal/dev boot.
586
587 Returns:
588 True if the root device matched and on normal/dev boot;
589 otherwise, False.
590 """
591 return self.root_part_checker(part) and \
592 self.crossystem_checker({
593 'mainfw_type': ('normal', 'developer'),
594 'recoverysw_boot': '0',
595 })
596
597
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800598 def _join_part(self, dev, part):
599 """Return a concatenated string of device and partition number.
600
601 Args:
602 dev: A string of device, e.g.'/dev/sda'.
603 part: A string of partition number, e.g.'3'.
604
605 Returns:
606 A concatenated string of device and partition number, e.g.'/dev/sda3'.
607
608 >>> seq = FAFTSequence()
609 >>> seq._join_part('/dev/sda', '3')
610 '/dev/sda3'
611 >>> seq._join_part('/dev/mmcblk0', '2')
612 '/dev/mmcblk0p2'
613 """
614 if 'mmcblk' in dev:
615 return dev + 'p' + part
616 else:
617 return dev + part
618
619
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800620 def copy_kernel_and_rootfs(self, from_part, to_part):
621 """Copy kernel and rootfs from from_part to to_part.
622
623 Args:
624 from_part: A string of partition number to be copied from.
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800625 to_part: A string of partition number to be copied to.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800626 """
627 root_dev = self.faft_client.get_root_dev()
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800628 logging.info('Copying kernel from %s to %s. Please wait...' %
629 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800630 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800631 (self._join_part(root_dev, self.KERNEL_MAP[from_part]),
632 self._join_part(root_dev, self.KERNEL_MAP[to_part])))
633 logging.info('Copying rootfs from %s to %s. Please wait...' %
634 (from_part, to_part))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800635 self.faft_client.run_shell_command('dd if=%s of=%s bs=4M' %
Tom Wai-Hong Tamf2103be2011-11-10 07:26:56 +0800636 (self._join_part(root_dev, self.ROOTFS_MAP[from_part]),
637 self._join_part(root_dev, self.ROOTFS_MAP[to_part])))
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800638
639
640 def ensure_kernel_boot(self, part):
641 """Ensure the request kernel boot.
642
643 If not, it duplicates the current kernel to the requested kernel
644 and sets the requested higher priority to ensure it boot.
645
646 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800647 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800648 """
649 if not self.root_part_checker(part):
650 self.copy_kernel_and_rootfs(from_part=self.OTHER_KERNEL_MAP[part],
651 to_part=part)
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800652 self.run_faft_step({
653 'userspace_action': (self.reset_and_prioritize_kernel, part),
654 })
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800655
656
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800657 def send_ctrl_d_to_dut(self):
658 """Send Ctrl-D key to DUT."""
659 if self._customized_ctrl_d_key_command:
660 logging.info('running the customized Ctrl-D key command')
661 os.system(self._customized_ctrl_d_key_command)
662 else:
663 self.servo.ctrl_d()
664
665
666 def send_enter_to_dut(self):
667 """Send Enter key to DUT."""
668 if self._customized_enter_key_command:
669 logging.info('running the customized Enter key command')
670 os.system(self._customized_enter_key_command)
671 else:
672 self.servo.enter_key()
673
674
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800675 def wait_fw_screen_and_ctrl_d(self):
676 """Wait for firmware warning screen and press Ctrl-D."""
677 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam1db43832011-12-09 10:50:56 +0800678 self.send_ctrl_d_to_dut()
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800679
680
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800681 def wait_fw_screen_and_trigger_recovery(self, need_dev_transition=False):
682 """Wait for firmware warning screen and trigger recovery boot."""
683 time.sleep(self.FIRMWARE_SCREEN_DELAY)
684 self.send_enter_to_dut()
685
686 # For Alex/ZGB, there is a dev warning screen in text mode.
687 # Skip it by pressing Ctrl-D.
688 if need_dev_transition:
689 time.sleep(self.TEXT_SCREEN_DELAY)
690 self.send_ctrl_d_to_dut()
691
692
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800693 def wait_fw_screen_and_plug_usb(self):
694 """Wait for firmware warning screen and then unplug and plug the USB."""
Tom Wai-Hong Tama79574c2012-02-07 09:29:03 +0800695 time.sleep(self.USB_LOAD_DELAY)
Tom Wai-Hong Tam5d2f4702011-12-06 10:42:31 +0800696 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
697 time.sleep(self.USB_PLUG_DELAY)
698 self.servo.set('usb_mux_sel1', 'dut_sees_usbkey')
699
700
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800701 def wait_fw_screen_and_press_power(self):
702 """Wait for firmware warning screen and press power button."""
703 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +0800704 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800705
706
Tom Wai-Hong Tam4f5e5922012-07-27 16:23:15 +0800707 def wait_longer_fw_screen_and_press_power(self):
708 """Wait for firmware screen without timeout and press power button."""
709 time.sleep(self.DEV_SCREEN_TIMEOUT)
710 self.wait_fw_screen_and_press_power()
711
712
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800713 def wait_fw_screen_and_close_lid(self):
714 """Wait for firmware warning screen and close lid."""
715 time.sleep(self.FIRMWARE_SCREEN_DELAY)
716 self.servo.lid_close()
717
718
Tom Wai-Hong Tam473cfa72012-07-27 17:16:57 +0800719 def wait_longer_fw_screen_and_close_lid(self):
720 """Wait for firmware screen without timeout and close lid."""
721 time.sleep(self.FIRMWARE_SCREEN_DELAY)
722 self.wait_fw_screen_and_close_lid()
723
724
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800725 def setup_tried_fwb(self, tried_fwb):
726 """Setup for fw B tried state.
727
728 It makes sure the system in the requested fw B tried state. If not, it
729 tries to do so.
730
731 Args:
732 tried_fwb: True if requested in tried_fwb=1; False if tried_fwb=0.
733 """
734 if tried_fwb:
735 if not self.crossystem_checker({'tried_fwb': '1'}):
736 logging.info(
737 'Firmware is not booted with tried_fwb. Reboot into it.')
738 self.run_faft_step({
739 'userspace_action': self.faft_client.set_try_fw_b,
740 })
741 else:
742 if not self.crossystem_checker({'tried_fwb': '0'}):
743 logging.info(
744 'Firmware is booted with tried_fwb. Reboot to clear.')
745 self.run_faft_step({})
746
747
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800748 def enable_rec_mode_and_reboot(self):
749 """Switch to rec mode and reboot.
750
751 This method emulates the behavior of the old physical recovery switch,
752 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled
753 recovery mode, i.e. just press Power + Esc + Refresh.
754 """
Tom Wai-Hong Tamac943172012-08-01 10:38:39 +0800755 if self._customized_rec_reboot_command:
756 logging.info('running the customized rec reboot command')
757 os.system(self._customized_rec_reboot_command)
758 else:
759 self.servo.enable_recovery_mode()
760 self.cold_reboot()
761 time.sleep(self.EC_REBOOT_DELAY)
762 self.servo.disable_recovery_mode()
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800763
764
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800765 def enable_dev_mode_and_reboot(self):
766 """Switch to developer mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800767 if self.client_attr.keyboard_dev:
768 self.enable_keyboard_dev_mode()
769 else:
770 self.servo.enable_development_mode()
771 self.faft_client.run_shell_command(
772 'chromeos-firmwareupdate --mode todev && reboot')
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800773
774
Tom Wai-Hong Tam0b9e6d72012-07-31 20:54:06 +0800775 def enable_normal_mode_and_reboot(self):
776 """Switch to normal mode and reboot."""
Vic Yange7553162012-06-20 16:20:47 +0800777 if self.client_attr.keyboard_dev:
778 self.disable_keyboard_dev_mode()
779 else:
780 self.servo.disable_development_mode()
781 self.faft_client.run_shell_command(
782 'chromeos-firmwareupdate --mode tonormal && reboot')
783
784
785 def wait_fw_screen_and_switch_keyboard_dev_mode(self, dev):
786 """Wait for firmware screen and then switch into or out of dev mode.
787
788 Args:
789 dev: True if switching into dev mode. Otherwise, False.
790 """
791 time.sleep(self.FIRMWARE_SCREEN_DELAY)
792 if dev:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800793 self.send_ctrl_d_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800794 else:
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800795 self.send_enter_to_dut()
Tom Wai-Hong Tam1408f172012-07-31 15:06:21 +0800796 time.sleep(self.FIRMWARE_SCREEN_DELAY)
Tom Wai-Hong Tamfe314ac2012-07-25 14:14:17 +0800797 self.send_enter_to_dut()
Vic Yange7553162012-06-20 16:20:47 +0800798
799
800 def enable_keyboard_dev_mode(self):
801 logging.info("Enabling keyboard controlled developer mode")
Tom Wai-Hong Tamf1a17d72012-07-26 11:39:52 +0800802 # Plug out USB disk for preventing recovery boot without warning
803 self.servo.set('usb_mux_sel1', 'servo_sees_usbkey')
Vic Yange7553162012-06-20 16:20:47 +0800804 # Rebooting EC with rec mode on. Should power on AP.
Tom Wai-Hong Tama373f802012-07-31 21:16:48 +0800805 self.enable_rec_mode_and_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800806 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800807 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=True)
Vic Yange7553162012-06-20 16:20:47 +0800808
809
810 def disable_keyboard_dev_mode(self):
811 logging.info("Disabling keyboard controlled developer mode")
812 self.servo.disable_recovery_mode()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800813 self.cold_reboot()
Tom Wai-Hong Tam8c54eb82012-08-01 10:31:07 +0800814 self.wait_for_client_offline()
Vic Yange7553162012-06-20 16:20:47 +0800815 self.wait_fw_screen_and_switch_keyboard_dev_mode(dev=False)
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800816
817
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800818 def setup_dev_mode(self, dev_mode):
819 """Setup for development mode.
820
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800821 It makes sure the system in the requested normal/dev mode. If not, it
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800822 tries to do so.
823
824 Args:
825 dev_mode: True if requested in dev mode; False if normal mode.
826 """
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800827 # Change the default firmware_action for dev mode passing the fw screen.
828 self.register_faft_template({
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800829 'firmware_action': (self.wait_fw_screen_and_ctrl_d if dev_mode
830 else None),
831 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800832 if dev_mode:
Vic Yange7553162012-06-20 16:20:47 +0800833 if (not self.client_attr.keyboard_dev and
834 not self.crossystem_checker({'devsw_cur': '1'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800835 logging.info('Dev switch is not on. Now switch it on.')
836 self.servo.enable_development_mode()
837 if not self.crossystem_checker({'devsw_boot': '1',
838 'mainfw_type': 'developer'}):
839 logging.info('System is not in dev mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800840 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800841 'userspace_action': None if self.client_attr.keyboard_dev
842 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800843 'chromeos-firmwareupdate --mode todev && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800844 'reboot_action': self.enable_keyboard_dev_mode if
845 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800846 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800847 else:
Vic Yange7553162012-06-20 16:20:47 +0800848 if (not self.client_attr.keyboard_dev and
849 not self.crossystem_checker({'devsw_cur': '0'})):
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800850 logging.info('Dev switch is not off. Now switch it off.')
851 self.servo.disable_development_mode()
852 if not self.crossystem_checker({'devsw_boot': '0',
853 'mainfw_type': 'normal'}):
854 logging.info('System is not in normal mode. Reboot into it.')
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800855 self.run_faft_step({
Vic Yange7553162012-06-20 16:20:47 +0800856 'userspace_action': None if self.client_attr.keyboard_dev
857 else (self.faft_client.run_shell_command,
Tom Wai-Hong Tamc7ecfca2011-12-06 11:12:31 +0800858 'chromeos-firmwareupdate --mode tonormal && reboot'),
Vic Yange7553162012-06-20 16:20:47 +0800859 'reboot_action': self.disable_keyboard_dev_mode if
860 self.client_attr.keyboard_dev else None,
Tom Wai-Hong Tamfd590c92011-11-25 11:50:57 +0800861 })
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800862
863
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800864 def setup_kernel(self, part):
865 """Setup for kernel test.
866
867 It makes sure both kernel A and B bootable and the current boot is
868 the requested kernel part.
869
870 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800871 part: A string of kernel partition number or 'a'/'b'.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800872 """
873 self.ensure_kernel_boot(part)
874 self.copy_kernel_and_rootfs(from_part=part,
875 to_part=self.OTHER_KERNEL_MAP[part])
876 self.reset_and_prioritize_kernel(part)
877
878
879 def reset_and_prioritize_kernel(self, part):
880 """Make the requested partition highest priority.
881
882 This function also reset kerenl A and B to bootable.
883
884 Args:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +0800885 part: A string of partition number to be prioritized.
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800886 """
887 root_dev = self.faft_client.get_root_dev()
888 # Reset kernel A and B to bootable.
889 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
890 (self.KERNEL_MAP['a'], root_dev))
891 self.faft_client.run_shell_command('cgpt add -i%s -P1 -S1 -T0 %s' %
892 (self.KERNEL_MAP['b'], root_dev))
893 # Set kernel part highest priority.
894 self.faft_client.run_shell_command('cgpt prioritize -i%s %s' %
895 (self.KERNEL_MAP[part], root_dev))
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800896 # Safer to sync and wait until the cgpt status written to the disk.
897 self.faft_client.run_shell_command('sync')
898 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tamcfda61f2011-11-02 17:41:01 +0800899
900
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800901 def warm_reboot(self):
902 """Request a warm reboot.
903
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800904 A wrapper for underlying servo warm reset.
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800905 """
Tom Wai-Hong Tamb06f0802012-07-31 16:27:50 +0800906 # Use cold reset if the warm reset is broken.
907 if self.client_attr.broken_warm_reset:
908 self.servo.cold_reset()
909 else:
910 self.servo.warm_reset()
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800911
912
913 def cold_reboot(self):
914 """Request a cold reboot.
915
916 A wrapper for underlying servo cold reset.
917 """
918 if self.check_ec_capability():
919 # We don't use servo.cold_reset() here because software sync is
920 # not yet finished, and device may or may not come up after cold
921 # reset. Pressing power button before firmware comes up solves this.
922 #
923 # The correct behavior should be (not work now):
924 # - If rebooting EC with rec mode on, power on AP and it boots
925 # into recovery mode.
926 # - If rebooting EC with rec mode off, power on AP for software
927 # sync. Then AP checks if lid open or not. If lid open, continue;
928 # otherwise, shut AP down and need servo for a power button
929 # press.
930 self.servo.set('cold_reset', 'on')
931 self.servo.set('cold_reset', 'off')
932 time.sleep(self.POWER_BTN_DELAY)
933 self.servo.power_short_press()
934 else:
935 self.servo.cold_reset()
936
937
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800938 def sync_and_warm_reboot(self):
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800939 """Request the client sync and do a warm reboot.
940
941 This is the default reboot action on FAFT.
942 """
943 self.faft_client.run_shell_command('sync')
Tom Wai-Hong Tam6a863ba2011-12-08 10:13:28 +0800944 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800945 self.warm_reboot()
Tom Wai-Hong Tamf1e34972011-11-02 17:07:04 +0800946
947
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800948 def sync_and_cold_reboot(self):
949 """Request the client sync and do a cold reboot.
950
951 This reboot action is used to reset EC for recovery mode.
952 """
953 self.faft_client.run_shell_command('sync')
954 time.sleep(self.SYNC_DELAY)
Tom Wai-Hong Tam7ad99ab2012-07-30 19:30:51 +0800955 self.cold_reboot()
Tom Wai-Hong Tamb21b6b42012-07-26 10:46:30 +0800956
957
Vic Yang59cac9c2012-05-21 15:28:42 +0800958 def sync_and_ec_reboot(self):
959 """Request the client sync and do a EC triggered reboot."""
960 self.faft_client.run_shell_command('sync')
961 time.sleep(self.SYNC_DELAY)
962 self.faft_client.run_shell_command('(sleep %d; ectool reboot_ec)&' %
963 self.EC_REBOOT_DELAY)
Vic Yangf86728a2012-07-30 10:44:07 +0800964 time.sleep(self.EC_REBOOT_DELAY)
965 self.check_lid_and_power_on()
966
967
968 def check_lid_and_power_on(self):
969 """
970 On devices with EC software sync, system powers on after EC reboots if
971 lid is open. Otherwise, the EC shuts down CPU after about 3 seconds.
972 This method checks lid switch state and presses power button if
973 necessary.
974 """
975 if self.servo.get("lid_open") == "no":
976 time.sleep(self.SOFTWARE_SYNC_DELAY)
977 self.servo.power_short_press()
Vic Yang59cac9c2012-05-21 15:28:42 +0800978
979
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800980 def _modify_usb_kernel(self, usb_dev, from_magic, to_magic):
981 """Modify the kernel header magic in USB stick.
982
983 The kernel header magic is the first 8-byte of kernel partition.
984 We modify it to make it fail on kernel verification check.
985
986 Args:
987 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
988 from_magic: A string of magic which we change it from.
989 to_magic: A string of magic which we change it to.
990
991 Raises:
992 error.TestError: if failed to change magic.
993 """
994 assert len(from_magic) == 8
995 assert len(to_magic) == 8
Tom Wai-Hong Tama1d9a0f2011-12-23 09:13:33 +0800996 # USB image only contains one kernel.
997 kernel_part = self._join_part(usb_dev, self.KERNEL_MAP['a'])
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +0800998 read_cmd = "sudo dd if=%s bs=8 count=1 2>/dev/null" % kernel_part
999 current_magic = utils.system_output(read_cmd)
1000 if current_magic == to_magic:
1001 logging.info("The kernel magic is already %s." % current_magic)
1002 return
1003 if current_magic != from_magic:
1004 raise error.TestError("Invalid kernel image on USB: wrong magic.")
1005
1006 logging.info('Modify the kernel magic in USB, from %s to %s.' %
1007 (from_magic, to_magic))
1008 write_cmd = ("echo -n '%s' | sudo dd of=%s oflag=sync conv=notrunc "
1009 " 2>/dev/null" % (to_magic, kernel_part))
1010 utils.system(write_cmd)
1011
1012 if utils.system_output(read_cmd) != to_magic:
1013 raise error.TestError("Failed to write new magic.")
1014
1015
1016 def corrupt_usb_kernel(self, usb_dev):
1017 """Corrupt USB kernel by modifying its magic from CHROMEOS to CORRUPTD.
1018
1019 Args:
1020 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1021 """
1022 self._modify_usb_kernel(usb_dev, self.CHROMEOS_MAGIC,
1023 self.CORRUPTED_MAGIC)
1024
1025
1026 def restore_usb_kernel(self, usb_dev):
1027 """Restore USB kernel by modifying its magic from CORRUPTD to CHROMEOS.
1028
1029 Args:
1030 usb_dev: A string of USB stick path on the host, like '/dev/sdc'.
1031 """
1032 self._modify_usb_kernel(usb_dev, self.CORRUPTED_MAGIC,
1033 self.CHROMEOS_MAGIC)
1034
1035
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001036 def _call_action(self, action_tuple):
1037 """Call the action function with/without arguments.
1038
1039 Args:
1040 action_tuple: A function, or a tuple which consisted of a function
1041 and its arguments (if any).
1042
1043 Returns:
1044 The result value of the action function.
1045 """
1046 if isinstance(action_tuple, tuple):
1047 action = action_tuple[0]
1048 args = action_tuple[1:]
1049 if callable(action):
1050 logging.info('calling %s with parameter %s' % (
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001051 str(action), str(action_tuple[1])))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001052 return action(*args)
1053 else:
1054 logging.info('action is not callable!')
1055 else:
1056 action = action_tuple
1057 if action is not None:
1058 if callable(action):
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001059 logging.info('calling %s' % str(action))
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001060 return action()
1061 else:
1062 logging.info('action is not callable!')
1063
1064 return None
1065
1066
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001067 def run_shutdown_process(self, shutdown_action, pre_power_action=None,
1068 post_power_action=None):
1069 """Run shutdown_action(), which makes DUT shutdown, and power it on.
1070
1071 Args:
1072 shutdown_action: a function which makes DUT shutdown, like pressing
1073 power key.
1074 pre_power_action: a function which is called before next power on.
1075 post_power_action: a function which is called after next power on.
1076
1077 Raises:
1078 error.TestFail: if the shutdown_action() failed to turn DUT off.
1079 """
1080 self._call_action(shutdown_action)
1081 logging.info('Wait to ensure DUT shut down...')
1082 try:
1083 self.wait_for_client()
1084 raise error.TestFail(
1085 'Should shut the device down after calling %s.' %
1086 str(shutdown_action))
1087 except AssertionError:
1088 logging.info(
1089 'DUT is surely shutdown. We are going to power it on again...')
1090
1091 if pre_power_action:
1092 self._call_action(pre_power_action)
Tom Wai-Hong Tam610262a2012-01-12 14:16:53 +08001093 self.servo.power_short_press()
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001094 if post_power_action:
1095 self._call_action(post_power_action)
1096
1097
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001098 def register_faft_template(self, template):
1099 """Register FAFT template, the default FAFT_STEP of each step.
1100
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001101 Any missing field falls back to the original faft_template.
1102
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001103 Args:
1104 template: A FAFT_STEP dict.
1105 """
Tom Wai-Hong Tam109f63c2011-12-08 14:58:27 +08001106 self._faft_template.update(template)
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001107
1108
1109 def register_faft_sequence(self, sequence):
1110 """Register FAFT sequence.
1111
1112 Args:
1113 sequence: A FAFT_SEQUENCE array which consisted of FAFT_STEP dicts.
1114 """
1115 self._faft_sequence = sequence
1116
1117
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001118 def run_faft_step(self, step, no_reboot=False):
1119 """Run a single FAFT step.
1120
1121 Any missing field falls back to faft_template. An empty step means
1122 running the default faft_template.
1123
1124 Args:
1125 step: A FAFT_STEP dict.
1126 no_reboot: True to prevent running reboot_action and firmware_action.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001127
1128 Raises:
Tom Wai-Hong Tama9c1a502011-11-10 06:39:26 +08001129 error.TestFail: An error when the test failed.
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001130 error.TestError: An error when the given step is not valid.
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001131 """
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001132 FAFT_STEP_KEYS = ('state_checker', 'userspace_action', 'reboot_action',
1133 'firmware_action', 'install_deps_after_boot')
1134
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001135 test = {}
1136 test.update(self._faft_template)
1137 test.update(step)
1138
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001139 for key in test:
1140 if key not in FAFT_STEP_KEYS:
Tom Wai-Hong Tam78709592011-12-19 11:16:50 +08001141 raise error.TestError('Invalid key in FAFT step: %s', key)
Tom Wai-Hong Tamd8445dc2011-12-15 09:00:04 +08001142
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001143 if test['state_checker']:
1144 if not self._call_action(test['state_checker']):
1145 raise error.TestFail('State checker failed!')
1146
1147 self._call_action(test['userspace_action'])
1148
1149 # Don't run reboot_action and firmware_action if no_reboot is True.
1150 if not no_reboot:
1151 self._call_action(test['reboot_action'])
1152 self.wait_for_client_offline()
1153 self._call_action(test['firmware_action'])
1154
1155 if 'install_deps_after_boot' in test:
1156 self.wait_for_client(
1157 install_deps=test['install_deps_after_boot'])
1158 else:
1159 self.wait_for_client()
1160
1161
1162 def run_faft_sequence(self):
1163 """Run FAFT sequence which was previously registered."""
Tom Wai-Hong Tama70f0fe2011-09-02 18:28:47 +08001164 sequence = self._faft_sequence
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001165 index = 1
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001166 for step in sequence:
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001167 logging.info('======== Running FAFT sequence step %d ========' %
1168 index)
Tom Wai-Hong Tam2c50dff2011-11-11 07:01:01 +08001169 # Don't reboot in the last step.
1170 self.run_faft_step(step, no_reboot=(step is sequence[-1]))
Tom Wai-Hong Tame8f291a2011-12-08 22:03:53 +08001171 index += 1