1# Copyright 2019 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
7import os
8
9from autotest_lib.client.common_lib import error
10from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
11
12
13class firmware_WilcoDiagnosticsMode(FirmwareTest):
14    """Corrupt the Wilco diagnostics image and then reinstall it.
15
16    Wilco supports entry into a diagnostics image from recovery mode. The image
17    is stored in the RW_LEGACY firmware section and updated during AP firmware
18    updates.  Entry into the image should fail if the image is corrupted.
19    Updating the firmware should restore the diagnostics image.
20    """
21    version = 1
22
23    # The delay between pressing <F12> to enter diagnostics mode and reaching
24    # the confirmation screen; typically about 10 seconds; overshoot to be safe.
25    DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS = 15
26    # The delay between pressing <Power> to confirm entry to diagnostics mode
27    # and rebooting into diagnostics mode.
28    DIAGNOSTICS_CONFIRM_REBOOT_DELAY_SECONDS = 8
29    # The delay between rebooting to enter diagnostics mode and rebooting again
30    # if that fails.
31    DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS = 8
32    # The name of the diagnostics image file in CBFS.
33    DIAG_CBFS_NAME = 'altfw/diag'
34
35    def initialize(self, host, cmdline_args):
36        super(firmware_WilcoDiagnosticsMode, self).initialize(
37                host, cmdline_args)
38
39        if not self.faft_config.has_diagnostics_image:
40            raise error.TestNAError('No diagnostics image for this board.')
41
42        self.setup_firmwareupdate_shellball(shellball=None)
43        # Make sure that the shellball is retained over subsequent power cycles.
44        self.blocking_sync()
45        self.switcher.setup_mode('normal')
46
47    def cleanup(self):
48        self._client.reset_via_servo()
49
50        super(firmware_WilcoDiagnosticsMode, self).cleanup()
51
52    def _corrupt_diagnostics_image(self):
53        # Extract the diagnostics image from the firmware image, corrupt the
54        # image, and write a new firmware image with that corrupt diagnostics
55        # image.
56        local_filename = 'diag.bin'
57        cbfs_work_dir = self.faft_client.updater.cbfs_setup_work_dir()
58        bios_cbfs_path = os.path.join(cbfs_work_dir,
59                self.faft_client.updater.get_bios_relative_path())
60        diag_cbfs_path = os.path.join(cbfs_work_dir, local_filename)
61
62        logging.info('Extracting diagnostics')
63        self.faft_client.updater.cbfs_extract_diagnostics(self.DIAG_CBFS_NAME,
64                diag_cbfs_path)
65
66        logging.info('Corrupting diagnostics')
67        self.faft_client.updater.corrupt_diagnostics_image(diag_cbfs_path)
68
69        logging.info('Replacing diagnostics')
70        self.faft_client.updater.cbfs_replace_diagnostics(self.DIAG_CBFS_NAME,
71                diag_cbfs_path)
72
73        logging.info('Writing back BIOS')
74        self.faft_client.bios.write_whole(bios_cbfs_path)
75        self.switcher.mode_aware_reboot()
76
77    def _press_f12(self):
78        self.servo.set_nocheck('arb_key_config', '<f12>')
79        self.servo.set_nocheck('arb_key', 'tab')
80
81    def _enter_diagnostics_mode(self):
82        # Reboot to the recovery screen, press <F12>, and press power to
83        # confirm.
84        logging.info('Rebooting to recovery screen')
85        self.switcher.enable_rec_mode_and_reboot(usb_state='host')
86        time.sleep(self.faft_config.firmware_screen)
87        logging.info('Pressing <F12>')
88        self._press_f12()
89        time.sleep(self.DIAGNOSTICS_CONFIRM_SCREEN_DELAY_SECONDS)
90        logging.info('Pressing <Power> to confirm')
91        self.servo.power_short_press()
92        # At this point, the DUT will try to reboot into diagnostics mode.
93
94    def run_once(self):
95        """Run the body of the test."""
96        logging.info('Attempting to enter diagnostics mode')
97        self._enter_diagnostics_mode()
98        # Wait long enough that DUT would have rebooted to normal mode if
99        # diagnostics mode failed.
100        time.sleep(self.DIAGNOSTICS_CONFIRM_REBOOT_DELAY_SECONDS +
101                self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS +
102                self.faft_config.delay_reboot_to_ping)
103        self.switcher.wait_for_client_offline(timeout=5)
104        logging.info('DUT offline after entering diagnostics mode')
105        self._client.reset_via_servo()
106        self.switcher.wait_for_client()
107
108        # Corrupt the diagnostics image, try to reboot into diagnostics mode,
109        # and verify that the DUT ends up in normal mode (indicating failure to
110        # enter diagnostics mode).
111        self._corrupt_diagnostics_image()
112        self._enter_diagnostics_mode()
113        self.switcher.wait_for_client()
114        self.checkers.mode_checker('normal')
115
116        # Update the firmware to restore the diagnostics image, reboot into
117        # diagnostics mode, and verify that the DUT goes down (indicating
118        # success).
119        logging.info('Updating firmware')
120        self.faft_client.updater.run_autoupdate(None)
121        logging.info('Rebooting to apply firmware update')
122        self.switcher.mode_aware_reboot()
123
124        logging.info('Attempting to enter diagnostics mode')
125        self._enter_diagnostics_mode()
126        # Wait long enough that DUT would have rebooted if diagnostics mode
127        # failed.
128        time.sleep(self.DIAGNOSTICS_CONFIRM_REBOOT_DELAY_SECONDS +
129                self.DIAGNOSTICS_FAIL_REBOOT_DELAY_SECONDS +
130                self.faft_config.delay_reboot_to_ping)
131        self.switcher.wait_for_client_offline(timeout=5)
132        logging.info('DUT offline after entering diagnostics mode')
133