1# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging
6import time
7
8from autotest_lib.server.cros import vboot_constants as vboot
9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
10
11
12class firmware_RecoveryButton(FirmwareTest):
13    """
14    Servo based recovery button test.
15
16    This test requires a USB disk plugged-in, which contains a ChromeOS test
17    image (built by "build_image --test"). On runtime, this test emulates
18    recovery button pressed and reboots. It then triggers recovery mode in
19    two cases: (1) plug in the USB disk before power-on (2) plug in the USB
20    disk after power-on, ideally around the time when INSERT screen shows up.
21    Both cases should lead to successful recovery boot.
22    """
23    version = 1
24    NEEDS_SERVO_USB = True
25
26    def ensure_normal_boot(self):
27        """Ensure normal mode boot this time.
28
29        If not, it may be a test failure during step 2, try to recover to
30        normal mode by setting no recovery mode and rebooting the machine.
31        """
32        if not self.checkers.crossystem_checker({
33                'mainfw_type': ('normal', 'developer')
34        }):
35            self.servo.disable_recovery_mode()
36            self.switcher.mode_aware_reboot()
37
38    def check_recovery_state(self):
39        """Check if this is a recovery boot."""
40        self.check_state((self.checkers.crossystem_checker, {
41                'mainfw_type':
42                'recovery',
43                'recovery_reason':
44                vboot.RECOVERY_REASON['RO_MANUAL'],
45        }))
46
47    def initialize(self, host, cmdline_args, dev_mode=False, ec_wp=None):
48        super(firmware_RecoveryButton, self).initialize(
49                host, cmdline_args, ec_wp=ec_wp)
50        self.switcher.setup_mode('dev' if dev_mode else 'normal')
51        self.setup_usbkey(usbkey=True, host=False)
52
53    def cleanup(self):
54        try:
55            self.ensure_normal_boot()
56        except Exception as e:
57            logging.error("Caught exception: %s", str(e))
58        super(firmware_RecoveryButton, self).cleanup()
59
60    def run_once(self, dev_mode=False):
61        """Runs a single iteration of the test."""
62        logging.info("Reboot to recovery mode with the USB stick plugged.")
63        self.check_state((self.checkers.crossystem_checker, {
64                'mainfw_type': 'developer' if dev_mode else 'normal',
65        }))
66        self.switcher.reboot_to_mode(to_mode="rec")
67
68        logging.info("Expect a recovery boot from the USB stick.")
69        self.check_recovery_state()
70
71        logging.info("Power off, unplug the USB stick and reboot to rec mode.")
72        self.switcher.enable_rec_mode_and_reboot(usb_state='host')
73
74        logging.info("Plug in the USB stick after a delay")
75        time.sleep(self.faft_config.firmware_screen)
76        self.servo.switch_usbkey('dut')
77        logging.info("Expect a recovery boot from the USB stick.")
78        self.switcher.wait_for_client()
79        self.check_recovery_state()
80
81        logging.info("Reboot and then expected normal/dev boot.")
82        self.switcher.mode_aware_reboot()
83        is_jetstream = self.faft_config.mode_switcher_type == \
84                'jetstream_switcher'
85        self.check_state((self.checkers.crossystem_checker, {
86                'mainfw_type':
87                'developer' if dev_mode and not is_jetstream else 'normal',
88        }))
89