1# Copyright (c) 2017 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
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
9
10
11class firmware_RecoveryCacheBootKeys(FirmwareTest):
12    """
13    This test ensures that when booting to recovery mode the device will use the
14    cache instead training memory every boot.
15    """
16    version = 1
17    NEEDS_SERVO_USB = True
18
19    # Message in older mrc_cache driver code
20    USED_CACHE_MSG1 = ('MRC: Hash comparison successful. '
21                       'Using data from RECOVERY_MRC_CACHE')
22    # Message in newer mrc_cache driver code
23    USED_CACHE_MSG2 = ('MRC: Hash idx 0x100b comparison successful.')
24    REBUILD_CACHE_MSG = "MRC: cache data 'RECOVERY_MRC_CACHE' needs update."
25    RECOVERY_CACHE_SECTION = 'RECOVERY_MRC_CACHE'
26    FIRMWARE_LOG_CMD = 'cbmem -1' + ' | grep ' + REBUILD_CACHE_MSG[:3]
27    RECOVERY_REASON_REBUILD_CMD = 'crossystem recovery_request=0xC4'
28
29    def initialize(self, host, cmdline_args, dev_mode=False):
30        super(firmware_RecoveryCacheBootKeys, self).initialize(
31                host, cmdline_args)
32
33        if not self.faft_config.rec_force_mrc:
34            raise error.TestNAError('DUT cannot force memory training.')
35
36        self.client = host
37        self.dev_mode = dev_mode
38        self.switcher.setup_mode('dev' if dev_mode else 'normal',
39                                 allow_gbb_force=True)
40        self.setup_usbkey(usbkey=True, host=False)
41
42    def cleanup(self):
43        super(firmware_RecoveryCacheBootKeys, self).cleanup()
44        self.switcher.simple_reboot()
45
46    def boot_to_recovery(self, rebuild_mrc_cache=False):
47        """Boots the device into recovery mode."""
48        if rebuild_mrc_cache:
49            self.switcher.reboot_to_mode(to_mode='rec_force_mrc')
50        else:
51            self.switcher.reboot_to_mode(to_mode='rec')
52
53        self.check_state((self.checkers.crossystem_checker, {
54                'mainfw_type': 'recovery'
55        }))
56
57    def cache_exist(self):
58        """Checks the firmware log to ensure that the recovery cache exists.
59
60        @return True if cache exists
61        """
62        logging.info("Checking if device has RECOVERY_MRC_CACHE")
63
64        # If flashrom can read the section, this means it exists.
65        command = ('flashrom -p host -r -i %s:/dev/null'
66                   % self.RECOVERY_CACHE_SECTION)
67
68        return self.faft_client.system.run_shell_command_check_output(
69            command, 'SUCCESS')
70
71    def check_cache_used(self):
72        """Checks the firmware log to ensure that the recovery cache was used
73        during recovery boot.
74
75        @return True if cache used
76        """
77        logging.info('Checking if cache was used.')
78
79        return (self.faft_client.system.run_shell_command_check_output(
80                self.FIRMWARE_LOG_CMD, self.USED_CACHE_MSG1)
81                or self.faft_client.system.run_shell_command_check_output(
82                        self.FIRMWARE_LOG_CMD, self.USED_CACHE_MSG2))
83
84    def check_cache_rebuilt(self):
85        """Checks the firmware log to ensure that the recovery cache was rebuilt
86        during recovery boot.
87
88        @return True if cache rebuilt
89        """
90        logging.info('Checking if cache was rebuilt.')
91
92        return self.faft_client.system.run_shell_command_check_output(
93                self.FIRMWARE_LOG_CMD, self.REBUILD_CACHE_MSG)
94
95    def run_once(self):
96        """Runs a single iteration of the test."""
97        if not self.cache_exist():
98            raise error.TestNAError('No RECOVERY_MRC_CACHE was found on DUT.')
99
100        logging.info('Ensure we\'ve done memory training.')
101        self.boot_to_recovery()
102
103        # With EFS, when the EC is in RO and we do a recovery boot, it
104        # causes the EC to do a soft reboot, which will in turn cause
105        # a PMIC reset (double reboot) on SKL/KBL power architectures.
106        # This causes us to lose the request to do MRC training for
107        # this test.  The solution is to make sure that the EC is in
108        # RW before doing a recovery boot to ensure that the double
109        # reboot does not occur and information/requests are not lost.
110        self.switcher.simple_reboot('cold')
111
112        logging.info('Checking recovery boot.')
113        self.boot_to_recovery()
114
115        if not self.check_cache_used():
116            raise error.TestFail('Recovery Cache was not used.')
117
118        self.switcher.simple_reboot('cold')
119
120        logging.info('Checking recovery boot with forced MRC cache training.')
121        self.boot_to_recovery(rebuild_mrc_cache=True)
122        self.switcher.wait_for_client()
123
124        if not self.check_cache_rebuilt():
125            raise error.TestFail('Recovery Cache was not rebuilt.')
126
127        logging.info('Reboot out of Recovery')
128        self.switcher.simple_reboot()
129