| # Copyright (c) 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import re |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros.faft.firmware_test import FirmwareTest |
| |
| |
| class firmware_RecoveryCacheBootKeys(FirmwareTest): |
| """ |
| This test ensures that when booting to recovery mode the device will use the |
| cache instead training memory every boot. |
| """ |
| version = 1 |
| |
| USED_CACHE_MSG = ('MRC: Hash comparison successful. ' |
| 'Using data from RECOVERY_MRC_CACHE') |
| REBUILD_CACHE_MSG = "MRC: cache data 'RECOVERY_MRC_CACHE' needs update." |
| RECOVERY_CACHE_SECTION = 'RECOVERY_MRC_CACHE' |
| FIRMWARE_LOG_CMD = 'cbmem -1' + ' | grep ' + REBUILD_CACHE_MSG[:3] |
| FMAP_CMD = 'mosys eeprom map' |
| RECOVERY_REASON_REBUILD_CMD = 'crossystem recovery_request=0xC4' |
| |
| def initialize(self, host, cmdline_args, dev_mode=False): |
| super(firmware_RecoveryCacheBootKeys, self).initialize( |
| host, cmdline_args) |
| self.client = host |
| self.dev_mode = dev_mode |
| self.switcher.setup_mode('dev' if dev_mode else 'normal') |
| self.setup_usbkey(usbkey=True, host=False) |
| |
| def cleanup(self): |
| super(firmware_RecoveryCacheBootKeys, self).cleanup() |
| self.switcher.simple_reboot() |
| |
| def boot_to_recovery(self, rebuild_mrc_cache=False): |
| """Boots the device into recovery mode.""" |
| if rebuild_mrc_cache: |
| self.switcher.reboot_to_mode(to_mode='rec_force_mrc') |
| else: |
| self.switcher.reboot_to_mode(to_mode='rec') |
| |
| self.check_state((self.checkers.crossystem_checker, { |
| 'mainfw_type': 'recovery' |
| })) |
| |
| def run_command(self, command): |
| """Runs the specified command and returns the output |
| as a list of strings. |
| |
| @param command: The command to run on the DUT |
| @return A list of strings of the command output |
| """ |
| logging.info('Command to run: %s', command) |
| |
| output = self.faft_client.system.run_shell_command_get_output(command) |
| |
| logging.info('Command output: %s', output) |
| |
| return output |
| |
| def check_command_output(self, cmd, pattern): |
| """Checks the output of the given command for the given pattern |
| |
| @param cmd: The command to run |
| @param pattern: The pattern to search for |
| @return True if pattern found |
| """ |
| |
| logging.info('Checking %s output for %s', cmd, pattern) |
| |
| checker = re.compile(pattern) |
| |
| cmd_output = self.run_command(cmd) |
| |
| for line in cmd_output: |
| if re.search(checker, line): |
| return True |
| |
| return False |
| |
| def cache_exist(self): |
| """Checks the firmware log to ensure that the recovery cache exists. |
| |
| @return True if cache exists |
| """ |
| logging.info("Checking if device has RECOVERY_MRC_CACHE") |
| |
| return self.check_command_output(self.FMAP_CMD, |
| self.RECOVERY_CACHE_SECTION) |
| |
| def check_cache_used(self): |
| """Checks the firmware log to ensure that the recovery cache was used |
| during recovery boot. |
| |
| @return True if cache used |
| """ |
| logging.info('Checking if cache was used.') |
| |
| return self.check_command_output(self.FIRMWARE_LOG_CMD, |
| self.USED_CACHE_MSG) |
| |
| def check_cache_rebuilt(self): |
| """Checks the firmware log to ensure that the recovery cache was rebuilt |
| during recovery boot. |
| |
| @return True if cache rebuilt |
| """ |
| logging.info('Checking if cache was rebuilt.') |
| |
| return self.check_command_output(self.FIRMWARE_LOG_CMD, |
| self.REBUILD_CACHE_MSG) |
| |
| def run_once(self): |
| """Runs a single iteration of the test.""" |
| if not self.cache_exist(): |
| raise error.TestNAError('No RECOVERY_MRC_CACHE was found on DUT.') |
| |
| logging.info('Ensure we\'ve done memory training.') |
| self.boot_to_recovery() |
| |
| # With EFS, when the EC is in RO and we do a recovery boot, it |
| # causes the EC to do a soft reboot, which will in turn cause |
| # a PMIC reset (double reboot) on SKL/KBL power architectures. |
| # This causes us to lose the request to do MRC training for |
| # this test. The solution is to make sure that the EC is in |
| # RW before doing a recovery boot to ensure that the double |
| # reboot does not occur and information/requests are not lost. |
| self.switcher.simple_reboot('cold') |
| |
| logging.info('Checking recovery boot.') |
| self.boot_to_recovery() |
| |
| if not self.check_cache_used(): |
| raise error.TestFail('Recovery Cache was not used.') |
| |
| self.switcher.simple_reboot('cold') |
| |
| logging.info('Checking recovery boot with forced MRC cache training.') |
| self.boot_to_recovery(rebuild_mrc_cache=True) |
| self.switcher.wait_for_client() |
| |
| if not self.check_cache_rebuilt(): |
| raise error.TestFail('Recovery Cache was not rebuilt.') |
| |
| logging.info('Reboot out of Recovery') |
| self.switcher.simple_reboot() |