1# Copyright (c) 2012 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.client.common_lib import error 9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 10 11 12class firmware_ECBootTime(FirmwareTest): 13 """ 14 Servo based EC boot time test. 15 """ 16 version = 1 17 18 def initialize(self, host, cmdline_args): 19 super(firmware_ECBootTime, self).initialize(host, cmdline_args) 20 # Don't bother if there is no Chrome EC. 21 if not self.check_ec_capability(): 22 raise error.TestNAError("Nothing needs to be tested on this device") 23 # Only run in normal mode 24 self.switcher.setup_mode('normal') 25 self.host = host 26 27 def check_boot_time(self): 28 """Check EC and AP boot times""" 29 # Initialize a list of two strings, one printed by the EC when the AP 30 # is taken out of reset, and another one printed when the EC observes 31 # the AP running. These strings are used as for console output anchors 32 # when calculating the AP boot time. 33 # 34 # This is very approximate, a better long term solution would be to 35 # have the EC print the same fixed strings for these two events on all 36 # platforms. http://crosbug.com/p/21628 has been opened to track this 37 # issue. 38 if self._x86: 39 boot_anchors = ["\[([0-9\.]+) PB", 40 "\[([0-9\.]+) [^\r\n]*(HC 0x|Port 80|ACPI query)"] 41 elif self._arm_legacy: 42 boot_anchors = ["\[([0-9\.]+) AP running ...", 43 "\[([0-9\.]+) XPSHOLD seen"] 44 else: 45 boot_anchors = ["\[([0-9\.]+) power state 1 = S5", 46 "\[([0-9\.]+) power state 3 = S0"] 47 48 # regular expression to say that EC is ready. For systems that 49 # run out of ram there is a second boot where the PMIC is 50 # asked to power cycle the EC to be 100% sure (I wish) that 51 # the code is clean. Looking for the "Inits done" generates a 52 # match after the first boot, and introduces a race between 53 # the EC booting the second time and the test sending the 54 # power_cmd. 55 if self._doubleboot: 56 ec_ready = ["(?ms)UART.*UART.*?\[([0-9.]+) "] 57 else: 58 ec_ready = ["([0-9.]+) Inits done"] 59 60 if self.faft_config.ec_has_powerbtn_cmd: 61 # powerbtn takes ms while hold_pwr_button_powero is seconds. 62 hold_ms = int(1000 * self.faft_config.hold_pwr_button_poweron) 63 power_cmd = 'powerbtn %s' % hold_ms 64 else: 65 power_cmd = 'power on' 66 67 # Try the EC reboot command several times in case the console 68 # output is not clean enough for the full string to be found. 69 retry = 10 70 while retry > 0: 71 retry = retry - 1 72 try: 73 reboot = self.ec.send_command_get_output( 74 "reboot ap-off", ec_ready) 75 break 76 except error.TestFail: 77 logging.info("Unable to parse EC console output, " 78 "%d more attempts", retry) 79 if retry == 0: 80 raise error.TestFail("Unable to reboot EC cleanly, " + 81 "Please try removing AC power") 82 logging.debug("reboot: %r", reboot) 83 84 # The EC console must be available 1 second after startup 85 time.sleep(1) 86 87 version = self.ec.get_version() 88 89 if not version: 90 raise error.TestFail("Unable to get EC console.") 91 92 # Wait until the ap enter the G3 93 time.sleep(self.faft_config.ec_reboot_to_g3_delay) 94 95 # Enable printing host commands. Some boards have it disabled by default 96 # After reboot it will be restored to default 97 self.ec.send_command("hcdebug normal") 98 99 # Enable port80 output for x86 devices so there is an early signal from 100 # the host that it is booting, instead of relying on an EC transaction. 101 if self._x86: 102 self.ec.send_command("port80 intprint") 103 104 # Switch on the AP 105 power_press = self.ec.send_command_get_output( 106 power_cmd, boot_anchors) 107 108 # TODO(crbug.com/847289): reboot_time only measures the time spent in 109 # EC's main function, which is not a good measure of "EC cold boot time" 110 reboot_time = float(reboot[0][1]) 111 power_press_time = float(power_press[0][1]) 112 firmware_resp_time = float(power_press[1][1]) 113 boot_time = firmware_resp_time - power_press_time 114 logging.info("EC cold boot time: %f s", reboot_time) 115 if reboot_time > 1.0: 116 raise error.TestFail("EC cold boot time longer than 1 second.") 117 logging.info("EC boot time: %f s", boot_time) 118 if boot_time > 1.0: 119 raise error.TestFail("Boot time longer than 1 second.") 120 121 def is_arm_legacy_board(self): 122 """Detect whether the board is a legacy ARM board. 123 124 This group of boards prints specific strings on the EC console when the 125 EC and AP come out of reset. 126 """ 127 128 arm_legacy = ('snow', 'spring', 'pit', 'pi', 'big', 'blaze', 'kitty') 129 output = self.faft_client.system.get_platform_name() 130 return output.lower() in arm_legacy 131 132 def run_once(self): 133 """Execute the main body of the test. 134 """ 135 136 self._x86 = ('x86' in self.faft_config.ec_capability) 137 self._doubleboot = ('doubleboot' in self.faft_config.ec_capability) 138 self._arm_legacy = self.is_arm_legacy_board() 139 logging.info("Reboot and check EC cold boot time and host boot time.") 140 self.switcher.mode_aware_reboot('custom', self.check_boot_time) 141 142 def cleanup(self): 143 try: 144 # Restore the ec_uart_regexp to None 145 self.ec.set_uart_regexp('None') 146 147 # Reboot the EC and wait for the host to come up so it is ready for 148 # the next test. 149 self.ec.reboot() 150 self.host.wait_up(timeout=30) 151 except Exception as e: 152 logging.error("Caught exception: %s", str(e)) 153 super(firmware_ECBootTime, self).cleanup() 154